/* * 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; }