Files
handleaiuinew/env/LMI/include/kApi/Threads/kLock.cpp
T

459 lines
10 KiB
C++
Raw Normal View History

2024-05-18 23:53:26 +08:00
/**
* @file kLock.cpp
* @brief Declares the kLock class.
*
* @internal
* Copyright (C) 2005-2021 by LMI Technologies Inc.
* Licensed under the MIT License.
* Redistributed files must retain the above copyright notice.
*/
#define K_PLATFORM
#include <kApi/Threads/kLock.h>
kBeginValueEx(k, kLockOption)
kAddEnumerator(kLockOption, kLOCK_OPTION_TIMEOUT)
kAddEnumerator(kLockOption, xkLOCK_OPTION_PRIORITY_INHERITANCE)
kEndValueEx()
kBeginClassEx(k, kLock)
kAddPrivateVMethod(kLock, kObject, VRelease)
kEndClassEx()
kFx(kStatus) kLock_Construct(kLock* lock, kAlloc allocator)
{
return kLock_ConstructEx(lock, kFALSE, allocator);
}
kFx(kStatus) kLock_ConstructEx(kLock* lock, kLockOption options, kAlloc allocator)
{
kAlloc alloc = kAlloc_Fallback(allocator);
kType type = kTypeOf(kLock);
kStatus status;
kCheck(kAlloc_GetObject(alloc, type, lock));
if (!kSuccess(status = xkLock_Init(*lock, options, type, alloc)))
{
kAlloc_FreeRef(alloc, lock);
}
return status;
}
#if defined(K_WINDOWS)
kFx(kStatus) xkLock_Init(kLock lock, kLockOption options, kType type, kAlloc allocator)
{
kObjR(kLock, lock);
kCheck(kObject_Init(lock, type, allocator));
obj->options = options;
kZero(obj->criticalSection);
kZero(obj->mutex);
//on Windows, critical sections are faster than mutexes; accordingly, we'll
//only use a mutex when the lock needs to support timeouts.
//priority inheritance isn't supported, but Windows supports random boosting by default
if ((obj->options & kLOCK_OPTION_TIMEOUT) != 0)
{
if (kIsNull(obj->mutex = CreateMutex(NULL, FALSE, NULL)))
{
kObject_VRelease(lock);
return kERROR_OS;
}
}
else
{
InitializeCriticalSection(&obj->criticalSection);
}
return kOK;
}
kFx(kStatus) xkLock_VRelease(kLock lock)
{
kObj(kLock, lock);
if ((obj->options & kLOCK_OPTION_TIMEOUT) != 0)
{
if (!kIsNull(obj->mutex))
{
CloseHandle(obj->mutex);
}
obj->mutex = kNULL;
}
else
{
DeleteCriticalSection(&obj->criticalSection);
}
kCheck(kObject_VRelease(lock));
return kOK;
}
kFx(kStatus) kLock_Enter(kLock lock)
{
kObj(kLock, lock);
if ((obj->options & kLOCK_OPTION_TIMEOUT) != 0)
{
DWORD waitResult = WaitForSingleObject(obj->mutex, kOS_INFINITE);
if (waitResult == WAIT_TIMEOUT)
{
return kERROR_TIMEOUT;
}
else if (waitResult != WAIT_OBJECT_0)
{
return kERROR_OS;
}
}
else
{
EnterCriticalSection(&obj->criticalSection);
}
return kOK;
}
kFx(kStatus) kLock_EnterEx(kLock lock, k64u timeout)
{
kObj(kLock, lock);
kAssert((obj->options & kLOCK_OPTION_TIMEOUT) != 0 || (timeout == kINFINITE));
if ((obj->options & kLOCK_OPTION_TIMEOUT) != 0)
{
DWORD osTimeout = (DWORD)xkTimeToKernelTime(timeout);
DWORD waitResult = WaitForSingleObject(obj->mutex, osTimeout);
if (waitResult == WAIT_TIMEOUT)
{
return kERROR_TIMEOUT;
}
else if (waitResult != WAIT_OBJECT_0)
{
return kERROR_OS;
}
}
else
{
EnterCriticalSection(&obj->criticalSection);
}
return kOK;
}
kFx(kStatus) kLock_Exit(kLock lock)
{
kObj(kLock, lock);
if ((obj->options & kLOCK_OPTION_TIMEOUT) != 0)
{
if (!ReleaseMutex(obj->mutex))
{
return kERROR_OS;
}
}
else
{
LeaveCriticalSection(&obj->criticalSection);
}
return kOK;
}
#elif defined(K_TI_BIOS)
//Note: this implementation does not support priority inheritance
kFx(kStatus) xkLock_Init(kLock lock, kLockOption options, kType type, kAlloc allocator)
{
kObjR(kLock, lock);
Semaphore_Params params;
xdc_runtime_Error_Block eb;
kCheck(kObject_Init(lock, type, allocator));
obj->owner = kNULL;
obj->handle = kNULL;
obj->count = 0;
obj->options = options;
xdc_runtime_Error_init(&eb);
ti_sysbios_knl_Semaphore_Params_init(&params);
params.mode = Semaphore_Mode_BINARY;
if (kIsNull(obj->handle = ti_sysbios_knl_Semaphore_create(1, &params, &eb)))
{
kObject_VRelease(lock);
return kERROR_OS;
}
return kOK;
}
kFx(kStatus) xkLock_VRelease(kLock lock)
{
kObj(kLock, lock);
if (!kIsNull(obj->handle))
{
ti_sysbios_knl_Semaphore_delete(&obj->handle);
}
kCheck(kObject_VRelease(lock));
return kOK;
}
kFx(kStatus) kLock_Enter(kLock lock)
{
kObj(kLock, lock);
ti_sysbios_knl_Task_Handle self = ti_sysbios_knl_Task_self();
//this isn't great general-purpose code; if it were intended to work on a wide variety of architectures,
//we would use kAtomicPointer for accessing obj->owner. however, the approach below is measurably faster
//and is essentially what TI does in the SYS/BIOS gate module. if it's good enough for TI, it's good enough for us.
if (obj->owner == self)
{
obj->count++;
return kOK;
}
else if (ti_sysbios_knl_Semaphore_pend(obj->handle, kOS_INFINITE))
{
obj->owner = self;
obj->count++;
return kOK;
}
else
{
return kERROR_TIMEOUT;
}
}
kFx(kStatus) kLock_EnterEx(kLock lock, k64u timeout)
{
kObj(kLock, lock);
k32u osTimeout = (k32u) xkTimeToKernelTime(timeout);
ti_sysbios_knl_Task_Handle self = ti_sysbios_knl_Task_self();
kAssert(((obj->options & kLOCK_OPTION_TIMEOUT) != 0) || (timeout == kINFINITE));
//this isn't great general-purpose code; if it were intended to work on a wide variety of architectures,
//we would use kAtomicPointer for accessing obj->owner. however, the approach below is measurably faster
//and is essentially what TI does in the SYS/BIOS gate module. if it's good enough for TI, it's good enough for us.
if (obj->owner == self)
{
obj->count++;
return kOK;
}
else if (ti_sysbios_knl_Semaphore_pend(obj->handle, osTimeout))
{
obj->owner = self;
obj->count++;
return kOK;
}
else
{
return kERROR_TIMEOUT;
}
}
kFx(kStatus) kLock_Exit(kLock lock)
{
kObj(kLock, lock);
kAssert(obj->count != 0);
obj->count--;
if (obj->count == 0)
{
obj->owner = kNULL;
ti_sysbios_knl_Semaphore_post(obj->handle);
}
return kOK;
}
#elif defined(K_VX_KERNEL)
kFx(kStatus) xkLock_Init(kLock lock, kLockOption options, kType type, kAlloc allocator)
{
kObjR(kLock, lock);
kCheck(kObject_Init(lock, type, allocator));
obj->options = options;
if ((options & xkLOCK_OPTION_PRIORITY_INHERITANCE) != 0)
{
obj->id = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);
}
else
{
obj->id = semMCreate(SEM_Q_FIFO);
}
if (obj->id == SEM_ID_NULL)
{
kObject_VRelease(lock);
return kERROR;
}
return kOK;
}
kFx(kStatus) xkLock_VRelease(kLock lock)
{
kObj(kLock, lock);
if (obj->id != SEM_ID_NULL)
{
semDelete(obj->id);
}
kCheck(kObject_VRelease(lock));
return kOK;
}
kFx(kStatus) kLock_Enter(kLock lock)
{
kObj(kLock, lock);
return (semMTake_inline(obj->id, kOS_INFINITE, SEM_NO_ID_VALIDATE | SEM_NO_ERROR_CHECK | SEM_NO_SYSTEM_VIEWER) == OK) ? kOK : kERROR_TIMEOUT;
}
kFx(kStatus) kLock_EnterEx(kLock lock, k64u timeout)
{
kObj(kLock, lock);
_Vx_ticks_t osTimeout = (_Vx_ticks_t) xkTimeToKernelTime(timeout);
kAssert(((obj->options & kLOCK_OPTION_TIMEOUT) != 0) || (timeout == kINFINITE));
return (semMTake_inline(obj->id, osTimeout, SEM_NO_ID_VALIDATE | SEM_NO_ERROR_CHECK | SEM_NO_SYSTEM_VIEWER) == OK) ? kOK : kERROR_TIMEOUT;
}
kFx(kStatus) kLock_Exit(kLock lock)
{
kObj(kLock, lock);
return (semMGive_inline(obj->id, SEM_NO_ID_VALIDATE | SEM_NO_ERROR_CHECK | SEM_NO_EVENT_SEND | SEM_NO_SYSTEM_VIEWER ) == OK) ? kOK : kERROR_OS;
}
#elif defined(K_POSIX)
kFx(kStatus) xkLock_Init(kLock lock, kLockOption options, kType type, kAlloc allocator)
{
kObjR(kLock, lock);
pthread_mutexattr_t mutexattr;
kBool attrInit = kFALSE;
kBool mutexInit = kFALSE;
kStatus status;
kCheck(kObject_Init(lock, type, allocator));
kZero(obj->mutex);
obj->options = options;
kTry
{
kTest(pthread_mutexattr_init(&mutexattr) == 0);
attrInit = kTRUE;
kTest(pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE) == 0);
if ((obj->options & xkLOCK_OPTION_PRIORITY_INHERITANCE) != 0)
{
kTest(pthread_mutexattr_setprotocol(&mutexattr, PTHREAD_PRIO_INHERIT) == 0);
}
kTest(pthread_mutex_init(&obj->mutex, &mutexattr) == 0);
mutexInit = kTRUE;
}
kCatchEx(&status)
{
if (mutexInit) pthread_mutex_destroy(&obj->mutex);
kObject_VRelease(lock);
kEndCatchEx(status);
}
kFinallyEx
{
if (attrInit) pthread_mutexattr_destroy(&mutexattr);
kEndFinallyEx();
}
return kOK;
}
kFx(kStatus) xkLock_VRelease(kLock lock)
{
kObj(kLock, lock);
kCheck(pthread_mutex_destroy(&obj->mutex) == 0);
kCheck(kObject_VRelease(lock));
return kOK;
}
kFx(kStatus) kLock_Enter(kLock lock)
{
kObj(kLock, lock);
kCheckTrue(pthread_mutex_lock(&obj->mutex) == 0, kERROR_OS);
return kOK;
}
kFx(kStatus) kLock_EnterEx(kLock lock, k64u timeout)
{
kObj(kLock, lock);
struct timespec tm;
int result = 0;
kAssert(((obj->options & kLOCK_OPTION_TIMEOUT) != 0) || (timeout == kINFINITE));
if (timeout == kINFINITE)
{
kCheckTrue(pthread_mutex_lock(&obj->mutex) == 0, kERROR_OS);
}
else
{
kCheck(xkFormatTimeout(timeout, &tm));
result = pthread_mutex_timedlock(&obj->mutex, &tm);
if (result == ETIMEDOUT)
{
return kERROR_TIMEOUT;
}
else if (result != 0)
{
return kERROR_OS;
}
}
return kOK;
}
kFx(kStatus) kLock_Exit(kLock lock)
{
kObj(kLock, lock);
kCheck(pthread_mutex_unlock(&obj->mutex) == 0);
return kOK;
}
#endif