blob: 4819bc50f8284ab75a78878cee3fe499bc6a90cf [file] [log] [blame]
/*
* Copyright (c) 2016, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* @brief
* This module contains routines and type definitions for managing reference
* counts.
*
* N.B. The functions defined here use the minimum fencing required for correct
* management of the reference count contract. No additional memory
* ordering should be assumed.
*/
#pragma once
//
// Architecture support macros.
// (Undefined at the bottom to avoid global namespace pollution)
//
#if defined(_WIN64)
#define RtlIncrementLongPtrNoFence InterlockedIncrementNoFence64
#define RtlDecrementLongPtrRelease InterlockedDecrementRelease64
#define RtlExchangeAddLongPtrNoFence InterlockedExchangeAddNoFence64
#define RtlExchangeAddLongPtrRelease InterlockedExchangeAddRelease64
#define RtlCompareExchangeLongPtrNoFence InterlockedCompareExchangeNoFence64
#define RtlCompareExchangeLongPtrRelease InterlockedCompareExchangeRelease64
#else
#define RtlIncrementLongPtrNoFence InterlockedIncrementNoFence
#define RtlDecrementLongPtrRelease InterlockedDecrementRelease
#define RtlExchangeAddLongPtrNoFence InterlockedExchangeAddNoFence
#define RtlExchangeAddLongPtrRelease InterlockedExchangeAddRelease
#define RtlCompareExchangeLongPtrNoFence InterlockedCompareExchangeNoFence
#define RtlCompareExchangeLongPtrRelease InterlockedCompareExchangeRelease
#endif
#if defined(_X86_) || defined(_AMD64_)
#define RtlBarrierAfterInterlock()
#elif defined(_ARM64_)
#define RtlBarrierAfterInterlock() __dmb(_ARM64_BARRIER_ISH)
#elif defined(_ARM_)
#define RtlBarrierAfterInterlock() __dmb(_ARM_BARRIER_ISH)
#else
#define Unsupported architecture.
#endif
#define RTL_REF_COUNT_INIT 1
FORCEINLINE
VOID
RtlInitializeReferenceCount (
_Out_ PRTL_REFERENCE_COUNT RefCount
)
/*++
Routine Description:
This function initializes a reference count to 1.
Arguments:
RefCount - Supplies a pointer to a reference count to initialize.
Return Value:
None.
--*/
{
*RefCount = RTL_REF_COUNT_INIT;
return;
}
FORCEINLINE
VOID
RtlInitializeReferenceCountEx (
_Out_ PRTL_REFERENCE_COUNT RefCount,
_In_ ULONG Bias
)
/*++
Routine Description:
This function initializes a reference count to a positive value.
Arguments:
RefCount - Supplies a pointer to a reference count to initialize.
Bias - Supplies an initial reference count (must be positive).
Return Value:
None.
--*/
{
*RefCount = Bias;
return;
}
FORCEINLINE
VOID
RtlIncrementReferenceCount (
_Inout_ PRTL_REFERENCE_COUNT RefCount
)
/*++
Routine Description:
This function increments the specified reference count, preventing object
deletion.
Arguments:
RefCount - Supplies a pointer to a reference count.
Return Value:
None.
--*/
{
if (RtlIncrementLongPtrNoFence(RefCount) > 1) {
return;
}
__fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
}
FORCEINLINE
VOID
RtlIncrementReferenceCountEx (
_Inout_ PRTL_REFERENCE_COUNT RefCount,
_In_ ULONG Bias
)
/*++
Routine Description:
This function increases the specified reference count by the specified bias,
preventing object deletion.
Arguments:
RefCount - Supplies a pointer to a reference count.
Bias - Supplies a reference bias amount.
Return Value:
None.
--*/
{
if (RtlExchangeAddLongPtrNoFence(RefCount, Bias) > 0) {
return;
}
__fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
}
FORCEINLINE
BOOLEAN
RtlIncrementReferenceCountNonZero (
_Inout_ volatile RTL_REFERENCE_COUNT *RefCount,
_In_ ULONG Bias
)
/*++
Routine Description:
This function increases the specified reference count by the specified bias,
unless the reference count was previously zero.
Arguments:
RefCount - Supplies a pointer to a reference count.
Bias - Supplies a reference bias amount.
Return Value:
TRUE if the reference count was incremented, FALSE otherwise.
--*/
{
RTL_REFERENCE_COUNT NewValue;
RTL_REFERENCE_COUNT OldValue;
PrefetchForWrite(RefCount);
OldValue = ReadLongPtrNoFence(RefCount);
for (;;) {
NewValue = OldValue + Bias;
if ((ULONG_PTR)NewValue > Bias) {
NewValue = RtlCompareExchangeLongPtrNoFence(RefCount,
NewValue,
OldValue);
if (NewValue == OldValue) {
return TRUE;
}
OldValue = NewValue;
} else if ((ULONG_PTR)NewValue == Bias) {
return FALSE;
} else {
__fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
}
}
}
FORCEINLINE
BOOLEAN
RtlDecrementReferenceCount (
_Inout_ PRTL_REFERENCE_COUNT RefCount
)
/*++
Routine Description:
This function reduces the specified reference count, potentially triggering
the destruction of the guarded object.
Arguments:
RefCount - Supplies a pointer to a reference count.
Return Value:
TRUE if the object should be destroyed, FALSE otherwise.
--*/
{
RTL_REFERENCE_COUNT NewValue;
//
// A release fence is required to ensure all guarded memory accesses are
// complete before any thread can begin destroying the object.
//
NewValue = RtlDecrementLongPtrRelease(RefCount);
if (NewValue > 0) {
return FALSE;
} else if (NewValue == 0) {
//
// An acquire fence is required before object destruction to ensure
// that the destructor cannot observe values changing on other threads.
//
RtlBarrierAfterInterlock();
return TRUE;
}
__fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
return FALSE;
}
FORCEINLINE
BOOLEAN
RtlDecrementReferenceCountEx (
_Inout_ PRTL_REFERENCE_COUNT RefCount,
_In_ ULONG Bias
)
/*++
Routine Description:
This function reduces the specified reference count by the specified amount,
potentially triggering the destruction of the guarded object.
Arguments:
RefCount - Supplies a pointer to a reference count.
Bias - Supplies a reference bias amount.
Return Value:
TRUE if the object should be destroyed, FALSE otherwise.
--*/
{
RTL_REFERENCE_COUNT NewValue;
//
// A release fence is required to ensure all guarded memory accesses are
// complete before any thread can begin destroying the object.
//
NewValue = RtlExchangeAddLongPtrRelease(RefCount, -(LONG)Bias) - Bias;
if (NewValue > 0) {
return FALSE;
} else if (NewValue == 0) {
//
// An acquire fence is required before object destruction to ensure
// that the destructor cannot observe values changing on other threads.
//
RtlBarrierAfterInterlock();
return TRUE;
}
__fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
return FALSE;
}
FORCEINLINE
BOOLEAN
RtlDecrementReferenceCountNonZero (
_Inout_ volatile RTL_REFERENCE_COUNT *RefCount,
_In_ ULONG Bias
)
/*++
Routine Description:
This function reduces the specified reference count by the specified amount,
unless doing so would result in a zero value.
Arguments:
RefCount - Supplies a pointer to a reference count.
Bias - Supplies a reference bias amount.
Return Value:
TRUE if the reference count would be zero, FALSE otherwise.
--*/
{
RTL_REFERENCE_COUNT NewValue;
RTL_REFERENCE_COUNT OldValue;
PrefetchForWrite(RefCount);
OldValue = ReadLongPtrNoFence(RefCount);
for (;;) {
NewValue = OldValue - Bias;
if (NewValue > 0) {
//
// A release fence is required to ensure all guarded memory
// accesses are complete before any thread can begin destroying
// the object.
//
NewValue = RtlCompareExchangeLongPtrRelease(RefCount,
NewValue,
OldValue);
if (NewValue == OldValue) {
return FALSE;
}
OldValue = NewValue;
} else if (NewValue == 0) {
return TRUE;
} else {
__fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
}
}
}
#undef RtlIncrementLongPtrNoFence
#undef RtlDecrementLongPtrRelease
#undef RtlExchangeAddLongPtrNoFence
#undef RtlExchangeAddLongPtrRelease
#undef RtlCompareExchangeLongPtrNoFence
#undef RtlCompareExchangeLongPtrRelease
#undef RtlBarrierAfterInterlock