541 lines
16 KiB
C
541 lines
16 KiB
C
/*
|
|
* Copyright (c) 2013-2019 Arm Limited. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
|
* not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* -----------------------------------------------------------------------------
|
|
*
|
|
* Project: CMSIS-RTOS RTX
|
|
* Title: Mutex functions
|
|
*
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "rtx_lib.h"
|
|
|
|
// OS Runtime Object Memory Usage
|
|
#if ((defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0)))
|
|
osRtxObjectMemUsage_t osRtxMutexMemUsage
|
|
__attribute__((section(".data.os.mutex.obj"))) = {0U, 0U, 0U};
|
|
#endif
|
|
|
|
// ==== Library functions ====
|
|
|
|
/// Release Mutex list when owner Thread terminates.
|
|
/// \param[in] mutex mutex object.
|
|
/// \return 1 - success, 0 - failure.
|
|
void osRtxMutexOwnerRelease(os_mutex_t *mutex_list) {
|
|
os_mutex_t *mutex;
|
|
os_mutex_t *mutex_next;
|
|
os_thread_t *thread;
|
|
|
|
mutex = mutex_list;
|
|
while (mutex != NULL) {
|
|
mutex_next = mutex->owner_next;
|
|
// Check if Mutex is Robust
|
|
if ((mutex->attr & osMutexRobust) != 0U) {
|
|
// Clear Lock counter
|
|
mutex->lock = 0U;
|
|
EvrRtxMutexReleased(mutex, 0U);
|
|
// Check if Thread is waiting for a Mutex
|
|
if (mutex->thread_list != NULL) {
|
|
// Wakeup waiting Thread with highest Priority
|
|
thread = osRtxThreadListGet(osRtxObject(mutex));
|
|
osRtxThreadWaitExit(thread, (uint32_t)osOK, FALSE);
|
|
// Thread is the new Mutex owner
|
|
mutex->owner_thread = thread;
|
|
mutex->owner_prev = NULL;
|
|
mutex->owner_next = thread->mutex_list;
|
|
if (thread->mutex_list != NULL) {
|
|
thread->mutex_list->owner_prev = mutex;
|
|
}
|
|
thread->mutex_list = mutex;
|
|
mutex->lock = 1U;
|
|
EvrRtxMutexAcquired(mutex, 1U);
|
|
}
|
|
}
|
|
mutex = mutex_next;
|
|
}
|
|
}
|
|
|
|
// ==== Service Calls ====
|
|
|
|
/// Create and Initialize a Mutex object.
|
|
/// \note API identical to osMutexNew
|
|
static osMutexId_t svcRtxMutexNew(const osMutexAttr_t *attr) {
|
|
os_mutex_t *mutex;
|
|
uint32_t attr_bits;
|
|
uint8_t flags;
|
|
const char *name;
|
|
|
|
// Process attributes
|
|
if (attr != NULL) {
|
|
name = attr->name;
|
|
attr_bits = attr->attr_bits;
|
|
// lint -e{9079} "conversion from pointer to void to pointer to other type"
|
|
// [MISRA Note 6]
|
|
mutex = attr->cb_mem;
|
|
if (mutex != NULL) {
|
|
// lint -e(923) -e(9078) "cast from pointer to unsigned int" [MISRA Note
|
|
// 7]
|
|
if ((((uint32_t)mutex & 3U) != 0U) ||
|
|
(attr->cb_size < sizeof(os_mutex_t))) {
|
|
EvrRtxMutexError(NULL, osRtxErrorInvalidControlBlock);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return NULL;
|
|
}
|
|
} else {
|
|
if (attr->cb_size != 0U) {
|
|
EvrRtxMutexError(NULL, osRtxErrorInvalidControlBlock);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return NULL;
|
|
}
|
|
}
|
|
} else {
|
|
name = NULL;
|
|
attr_bits = 0U;
|
|
mutex = NULL;
|
|
}
|
|
|
|
// Allocate object memory if not provided
|
|
if (mutex == NULL) {
|
|
if (osRtxInfo.mpi.mutex != NULL) {
|
|
// lint -e{9079} "conversion from pointer to void to pointer to other
|
|
// type" [MISRA Note 5]
|
|
mutex = osRtxMemoryPoolAlloc(osRtxInfo.mpi.mutex);
|
|
} else {
|
|
// lint -e{9079} "conversion from pointer to void to pointer to other
|
|
// type" [MISRA Note 5]
|
|
mutex = osRtxMemoryAlloc(osRtxInfo.mem.common, sizeof(os_mutex_t), 1U);
|
|
}
|
|
#if (defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0))
|
|
if (mutex != NULL) {
|
|
uint32_t used;
|
|
osRtxMutexMemUsage.cnt_alloc++;
|
|
used = osRtxMutexMemUsage.cnt_alloc - osRtxMutexMemUsage.cnt_free;
|
|
if (osRtxMutexMemUsage.max_used < used) {
|
|
osRtxMutexMemUsage.max_used = used;
|
|
}
|
|
}
|
|
#endif
|
|
flags = osRtxFlagSystemObject;
|
|
} else {
|
|
flags = 0U;
|
|
}
|
|
|
|
if (mutex != NULL) {
|
|
// Initialize control block
|
|
mutex->id = osRtxIdMutex;
|
|
mutex->flags = flags;
|
|
mutex->attr = (uint8_t)attr_bits;
|
|
mutex->name = name;
|
|
mutex->thread_list = NULL;
|
|
mutex->owner_thread = NULL;
|
|
mutex->owner_prev = NULL;
|
|
mutex->owner_next = NULL;
|
|
mutex->lock = 0U;
|
|
|
|
EvrRtxMutexCreated(mutex, mutex->name);
|
|
} else {
|
|
EvrRtxMutexError(NULL, (int32_t)osErrorNoMemory);
|
|
}
|
|
|
|
return mutex;
|
|
}
|
|
|
|
/// Get name of a Mutex object.
|
|
/// \note API identical to osMutexGetName
|
|
static const char *svcRtxMutexGetName(osMutexId_t mutex_id) {
|
|
os_mutex_t *mutex = osRtxMutexId(mutex_id);
|
|
|
|
// Check parameters
|
|
if ((mutex == NULL) || (mutex->id != osRtxIdMutex)) {
|
|
EvrRtxMutexGetName(mutex, NULL);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return NULL;
|
|
}
|
|
|
|
EvrRtxMutexGetName(mutex, mutex->name);
|
|
|
|
return mutex->name;
|
|
}
|
|
|
|
/// Acquire a Mutex or timeout if it is locked.
|
|
/// \note API identical to osMutexAcquire
|
|
static osStatus_t svcRtxMutexAcquire(osMutexId_t mutex_id, uint32_t timeout) {
|
|
os_mutex_t *mutex = osRtxMutexId(mutex_id);
|
|
os_thread_t *thread;
|
|
osStatus_t status;
|
|
|
|
// Check running thread
|
|
thread = osRtxThreadGetRunning();
|
|
if (thread == NULL) {
|
|
EvrRtxMutexError(mutex, osRtxErrorKernelNotRunning);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return osError;
|
|
}
|
|
|
|
// Check parameters
|
|
if ((mutex == NULL) || (mutex->id != osRtxIdMutex)) {
|
|
EvrRtxMutexError(mutex, (int32_t)osErrorParameter);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return osErrorParameter;
|
|
}
|
|
|
|
// Check if Mutex is not locked
|
|
if (mutex->lock == 0U) {
|
|
// Acquire Mutex
|
|
mutex->owner_thread = thread;
|
|
mutex->owner_prev = NULL;
|
|
mutex->owner_next = thread->mutex_list;
|
|
if (thread->mutex_list != NULL) {
|
|
thread->mutex_list->owner_prev = mutex;
|
|
}
|
|
thread->mutex_list = mutex;
|
|
mutex->lock = 1U;
|
|
EvrRtxMutexAcquired(mutex, mutex->lock);
|
|
status = osOK;
|
|
} else {
|
|
// Check if Mutex is recursive and running Thread is the owner
|
|
if (((mutex->attr & osMutexRecursive) != 0U) &&
|
|
(mutex->owner_thread == thread)) {
|
|
// Try to increment lock counter
|
|
if (mutex->lock == osRtxMutexLockLimit) {
|
|
EvrRtxMutexError(mutex, osRtxErrorMutexLockLimit);
|
|
status = osErrorResource;
|
|
} else {
|
|
mutex->lock++;
|
|
EvrRtxMutexAcquired(mutex, mutex->lock);
|
|
status = osOK;
|
|
}
|
|
} else {
|
|
// Check if timeout is specified
|
|
if (timeout != 0U) {
|
|
// Check if Priority inheritance protocol is enabled
|
|
if ((mutex->attr & osMutexPrioInherit) != 0U) {
|
|
// Raise priority of owner Thread if lower than priority of running
|
|
// Thread
|
|
if (mutex->owner_thread->priority < thread->priority) {
|
|
mutex->owner_thread->priority = thread->priority;
|
|
osRtxThreadListSort(mutex->owner_thread);
|
|
}
|
|
}
|
|
EvrRtxMutexAcquirePending(mutex, timeout);
|
|
// Suspend current Thread
|
|
if (osRtxThreadWaitEnter(osRtxThreadWaitingMutex, timeout)) {
|
|
osRtxThreadListPut(osRtxObject(mutex), thread);
|
|
} else {
|
|
EvrRtxMutexAcquireTimeout(mutex);
|
|
}
|
|
status = osErrorTimeout;
|
|
} else {
|
|
EvrRtxMutexNotAcquired(mutex);
|
|
status = osErrorResource;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/// Release a Mutex that was acquired by osMutexAcquire.
|
|
/// \note API identical to osMutexRelease
|
|
static osStatus_t svcRtxMutexRelease(osMutexId_t mutex_id) {
|
|
os_mutex_t *mutex = osRtxMutexId(mutex_id);
|
|
const os_mutex_t *mutex0;
|
|
os_thread_t *thread;
|
|
int8_t priority;
|
|
|
|
// Check running thread
|
|
thread = osRtxThreadGetRunning();
|
|
if (thread == NULL) {
|
|
EvrRtxMutexError(mutex, osRtxErrorKernelNotRunning);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return osError;
|
|
}
|
|
|
|
// Check parameters
|
|
if ((mutex == NULL) || (mutex->id != osRtxIdMutex)) {
|
|
EvrRtxMutexError(mutex, (int32_t)osErrorParameter);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return osErrorParameter;
|
|
}
|
|
|
|
// Check if Mutex is not locked
|
|
if (mutex->lock == 0U) {
|
|
EvrRtxMutexError(mutex, osRtxErrorMutexNotLocked);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return osErrorResource;
|
|
}
|
|
|
|
// Check if running Thread is not the owner
|
|
if (mutex->owner_thread != thread) {
|
|
EvrRtxMutexError(mutex, osRtxErrorMutexNotOwned);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return osErrorResource;
|
|
}
|
|
|
|
// Decrement Lock counter
|
|
mutex->lock--;
|
|
EvrRtxMutexReleased(mutex, mutex->lock);
|
|
|
|
// Check Lock counter
|
|
if (mutex->lock == 0U) {
|
|
|
|
// Remove Mutex from Thread owner list
|
|
if (mutex->owner_next != NULL) {
|
|
mutex->owner_next->owner_prev = mutex->owner_prev;
|
|
}
|
|
if (mutex->owner_prev != NULL) {
|
|
mutex->owner_prev->owner_next = mutex->owner_next;
|
|
} else {
|
|
thread->mutex_list = mutex->owner_next;
|
|
}
|
|
|
|
// Restore running Thread priority
|
|
if ((mutex->attr & osMutexPrioInherit) != 0U) {
|
|
priority = thread->priority_base;
|
|
mutex0 = thread->mutex_list;
|
|
while (mutex0 != NULL) {
|
|
// Mutexes owned by running Thread
|
|
if ((mutex0->thread_list != NULL) &&
|
|
(mutex0->thread_list->priority > priority)) {
|
|
// Higher priority Thread is waiting for Mutex
|
|
priority = mutex0->thread_list->priority;
|
|
}
|
|
mutex0 = mutex0->owner_next;
|
|
}
|
|
thread->priority = priority;
|
|
}
|
|
|
|
// Check if Thread is waiting for a Mutex
|
|
if (mutex->thread_list != NULL) {
|
|
// Wakeup waiting Thread with highest Priority
|
|
thread = osRtxThreadListGet(osRtxObject(mutex));
|
|
osRtxThreadWaitExit(thread, (uint32_t)osOK, FALSE);
|
|
// Thread is the new Mutex owner
|
|
mutex->owner_thread = thread;
|
|
mutex->owner_prev = NULL;
|
|
mutex->owner_next = thread->mutex_list;
|
|
if (thread->mutex_list != NULL) {
|
|
thread->mutex_list->owner_prev = mutex;
|
|
}
|
|
thread->mutex_list = mutex;
|
|
mutex->lock = 1U;
|
|
EvrRtxMutexAcquired(mutex, 1U);
|
|
}
|
|
|
|
osRtxThreadDispatch(NULL);
|
|
}
|
|
|
|
return osOK;
|
|
}
|
|
|
|
/// Get Thread which owns a Mutex object.
|
|
/// \note API identical to osMutexGetOwner
|
|
static osThreadId_t svcRtxMutexGetOwner(osMutexId_t mutex_id) {
|
|
os_mutex_t *mutex = osRtxMutexId(mutex_id);
|
|
|
|
// Check parameters
|
|
if ((mutex == NULL) || (mutex->id != osRtxIdMutex)) {
|
|
EvrRtxMutexGetOwner(mutex, NULL);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return NULL;
|
|
}
|
|
|
|
// Check if Mutex is not locked
|
|
if (mutex->lock == 0U) {
|
|
EvrRtxMutexGetOwner(mutex, NULL);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return NULL;
|
|
}
|
|
|
|
EvrRtxMutexGetOwner(mutex, mutex->owner_thread);
|
|
|
|
return mutex->owner_thread;
|
|
}
|
|
|
|
/// Delete a Mutex object.
|
|
/// \note API identical to osMutexDelete
|
|
static osStatus_t svcRtxMutexDelete(osMutexId_t mutex_id) {
|
|
os_mutex_t *mutex = osRtxMutexId(mutex_id);
|
|
const os_mutex_t *mutex0;
|
|
os_thread_t *thread;
|
|
int8_t priority;
|
|
|
|
// Check parameters
|
|
if ((mutex == NULL) || (mutex->id != osRtxIdMutex)) {
|
|
EvrRtxMutexError(mutex, (int32_t)osErrorParameter);
|
|
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
|
|
return osErrorParameter;
|
|
}
|
|
|
|
// Check if Mutex is locked
|
|
if (mutex->lock != 0U) {
|
|
|
|
thread = mutex->owner_thread;
|
|
|
|
// Remove Mutex from Thread owner list
|
|
if (mutex->owner_next != NULL) {
|
|
mutex->owner_next->owner_prev = mutex->owner_prev;
|
|
}
|
|
if (mutex->owner_prev != NULL) {
|
|
mutex->owner_prev->owner_next = mutex->owner_next;
|
|
} else {
|
|
thread->mutex_list = mutex->owner_next;
|
|
}
|
|
|
|
// Restore owner Thread priority
|
|
if ((mutex->attr & osMutexPrioInherit) != 0U) {
|
|
priority = thread->priority_base;
|
|
mutex0 = thread->mutex_list;
|
|
while (mutex0 != NULL) {
|
|
// Mutexes owned by running Thread
|
|
if ((mutex0->thread_list != NULL) &&
|
|
(mutex0->thread_list->priority > priority)) {
|
|
// Higher priority Thread is waiting for Mutex
|
|
priority = mutex0->thread_list->priority;
|
|
}
|
|
mutex0 = mutex0->owner_next;
|
|
}
|
|
if (thread->priority != priority) {
|
|
thread->priority = priority;
|
|
osRtxThreadListSort(thread);
|
|
}
|
|
}
|
|
|
|
// Unblock waiting threads
|
|
while (mutex->thread_list != NULL) {
|
|
thread = osRtxThreadListGet(osRtxObject(mutex));
|
|
osRtxThreadWaitExit(thread, (uint32_t)osErrorResource, FALSE);
|
|
}
|
|
|
|
osRtxThreadDispatch(NULL);
|
|
}
|
|
|
|
// Mark object as invalid
|
|
mutex->id = osRtxIdInvalid;
|
|
|
|
// Free object memory
|
|
if ((mutex->flags & osRtxFlagSystemObject) != 0U) {
|
|
if (osRtxInfo.mpi.mutex != NULL) {
|
|
(void)osRtxMemoryPoolFree(osRtxInfo.mpi.mutex, mutex);
|
|
} else {
|
|
(void)osRtxMemoryFree(osRtxInfo.mem.common, mutex);
|
|
}
|
|
#if (defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0))
|
|
osRtxMutexMemUsage.cnt_free++;
|
|
#endif
|
|
}
|
|
|
|
EvrRtxMutexDestroyed(mutex);
|
|
|
|
return osOK;
|
|
}
|
|
|
|
// Service Calls definitions
|
|
// lint ++flb "Library Begin" [MISRA Note 11]
|
|
SVC0_1(MutexNew, osMutexId_t, const osMutexAttr_t *)
|
|
SVC0_1(MutexGetName, const char *, osMutexId_t)
|
|
SVC0_2(MutexAcquire, osStatus_t, osMutexId_t, uint32_t)
|
|
SVC0_1(MutexRelease, osStatus_t, osMutexId_t)
|
|
SVC0_1(MutexGetOwner, osThreadId_t, osMutexId_t)
|
|
SVC0_1(MutexDelete, osStatus_t, osMutexId_t)
|
|
// lint --flb "Library End"
|
|
|
|
// ==== Public API ====
|
|
|
|
/// Create and Initialize a Mutex object.
|
|
osMutexId_t osMutexNew(const osMutexAttr_t *attr) {
|
|
osMutexId_t mutex_id;
|
|
|
|
EvrRtxMutexNew(attr);
|
|
if (IsIrqMode() || IsIrqMasked()) {
|
|
EvrRtxMutexError(NULL, (int32_t)osErrorISR);
|
|
mutex_id = NULL;
|
|
} else {
|
|
mutex_id = __svcMutexNew(attr);
|
|
}
|
|
return mutex_id;
|
|
}
|
|
|
|
/// Get name of a Mutex object.
|
|
const char *osMutexGetName(osMutexId_t mutex_id) {
|
|
const char *name;
|
|
|
|
if (IsIrqMode() || IsIrqMasked()) {
|
|
EvrRtxMutexGetName(mutex_id, NULL);
|
|
name = NULL;
|
|
} else {
|
|
name = __svcMutexGetName(mutex_id);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/// Acquire a Mutex or timeout if it is locked.
|
|
osStatus_t osMutexAcquire(osMutexId_t mutex_id, uint32_t timeout) {
|
|
osStatus_t status;
|
|
|
|
EvrRtxMutexAcquire(mutex_id, timeout);
|
|
if (IsIrqMode() || IsIrqMasked()) {
|
|
EvrRtxMutexError(mutex_id, (int32_t)osErrorISR);
|
|
status = osErrorISR;
|
|
} else {
|
|
status = __svcMutexAcquire(mutex_id, timeout);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/// Release a Mutex that was acquired by \ref osMutexAcquire.
|
|
osStatus_t osMutexRelease(osMutexId_t mutex_id) {
|
|
osStatus_t status;
|
|
|
|
EvrRtxMutexRelease(mutex_id);
|
|
if (IsIrqMode() || IsIrqMasked()) {
|
|
EvrRtxMutexError(mutex_id, (int32_t)osErrorISR);
|
|
status = osErrorISR;
|
|
} else {
|
|
status = __svcMutexRelease(mutex_id);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/// Get Thread which owns a Mutex object.
|
|
osThreadId_t osMutexGetOwner(osMutexId_t mutex_id) {
|
|
osThreadId_t thread;
|
|
|
|
if (IsIrqMode() || IsIrqMasked()) {
|
|
EvrRtxMutexGetOwner(mutex_id, NULL);
|
|
thread = NULL;
|
|
} else {
|
|
thread = __svcMutexGetOwner(mutex_id);
|
|
}
|
|
return thread;
|
|
}
|
|
|
|
/// Delete a Mutex object.
|
|
osStatus_t osMutexDelete(osMutexId_t mutex_id) {
|
|
osStatus_t status;
|
|
|
|
EvrRtxMutexDelete(mutex_id);
|
|
if (IsIrqMode() || IsIrqMasked()) {
|
|
EvrRtxMutexError(mutex_id, (int32_t)osErrorISR);
|
|
status = osErrorISR;
|
|
} else {
|
|
status = __svcMutexDelete(mutex_id);
|
|
}
|
|
return status;
|
|
}
|