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