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