|
ChibiOS/RT
2.5.1 |
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 /** @} */