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 chsem.c 00029 * @brief Semaphores code. 00030 * 00031 * @addtogroup semaphores 00032 * @details Semaphores related APIs and services. 00033 * 00034 * <h2>Operation mode</h2> 00035 * Semaphores are a flexible synchronization primitive, ChibiOS/RT 00036 * implements semaphores in their "counting semaphores" variant as 00037 * defined by Edsger Dijkstra plus several enhancements like: 00038 * - Wait operation with timeout. 00039 * - Reset operation. 00040 * - Atomic wait+signal operation. 00041 * - Return message from the wait operation (OK, RESET, TIMEOUT). 00042 * . 00043 * The binary semaphores variant can be easily implemented using 00044 * counting semaphores.<br> 00045 * Operations defined for semaphores: 00046 * - <b>Signal</b>: The semaphore counter is increased and if the 00047 * result is non-positive then a waiting thread is removed from 00048 * the semaphore queue and made ready for execution. 00049 * - <b>Wait</b>: The semaphore counter is decreased and if the result 00050 * becomes negative the thread is queued in the semaphore and 00051 * suspended. 00052 * - <b>Reset</b>: The semaphore counter is reset to a non-negative 00053 * value and all the threads in the queue are released. 00054 * . 00055 * Semaphores can be used as guards for mutual exclusion zones 00056 * (note that mutexes are recommended for this kind of use) but 00057 * also have other uses, queues guards and counters as example.<br> 00058 * Semaphores usually use a FIFO queuing strategy but it is possible 00059 * to make them order threads by priority by enabling 00060 * @p CH_USE_SEMAPHORES_PRIORITY in @p chconf.h.<br> 00061 * In order to use the Semaphores APIs the @p CH_USE_SEMAPHORES 00062 * option must be enabled in @p chconf.h. 00063 * @{ 00064 */ 00065 00066 #include "ch.h" 00067 00068 #if CH_USE_SEMAPHORES 00069 00070 #if CH_USE_SEMAPHORES_PRIORITY 00071 #define sem_insert(tp, qp) prio_insert(tp, qp) 00072 #else 00073 #define sem_insert(tp, qp) queue_insert(tp, qp) 00074 #endif 00075 00076 /** 00077 * @brief Initializes a semaphore with the specified counter value. 00078 * 00079 * @param[out] sp pointer to a @p Semaphore structure 00080 * @param[in] n initial value of the semaphore counter. Must be 00081 * non-negative. 00082 */ 00083 void chSemInit(Semaphore *sp, cnt_t n) { 00084 00085 chDbgCheck((sp != NULL) && (n >= 0), "chSemInit"); 00086 00087 queue_init(&sp->s_queue); 00088 sp->s_cnt = n; 00089 } 00090 00091 /** 00092 * @brief Performs a reset operation on the semaphore. 00093 * @note The released threads can recognize they were waked up by a reset 00094 * rather than a signal because the @p chSemWait() will return 00095 * @p RDY_RESET instead of @p RDY_OK. 00096 * 00097 * @param[in] sp pointer to a @p Semaphore structure 00098 * @param[in] n the new value of the semaphore counter. The value must 00099 * be non-negative. 00100 */ 00101 void chSemReset(Semaphore *sp, cnt_t n) { 00102 00103 chSysLock(); 00104 chSemResetI(sp, n); 00105 chSchRescheduleS(); 00106 chSysUnlock(); 00107 } 00108 00109 /** 00110 * @brief Performs a reset operation on the semaphore. 00111 * @note The released threads can recognize they were waked up by a reset 00112 * rather than a signal because the @p chSemWait() will return 00113 * @p RDY_RESET instead of @p RDY_OK. 00114 * @note This function does not reschedule. 00115 * 00116 * @param[in] sp pointer to a @p Semaphore structure 00117 * @param[in] n the new value of the semaphore counter. The value must 00118 * be non-negative. 00119 */ 00120 void chSemResetI(Semaphore *sp, cnt_t n) { 00121 cnt_t cnt; 00122 00123 chDbgCheck((sp != NULL) && (n >= 0), "chSemResetI"); 00124 00125 cnt = sp->s_cnt; 00126 sp->s_cnt = n; 00127 while (++cnt <= 0) 00128 chSchReadyI(lifo_remove(&sp->s_queue))->p_u.rdymsg = RDY_RESET; 00129 } 00130 00131 /** 00132 * @brief Performs a wait operation on a semaphore. 00133 * 00134 * @param[in] sp pointer to a @p Semaphore structure 00135 * @retval RDY_OK if the semaphore was signaled or not taken. 00136 * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). 00137 */ 00138 msg_t chSemWait(Semaphore *sp) { 00139 msg_t msg; 00140 00141 chSysLock(); 00142 msg = chSemWaitS(sp); 00143 chSysUnlock(); 00144 return msg; 00145 } 00146 00147 /** 00148 * @brief Performs a wait operation on a semaphore. 00149 * 00150 * @param[in] sp pointer to a @p Semaphore structure 00151 * @retval RDY_OK if the semaphore was signaled or not taken. 00152 * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). 00153 */ 00154 msg_t chSemWaitS(Semaphore *sp) { 00155 00156 chDbgCheck(sp != NULL, "chSemWaitS"); 00157 00158 if (--sp->s_cnt < 0) { 00159 currp->p_u.wtobjp = sp; 00160 sem_insert(currp, &sp->s_queue); 00161 chSchGoSleepS(THD_STATE_WTSEM); 00162 return currp->p_u.rdymsg; 00163 } 00164 return RDY_OK; 00165 } 00166 00167 /** 00168 * @brief Performs a wait operation on a semaphore with timeout specification. 00169 * 00170 * @param[in] sp pointer to a @p Semaphore structure 00171 * @param[in] time the number of ticks before the operation timeouts, 00172 * the following special values are allowed: 00173 * - @a TIME_IMMEDIATE immediate timeout. 00174 * - @a TIME_INFINITE no timeout. 00175 * . 00176 * @retval RDY_OK if the semaphore was signaled or not taken. 00177 * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). 00178 * @retval RDY_TIMEOUT if the semaphore was not signaled or reset within the 00179 * specified timeout. 00180 */ 00181 msg_t chSemWaitTimeout(Semaphore *sp, systime_t time) { 00182 msg_t msg; 00183 00184 chSysLock(); 00185 msg = chSemWaitTimeoutS(sp, time); 00186 chSysUnlock(); 00187 return msg; 00188 } 00189 00190 /** 00191 * @brief Performs a wait operation on a semaphore with timeout specification. 00192 * 00193 * @param[in] sp pointer to a @p Semaphore structure 00194 * @param[in] time the number of ticks before the operation timeouts, 00195 * the following special values are allowed: 00196 * - @a TIME_IMMEDIATE immediate timeout. 00197 * - @a TIME_INFINITE no timeout. 00198 * . 00199 * @retval RDY_OK if the semaphore was signaled or not taken. 00200 * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). 00201 * @retval RDY_TIMEOUT if the semaphore was not signaled or reset within the 00202 * specified timeout. 00203 */ 00204 msg_t chSemWaitTimeoutS(Semaphore *sp, systime_t time) { 00205 00206 chDbgCheck(sp != NULL, "chSemWaitTimeoutS"); 00207 00208 if (--sp->s_cnt < 0) { 00209 if (TIME_IMMEDIATE == time) { 00210 sp->s_cnt++; 00211 return RDY_TIMEOUT; 00212 } 00213 currp->p_u.wtobjp = sp; 00214 sem_insert(currp, &sp->s_queue); 00215 return chSchGoSleepTimeoutS(THD_STATE_WTSEM, time); 00216 } 00217 return RDY_OK; 00218 } 00219 00220 /** 00221 * @brief Performs a signal operation on a semaphore. 00222 * 00223 * @param[in] sp pointer to a @p Semaphore structure 00224 */ 00225 void chSemSignal(Semaphore *sp) { 00226 00227 chDbgCheck(sp != NULL, "chSemSignal"); 00228 00229 chSysLock(); 00230 if (++sp->s_cnt <= 0) 00231 chSchWakeupS(fifo_remove(&sp->s_queue), RDY_OK); 00232 chSysUnlock(); 00233 } 00234 00235 /** 00236 * @brief Performs a signal operation on a semaphore. 00237 * @note This function does not reschedule. 00238 * 00239 * @param[in] sp pointer to a @p Semaphore structure 00240 */ 00241 void chSemSignalI(Semaphore *sp) { 00242 00243 chDbgCheck(sp != NULL, "chSemSignalI"); 00244 00245 if (++sp->s_cnt <= 0) { 00246 /* note, it is done this way in order to allow a tail call on 00247 chSchReadyI().*/ 00248 Thread *tp = fifo_remove(&sp->s_queue); 00249 tp->p_u.rdymsg = RDY_OK; 00250 chSchReadyI(tp); 00251 } 00252 } 00253 00254 #if CH_USE_SEMSW 00255 /** 00256 * @brief Performs atomic signal and wait operations on two semaphores. 00257 * @note The function is available only if the @p CH_USE_SEMSW 00258 * option is enabled in @p chconf.h. 00259 * 00260 * @param[in] sps pointer to a @p Semaphore structure to be signaled 00261 * @param[in] spw pointer to a @p Semaphore structure to be wait on 00262 * @retval RDY_OK if the semaphore was signaled or not taken. 00263 * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). 00264 */ 00265 msg_t chSemSignalWait(Semaphore *sps, Semaphore *spw) { 00266 msg_t msg; 00267 00268 chDbgCheck((sps != NULL) && (spw != NULL), "chSemSignalWait"); 00269 00270 chSysLock(); 00271 if (++sps->s_cnt <= 0) 00272 chSchReadyI(fifo_remove(&sps->s_queue))->p_u.rdymsg = RDY_OK; 00273 if (--spw->s_cnt < 0) { 00274 Thread *ctp = currp; 00275 sem_insert(ctp, &spw->s_queue); 00276 ctp->p_u.wtobjp = spw; 00277 chSchGoSleepS(THD_STATE_WTSEM); 00278 msg = ctp->p_u.rdymsg; 00279 } 00280 else { 00281 chSchRescheduleS(); 00282 msg = RDY_OK; 00283 } 00284 chSysUnlock(); 00285 return msg; 00286 } 00287 #endif /* CH_USE_SEMSW */ 00288 00289 #endif /* CH_USE_SEMAPHORES */ 00290 00291 /** @} */