ChibiOS/RT
2.5.1
chmtx.c
Go to the documentation of this file.
00001 /*
00002     ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
00003                  2011,2012 Giovanni Di Sirio.
00004 
00005     This file is part of ChibiOS/RT.
00006 
00007     ChibiOS/RT is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 3 of the License, or
00010     (at your option) any later version.
00011 
00012     ChibiOS/RT is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 */
00020 
00021 /**
00022  * @file    chmtx.c
00023  * @brief   Mutexes code.
00024  *
00025  * @addtogroup mutexes
00026  * @details Mutexes related APIs and services.
00027  *
00028  *          <h2>Operation mode</h2>
00029  *          A mutex is a threads synchronization object that can be in two
00030  *          distinct states:
00031  *          - Not owned (unlocked).
00032  *          - Owned by a thread (locked).
00033  *          .
00034  *          Operations defined for mutexes:
00035  *          - <b>Lock</b>: The mutex is checked, if the mutex is not owned by
00036  *            some other thread then it is associated to the locking thread
00037  *            else the thread is queued on the mutex in a list ordered by
00038  *            priority.
00039  *          - <b>Unlock</b>: The mutex is released by the owner and the highest
00040  *            priority thread waiting in the queue, if any, is resumed and made
00041  *            owner of the mutex.
00042  *          .
00043  *          <h2>Constraints</h2>
00044  *          In ChibiOS/RT the Unlock operations are always performed in
00045  *          lock-reverse order. The unlock API does not even have a parameter,
00046  *          the mutex to unlock is selected from an internal, per-thread, stack
00047  *          of owned mutexes. This both improves the performance and is
00048  *          required for an efficient implementation of the priority
00049  *          inheritance mechanism.
00050  *
00051  *          <h2>The priority inversion problem</h2>
00052  *          The mutexes in ChibiOS/RT implements the <b>full</b> priority
00053  *          inheritance mechanism in order handle the priority inversion
00054  *          problem.<br>
00055  *          When a thread is queued on a mutex, any thread, directly or
00056  *          indirectly, holding the mutex gains the same priority of the
00057  *          waiting thread (if their priority was not already equal or higher).
00058  *          The mechanism works with any number of nested mutexes and any
00059  *          number of involved threads. The algorithm complexity (worst case)
00060  *          is N with N equal to the number of nested mutexes.
00061  * @pre     In order to use the mutex APIs the @p CH_USE_MUTEXES option
00062  *          must be enabled in @p chconf.h.
00063  * @post    Enabling mutexes requires 5-12 (depending on the architecture)
00064  *          extra bytes in the @p Thread structure.
00065  * @{
00066  */
00067 
00068 #include "ch.h"
00069 
00070 #if CH_USE_MUTEXES || defined(__DOXYGEN__)
00071 
00072 /**
00073  * @brief   Initializes s @p Mutex structure.
00074  *
00075  * @param[out] mp       pointer to a @p Mutex structure
00076  *
00077  * @init
00078  */
00079 void chMtxInit(Mutex *mp) {
00080 
00081   chDbgCheck(mp != NULL, "chMtxInit");
00082 
00083   queue_init(&mp->m_queue);
00084   mp->m_owner = NULL;
00085 }
00086 
00087 /**
00088  * @brief   Locks the specified mutex.
00089  * @post    The mutex is locked and inserted in the per-thread stack of owned
00090  *          mutexes.
00091  *
00092  * @param[in] mp        pointer to the @p Mutex structure
00093  *
00094  * @api
00095  */
00096 void chMtxLock(Mutex *mp) {
00097 
00098   chSysLock();
00099 
00100   chMtxLockS(mp);
00101 
00102   chSysUnlock();
00103 }
00104 
00105 /**
00106  * @brief   Locks the specified mutex.
00107  * @post    The mutex is locked and inserted in the per-thread stack of owned
00108  *          mutexes.
00109  *
00110  * @param[in] mp        pointer to the @p Mutex structure
00111  *
00112  * @sclass
00113  */
00114 void chMtxLockS(Mutex *mp) {
00115   Thread *ctp = currp;
00116 
00117   chDbgCheckClassS();
00118   chDbgCheck(mp != NULL, "chMtxLockS");
00119 
00120   /* Is the mutex already locked? */
00121   if (mp->m_owner != NULL) {
00122     /* Priority inheritance protocol; explores the thread-mutex dependencies
00123        boosting the priority of all the affected threads to equal the priority
00124        of the running thread requesting the mutex.*/
00125     Thread *tp = mp->m_owner;
00126     /* Does the running thread have higher priority than the mutex
00127        owning thread? */
00128     while (tp->p_prio < ctp->p_prio) {
00129       /* Make priority of thread tp match the running thread's priority.*/
00130       tp->p_prio = ctp->p_prio;
00131       /* The following states need priority queues reordering.*/
00132       switch (tp->p_state) {
00133       case THD_STATE_WTMTX:
00134         /* Re-enqueues the mutex owner with its new priority.*/
00135         prio_insert(dequeue(tp), (ThreadsQueue *)tp->p_u.wtobjp);
00136         tp = ((Mutex *)tp->p_u.wtobjp)->m_owner;
00137         continue;
00138 #if CH_USE_CONDVARS |                                                       \
00139     (CH_USE_SEMAPHORES && CH_USE_SEMAPHORES_PRIORITY) |                     \
00140     (CH_USE_MESSAGES && CH_USE_MESSAGES_PRIORITY)
00141 #if CH_USE_CONDVARS
00142       case THD_STATE_WTCOND:
00143 #endif
00144 #if CH_USE_SEMAPHORES && CH_USE_SEMAPHORES_PRIORITY
00145       case THD_STATE_WTSEM:
00146 #endif
00147 #if CH_USE_MESSAGES && CH_USE_MESSAGES_PRIORITY
00148       case THD_STATE_SNDMSGQ:
00149 #endif
00150         /* Re-enqueues tp with its new priority on the queue.*/
00151         prio_insert(dequeue(tp), (ThreadsQueue *)tp->p_u.wtobjp);
00152         break;
00153 #endif
00154       case THD_STATE_READY:
00155 #if CH_DBG_ENABLE_ASSERTS
00156         /* Prevents an assertion in chSchReadyI().*/
00157         tp->p_state = THD_STATE_CURRENT;
00158 #endif
00159         /* Re-enqueues tp with its new priority on the ready list.*/
00160         chSchReadyI(dequeue(tp));
00161         break;
00162       }
00163       break;
00164     }
00165     /* Sleep on the mutex.*/
00166     prio_insert(ctp, &mp->m_queue);
00167     ctp->p_u.wtobjp = mp;
00168     chSchGoSleepS(THD_STATE_WTMTX);
00169     /* It is assumed that the thread performing the unlock operation assigns
00170        the mutex to this thread.*/
00171     chDbgAssert(mp->m_owner == ctp, "chMtxLockS(), #1", "not owner");
00172     chDbgAssert(ctp->p_mtxlist == mp, "chMtxLockS(), #2", "not owned");
00173   }
00174   else {
00175     /* It was not owned, inserted in the owned mutexes list.*/
00176     mp->m_owner = ctp;
00177     mp->m_next = ctp->p_mtxlist;
00178     ctp->p_mtxlist = mp;
00179   }
00180 }
00181 
00182 /**
00183  * @brief   Tries to lock a mutex.
00184  * @details This function attempts to lock a mutex, if the mutex is already
00185  *          locked by another thread then the function exits without waiting.
00186  * @post    The mutex is locked and inserted in the per-thread stack of owned
00187  *          mutexes.
00188  * @note    This function does not have any overhead related to the
00189  *          priority inheritance mechanism because it does not try to
00190  *          enter a sleep state.
00191  *
00192  * @param[in] mp        pointer to the @p Mutex structure
00193  * @return              The operation status.
00194  * @retval TRUE         if the mutex has been successfully acquired
00195  * @retval FALSE        if the lock attempt failed.
00196  *
00197  * @api
00198  */
00199 bool_t chMtxTryLock(Mutex *mp) {
00200   bool_t b;
00201 
00202   chSysLock();
00203 
00204   b = chMtxTryLockS(mp);
00205 
00206   chSysUnlock();
00207   return b;
00208 }
00209 
00210 /**
00211  * @brief   Tries to lock a mutex.
00212  * @details This function attempts to lock a mutex, if the mutex is already
00213  *          taken by another thread then the function exits without waiting.
00214  * @post    The mutex is locked and inserted in the per-thread stack of owned
00215  *          mutexes.
00216  * @note    This function does not have any overhead related to the
00217  *          priority inheritance mechanism because it does not try to
00218  *          enter a sleep state.
00219  *
00220  * @param[in] mp        pointer to the @p Mutex structure
00221  * @return              The operation status.
00222  * @retval TRUE         if the mutex has been successfully acquired
00223  * @retval FALSE        if the lock attempt failed.
00224  *
00225  * @sclass
00226  */
00227 bool_t chMtxTryLockS(Mutex *mp) {
00228 
00229   chDbgCheckClassS();
00230   chDbgCheck(mp != NULL, "chMtxTryLockS");
00231 
00232   if (mp->m_owner != NULL)
00233     return FALSE;
00234   mp->m_owner = currp;
00235   mp->m_next = currp->p_mtxlist;
00236   currp->p_mtxlist = mp;
00237   return TRUE;
00238 }
00239 
00240 /**
00241  * @brief   Unlocks the next owned mutex in reverse lock order.
00242  * @pre     The invoking thread <b>must</b> have at least one owned mutex.
00243  * @post    The mutex is unlocked and removed from the per-thread stack of
00244  *          owned mutexes.
00245  *
00246  * @return              A pointer to the unlocked mutex.
00247  *
00248  * @api
00249  */
00250 Mutex *chMtxUnlock(void) {
00251   Thread *ctp = currp;
00252   Mutex *ump, *mp;
00253 
00254   chSysLock();
00255   chDbgAssert(ctp->p_mtxlist != NULL,
00256               "chMtxUnlock(), #1",
00257               "owned mutexes list empty");
00258   chDbgAssert(ctp->p_mtxlist->m_owner == ctp,
00259               "chMtxUnlock(), #2",
00260               "ownership failure");
00261   /* Removes the top Mutex from the Thread's owned mutexes list and marks it
00262      as not owned.*/
00263   ump = ctp->p_mtxlist;
00264   ctp->p_mtxlist = ump->m_next;
00265   /* If a thread is waiting on the mutex then the fun part begins.*/
00266   if (chMtxQueueNotEmptyS(ump)) {
00267     Thread *tp;
00268 
00269     /* Recalculates the optimal thread priority by scanning the owned
00270        mutexes list.*/
00271     tprio_t newprio = ctp->p_realprio;
00272     mp = ctp->p_mtxlist;
00273     while (mp != NULL) {
00274       /* If the highest priority thread waiting in the mutexes list has a
00275          greater priority than the current thread base priority then the final
00276          priority will have at least that priority.*/
00277       if (chMtxQueueNotEmptyS(mp) && (mp->m_queue.p_next->p_prio > newprio))
00278         newprio = mp->m_queue.p_next->p_prio;
00279       mp = mp->m_next;
00280     }
00281     /* Assigns to the current thread the highest priority among all the
00282        waiting threads.*/
00283     ctp->p_prio = newprio;
00284     /* Awakens the highest priority thread waiting for the unlocked mutex and
00285        assigns the mutex to it.*/
00286     tp = fifo_remove(&ump->m_queue);
00287     ump->m_owner = tp;
00288     ump->m_next = tp->p_mtxlist;
00289     tp->p_mtxlist = ump;
00290     chSchWakeupS(tp, RDY_OK);
00291   }
00292   else
00293     ump->m_owner = NULL;
00294   chSysUnlock();
00295   return ump;
00296 }
00297 
00298 /**
00299  * @brief   Unlocks the next owned mutex in reverse lock order.
00300  * @pre     The invoking thread <b>must</b> have at least one owned mutex.
00301  * @post    The mutex is unlocked and removed from the per-thread stack of
00302  *          owned mutexes.
00303  * @post    This function does not reschedule so a call to a rescheduling
00304  *          function must be performed before unlocking the kernel.
00305  *
00306  * @return              A pointer to the unlocked mutex.
00307  *
00308  * @sclass
00309  */
00310 Mutex *chMtxUnlockS(void) {
00311   Thread *ctp = currp;
00312   Mutex *ump, *mp;
00313 
00314   chDbgCheckClassS();
00315   chDbgAssert(ctp->p_mtxlist != NULL,
00316               "chMtxUnlockS(), #1",
00317               "owned mutexes list empty");
00318   chDbgAssert(ctp->p_mtxlist->m_owner == ctp,
00319               "chMtxUnlockS(), #2",
00320               "ownership failure");
00321 
00322   /* Removes the top Mutex from the owned mutexes list and marks it as not
00323      owned.*/
00324   ump = ctp->p_mtxlist;
00325   ctp->p_mtxlist = ump->m_next;
00326   /* If a thread is waiting on the mutex then the fun part begins.*/
00327   if (chMtxQueueNotEmptyS(ump)) {
00328     Thread *tp;
00329 
00330     /* Recalculates the optimal thread priority by scanning the owned
00331        mutexes list.*/
00332     tprio_t newprio = ctp->p_realprio;
00333     mp = ctp->p_mtxlist;
00334     while (mp != NULL) {
00335       /* If the highest priority thread waiting in the mutexes list has a
00336          greater priority than the current thread base priority then the final
00337          priority will have at least that priority.*/
00338       if (chMtxQueueNotEmptyS(mp) && (mp->m_queue.p_next->p_prio > newprio))
00339         newprio = mp->m_queue.p_next->p_prio;
00340       mp = mp->m_next;
00341     }
00342     ctp->p_prio = newprio;
00343     /* Awakens the highest priority thread waiting for the unlocked mutex and
00344        assigns the mutex to it.*/
00345     tp = fifo_remove(&ump->m_queue);
00346     ump->m_owner = tp;
00347     ump->m_next = tp->p_mtxlist;
00348     tp->p_mtxlist = ump;
00349     chSchReadyI(tp);
00350   }
00351   else
00352     ump->m_owner = NULL;
00353   return ump;
00354 }
00355 
00356 /**
00357  * @brief   Unlocks all the mutexes owned by the invoking thread.
00358  * @post    The stack of owned mutexes is emptied and all the found
00359  *          mutexes are unlocked.
00360  * @note    This function is <b>MUCH MORE</b> efficient than releasing the
00361  *          mutexes one by one and not just because the call overhead,
00362  *          this function does not have any overhead related to the priority
00363  *          inheritance mechanism.
00364  *
00365  * @api
00366  */
00367 void chMtxUnlockAll(void) {
00368   Thread *ctp = currp;
00369 
00370   chSysLock();
00371   if (ctp->p_mtxlist != NULL) {
00372     do {
00373       Mutex *ump = ctp->p_mtxlist;
00374       ctp->p_mtxlist = ump->m_next;
00375       if (chMtxQueueNotEmptyS(ump)) {
00376         Thread *tp = fifo_remove(&ump->m_queue);
00377         ump->m_owner = tp;
00378         ump->m_next = tp->p_mtxlist;
00379         tp->p_mtxlist = ump;
00380         chSchReadyI(tp);
00381       }
00382       else
00383         ump->m_owner = NULL;
00384     } while (ctp->p_mtxlist != NULL);
00385     ctp->p_prio = ctp->p_realprio;
00386     chSchRescheduleS();
00387   }
00388   chSysUnlock();
00389 }
00390 
00391 #endif /* CH_USE_MUTEXES */
00392 
00393 /** @} */