2025-11-05 21:13:32 +01:00
|
|
|
/*
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
*
|
|
|
|
|
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
|
|
|
* copies or substantial portions of the Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
|
* SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
**********************************************************************************************************************/
|
|
|
|
|
/**
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
* @file palMutex.h
|
|
|
|
|
* @brief PAL utility collection Mutex and MutexAuto class declarations.
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "palAssert.h"
|
|
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
// WIN32_NO_STATUS prevents winnt.h from re-defining NTSTATUS macros that cause build warnings if
|
|
|
|
|
// ntstatus.h is also included.
|
|
|
|
|
#define WIN32_NO_STATUS
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#undef WIN32_NO_STATUS
|
|
|
|
|
#else
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace Util
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
* @brief Platform-agnostic mutex primitive.
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
*/
|
|
|
|
|
class Mutex
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
/// Defines MutexData as a Windows CRITICAL_SECTION
|
|
|
|
|
typedef CRITICAL_SECTION MutexData;
|
|
|
|
|
Mutex() noexcept : m_osMutex {} { InitializeCriticalSection(&m_osMutex); }
|
|
|
|
|
~Mutex() { DeleteCriticalSection(&m_osMutex); };
|
|
|
|
|
#else
|
|
|
|
|
/// Defines MutexData as a unix pthread_mutex_t
|
|
|
|
|
typedef pthread_mutex_t MutexData;
|
|
|
|
|
Mutex() noexcept : m_osMutex {} { pthread_mutex_init(&m_osMutex, nullptr); }
|
|
|
|
|
~Mutex() { pthread_mutex_destroy(&m_osMutex); };
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/// Enters the critical section if it is not contended. If it is contended, wait for the critical section to become
|
|
|
|
|
/// available, then enter it.
|
|
|
|
|
void Lock();
|
|
|
|
|
|
|
|
|
|
/// Enters the critical section if it is not contended. Does not wait for the critical section to become available
|
|
|
|
|
/// if it is contended.
|
|
|
|
|
///
|
|
|
|
|
/// @returns True if the critical section was entered, false otherwise.
|
|
|
|
|
bool TryLock();
|
|
|
|
|
|
|
|
|
|
/// Leaves the critical section.
|
|
|
|
|
void Unlock();
|
|
|
|
|
|
|
|
|
|
/// Returns the OS specific mutex data.
|
|
|
|
|
MutexData* GetMutexData() { return &m_osMutex; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
MutexData m_osMutex; ///< Opaque structure to the OS-specific Mutex data
|
|
|
|
|
|
|
|
|
|
PAL_DISALLOW_COPY_AND_ASSIGN(Mutex);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
* @brief A "resource acquisition is initialization" (RAII) wrapper for the Mutex class.
|
|
|
|
|
*
|
|
|
|
|
* The RAII paradigm allows critical sections to be automatically acquired during this class' constructor, and
|
|
|
|
|
* automatically released when a stack-allocated wrapper object goes out-of-scope. As such, it only makes sense to use
|
|
|
|
|
* this class for stack-allocated objects.
|
|
|
|
|
*
|
|
|
|
|
* This object will ensure that anything between when the object is allocated on the stack and when it goes out of scope
|
|
|
|
|
* will be protected from access by multiple threads. See the below example.
|
|
|
|
|
*
|
|
|
|
|
* [Code not protected]
|
|
|
|
|
* {
|
|
|
|
|
* [Code not protected]
|
|
|
|
|
* MutexAuto lock(pPtrToMutex);
|
|
|
|
|
* [Code is protected]
|
|
|
|
|
* }
|
|
|
|
|
* [Code not protected]
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
*/
|
|
|
|
|
class MutexAuto
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/// Locks the given Mutex.
|
|
|
|
|
explicit MutexAuto(Mutex* pMutex) : m_pMutex(pMutex)
|
|
|
|
|
{
|
|
|
|
|
PAL_ASSERT(m_pMutex != nullptr);
|
|
|
|
|
m_pMutex->Lock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Unlocks the Mutex we locked in the constructor.
|
|
|
|
|
~MutexAuto()
|
|
|
|
|
{
|
|
|
|
|
m_pMutex->Unlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Mutex* const m_pMutex; ///< The Mutex which this object wraps.
|
|
|
|
|
|
|
|
|
|
PAL_DISALLOW_DEFAULT_CTOR(MutexAuto);
|
|
|
|
|
PAL_DISALLOW_COPY_AND_ASSIGN(MutexAuto);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
* @brief Platform-agnostic rw lock primitive.
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
*/
|
|
|
|
|
class RWLock
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
/// Defines RWLockData as a Windows RWLOCK
|
|
|
|
|
typedef SRWLOCK RWLockData;
|
|
|
|
|
RWLock() noexcept : m_osRWLock {} { InitializeSRWLock(&m_osRWLock); }
|
|
|
|
|
~RWLock() noexcept { /* No Win32 destory function */ };
|
|
|
|
|
#else
|
|
|
|
|
/// Defines RWLockData as a unix pthread_rwlock_t
|
|
|
|
|
typedef pthread_rwlock_t RWLockData;
|
|
|
|
|
/// @note pthread_rwlock_init will not fail as called
|
|
|
|
|
RWLock() noexcept : m_osRWLock {} { pthread_rwlock_init(&m_osRWLock, nullptr); }
|
|
|
|
|
~RWLock() noexcept { pthread_rwlock_destroy(&m_osRWLock); };
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/// Enumerates the lock type of RWLockAuto
|
|
|
|
|
enum LockType
|
|
|
|
|
{
|
|
|
|
|
ReadOnly = 0, ///< Lock in readonly mode, in other words shared mode.
|
|
|
|
|
ReadWrite ///< Lock in readwrite mode, in other words exclusive mode.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Acquires a rw lock in shared mode if it is not contended in exclusive mode.
|
|
|
|
|
/// If it is contended, wait for rw lock to become available, then enter it.
|
|
|
|
|
void LockForRead();
|
|
|
|
|
|
|
|
|
|
/// Acquires a rw lock in exclusive mode if it is not contended.
|
|
|
|
|
/// If it is contended, wait for rw lock to become available, then enter it.
|
|
|
|
|
void LockForWrite();
|
|
|
|
|
|
|
|
|
|
/// Try to acquires a rw lock in shared mode if it is not contended in exclusive mode.
|
|
|
|
|
/// Does not wait for the rw lock to become available.
|
|
|
|
|
/// @returns True if the rw lock was acquired, false otherwise.
|
|
|
|
|
bool TryLockForRead();
|
|
|
|
|
|
|
|
|
|
/// Try to acquires a rw lock in exclusive mode if it is not contended.
|
|
|
|
|
/// Does not wait for the rw lock to become available.
|
|
|
|
|
/// @returns True if the rw lock was acquired, false otherwise.
|
|
|
|
|
bool TryLockForWrite();
|
|
|
|
|
|
|
|
|
|
/// Release the rw lock which is previously contended in shared mode.
|
|
|
|
|
void UnlockForRead();
|
|
|
|
|
|
|
|
|
|
/// Release the rw lock which is previously contended in exclusive mode.
|
|
|
|
|
void UnlockForWrite();
|
|
|
|
|
|
|
|
|
|
/// Returns the OS specific RWLOCK data.
|
|
|
|
|
RWLockData* GetRWLockData() { return &m_osRWLock; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
RWLockData m_osRWLock; ///< Opaque structure to the OS-specific RWLock data
|
|
|
|
|
|
|
|
|
|
PAL_DISALLOW_COPY_AND_ASSIGN(RWLock);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
* @brief A "resource acquisition is initialization" (RAII) wrapper for the RWLock class.
|
|
|
|
|
*
|
|
|
|
|
* The RAII paradigm allows rw lcok to be automatically acquired during this class' constructor, and
|
|
|
|
|
* automatically released when a stack-allocated wrapper object goes out-of-scope. As such, it only makes sense to use
|
|
|
|
|
* this class for stack-allocated objects.
|
|
|
|
|
*
|
|
|
|
|
* This object will ensure that anything between when the object is allocated on the stack and when it goes out of scope
|
|
|
|
|
* will be protected from access by multiple threads. See the below example.
|
|
|
|
|
*
|
|
|
|
|
* [Code not protected]
|
|
|
|
|
* {
|
|
|
|
|
* [Code not protected]
|
|
|
|
|
* RWLockAuto lock(pPtrToMutex, type);
|
|
|
|
|
* [Code is protected]
|
|
|
|
|
* }
|
|
|
|
|
* [Code not protected]
|
|
|
|
|
***********************************************************************************************************************
|
|
|
|
|
*/
|
|
|
|
|
template <RWLock::LockType type>
|
|
|
|
|
class RWLockAuto
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/// Locks the given RWLock.
|
|
|
|
|
explicit RWLockAuto(RWLock* pRWLock) : m_pRWLock(pRWLock)
|
|
|
|
|
{
|
|
|
|
|
PAL_ASSERT(m_pRWLock != nullptr);
|
|
|
|
|
if (type == RWLock::ReadOnly)
|
|
|
|
|
{
|
|
|
|
|
m_pRWLock->LockForRead();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_pRWLock->LockForWrite();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Unlocks the RWLock we locked in the constructor.
|
|
|
|
|
~RWLockAuto()
|
|
|
|
|
{
|
|
|
|
|
if (type == RWLock::ReadOnly)
|
|
|
|
|
{
|
|
|
|
|
m_pRWLock->UnlockForRead();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_pRWLock->UnlockForWrite();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
RWLock* const m_pRWLock; ///< The RWLock which this object wraps.
|
|
|
|
|
|
|
|
|
|
PAL_DISALLOW_DEFAULT_CTOR(RWLockAuto);
|
|
|
|
|
PAL_DISALLOW_COPY_AND_ASSIGN(RWLockAuto);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Yields the current thread to another thread in the ready state (if available).
|
|
|
|
|
extern void YieldThread();
|
|
|
|
|
|
|
|
|
|
/// Atomic write of 64-bit unsigned integer, using a relaxed memory ordering policy.
|
|
|
|
|
/// If you need to synchronize more than just pTarget, you may need a new function.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in] pTarget Pointer to the value to be read.
|
|
|
|
|
///
|
|
|
|
|
/// @returns The original value of *pTarget.
|
|
|
|
|
extern void AtomicWriteRelaxed64(volatile uint64* pTarget, uint64 newValue);
|
|
|
|
|
|
|
|
|
|
/// Atomic read of 64-bit unsigned integer, using a relaxed memory ordering policy.
|
|
|
|
|
/// If you need to synchronize more than just pTarget, you may need a new function.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in] pTarget Pointer to the value to be read.
|
|
|
|
|
///
|
|
|
|
|
/// @returns The original value of *pTarget.
|
|
|
|
|
extern uint64 AtomicReadRelaxed64(const volatile uint64* pTarget);
|
|
|
|
|
|
|
|
|
|
/// Atomically increments the specified 32-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pValue Pointer to the value to be incremented.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Result of the increment operation.
|
|
|
|
|
extern uint32 AtomicIncrement(volatile uint32* pValue);
|
|
|
|
|
|
|
|
|
|
/// Atomically increment a 64-bit-unsigned integer
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pAddend Pointer to the value to be incremented
|
|
|
|
|
///
|
|
|
|
|
/// @returns Result of the increment operation.
|
|
|
|
|
extern uint64 AtomicIncrement64(volatile uint64* pAddend);
|
|
|
|
|
|
|
|
|
|
/// Atomically decrements the specified 32-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pValue Pointer to the value to be decremented.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Result of the decrement operation.
|
|
|
|
|
extern uint32 AtomicDecrement(volatile uint32* pValue);
|
|
|
|
|
|
|
|
|
|
/// Atomically decrements the specified 64-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pValue Pointer to the value to be decremented.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Result of the decrement operation.
|
|
|
|
|
extern uint32 AtomicDecrement64(volatile uint64* pValue);
|
|
|
|
|
|
|
|
|
|
/// Performs an atomic compare and swap operation on two 32-bit unsigned integers. This operation compares *pTarget
|
|
|
|
|
/// with oldValue and replaces it with newValue if they match. If the values don't match, no action is taken.
|
|
|
|
|
/// The original value of *pTarget is returned as a result.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
|
|
|
|
/// @param [in] oldValue Value to compare *pTarget to.
|
|
|
|
|
/// @param [in] newValue Value to replace *pTarget with if *pTarget matches oldValue.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Previous value at *pTarget.
|
|
|
|
|
extern uint32 AtomicCompareAndSwap(volatile uint32* pTarget, uint32 oldValue, uint32 newValue);
|
|
|
|
|
|
|
|
|
|
/// Atomically exchanges a pair of 32-bit unsigned integers.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
|
|
|
|
/// @param [in] value New value to be stored in *pTarget.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Previous value at *pTarget.
|
|
|
|
|
extern uint32 AtomicExchange(volatile uint32* pTarget, uint32 value);
|
|
|
|
|
|
|
|
|
|
/// Atomically exchanges a pair of 64-bit unsigned integers.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
|
|
|
|
/// @param [in] value New value to be stored in *pTarget.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Previous value at *pTarget.
|
|
|
|
|
extern uint64 AtomicExchange64(volatile uint64* pTarget, uint64 value);
|
|
|
|
|
|
|
|
|
|
/// Atomically exchanges a pair of pointers.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] ppTarget Pointer to the address to exchange. The function sets the address pointed to by *ppTarget
|
|
|
|
|
/// to pValue.
|
|
|
|
|
/// @param [in] pValue New pointer to be stored in *ppTarget.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Previous value at *ppTarget.
|
|
|
|
|
extern void* AtomicExchangePointer(void*volatile* ppTarget, void* pValue);
|
|
|
|
|
|
|
|
|
|
/// Performs an atomic compare and swap operation on a pair of pointers. This operation compares *ppTarget
|
|
|
|
|
/// with pOldValue and replaces it with pNewValue if they match. If the values don't match, no action is taken.
|
|
|
|
|
/// The original value of *ppTarget is returned as a result.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] ppTarget Pointer to the destination value of the operation.
|
|
|
|
|
|
|
|
|
|
/// @param [in] pOldValue Old pointer to compare *ppTarget to.
|
|
|
|
|
/// @param [in] pNewValue New pointer to replace *ppTarget with if *ppTarget matches pOldValue.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Previous value at *ppTarget.
|
|
|
|
|
extern void* AtomicCompareExchangePointer(void*volatile* ppTarget, void* pOldValue, void* pNewValue);
|
|
|
|
|
|
|
|
|
|
/// Atomically add a value to the specific 32-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pAddend Pointer to the value to be modified.
|
|
|
|
|
/// @param [in] value Value to add to *pAddend.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Result of the add operation.
|
|
|
|
|
extern uint32 AtomicAdd(volatile uint32* pAddend, uint32 value);
|
|
|
|
|
|
|
|
|
|
/// Atomically add a value to the specified 64-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pAddend Pointer to the value to be modified.
|
|
|
|
|
/// @param [in] value Value to add to *pAddend.
|
|
|
|
|
///
|
|
|
|
|
/// @returns Result of the add operation.
|
|
|
|
|
extern uint64 AtomicAdd64(volatile uint64* pAddend, uint64 value);
|
|
|
|
|
|
|
|
|
|
/// Atomically OR a value to the specific 32-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pTarget Pointer to the value to be modified.
|
|
|
|
|
/// @param [in] value Value to OR to *pTarget.
|
|
|
|
|
///
|
|
|
|
|
/// @returns The original value of *pTarget.
|
|
|
|
|
extern uint32 AtomicOr(volatile uint32* pTarget, uint32 value);
|
|
|
|
|
|
|
|
|
|
/// Atomically OR a value to the specified 64-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pTarget Pointer to the value to be modified.
|
|
|
|
|
/// @param [in] value Value to OR to *pTarget.
|
|
|
|
|
///
|
|
|
|
|
/// @returns The original value of *pTarget.
|
|
|
|
|
extern uint64 AtomicOr64(volatile uint64* pTarget, uint64 value);
|
|
|
|
|
|
|
|
|
|
/// Atomically AND a value to the specific 32-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pTarget Pointer to the value to be modified.
|
|
|
|
|
/// @param [in] value Value to AND to *pTarget.
|
|
|
|
|
///
|
|
|
|
|
/// @returns The original value of *pTarget.
|
|
|
|
|
extern uint32 AtomicAnd(volatile uint32* pTarget, uint32 value);
|
|
|
|
|
|
|
|
|
|
/// Atomically AND a value to the specified 64-bit unsigned integer.
|
|
|
|
|
///
|
|
|
|
|
/// @param [in,out] pTarget Pointer to the value to be modified.
|
|
|
|
|
/// @param [in] value Value to AND to *pTarget.
|
|
|
|
|
///
|
|
|
|
|
/// @returns The original value of *pTarget.
|
|
|
|
|
extern uint64 AtomicAnd64(volatile uint64* pTarget, uint64 value);
|
|
|
|
|
|
|
|
|
|
} // Util
|