From 48026bb824fd2d9cfb00ecd040db6ef3a416bae9 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Fri, 22 Jan 2021 21:43:36 -0500 Subject: upload initial port --- ChibiOS_20.3.2/os/rt/src/chmtx.c | 537 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 537 insertions(+) create mode 100644 ChibiOS_20.3.2/os/rt/src/chmtx.c (limited to 'ChibiOS_20.3.2/os/rt/src/chmtx.c') diff --git a/ChibiOS_20.3.2/os/rt/src/chmtx.c b/ChibiOS_20.3.2/os/rt/src/chmtx.c new file mode 100644 index 0000000..e7744f1 --- /dev/null +++ b/ChibiOS_20.3.2/os/rt/src/chmtx.c @@ -0,0 +1,537 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file rt/src/chmtx.c + * @brief Mutexes code. + * + * @addtogroup mutexes + * @details Mutexes related APIs and services. + *

Operation mode

+ * A mutex is a threads synchronization object that can be in two + * distinct states: + * - Not owned (unlocked). + * - Owned by a thread (locked). + * . + * Operations defined for mutexes: + * - Lock: The mutex is checked, if the mutex is not owned by + * some other thread then it is associated to the locking thread + * else the thread is queued on the mutex in a list ordered by + * priority. + * - Unlock: The mutex is released by the owner and the highest + * priority thread waiting in the queue, if any, is resumed and made + * owner of the mutex. + * . + *

Constraints

+ * In ChibiOS/RT the Unlock operations must always be performed + * in lock-reverse order. This restriction both improves the + * performance and is required for an efficient implementation + * of the priority inheritance mechanism.
+ * Operating under this restriction also ensures that deadlocks + * are no possible. + * + *

Recursive mode

+ * By default mutexes are not recursive, this mean that it is not + * possible to take a mutex already owned by the same thread. + * It is possible to enable the recursive behavior by enabling the + * option @p CH_CFG_USE_MUTEXES_RECURSIVE. + * + *

The priority inversion problem

