You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

891 lines
26 KiB

/*
* Copyright 2002-2019 Intel Corporation.
*
* This software and the related documents are Intel copyrighted materials, and your
* use of them is governed by the express license under which they were provided to
* you ("License"). Unless the License provides otherwise, you may not use, modify,
* copy, publish, distribute, disclose or transmit this software or the related
* documents without Intel's prior written permission.
*
* This software and the related documents are provided as is, with no express or
* implied warranties, other than those that are expressly stated in the License.
*/
// <COMPONENT>: pinvm
// <FILE-TYPE>: public header
#ifndef PINSYNC_HPP
#define PINSYNC_HPP
#include "os-apis.h"
#include <map>
#include <string>
namespace PINVM {
/*!
* Interface for generic lock
*/
class ILOCK /*<INTERFACE>*/
{
public:
/*!
* Destructor
*/
virtual ~ILOCK() {}
/*!
* Blocks the caller until the lock can be acquired.
*/
virtual void Lock() = 0;
/*!
* Releases the lock.
*/
virtual void Unlock() = 0;
/*!
* Attempts to acquire the lock, but does not block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
virtual bool TryLock() = 0;
};
/*!
* Basic non-recursive lock.
*/
class PINSYNC_LOCK /*<UTILITY>*/
{
public:
/*!
* The initial sate of the lock is "not locked".
*/
PINSYNC_LOCK() { OS_MutexInit(&_impl); }
/*!
* It is not necessary to call this method. It is provided only for symmetry.
*
* @return Always returns TRUE.
*/
bool Initialize() { OS_MutexInit(&_impl); return true; }
/*!
* Destroy a mutex
*/
void Destroy() { OS_MutexDestroy(&_impl); }
/*!
* Set the state of the lock to "not locked", even if the calling thread
* does not own the lock.
*/
void Reset() { OS_MutexUnlock(&_impl); }
/*!
* Blocks the caller until the lock can be acquired.
*/
void Lock() { OS_MutexLock(&_impl); }
/*!
* Releases the lock.
*/
void Unlock() { OS_MutexUnlock(&_impl); }
/*!
* Attempts to acquire the lock, but does not block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
bool TryLock() {return OS_MutexTryLock(&_impl); }
private:
OS_MUTEX_TYPE _impl;
};
/*!
* Basic non-recursive lock with POD semantics.
*/
typedef struct
{
public:
/*!
* It is not necessary to call this method. It is provided only for symmetry.
*
* @return Always returns TRUE.
*/
bool Initialize() { OS_MutexInit(&_impl); return true; }
/*!
* Destroy a mutex
*/
void Destroy() { OS_MutexDestroy(&_impl); }
/*!
* Set the state of the lock to "not locked", even if the calling thread
* does not own the lock.
*/
void Reset() { OS_MutexUnlock(&_impl); }
/*!
* Blocks the caller until the lock can be acquired.
*/
void Lock() { OS_MutexLock(&_impl); }
/*!
* Releases the lock.
*/
void Unlock() { OS_MutexUnlock(&_impl); }
/*!
* Attempts to acquire the lock, but does not block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
bool TryLock() {return OS_MutexTryLock(&_impl); }
OS_MUTEX_TYPE _impl;
} PINSYNC_POD_LOCK;
/*!
* Basic non-recursive lock with SAFEPOD semantics.
*/
typedef PINSYNC_POD_LOCK PINSYNC_SAFEPOD_LOCK;
/*!
* Read-writer lock.
*/
class PINSYNC_RWLOCK /*<UTILITY>*/
{
public:
/*!
* The new lock is initially not acquired
*/
PINSYNC_RWLOCK() { OS_RWLockInitialize(&_impl); }
/*!
* Destructor
*/
~PINSYNC_RWLOCK() { Destroy(); }
/*!
* It is not necessary to call this method. It is provided only for symmetry.
*
* @return Always returns TRUE.
*/
bool Initialize() { OS_RWLockInitialize(&_impl); return true;}
/*!
* Destroy a read-writer lock
*/
void Destroy() { OS_RWLockDestroy(&_impl); }
/*!
* Set the state of the lock to "not locked", even if the calling thread
* does not own the lock.
*/
void Reset() { Destroy(); Initialize(); }
/*!
* Acquire the lock for "read" access. Multiple "reader" threads may
* simultaneously acquire the lock.
*/
void ReadLock() { OS_RWLockAcquireRead(&_impl); }
/*!
* Acquire the lock for exclusive "write" access. A "writer" thread has
* exclusive ownership of the lock, not shared by any other "reader" or
* "writer" threads.
*/
void WriteLock() { OS_RWLockAcquireWrite(&_impl); }
/*!
* Release the lock. Used for both "readers" and "writers".
*/
void Unlock() { OS_RWLockRelease(&_impl); }
/*!
* Attempts to acquire the lock as a "reader" thread, but does not
* block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
bool TryReadLock() { return OS_RWLockTryAcquireRead(&_impl); }
/*!
* Attempts to acquire the lock as an exclusive "writer" thread, but
* does not block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
bool TryWriteLock() { return OS_RWLockTryAcquireWrite(&_impl); }
private:
OS_APIS_RW_LOCK_T _impl;
};
/*!
* Read-writer lock with POD semantics.
*/
typedef struct
{
public:
/*!
* It is not necessary to call this method. It is provided only for symmetry.
*
* @return Always returns TRUE.
*/
bool Initialize() { OS_RWLockInitialize(&_impl); return true;}
/*!
* Destroy a read-writer lock
*/
void Destroy() { OS_RWLockDestroy(&_impl); }
/*!
* Set the state of the lock to "not locked", even if the calling thread
* does not own the lock.
*/
void Reset() { OS_RWLockRelease(&_impl); }
/*!
* Acquire the lock for "read" access. Multiple "reader" threads may
* simultaneously acquire the lock.
*/
void ReadLock() { OS_RWLockAcquireRead(&_impl); }
/*!
* Acquire the lock for exclusive "write" access. A "writer" thread has
* exclusive ownership of the lock, not shared by any other "reader" or
* "writer" threads.
*/
void WriteLock() { OS_RWLockAcquireWrite(&_impl); }
/*!
* Release the lock. Used for both "readers" and "writers".
*/
void Unlock() { OS_RWLockRelease(&_impl); }
/*!
* Attempts to acquire the lock as a "reader" thread, but does not
* block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
bool TryReadLock() { return OS_RWLockTryAcquireRead(&_impl); }
/*!
* Attempts to acquire the lock as an exclusive "writer" thread, but
* does not block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
bool TryWriteLock() { return OS_RWLockTryAcquireWrite(&_impl); }
OS_APIS_RW_LOCK_T _impl;
} PINSYNC_POD_RWLOCK;
#ifdef CC_FAST_LOOKUP
/*!
* Recursive Read-writer lock.
*/
template <int MaxThreads>
class PINSYNC_RECURSIVE_RWLOCK /*<UTILITY>*/
{
public:
/*!
* The new lock is initially not acquired
*/
PINSYNC_RECURSIVE_RWLOCK()
{
_writer_tid = INVALID_NATIVE_TID;
_write_recursion_level = 0;
_read_recursion_level.ClearNonAtomic();
_impl.Initialize();
_mutex.Initialize();
_snapshots.clear();
_pausedInAtLeastOneThread = false;
_dependent_lock = NULL;
}
virtual ~PINSYNC_RECURSIVE_RWLOCK()
{
_impl.Destroy();
_mutex.Destroy();
}
/*!
* Define a hierarchy between this lock and another lock.
* This lock must not be acquired if the dependent lock is already locked.
*/
void SetDependentLock(PINSYNC_RECURSIVE_RWLOCK* dependent_lock)
{
_dependent_lock = dependent_lock;
}
/*!
* Set the state of the lock to "not locked", even if the calling thread
* does not own the lock.
*/
void Reset()
{
_writer_tid = INVALID_NATIVE_TID;
_write_recursion_level = 0;
_read_recursion_level.ClearNonAtomic();
_impl.Reset();
_mutex.Reset();
_snapshots.clear();
_pausedInAtLeastOneThread = false;
_dependent_lock = NULL;
}
/*!
* Acquire the lock for "read" access. Multiple "reader" threads may
* simultaneously acquire the lock.
* To prevent a thread from deadlocking on itself, only nested Read locks are allowed.
*/
virtual void ReadLock(NATIVE_TID tid)
{
int recursion_level;
ASSERTXSLOW( IsPaused(tid) == false );
ASSERTXSLOW( IsLegalLockChain(tid) );
ASSERTX( MayAcquireReadLock(tid, &recursion_level) );
if (recursion_level == 0)
_impl.ReadLock();
RegisterReadLock(tid);
}
/*!
* Acquire the lock for exclusive "write" access. A "writer" thread has
* exclusive ownership of the lock, not shared by any other "reader" or
* "writer" threads.
* To prevent a thread from deadlocking on itself, once a thread took a Write lock,
* and for as long as it is locked, it may not take another lock, neither Read nor Write.
*/
virtual void WriteLock(NATIVE_TID tid)
{
int recursion_level;
ASSERTXSLOW( IsPaused(tid) == false );
ASSERTXSLOW( IsLegalLockChain(tid) );
ASSERTX( MayAcquireWriteLock(tid, &recursion_level) );
if (recursion_level == 0)
_impl.WriteLock();
RegisterWriteLock(tid);
}
/*!
* Release the lock. Used for both "readers" and "writers".
*/
virtual void Unlock(NATIVE_TID tid)
{
ASSERTXSLOW( IsPaused(tid) == false );
ASSERTXSLOW( IsLegalLockChain(tid) );
Unlock(tid, true);
}
/*!
* Release the lock if it is currently locked. Used for both "readers" and "writers".
*/
virtual void UnlockIfLocked(NATIVE_TID tid)
{
ASSERTXSLOW( IsPaused(tid) == false );
ASSERTXSLOW( IsLegalLockChain(tid) );
Unlock(tid, false);
}
/*!
* Save the state of all locks (either reader and writer locks) acquired by the current thread,
then release these locks so other threads can acquire the lock.
The state of all locks in saved in a way that allows the current thread to later
re-claim the exact locks when calling ResumeLock().
*/
virtual void PauseLock(NATIVE_TID tid)
{
ASSERTXSLOW( IsPaused(tid) == false );
ASSERTXSLOW( IsLegalLockChain(tid) );
int* rd_recursion_ptr = _read_recursion_level.Find(tid);
int rd_recursion = (NULL==rd_recursion_ptr) ? 0 : *rd_recursion_ptr;
int wr_recursion = (_writer_tid == tid) ? _write_recursion_level : 0;
if ((rd_recursion==0) && (wr_recursion==0))
{
return;
}
_mutex.Lock();
typename SNAPSHOTS_MAP::iterator it_snp = _snapshots.find(tid);
ASSERTX(it_snp == _snapshots.end());
_snapshots.insert(std::make_pair(tid, new Snapshot(rd_recursion, wr_recursion)));
_pausedInAtLeastOneThread = true;
if (wr_recursion >= 1)
{
_write_recursion_level = 1;
if (rd_recursion_ptr != NULL)
*rd_recursion_ptr = 0;
}
else
{
if (rd_recursion_ptr != NULL)
*rd_recursion_ptr = 1;
}
_mutex.Unlock();
Unlock(tid);
}
/*!
* Restore the lock state saved by the call to PauseLock() and acquire the lock.
* If the lock had both active Write locks and Read locks then the Write lock takes precedence
* since only Write can be at the top of the lock chain.
*/
virtual void ResumeLock(NATIVE_TID tid)
{
ASSERTXSLOW( IsLegalLockChain(tid) );
_mutex.Lock();
typename SNAPSHOTS_MAP::iterator it_snp = _snapshots.find(tid);
if (it_snp == _snapshots.end())
{
_mutex.Unlock();
return;
}
int rd_recursion = (it_snp->second)->read_recursion_level;
int wr_recursion = (it_snp->second)->write_recursion_level;
_snapshots.erase(it_snp);
_pausedInAtLeastOneThread = _snapshots.size()>0;
_mutex.Unlock();
if (wr_recursion)
{
WriteLock(tid);
wr_recursion--;
}
else if (rd_recursion)
{
ReadLock(tid);
rd_recursion--;
}
for (int i=0; i < wr_recursion; i++)
RegisterWriteLock(tid);
for (int i=0; i < rd_recursion; i++)
RegisterReadLock(tid);
}
/*!
* Attempts to acquire the lock as a "reader" thread, but does not
* block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
virtual bool TryReadLock(NATIVE_TID tid)
{
int recursion_level;
ASSERTXSLOW( IsPaused(tid) == false );
ASSERTXSLOW( IsLegalLockChain(tid) );
ASSERTX( MayAcquireReadLock(tid, &recursion_level) );
if (recursion_level == 0)
if (! _impl.TryReadLock())
return false;
RegisterReadLock(tid);
return true;
}
/*!
* Attempts to acquire the lock as an exclusive "writer" thread, but
* does not block the caller.
*
* @return Returns TRUE if the lock is acquired, FALSE if not.
*/
virtual bool TryWriteLock(NATIVE_TID tid)
{
ASSERTXSLOW( IsPaused(tid) == false );
ASSERTXSLOW( IsLegalLockChain(tid) );
int recursion_level;
ASSERTX( MayAcquireWriteLock(tid, &recursion_level) );
if (recursion_level == 0)
if (! _impl.TryWriteLock())
return false;
RegisterWriteLock(tid);
return true;
}
/*!
* Returns TRUE if the thread 'tid' has acquired either a Read lock or Write lock.
*
* @return Returns TRUE if a Read/Write lock is acquired, FALSE if not.
*/
virtual bool IsLockedByThread(NATIVE_TID tid)
{
if (_writer_tid == tid)
{
return true;
}
else
{
int* recursion_level_ptr = _read_recursion_level.Find(tid);
return (NULL != recursion_level_ptr) && (*recursion_level_ptr > 0);
}
}
private:
void Unlock(NATIVE_TID tid, bool assert_is_locked)
{
int recursion_level=0;
// Unregister Read locks first because only Write can be at the top of the lock chain
bool lock_unregistered = UnregisterReadLock(tid, &recursion_level);
if (lock_unregistered )
{
if (recursion_level==0)
recursion_level = GetWriteRecursionLevel(tid);
}
else
{
lock_unregistered = UnregisterWriteLock(tid, &recursion_level);
}
ASSERTX( (assert_is_locked==false) || lock_unregistered );
if (lock_unregistered && (recursion_level==0))
_impl.Unlock();
}
/*!
* Checks the conditions for whether it is legal to take a Read lock for this thread.
* Taking a Read lock is always legal.
* Only the first lock in the recursion chain for the current thread will be physically taken.
* If the current thread has an active Write lock then implicitly no other thread will modify
* the data so the requirement for the Read lock is met.
* If the current thread has an active Read lock then it is legal to take another Read lock.
*
* @param[in] tid The thread Id
* @param[out] recursionLevel The current recursion level, read + write.
* @return Returns TRUE if may acquire Read lock, FALSE if not.
*/
bool MayAcquireReadLock(NATIVE_TID tid, int* recursionLevel)
{
int* recursion_level_ptr = _read_recursion_level.Find(tid);
*recursionLevel = (NULL == recursion_level_ptr) ? 0 : *recursion_level_ptr;
if (tid == _writer_tid)
*recursionLevel += _write_recursion_level;
return true;
}
/*!
* Checks the conditions for whether it is legal to take a Write lock for this thread.
* Taking a Write lock is legal only if this thread does not have an active Read physical lock.
* If the thread has an active Write lock, it is legal to take another Write lock.
*
* @param[in] tid The thread Id
* @param[out] recursionLevel The current recursion level, read + write.
* @return Returns TRUE if may acquire Write lock, FALSE if not.
*/
bool MayAcquireWriteLock(NATIVE_TID tid, int* recursionLevel)
{
*recursionLevel = 0;
int* recursion_level_ptr = _read_recursion_level.Find(tid);
if ((tid == _writer_tid) || (NULL == recursion_level_ptr) || (*recursion_level_ptr==0))
{
if (tid == _writer_tid)
*recursionLevel += _write_recursion_level;
if (NULL != recursion_level_ptr)
*recursionLevel += *recursion_level_ptr;
return true;
}
return false;
}
/*!
* Register a Read lock.
*/
void RegisterReadLock(NATIVE_TID tid)
{
int *recursion_level_ptr = _read_recursion_level.Find(tid);
if (NULL != recursion_level_ptr)
ATOMIC::OPS::Increment(recursion_level_ptr, 1);
else
_read_recursion_level.Add(tid, 1);
}
/*!
* Unregister a Read lock, if one exists for tid.
*
* @param[in] tid The thread Id
* @param[out] recursionLevel The Read recursion level after unregister, or 0 if there was no Read lock for tid.
*
* @return Return true if this tid had an active Read lock, false otherwise.
*/
bool UnregisterReadLock(NATIVE_TID tid, int* recursionLevel)
{
*recursionLevel = 0;
int *recursion_level_ptr = _read_recursion_level.Find(tid);
if (NULL != recursion_level_ptr)
{
*recursionLevel = ATOMIC::OPS::Increment(recursion_level_ptr, -1) - 1;
ASSERTX(*recursionLevel >= 0);
if (*recursionLevel == 0)
_read_recursion_level.Remove(tid);
return true;
}
return false;
}
/*!
* Register a Write lock.
*/
void RegisterWriteLock(NATIVE_TID tid)
{
_writer_tid = tid;
ATOMIC::OPS::Increment(&_write_recursion_level, 1);
}
/*!
* Unregister a Write lock, if one exists for tid.
*
* @param[in] tid The thread Id
* @param[out] recursionLevel The Write recursion level after unregister, or 0 if there was no Write lock for tid.
*
* @return Return true if this tid had an active Write lock, false otherwise.
*/
bool UnregisterWriteLock(NATIVE_TID tid, int* recursionLevel)
{
*recursionLevel = 0;
if (_writer_tid == tid)
{
*recursionLevel = ATOMIC::OPS::Increment(&_write_recursion_level, -1) - 1;
if (_write_recursion_level == 0)
{
_writer_tid = INVALID_NATIVE_TID;
}
return true;
}
return false;
}
/*!
* Get the current write recursion level for tid
*
* @param[in] tid The thread Id
*
* @return Return 0 if tid has no active write lock, otherwise returns the write lock recursion level.
*/
int GetWriteRecursionLevel(NATIVE_TID tid)
{
return (_writer_tid == tid) ? _write_recursion_level : 0;
}
/*!
* Checks whether the lock for tid is in paused state.
*
* @return Returns TRUE if the lock for tid is in paused state, FALSE otherwise.
*/
bool IsPaused(NATIVE_TID tid)
{
if (! _pausedInAtLeastOneThread)
return false;
_mutex.Lock();
typename SNAPSHOTS_MAP::const_iterator it = _snapshots.find(tid);
bool is_paused = (it != _snapshots.end());
_mutex.Unlock();
return is_paused;
}
/*!
* Checks whether the lock chain is legal.
* Conditions for a legal lock chain:
* 1. There is no dependency on another lock.
* 2. This lock is already locked. In this case it doesn't matter whether the dependent
* lock is locked or not because we know that we will not take another physical lock.
* 3. The dependent lock is not in locked state.
*
* @return Returns TRUE if the lock chain is legal, FALSE otherwise.
*/
bool IsLegalLockChain(NATIVE_TID tid)
{
if (_dependent_lock==NULL)
return true;
if (IsLockedByThread(tid))
return true;
if (_dependent_lock->IsLockedByThread(tid)==false)
return true;
return false;
}
PINSYNC_POD_RWLOCK _impl;
NATIVE_TID _writer_tid;
int _write_recursion_level;
typedef ATOMIC::FIXED_MULTIMAP<NATIVE_TID, int, INVALID_NATIVE_TID, NATIVE_TID_CURRENT, MaxThreads> READERS_MAP;
READERS_MAP _read_recursion_level;
PINSYNC_POD_LOCK _mutex;
struct Snapshot
{
int read_recursion_level;
int write_recursion_level;
Snapshot(int rd_rec, int wr_rec) : read_recursion_level(rd_rec), write_recursion_level(wr_rec) {}
};
typedef std::map<NATIVE_TID, Snapshot*> SNAPSHOTS_MAP;
SNAPSHOTS_MAP _snapshots;
bool _pausedInAtLeastOneThread;
PINSYNC_RECURSIVE_RWLOCK* _dependent_lock;
};
#endif
/*!
* Binary semaphore.
* This synchronization object works as a barrier.
*/
class PINSYNC_SEMAPHORE /*<UTILITY>*/
{
public:
/*!
* The initial state of the semaphore is "clear".
*/
PINSYNC_SEMAPHORE() { OS_MutexInit(&_impl); Clear(); }
/*!
* Destructor
*/
~PINSYNC_SEMAPHORE() { Destroy(); }
/*!
* It is not necessary to call this method. It is provided only for symmetry.
*
* @return Always returns TRUE.
*/
bool Initialize() { OS_MutexInit(&_impl); Clear(); return true; }
/*!
* Destroy a semaphore
*/
void Destroy() {OS_MutexDestroy(&_impl); }
/*!
* Change the semaphore to "set" state and tell any waiters in Wait() or
* TimedWait() to resume. Those threads are guaranteed to return from
* Wait() or TimedWait() only if the semaphore is still "set" when they
* actually do resume running.
*/
void Set() { _isSet = true; OS_MutexUnlock(&_impl); }
/*!
* Change the semaphore to "clear" state.
*/
void Clear() { _isSet = false; OS_MutexTryLock(&_impl); }
/*!
* Check whether the semaphore's state is "set". This method always returns
* immediately.
*
* @return TRUE if the state is "set".
*/
bool IsSet() { return _isSet; }
/*!
* Block the calling thread until the semaphore's state is "set". This
* method returns immediately if the state is already "set".
*/
void Wait() { OS_MutexLock(&_impl); OS_MutexUnlock(&_impl); }
/*!
* Block the calling thread until the semaphore's state is "set" or until
* a timeout expires. This method returns immediately if the state is
* already "set".
*
* @param[in] timeout Maximum number of milliseconds to wait.
*
* @return TRUE if the semaphore is in "set" state. FALSE if this method
* returns instead due to the timeout.
*/
bool TimedWait(unsigned timeout)
{
bool res = OS_MutexTimedLock(&_impl, timeout);
if (res) OS_MutexUnlock(&_impl);
return res;
}
private:
OS_MUTEX_TYPE _impl;
/*!
* This member field was introduced only to be used in the IsSet() method.
* Because the implementation of Wait() on a "set" PINSYNC_SEMAPHORE is to immediately lock
* and unlock the '_impl' mutex, IsSet() might return 'false' in the short time between
* the lock and unlock.
* Using this member field as a return values from IsSet() will return the right result in this case.
*/
volatile bool _isSet;
};
/*!
* Binary semaphore with POD semantics.
* This synchronization object works as a barrier.
*/
typedef struct
{
public:
/*!
* Initialize a semaphore
*
* @return Always returns TRUE.
*/
bool Initialize() { OS_MutexInit(&_impl); Clear(); return true; }
/*!
* Destroy a semaphore
*/
void Destroy() {OS_MutexDestroy(&_impl); Clear(); }
/*!
* Change the semaphore to "set" state and tell any waiters in Wait() or
* TimedWait() to resume. Those threads are guaranteed to return from
* Wait() or TimedWait() only if the semaphore is still "set" when they
* actually do resume running.
*/
void Set() { OS_MutexUnlock(&_impl); }
/*!
* Change the semaphore to "clear" state.
*/
void Clear() { OS_MutexTryLock(&_impl); }
/*!
* Check whether the semaphore's state is "set". This method always returns
* immediately.
*
* @return TRUE if the state is "set".
*/
bool IsSet() { return !OS_MutexIsLocked(&_impl); }
/*!
* Block the calling thread until the semaphore's state is "set". This
* method returns immediately if the state is already "set".
*/
void Wait() { OS_MutexLock(&_impl); OS_MutexUnlock(&_impl); }
/*!
* Block the calling thread until the semaphore's state is "set" or until
* a timeout expires. This method returns immediately if the state is
* already "set".
*
* @param[in] timeout Maximum number of milliseconds to wait.
*
* @return TRUE if the semaphore is in "set" state. FALSE if this method
* returns instead due to the timeout.
*/
bool TimedWait(unsigned timeout)
{
bool res = OS_MutexTimedLock(&_impl, timeout);
if (res) OS_MutexUnlock(&_impl);
return res;
}
OS_MUTEX_TYPE _impl;
} PINSYNC_POD_SEMAPHORE;
} // namespace
#endif // file guard