/** * @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 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(¶ms); params.mode = Semaphore_Mode_BINARY; if (kIsNull(obj->handle = ti_sysbios_knl_Semaphore_create(1, ¶ms, &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