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
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
|