1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/include/ulib/utility/lock.h
stefanocasazza 1517b842c4 sync
2019-05-07 19:08:34 +02:00

236 lines
4.5 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// lock.h
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#ifndef ULIB_LOCK_H
#define ULIB_LOCK_H 1
#include <ulib/utility/semaphore.h>
class ULog;
class URDB;
class U_EXPORT ULock {
public:
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
ULock()
{
U_TRACE_CTOR(0, ULock, "")
sem = 0ULL;
}
~ULock()
{
U_TRACE_DTOR(0, ULock)
(void) reset();
}
// SERVICES
void lock()
{
U_TRACE_NO_PARAM(0, "ULock::lock()")
U_CHECK_MEMORY
if (isLocked() == false)
{
setLocked();
getPointerToSemaphore()->lock();
}
}
void unlock()
{
U_TRACE_NO_PARAM(0, "ULock::unlock()")
U_CHECK_MEMORY
if (isLocked())
{
getPointerToSemaphore()->unlock();
setUnLocked();
U_ASSERT_EQUALS(isLocked(), false)
}
}
void init(sem_t* ptr);
bool lock(time_t timeout);
// ATOMIC COUNTER
static void atomicIncrement(long* pvalue, long offset)
{
U_TRACE(0, "ULock::atomicIncrement(%p,%ld)", pvalue, offset)
# if defined(HAVE_GCC_ATOMICS) && defined(ENABLE_THREAD)
(void) __sync_add_and_fetch(pvalue, offset);
# else
*pvalue += offset;
# endif
}
static void atomicDecrement(long* pvalue, long offset)
{
U_TRACE(0, "ULock::atomicDecrement(%p,%ld)", pvalue, offset)
# if defined(HAVE_GCC_ATOMICS) && defined(ENABLE_THREAD)
(void) __sync_sub_and_fetch(pvalue, offset);
# else
*pvalue -= offset;
# endif
}
static void atomicIncrement(sig_atomic_t& value) { atomicIncrement((long*)&value, 1L); }
static void atomicDecrement(sig_atomic_t& value) { atomicDecrement((long*)&value, 1L); }
// SPIN LOCK
static bool spinlock(char* plock, uint32_t cnt)
{
U_TRACE(0+256, "ULock::spinlock(%p,%u)", plock, cnt)
do {
if (spinLockAcquire(plock)) U_RETURN(true);
}
while (cnt--);
U_RETURN(false);
}
// STREAM
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* dump(bool reset) const;
#endif
protected:
uint64_t sem;
bool reset()
{
U_TRACE_NO_PARAM(0, "ULock::reset()")
if (sem)
{
unlock();
U_DELETE(getPointerToSemaphore())
U_RETURN(true);
}
U_RETURN(false);
}
USemaphore* getPointerToSemaphore()
{
U_TRACE_NO_PARAM(0, "ULock::getPointerToSemaphore()")
USemaphore* psem = (USemaphore*)u_getPayload(sem);
U_INTERNAL_ASSERT_POINTER(psem)
U_RETURN_POINTER(psem, USemaphore);
}
// manage lock recursivity...
bool isLocked()
{
U_TRACE_NO_PARAM(0, "ULock::isLocked()")
U_INTERNAL_DUMP("u_getTag(%#llx) = %u", sem, u_getTag(sem))
if (u_getTag(sem) == U_TRUE_VALUE) U_RETURN(true);
U_RETURN(false);
}
void setLocked()
{
U_TRACE_NO_PARAM(0, "ULock::setLocked()")
u_setTag(U_TRUE_VALUE, &sem);
}
void setUnLocked()
{
U_TRACE_NO_PARAM(0, "ULock::setUnLocked()")
u_setTag(U_FALSE_VALUE, &sem);
}
// SPIN LOCK
static bool spinLockAcquire(char* ptr)
{
U_TRACE(0, "ULock::spinLockAcquire(%p)", ptr)
U_INTERNAL_ASSERT_POINTER(ptr)
// if not locked by another already, then we acquired it...
# if defined(HAVE_GCC_ATOMICS) && defined(ENABLE_THREAD)
if (__sync_lock_test_and_set(ptr, 1) == 0) U_RETURN(true);
# else
if (*ptr == 0)
{
*ptr = 1;
U_RETURN(true);
}
# endif
U_RETURN(false);
}
static void spinLockRelease(char* ptr)
{
U_TRACE(0, "ULock::spinLockRelease(%p)", ptr)
U_INTERNAL_ASSERT_POINTER(ptr)
# if defined(HAVE_GCC_ATOMICS) && defined(ENABLE_THREAD)
/**
* In theory __sync_lock_release should be used to release the lock.
* Unfortunately, it does not work properly alone. The workaround is
* that more conservative __sync_lock_test_and_set is used instead
*/
(void) __sync_lock_test_and_set(ptr, 0);
# else
*ptr = 0;
# endif
}
private:
U_DISALLOW_COPY_AND_ASSIGN(ULock)
friend class ULog;
friend class URDB;
};
#endif