+ * The mutexes in ChibiOS/RT implements the full priority + * inheritance mechanism in order handle the priority inversion + * problem.
+ * When a thread is queued on a mutex, any thread, directly or + * indirectly, holding the mutex gains the same priority of the + * waiting thread (if their priority was not already equal or higher). + * The mechanism works with any number of nested mutexes and any + * number of involved threads. The algorithm complexity (worst case) + * is N with N equal to the number of nested mutexes. + * @pre In order to use the mutex APIs the @p CH_CFG_USE_MUTEXES option + * must be enabled in @p chconf.h. + * @post Enabling mutexes requires 5-12 (depending on the architecture) + * extra bytes in the @p thread_t structure. + * @{ + */ + +#include "ch.h" + +#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/** + * @brief Initializes s @p mutex_t structure. + * + * @param[out] mp pointer to a @p mutex_t structure + * + * @init + */ +void chMtxObjectInit(mutex_t *mp) { + + chDbgCheck(mp != NULL); + + queue_init(&mp->queue); + mp->owner = NULL; +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + mp->cnt = (cnt_t)0; +#endif +} + +/** + * @brief Locks the specified mutex. + * @post The mutex is locked and inserted in the per-thread stack of owned + * mutexes. + * + * @param[in] mp pointer to the @p mutex_t structure + * + * @api + */ +void chMtxLock(mutex_t *mp) { + + chSysLock(); + chMtxLockS(mp); + chSysUnlock(); +} + +/** + * @brief Locks the specified mutex. + * @post The mutex is locked and inserted in the per-thread stack of owned + * mutexes. + * + * @param[in] mp pointer to the @p mutex_t structure + * + * @sclass + */ +void chMtxLockS(mutex_t *mp) { + thread_t *ctp = currp; + + chDbgCheckClassS(); + chDbgCheck(mp != NULL); + + /* Is the mutex already locked? */ + if (mp->owner != NULL) { +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + + chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive"); + + /* If the mutex is already owned by this thread, the counter is increased + and there is no need of more actions.*/ + if (mp->owner == ctp) { + mp->cnt++; + } + else { +#endif + /* Priority inheritance protocol; explores the thread-mutex dependencies + boosting the priority of all the affected threads to equal the + priority of the running thread requesting the mutex.*/ + thread_t *tp = mp->owner; + + /* Does the running thread have higher priority than the mutex + owning thread? */ + while (tp->prio < ctp->prio) { + /* Make priority of thread tp match the running thread's priority.*/ + tp->prio = ctp->prio; + + /* The following states need priority queues reordering.*/ + switch (tp->state) { + case CH_STATE_WTMTX: + /* Re-enqueues the mutex owner with its new priority.*/ + queue_prio_insert(queue_dequeue(tp), &tp->u.wtmtxp->queue); + tp = tp->u.wtmtxp->owner; + /*lint -e{9042} [16.1] Continues the while.*/ + continue; +#if (CH_CFG_USE_CONDVARS == TRUE) || \ + ((CH_CFG_USE_SEMAPHORES == TRUE) && \ + (CH_CFG_USE_SEMAPHORES_PRIORITY == TRUE)) || \ + ((CH_CFG_USE_MESSAGES == TRUE) && \ + (CH_CFG_USE_MESSAGES_PRIORITY == TRUE)) +#if CH_CFG_USE_CONDVARS == TRUE + case CH_STATE_WTCOND: +#endif +#if (CH_CFG_USE_SEMAPHORES == TRUE) && \ + (CH_CFG_USE_SEMAPHORES_PRIORITY == TRUE) + case CH_STATE_WTSEM: +#endif +#if (CH_CFG_USE_MESSAGES == TRUE) && (CH_CFG_USE_MESSAGES_PRIORITY == TRUE) + case CH_STATE_SNDMSGQ: +#endif + /* Re-enqueues tp with its new priority on the queue.*/ + queue_prio_insert(queue_dequeue(tp), &tp->u.wtmtxp->queue); + break; +#endif + case CH_STATE_READY: +#if CH_DBG_ENABLE_ASSERTS == TRUE + /* Prevents an assertion in chSchReadyI().*/ + tp->state = CH_STATE_CURRENT; +#endif + /* Re-enqueues tp with its new priority on the ready list.*/ + (void) chSchReadyI(queue_dequeue(tp)); + break; + default: + /* Nothing to do for other states.*/ + break; + } + break; + } + + /* Sleep on the mutex.*/ + queue_prio_insert(ctp, &mp->queue); + ctp->u.wtmtxp = mp; + chSchGoSleepS(CH_STATE_WTMTX); + + /* It is assumed that the thread performing the unlock operation assigns + the mutex to this thread.*/ + chDbgAssert(mp->owner == ctp, "not owner"); + chDbgAssert(ctp->mtxlist == mp, "not owned"); +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + chDbgAssert(mp->cnt == (cnt_t)1, "counter is not one"); + } +#endif + } + else { +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + chDbgAssert(mp->cnt == (cnt_t)0, "counter is not zero"); + + mp->cnt++; +#endif + /* It was not owned, inserted in the owned mutexes list.*/ + mp->owner = ctp; + mp->next = ctp->mtxlist; + ctp->mtxlist = mp; + } +} + +/** + * @brief Tries to lock a mutex. + * @details This function attempts to lock a mutex, if the mutex is already + * locked by another thread then the function exits without waiting. + * @post The mutex is locked and inserted in the per-thread stack of owned + * mutexes. + * @note This function does not have any overhead related to the + * priority inheritance mechanism because it does not try to + * enter a sleep state. + * + * @param[in] mp pointer to the @p mutex_t structure + * @return The operation status. + * @retval true if the mutex has been successfully acquired + * @retval false if the lock attempt failed. + * + * @api + */ +bool chMtxTryLock(mutex_t *mp) { + bool b; + + chSysLock(); + b = chMtxTryLockS(mp); + chSysUnlock(); + + return b; +} + +/** + * @brief Tries to lock a mutex. + * @details This function attempts to lock a mutex, if the mutex is already + * taken by another thread then the function exits without waiting. + * @post The mutex is locked and inserted in the per-thread stack of owned + * mutexes. + * @note This function does not have any overhead related to the + * priority inheritance mechanism because it does not try to + * enter a sleep state. + * + * @param[in] mp pointer to the @p mutex_t structure + * @return The operation status. + * @retval true if the mutex has been successfully acquired + * @retval false if the lock attempt failed. + * + * @sclass + */ +bool chMtxTryLockS(mutex_t *mp) { + + chDbgCheckClassS(); + chDbgCheck(mp != NULL); + + if (mp->owner != NULL) { +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + + chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive"); + + if (mp->owner == currp) { + mp->cnt++; + return true; + } +#endif + return false; + } +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + + chDbgAssert(mp->cnt == (cnt_t)0, "counter is not zero"); + + mp->cnt++; +#endif + mp->owner = currp; + mp->next = currp->mtxlist; + currp->mtxlist = mp; + return true; +} + +/** + * @brief Unlocks the specified mutex. + * @note Mutexes must be unlocked in reverse lock order. Violating this + * rules will result in a panic if assertions are enabled. + * @pre The invoking thread must have at least one owned mutex. + * @post The mutex is unlocked and removed from the per-thread stack of + * owned mutexes. + * + * @param[in] mp pointer to the @p mutex_t structure + * + * @api + */ +void chMtxUnlock(mutex_t *mp) { + thread_t *ctp = currp; + mutex_t *lmp; + + chDbgCheck(mp != NULL); + + chSysLock(); + + chDbgAssert(ctp->mtxlist != NULL, "owned mutexes list empty"); + chDbgAssert(ctp->mtxlist->owner == ctp, "ownership failure"); +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive"); + + if (--mp->cnt == (cnt_t)0) { +#endif + + chDbgAssert(ctp->mtxlist == mp, "not next in list"); + + /* Removes the top mutex from the thread's owned mutexes list and marks + it as not owned. Note, it is assumed to be the same mutex passed as + parameter of this function.*/ + ctp->mtxlist = mp->next; + + /* If a thread is waiting on the mutex then the fun part begins.*/ + if (chMtxQueueNotEmptyS(mp)) { + thread_t *tp; + + /* Recalculates the optimal thread priority by scanning the owned + mutexes list.*/ + tprio_t newprio = ctp->realprio; + lmp = ctp->mtxlist; + while (lmp != NULL) { + /* If the highest priority thread waiting in the mutexes list has a + greater priority than the current thread base priority then the + final priority will have at least that priority.*/ + if (chMtxQueueNotEmptyS(lmp) && + (lmp->queue.next->prio > newprio)) { + newprio = lmp->queue.next->prio; + } + lmp = lmp->next; + } + + /* Assigns to the current thread the highest priority among all the + waiting threads.*/ + ctp->prio = newprio; + + /* Awakens the highest priority thread waiting for the unlocked mutex and + assigns the mutex to it.*/ +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + mp->cnt = (cnt_t)1; +#endif + tp = queue_fifo_remove(&mp->queue); + mp->owner = tp; + mp->next = tp->mtxlist; + tp->mtxlist = mp; + + /* Note, not using chSchWakeupS() because that function expects the + current thread to have the higher or equal priority than the ones + in the ready list. This is not necessarily true here because we + just changed priority.*/ + (void) chSchReadyI(tp); + chSchRescheduleS(); + } + else { + mp->owner = NULL; + } +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + } +#endif + + chSysUnlock(); +} + +/** + * @brief Unlocks the specified mutex. + * @note Mutexes must be unlocked in reverse lock order. Violating this + * rules will result in a panic if assertions are enabled. + * @pre The invoking thread must have at least one owned mutex. + * @post The mutex is unlocked and removed from the per-thread stack of + * owned mutexes. + * @post This function does not reschedule so a call to a rescheduling + * function must be performed before unlocking the kernel. + * + * @param[in] mp pointer to the @p mutex_t structure + * + * @sclass + */ +void chMtxUnlockS(mutex_t *mp) { + thread_t *ctp = currp; + mutex_t *lmp; + + chDbgCheckClassS(); + chDbgCheck(mp != NULL); + + chDbgAssert(ctp->mtxlist != NULL, "owned mutexes list empty"); + chDbgAssert(ctp->mtxlist->owner == ctp, "ownership failure"); +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive"); + + if (--mp->cnt == (cnt_t)0) { +#endif + + chDbgAssert(ctp->mtxlist == mp, "not next in list"); + + /* Removes the top mutex from the thread's owned mutexes list and marks + it as not owned. Note, it is assumed to be the same mutex passed as + parameter of this function.*/ + ctp->mtxlist = mp->next; + + /* If a thread is waiting on the mutex then the fun part begins.*/ + if (chMtxQueueNotEmptyS(mp)) { + thread_t *tp; + + /* Recalculates the optimal thread priority by scanning the owned + mutexes list.*/ + tprio_t newprio = ctp->realprio; + lmp = ctp->mtxlist; + while (lmp != NULL) { + /* If the highest priority thread waiting in the mutexes list has a + greater priority than the current thread base priority then the + final priority will have at least that priority.*/ + if (chMtxQueueNotEmptyS(lmp) && + (lmp->queue.next->prio > newprio)) { + newprio = lmp->queue.next->prio; + } + lmp = lmp->next; + } + + /* Assigns to the current thread the highest priority among all the + waiting threads.*/ + ctp->prio = newprio; + + /* Awakens the highest priority thread waiting for the unlocked mutex and + assigns the mutex to it.*/ +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + mp->cnt = (cnt_t)1; +#endif + tp = queue_fifo_remove(&mp->queue); + mp->owner = tp; + mp->next = tp->mtxlist; + tp->mtxlist = mp; + (void) chSchReadyI(tp); + } + else { + mp->owner = NULL; + } +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + } +#endif +} + +/** + * @brief Unlocks all mutexes owned by the invoking thread. + * @post The stack of owned mutexes is emptied and all the found + * mutexes are unlocked. + * @post This function does not reschedule so a call to a rescheduling + * function must be performed before unlocking the kernel. + * @note This function is MUCH MORE efficient than releasing the + * mutexes one by one and not just because the call overhead, + * this function does not have any overhead related to the priority + * inheritance mechanism. + * + * @sclass + */ +void chMtxUnlockAllS(void) { + thread_t *ctp = currp; + + if (ctp->mtxlist != NULL) { + do { + mutex_t *mp = ctp->mtxlist; + ctp->mtxlist = mp->next; + if (chMtxQueueNotEmptyS(mp)) { + thread_t *tp; +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + mp->cnt = (cnt_t)1; +#endif + tp = queue_fifo_remove(&mp->queue); + mp->owner = tp; + mp->next = tp->mtxlist; + tp->mtxlist = mp; + (void) chSchReadyI(tp); + } + else { +#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE + mp->cnt = (cnt_t)0; +#endif + mp->owner = NULL; + } + } while (ctp->mtxlist != NULL); + ctp->prio = ctp->realprio; + chSchRescheduleS(); + } +} + +/** + * @brief Unlocks all mutexes owned by the invoking thread. + * @post The stack of owned mutexes is emptied and all the found + * mutexes are unlocked. + * @note This function is MUCH MORE efficient than releasing the + * mutexes one by one and not just because the call overhead, + * this function does not have any overhead related to the priority + * inheritance mechanism. + * + * @api + */ +void chMtxUnlockAll(void) { + + chSysLock(); + chMtxUnlockAllS(); + chSysUnlock(); +} + +#endif /* CH_CFG_USE_MUTEXES == TRUE */ + +/** @} */ -- cgit v1.2.3