/* ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. This file is part of ChibiOS/RT. ChibiOS/RT 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/RT 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 chmtx.c * @brief Mutexes code. * @addtogroup mutexes * @{ */ #include #if CH_USE_MUTEXES /** * @brief Initializes s @p Mutex structure. * * @param[out] mp pointer to a @p Mutex structure * @note This function can be invoked from within an interrupt handler even if * it is not an I-Class API because it does not touch any critical kernel * data structure. */ void chMtxInit(Mutex *mp) { chDbgCheck(mp != NULL, "chMtxInit"); queue_init(&mp->m_queue); mp->m_owner = NULL; } /** * @brief Locks the specified mutex. * * @param[in] mp pointer to the @p Mutex structure */ void chMtxLock(Mutex *mp) { chSysLock(); chMtxLockS(mp); chSysUnlock(); } /** * @brief Locks the specified mutex. * * @param[in] mp pointer to the @p Mutex structure * @note This function must be called within a @p chSysLock() / @p chSysUnlock() * block. */ void chMtxLockS(Mutex *mp) { chDbgCheck(mp != NULL, "chMtxLockS"); /* the mutex is already locked? */ if (mp->m_owner != NULL) { /* * 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 *tp = mp->m_owner; /* { tp is the thread currently owning the mutex } */ /* the running thread has higher priority than tp? */ while (tp->p_prio < currp->p_prio) { /* make priority of thread tp match the running thread's priority */ tp->p_prio = currp->p_prio; /* * The following states need priority queues reordering. */ switch (tp->p_state) { /* thread tp is waiting on a mutex? */ case PRWTMTX: /* Requeues tp with its new priority on the mutex wait queue. */ prio_insert(dequeue(tp), &tp->p_wtmtxp->m_queue); /* boost the owner of this mutex if needed */ tp = tp->p_wtmtxp->m_owner; continue; #if CH_USE_CONDVARS case PRWTCOND: /* Requeues tp with its new priority on the condvar queue. */ prio_insert(dequeue(tp), &tp->p_wtcondp->c_queue); break; #endif #if CH_USE_SEMAPHORES_PRIORITY case PRWTSEM: /* Requeues tp with its new priority on the semaphore queue. */ prio_insert(dequeue(tp), &tp->p_wtsemp->s_queue); break; #endif #if CH_USE_MESSAGES_PRIORITY case PRSNDMSG: /* Requeues tp with its new priority on the server thread queue. */ prio_insert(dequeue(tp), &tp->p_wtthdp->p_msgqueue); break; #endif /* thread tp is ready? */ case PRREADY: /* Requeue tp with its new priority on the ready list. */ chSchReadyI(dequeue(tp)); } break; } /* sleep on the mutex */ prio_insert(currp, &mp->m_queue); /* thread remembers the mutex where it is waiting on */ currp->p_wtmtxp = mp; chSchGoSleepS(PRWTMTX); chDbgAssert(mp->m_owner == NULL, "chMtxLockS(), #1", "still owned"); } /* * The mutex is now inserted in the owned mutexes list. */ mp->m_owner = currp; mp->m_next = currp->p_mtxlist; currp->p_mtxlist = mp; } /** * @brief Tries to lock a mutex. * @details This function does not have any overhead related to * the priority inheritance mechanism because it does not try to * enter a sleep state on the mutex. * * @param[in] mp pointer to the @p Mutex structure * @retval TRUE if the mutex was successfully acquired * @retval FALSE if the lock attempt failed. */ bool_t chMtxTryLock(Mutex *mp) { bool_t b; chSysLock(); b = chMtxTryLockS(mp); chSysUnlock(); return b; } /** * @brief Tries to lock a mutex. * @details This function does not have any overhead related to * the priority inheritance mechanism because it does not try to * enter a sleep state on the mutex. * @param[in] mp pointer to the @p Mutex structure * @retval TRUE if the mutex was successfully acquired * @retval FALSE if the lock attempt failed. * @note This function must be called within a @p chSysLock() / @p chSysUnlock() * block. */ bool_t chMtxTryLockS(Mutex *mp) { chDbgCheck(mp != NULL, "chMtxTryLockS"); if (mp->m_owner != NULL) return FALSE; mp->m_owner = currp; mp->m_next = currp->p_mtxlist; currp->p_mtxlist = mp; return TRUE; } /** * @brief Unlocks the next owned mutex in reverse lock order. * * @return The pointer to the unlocked mutex. */ Mutex *chMtxUnlock(void) { Mutex *ump, *mp; chSysLock(); chDbgAssert(currp->p_mtxlist != NULL, "chMtxUnlock(), #1", "owned mutexes list empty"); chDbgAssert(currp->p_mtxlist->m_owner == currp, "chMtxUnlock(), #2", "ownership failure"); /* remove the top Mutex from the Threads's owned mutexes list */ ump = currp->p_mtxlist; currp->p_mtxlist = ump->m_next; /* mark the Mutex as not owned */ ump->m_owner = NULL; /* * If a thread is waiting on the mutex then the hard part begins. */ if (chMtxQueueNotEmptyS(ump)) { /* get the highest priority thread waiting for the unlocked mutex */ Thread *tp = fifo_remove(&ump->m_queue); /* * Recalculates the optimal thread priority by scanning the owned mutexes list. */ tprio_t newprio = currp->p_realprio; /* iterate mp over all the (other) mutexes the current thread still owns */ mp = currp->p_mtxlist; while (mp != NULL) { /* mutex mp has a higher priority thread pending? */ if (chMtxQueueNotEmptyS(mp) && (mp->m_queue.p_next->p_prio > newprio)) /* boost current thread's priority to waiting thread */ newprio = mp->m_queue.p_next->p_prio; mp = mp->m_next; } /* (possibly) boost the priority of the current thread */ currp->p_prio = newprio; /* awaken the highest priority thread waiting for the unlocked mutex */ chSchWakeupS(tp, RDY_OK); } chSysUnlock(); return ump; } /** * @brief Unlocks the next owned mutex in reverse lock order. * * @return The pointer to the unlocked mutex. * @note This function must be called within a @p chSysLock() / @p chSysUnlock() * block. * @note This function does not reschedule internally. */ Mutex *chMtxUnlockS(void) { Mutex *ump, *mp; chDbgAssert(currp->p_mtxlist != NULL, "chMtxUnlockS(), #1", "owned mutexes list empty"); chDbgAssert(currp->p_mtxlist->m_owner == currp, "chMtxUnlockS(), #2", "ownership failure"); /* * Removes the top Mutex from the owned mutexes list and marks it as not owned. */ ump = currp->p_mtxlist; currp->p_mtxlist = ump->m_next; ump->m_owner = NULL; /* * If a thread is waiting on the mutex then the hard part begins. */ if (chMtxQueueNotEmptyS(ump)) { Thread *tp = fifo_remove(&ump->m_queue); /* * Recalculates the optimal thread priority by scanning the owned mutexes list. */ tprio_t newprio = currp->p_realprio; mp = currp->p_mtxlist; while (mp != NULL) { if (chMtxQueueNotEmptyS(mp) && (mp->m_queue.p_next->p_prio > newprio)) newprio = mp->m_queue.p_next->p_prio; mp = mp->m_next; } currp->p_prio = newprio; chSchReadyI(tp); } return ump; } /** * @brief Unlocks all the mutexes owned by the invoking thread. * @details 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. */ void chMtxUnlockAll(void) { chSysLock(); if (currp->p_mtxlist != NULL) { do { Mutex *mp = currp->p_mtxlist; currp->p_mtxlist = mp->m_next; mp->m_owner = NULL; if (chMtxQueueNotEmptyS(mp)) chSchReadyI(fifo_remove(&mp->m_queue)); } while (currp->p_mtxlist != NULL); currp->p_prio = currp->p_realprio; chSchRescheduleS(); } chSysUnlock(); } #endif /* CH_USE_MUTEXES */ /** @} */