|
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 chsem.c 00023 * @brief Semaphores code. 00024 * 00025 * @addtogroup semaphores 00026 * @details Semaphores related APIs and services. 00027 * 00028 * <h2>Operation mode</h2> 00029 * Semaphores are a flexible synchronization primitive, ChibiOS/RT 00030 * implements semaphores in their "counting semaphores" variant as 00031 * defined by Edsger Dijkstra plus several enhancements like: 00032 * - Wait operation with timeout. 00033 * - Reset operation. 00034 * - Atomic wait+signal operation. 00035 * - Return message from the wait operation (OK, RESET, TIMEOUT). 00036 * . 00037 * The binary semaphores variant can be easily implemented using 00038 * counting semaphores.<br> 00039 * Operations defined for semaphores: 00040 * - <b>Signal</b>: The semaphore counter is increased and if the 00041 * result is non-positive then a waiting thread is removed from 00042 * the semaphore queue and made ready for execution. 00043 * - <b>Wait</b>: The semaphore counter is decreased and if the result 00044 * becomes negative the thread is queued in the semaphore and 00045 * suspended. 00046 * - <b>Reset</b>: The semaphore counter is reset to a non-negative 00047 * value and all the threads in the queue are released. 00048 * . 00049 * Semaphores can be used as guards for mutual exclusion zones 00050 * (note that mutexes are recommended for this kind of use) but 00051 * also have other uses, queues guards and counters for example.<br> 00052 * Semaphores usually use a FIFO queuing strategy but it is possible 00053 * to make them order threads by priority by enabling 00054 * @p CH_USE_SEMAPHORES_PRIORITY in @p chconf.h. 00055 * @pre In order to use the semaphore APIs the @p CH_USE_SEMAPHORES 00056 * option must be enabled in @p chconf.h. 00057 * @{ 00058 */ 00059 00060 #include "ch.h" 00061 00062 #if CH_USE_SEMAPHORES || defined(__DOXYGEN__) 00063 00064 #if CH_USE_SEMAPHORES_PRIORITY 00065 #define sem_insert(tp, qp) prio_insert(tp, qp) 00066 #else 00067 #define sem_insert(tp, qp) queue_insert(tp, qp) 00068 #endif 00069 00070 /** 00071 * @brief Initializes a semaphore with the specified counter value. 00072 * 00073 * @param[out] sp pointer to a @p Semaphore structure 00074 * @param[in] n initial value of the semaphore counter. Must be 00075 * non-negative. 00076 * 00077 * @init 00078 */ 00079 void chSemInit(Semaphore *sp, cnt_t n) { 00080 00081 chDbgCheck((sp != NULL) && (n >= 0), "chSemInit"); 00082 00083 queue_init(&sp->s_queue); 00084 sp->s_cnt = n; 00085 } 00086 00087 /** 00088 * @brief Performs a reset operation on the semaphore. 00089 * @post After invoking this function all the threads waiting on the 00090 * semaphore, if any, are released and the semaphore counter is set 00091 * to the specified, non negative, value. 00092 * @note The released threads can recognize they were waked up by a reset 00093 * rather than a signal because the @p chSemWait() will return 00094 * @p RDY_RESET instead of @p RDY_OK. 00095 * 00096 * @param[in] sp pointer to a @p Semaphore structure 00097 * @param[in] n the new value of the semaphore counter. The value must 00098 * be non-negative. 00099 * 00100 * @api 00101 */ 00102 void chSemReset(Semaphore *sp, cnt_t n) { 00103 00104 chSysLock(); 00105 chSemResetI(sp, n); 00106 chSchRescheduleS(); 00107 chSysUnlock(); 00108 } 00109 00110 /** 00111 * @brief Performs a reset operation on the semaphore. 00112 * @post After invoking this function all the threads waiting on the 00113 * semaphore, if any, are released and the semaphore counter is set 00114 * to the specified, non negative, value. 00115 * @post This function does not reschedule so a call to a rescheduling 00116 * function must be performed before unlocking the kernel. Note that 00117 * interrupt handlers always reschedule on exit so an explicit 00118 * reschedule must not be performed in ISRs. 00119 * @note The released threads can recognize they were waked up by a reset 00120 * rather than a signal because the @p chSemWait() will return 00121 * @p RDY_RESET instead of @p RDY_OK. 00122 * 00123 * @param[in] sp pointer to a @p Semaphore structure 00124 * @param[in] n the new value of the semaphore counter. The value must 00125 * be non-negative. 00126 * 00127 * @iclass 00128 */ 00129 void chSemResetI(Semaphore *sp, cnt_t n) { 00130 cnt_t cnt; 00131 00132 chDbgCheckClassI(); 00133 chDbgCheck((sp != NULL) && (n >= 0), "chSemResetI"); 00134 chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || 00135 ((sp->s_cnt < 0) && notempty(&sp->s_queue)), 00136 "chSemResetI(), #1", 00137 "inconsistent semaphore"); 00138 00139 cnt = sp->s_cnt; 00140 sp->s_cnt = n; 00141 while (++cnt <= 0) 00142 chSchReadyI(lifo_remove(&sp->s_queue))->p_u.rdymsg = RDY_RESET; 00143 } 00144 00145 /** 00146 * @brief Performs a wait operation on a semaphore. 00147 * 00148 * @param[in] sp pointer to a @p Semaphore structure 00149 * @return A message specifying how the invoking thread has been 00150 * released from the semaphore. 00151 * @retval RDY_OK if the thread has not stopped on the semaphore or the 00152 * semaphore has been signaled. 00153 * @retval RDY_RESET if the semaphore has been reset using @p chSemReset(). 00154 * 00155 * @api 00156 */ 00157 msg_t chSemWait(Semaphore *sp) { 00158 msg_t msg; 00159 00160 chSysLock(); 00161 msg = chSemWaitS(sp); 00162 chSysUnlock(); 00163 return msg; 00164 } 00165 00166 /** 00167 * @brief Performs a wait operation on a semaphore. 00168 * 00169 * @param[in] sp pointer to a @p Semaphore structure 00170 * @return A message specifying how the invoking thread has been 00171 * released from the semaphore. 00172 * @retval RDY_OK if the thread has not stopped on the semaphore or the 00173 * semaphore has been signaled. 00174 * @retval RDY_RESET if the semaphore has been reset using @p chSemReset(). 00175 * 00176 * @sclass 00177 */ 00178 msg_t chSemWaitS(Semaphore *sp) { 00179 00180 chDbgCheckClassS(); 00181 chDbgCheck(sp != NULL, "chSemWaitS"); 00182 chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || 00183 ((sp->s_cnt < 0) && notempty(&sp->s_queue)), 00184 "chSemWaitS(), #1", 00185 "inconsistent semaphore"); 00186 00187 if (--sp->s_cnt < 0) { 00188 currp->p_u.wtobjp = sp; 00189 sem_insert(currp, &sp->s_queue); 00190 chSchGoSleepS(THD_STATE_WTSEM); 00191 return currp->p_u.rdymsg; 00192 } 00193 return RDY_OK; 00194 } 00195 00196 /** 00197 * @brief Performs a wait operation on a semaphore with timeout specification. 00198 * 00199 * @param[in] sp pointer to a @p Semaphore structure 00200 * @param[in] time the number of ticks before the operation timeouts, 00201 * the following special values are allowed: 00202 * - @a TIME_IMMEDIATE immediate timeout. 00203 * - @a TIME_INFINITE no timeout. 00204 * . 00205 * @return A message specifying how the invoking thread has been 00206 * released from the semaphore. 00207 * @retval RDY_OK if the thread has not stopped on the semaphore or the 00208 * semaphore has been signaled. 00209 * @retval RDY_RESET if the semaphore has been reset using @p chSemReset(). 00210 * @retval RDY_TIMEOUT if the semaphore has not been signaled or reset within 00211 * the specified timeout. 00212 * 00213 * @api 00214 */ 00215 msg_t chSemWaitTimeout(Semaphore *sp, systime_t time) { 00216 msg_t msg; 00217 00218 chSysLock(); 00219 msg = chSemWaitTimeoutS(sp, time); 00220 chSysUnlock(); 00221 return msg; 00222 } 00223 00224 /** 00225 * @brief Performs a wait operation on a semaphore with timeout specification. 00226 * 00227 * @param[in] sp pointer to a @p Semaphore structure 00228 * @param[in] time the number of ticks before the operation timeouts, 00229 * the following special values are allowed: 00230 * - @a TIME_IMMEDIATE immediate timeout. 00231 * - @a TIME_INFINITE no timeout. 00232 * . 00233 * @return A message specifying how the invoking thread has been 00234 * released from the semaphore. 00235 * @retval RDY_OK if the thread has not stopped on the semaphore or the 00236 * semaphore has been signaled. 00237 * @retval RDY_RESET if the semaphore has been reset using @p chSemReset(). 00238 * @retval RDY_TIMEOUT if the semaphore has not been signaled or reset within 00239 * the specified timeout. 00240 * 00241 * @sclass 00242 */ 00243 msg_t chSemWaitTimeoutS(Semaphore *sp, systime_t time) { 00244 00245 chDbgCheckClassS(); 00246 chDbgCheck(sp != NULL, "chSemWaitTimeoutS"); 00247 chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || 00248 ((sp->s_cnt < 0) && notempty(&sp->s_queue)), 00249 "chSemWaitTimeoutS(), #1", 00250 "inconsistent semaphore"); 00251 00252 if (--sp->s_cnt < 0) { 00253 if (TIME_IMMEDIATE == time) { 00254 sp->s_cnt++; 00255 return RDY_TIMEOUT; 00256 } 00257 currp->p_u.wtobjp = sp; 00258 sem_insert(currp, &sp->s_queue); 00259 return chSchGoSleepTimeoutS(THD_STATE_WTSEM, time); 00260 } 00261 return RDY_OK; 00262 } 00263 00264 /** 00265 * @brief Performs a signal operation on a semaphore. 00266 * 00267 * @param[in] sp pointer to a @p Semaphore structure 00268 * 00269 * @api 00270 */ 00271 void chSemSignal(Semaphore *sp) { 00272 00273 chDbgCheck(sp != NULL, "chSemSignal"); 00274 chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || 00275 ((sp->s_cnt < 0) && notempty(&sp->s_queue)), 00276 "chSemSignal(), #1", 00277 "inconsistent semaphore"); 00278 00279 chSysLock(); 00280 if (++sp->s_cnt <= 0) 00281 chSchWakeupS(fifo_remove(&sp->s_queue), RDY_OK); 00282 chSysUnlock(); 00283 } 00284 00285 /** 00286 * @brief Performs a signal operation on a semaphore. 00287 * @post This function does not reschedule so a call to a rescheduling 00288 * function must be performed before unlocking the kernel. Note that 00289 * interrupt handlers always reschedule on exit so an explicit 00290 * reschedule must not be performed in ISRs. 00291 * 00292 * @param[in] sp pointer to a @p Semaphore structure 00293 * 00294 * @iclass 00295 */ 00296 void chSemSignalI(Semaphore *sp) { 00297 00298 chDbgCheckClassI(); 00299 chDbgCheck(sp != NULL, "chSemSignalI"); 00300 chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || 00301 ((sp->s_cnt < 0) && notempty(&sp->s_queue)), 00302 "chSemSignalI(), #1", 00303 "inconsistent semaphore"); 00304 00305 if (++sp->s_cnt <= 0) { 00306 /* Note, it is done this way in order to allow a tail call on 00307 chSchReadyI().*/ 00308 Thread *tp = fifo_remove(&sp->s_queue); 00309 tp->p_u.rdymsg = RDY_OK; 00310 chSchReadyI(tp); 00311 } 00312 } 00313 00314 /** 00315 * @brief Adds the specified value to the semaphore counter. 00316 * @post This function does not reschedule so a call to a rescheduling 00317 * function must be performed before unlocking the kernel. Note that 00318 * interrupt handlers always reschedule on exit so an explicit 00319 * reschedule must not be performed in ISRs. 00320 * 00321 * @param[in] sp pointer to a @p Semaphore structure 00322 * @param[in] n value to be added to the semaphore counter. The value 00323 * must be positive. 00324 * 00325 * @iclass 00326 */ 00327 void chSemAddCounterI(Semaphore *sp, cnt_t n) { 00328 00329 chDbgCheckClassI(); 00330 chDbgCheck((sp != NULL) && (n > 0), "chSemAddCounterI"); 00331 chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || 00332 ((sp->s_cnt < 0) && notempty(&sp->s_queue)), 00333 "chSemAddCounterI(), #1", 00334 "inconsistent semaphore"); 00335 00336 while (n > 0) { 00337 if (++sp->s_cnt <= 0) 00338 chSchReadyI(fifo_remove(&sp->s_queue))->p_u.rdymsg = RDY_OK; 00339 n--; 00340 } 00341 } 00342 00343 #if CH_USE_SEMSW 00344 /** 00345 * @brief Performs atomic signal and wait operations on two semaphores. 00346 * @pre The configuration option @p CH_USE_SEMSW must be enabled in order 00347 * to use this function. 00348 * 00349 * @param[in] sps pointer to a @p Semaphore structure to be signaled 00350 * @param[in] spw pointer to a @p Semaphore structure to be wait on 00351 * @return A message specifying how the invoking thread has been 00352 * released from the semaphore. 00353 * @retval RDY_OK if the thread has not stopped on the semaphore or the 00354 * semaphore has been signaled. 00355 * @retval RDY_RESET if the semaphore has been reset using @p chSemReset(). 00356 * 00357 * @api 00358 */ 00359 msg_t chSemSignalWait(Semaphore *sps, Semaphore *spw) { 00360 msg_t msg; 00361 00362 chDbgCheck((sps != NULL) && (spw != NULL), "chSemSignalWait"); 00363 chDbgAssert(((sps->s_cnt >= 0) && isempty(&sps->s_queue)) || 00364 ((sps->s_cnt < 0) && notempty(&sps->s_queue)), 00365 "chSemSignalWait(), #1", 00366 "inconsistent semaphore"); 00367 chDbgAssert(((spw->s_cnt >= 0) && isempty(&spw->s_queue)) || 00368 ((spw->s_cnt < 0) && notempty(&spw->s_queue)), 00369 "chSemSignalWait(), #2", 00370 "inconsistent semaphore"); 00371 00372 chSysLock(); 00373 if (++sps->s_cnt <= 0) 00374 chSchReadyI(fifo_remove(&sps->s_queue))->p_u.rdymsg = RDY_OK; 00375 if (--spw->s_cnt < 0) { 00376 Thread *ctp = currp; 00377 sem_insert(ctp, &spw->s_queue); 00378 ctp->p_u.wtobjp = spw; 00379 chSchGoSleepS(THD_STATE_WTSEM); 00380 msg = ctp->p_u.rdymsg; 00381 } 00382 else { 00383 chSchRescheduleS(); 00384 msg = RDY_OK; 00385 } 00386 chSysUnlock(); 00387 return msg; 00388 } 00389 #endif /* CH_USE_SEMSW */ 00390 00391 #endif /* CH_USE_SEMAPHORES */ 00392 00393 /** @} */