ChibiOS/RT  6.1.4
chsem.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006,2007,2008,2009,2010,2011,2012,2013,2014,
3  2015,2016,2017,2018,2019,2020,2021 Giovanni Di Sirio.
4 
5  This file is part of ChibiOS.
6 
7  ChibiOS is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation version 3 of the License.
10 
11  ChibiOS is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 /**
21  * @file rt/src/chsem.c
22  * @brief Semaphores code.
23  *
24  * @addtogroup semaphores
25  * @details Semaphores related APIs and services.
26  * <h2>Operation mode</h2>
27  * Semaphores are a flexible synchronization primitive, ChibiOS/RT
28  * implements semaphores in their "counting semaphores" variant as
29  * defined by Edsger Dijkstra plus several enhancements like:
30  * - Wait operation with timeout.
31  * - Reset operation.
32  * - Atomic wait+signal operation.
33  * - Return message from the wait operation (OK, RESET, TIMEOUT).
34  * .
35  * The binary semaphores variant can be easily implemented using
36  * counting semaphores.<br>
37  * Operations defined for semaphores:
38  * - <b>Signal</b>: The semaphore counter is increased and if the
39  * result is non-positive then a waiting thread is removed from
40  * the semaphore queue and made ready for execution.
41  * - <b>Wait</b>: The semaphore counter is decreased and if the result
42  * becomes negative the thread is queued in the semaphore and
43  * suspended.
44  * - <b>Reset</b>: The semaphore counter is reset to a non-negative
45  * value and all the threads in the queue are released.
46  * .
47  * Semaphores can be used as guards for mutual exclusion zones
48  * (note that mutexes are recommended for this kind of use) but
49  * also have other uses, queues guards and counters for example.<br>
50  * Semaphores usually use a FIFO queuing strategy but it is possible
51  * to make them order threads by priority by enabling
52  * @p CH_CFG_USE_SEMAPHORES_PRIORITY in @p chconf.h.
53  * @pre In order to use the semaphore APIs the @p CH_CFG_USE_SEMAPHORES
54  * option must be enabled in @p chconf.h.
55  * @{
56  */
57 
58 #include "ch.h"
59 
60 #if (CH_CFG_USE_SEMAPHORES == TRUE) || defined(__DOXYGEN__)
61 
62 /*===========================================================================*/
63 /* Module exported variables. */
64 /*===========================================================================*/
65 
66 /*===========================================================================*/
67 /* Module local types. */
68 /*===========================================================================*/
69 
70 /*===========================================================================*/
71 /* Module local variables. */
72 /*===========================================================================*/
73 
74 /*===========================================================================*/
75 /* Module local functions. */
76 /*===========================================================================*/
77 
78 #if CH_CFG_USE_SEMAPHORES_PRIORITY == TRUE
79 #define sem_insert(tp, qp) ch_sch_prio_insert(&tp->hdr.queue, qp)
80 #else
81 #define sem_insert(tp, qp) ch_queue_insert(&tp->hdr.queue, qp)
82 #endif
83 
84 /*===========================================================================*/
85 /* Module exported functions. */
86 /*===========================================================================*/
87 
88 /**
89  * @brief Initializes a semaphore with the specified counter value.
90  *
91  * @param[out] sp pointer to a @p semaphore_t structure
92  * @param[in] n initial value of the semaphore counter. Must be
93  * non-negative.
94  *
95  * @init
96  */
97 void chSemObjectInit(semaphore_t *sp, cnt_t n) {
98 
99  chDbgCheck((sp != NULL) && (n >= (cnt_t)0));
100 
101  ch_queue_init(&sp->queue);
102  sp->cnt = n;
103 }
104 
105 /**
106  * @brief Performs a reset operation on the semaphore.
107  * @post After invoking this function all the threads waiting on the
108  * semaphore, if any, are released and the semaphore counter is set
109  * to the specified, non negative, value.
110  *
111  * @param[in] sp pointer to a @p semaphore_t structure
112  * @param[in] n the new value of the semaphore counter. The value must
113  * be non-negative.
114  * @param[in] msg message to be sent
115  *
116  * @api
117  */
118 void chSemResetWithMessage(semaphore_t *sp, cnt_t n, msg_t msg) {
119 
120  chSysLock();
121  chSemResetWithMessageI(sp, n, msg);
123  chSysUnlock();
124 }
125 
126 /**
127  * @brief Performs a reset operation on the semaphore.
128  * @post After invoking this function all the threads waiting on the
129  * semaphore, if any, are released and the semaphore counter is set
130  * to the specified, non negative, value.
131  * @post This function does not reschedule so a call to a rescheduling
132  * function must be performed before unlocking the kernel. Note that
133  * interrupt handlers always reschedule on exit so an explicit
134  * reschedule must not be performed in ISRs.
135  *
136  * @param[in] sp pointer to a @p semaphore_t structure
137  * @param[in] n the new value of the semaphore counter. The value must
138  * be non-negative.
139  * @param[in] msg message to be sent
140  *
141  * @iclass
142  */
143 void chSemResetWithMessageI(semaphore_t *sp, cnt_t n, msg_t msg) {
144 
146  chDbgCheck((sp != NULL) && (n >= (cnt_t)0));
147  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
148  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
149  "inconsistent semaphore");
150 
151  sp->cnt = n;
152  while (ch_queue_notempty(&sp->queue)) {
154  }
155 }
156 
157 /**
158  * @brief Performs a wait operation on a semaphore.
159  *
160  * @param[in] sp pointer to a @p semaphore_t structure
161  * @return A message specifying how the invoking thread has been
162  * released from the semaphore.
163  * @retval MSG_OK if the thread has not stopped on the semaphore or the
164  * semaphore has been signaled.
165  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
166  *
167  * @api
168  */
169 msg_t chSemWait(semaphore_t *sp) {
170  msg_t msg;
171 
172  chSysLock();
173  msg = chSemWaitS(sp);
174  chSysUnlock();
175 
176  return msg;
177 }
178 
179 /**
180  * @brief Performs a wait operation on a semaphore.
181  *
182  * @param[in] sp pointer to a @p semaphore_t structure
183  * @return A message specifying how the invoking thread has been
184  * released from the semaphore.
185  * @retval MSG_OK if the thread has not stopped on the semaphore or the
186  * semaphore has been signaled.
187  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
188  *
189  * @sclass
190  */
192 
194  chDbgCheck(sp != NULL);
195  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
196  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
197  "inconsistent semaphore");
198 
199  if (--sp->cnt < (cnt_t)0) {
200  currp->u.wtsemp = sp;
201  sem_insert(currp, &sp->queue);
203 
204  return currp->u.rdymsg;
205  }
206 
207  return MSG_OK;
208 }
209 
210 /**
211  * @brief Performs a wait operation on a semaphore with timeout specification.
212  *
213  * @param[in] sp pointer to a @p semaphore_t structure
214  * @param[in] timeout the number of ticks before the operation timeouts,
215  * the following special values are allowed:
216  * - @a TIME_IMMEDIATE immediate timeout.
217  * - @a TIME_INFINITE no timeout.
218  * .
219  * @return A message specifying how the invoking thread has been
220  * released from the semaphore.
221  * @retval MSG_OK if the thread has not stopped on the semaphore or the
222  * semaphore has been signaled.
223  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
224  * @retval MSG_TIMEOUT if the semaphore has not been signaled or reset within
225  * the specified timeout.
226  *
227  * @api
228  */
230  msg_t msg;
231 
232  chSysLock();
233  msg = chSemWaitTimeoutS(sp, timeout);
234  chSysUnlock();
235 
236  return msg;
237 }
238 
239 /**
240  * @brief Performs a wait operation on a semaphore with timeout specification.
241  *
242  * @param[in] sp pointer to a @p semaphore_t structure
243  * @param[in] timeout the number of ticks before the operation timeouts,
244  * the following special values are allowed:
245  * - @a TIME_IMMEDIATE immediate timeout.
246  * - @a TIME_INFINITE no timeout.
247  * .
248  * @return A message specifying how the invoking thread has been
249  * released from the semaphore.
250  * @retval MSG_OK if the thread has not stopped on the semaphore or the
251  * semaphore has been signaled.
252  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
253  * @retval MSG_TIMEOUT if the semaphore has not been signaled or reset within
254  * the specified timeout.
255  *
256  * @sclass
257  */
259 
261  chDbgCheck(sp != NULL);
262  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
263  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
264  "inconsistent semaphore");
265 
266  if (--sp->cnt < (cnt_t)0) {
267  if (TIME_IMMEDIATE == timeout) {
268  sp->cnt++;
269 
270  return MSG_TIMEOUT;
271  }
272  currp->u.wtsemp = sp;
273  sem_insert(currp, &sp->queue);
274 
275  return chSchGoSleepTimeoutS(CH_STATE_WTSEM, timeout);
276  }
277 
278  return MSG_OK;
279 }
280 
281 /**
282  * @brief Performs a signal operation on a semaphore.
283  *
284  * @param[in] sp pointer to a @p semaphore_t structure
285  *
286  * @api
287  */
289 
290  chDbgCheck(sp != NULL);
291 
292  chSysLock();
293  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
294  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
295  "inconsistent semaphore");
296  if (++sp->cnt <= (cnt_t)0) {
298  }
299  chSysUnlock();
300 }
301 
302 /**
303  * @brief Performs a signal operation on a semaphore.
304  * @post This function does not reschedule so a call to a rescheduling
305  * function must be performed before unlocking the kernel. Note that
306  * interrupt handlers always reschedule on exit so an explicit
307  * reschedule must not be performed in ISRs.
308  *
309  * @param[in] sp pointer to a @p semaphore_t structure
310  *
311  * @iclass
312  */
314 
316  chDbgCheck(sp != NULL);
317  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
318  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
319  "inconsistent semaphore");
320 
321  if (++sp->cnt <= (cnt_t)0) {
322  /* Note, it is done this way in order to allow a tail call on
323  chSchReadyI().*/
325  tp->u.rdymsg = MSG_OK;
326  (void) chSchReadyI(tp);
327  }
328 }
329 
330 /**
331  * @brief Adds the specified value to the semaphore counter.
332  * @post This function does not reschedule so a call to a rescheduling
333  * function must be performed before unlocking the kernel. Note that
334  * interrupt handlers always reschedule on exit so an explicit
335  * reschedule must not be performed in ISRs.
336  *
337  * @param[in] sp pointer to a @p semaphore_t structure
338  * @param[in] n value to be added to the semaphore counter. The value
339  * must be positive.
340  *
341  * @iclass
342  */
343 void chSemAddCounterI(semaphore_t *sp, cnt_t n) {
344 
346  chDbgCheck((sp != NULL) && (n > (cnt_t)0));
347  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
348  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
349  "inconsistent semaphore");
350 
351  while (n > (cnt_t)0) {
352  if (++sp->cnt <= (cnt_t)0) {
354  }
355  n--;
356  }
357 }
358 
359 /**
360  * @brief Performs atomic signal and wait operations on two semaphores.
361  *
362  * @param[in] sps pointer to a @p semaphore_t structure to be signaled
363  * @param[in] spw pointer to a @p semaphore_t structure to wait on
364  * @return A message specifying how the invoking thread has been
365  * released from the semaphore.
366  * @retval MSG_OK if the thread has not stopped on the semaphore or the
367  * semaphore has been signaled.
368  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
369  *
370  * @api
371  */
373  msg_t msg;
374 
375  chDbgCheck((sps != NULL) && (spw != NULL));
376 
377  chSysLock();
378  chDbgAssert(((sps->cnt >= (cnt_t)0) && ch_queue_isempty(&sps->queue)) ||
379  ((sps->cnt < (cnt_t)0) && ch_queue_notempty(&sps->queue)),
380  "inconsistent semaphore");
381  chDbgAssert(((spw->cnt >= (cnt_t)0) && ch_queue_isempty(&spw->queue)) ||
382  ((spw->cnt < (cnt_t)0) && ch_queue_notempty(&spw->queue)),
383  "inconsistent semaphore");
384  if (++sps->cnt <= (cnt_t)0) {
386  }
387  if (--spw->cnt < (cnt_t)0) {
388  thread_t *ctp = currp;
389  sem_insert(ctp, &spw->queue);
390  ctp->u.wtsemp = spw;
392  msg = ctp->u.rdymsg;
393  }
394  else {
396  msg = MSG_OK;
397  }
398  chSysUnlock();
399 
400  return msg;
401 }
402 
403 #endif /* CH_CFG_USE_SEMAPHORES == TRUE */
404 
405 /** @} */
chSysLock
static void chSysLock(void)
Enters the kernel lock state.
Definition: chsys.h:355
chSchReadyI
thread_t * chSchReadyI(thread_t *tp)
Inserts a thread in the Ready List placing it behind its peers.
Definition: chschd.c:153
currp
#define currp
Current thread pointer access macro.
Definition: chschd.h:462
ch_thread::rdymsg
msg_t rdymsg
Thread wakeup code.
Definition: chschd.h:201
chSemAddCounterI
void chSemAddCounterI(semaphore_t *sp, cnt_t n)
Adds the specified value to the semaphore counter.
Definition: chsem.c:343
chDbgAssert
#define chDbgAssert(c, r)
Condition assertion.
Definition: chdebug.h:127
chSemSignal
void chSemSignal(semaphore_t *sp)
Performs a signal operation on a semaphore.
Definition: chsem.c:288
chSemWait
msg_t chSemWait(semaphore_t *sp)
Performs a wait operation on a semaphore.
Definition: chsem.c:169
ch_queue_notempty
static bool ch_queue_notempty(const ch_queue_t *qp)
Evaluates to true if the specified queue is not empty.
Definition: chlists.h:234
ch_queue_isempty
static bool ch_queue_isempty(const ch_queue_t *qp)
Evaluates to true if the specified queue is empty.
Definition: chlists.h:221
chSchGoSleepTimeoutS
msg_t chSchGoSleepTimeoutS(tstate_t newstate, sysinterval_t timeout)
Puts the current thread to sleep into the specified state with timeout specification.
Definition: chschd.c:258
chDbgCheck
#define chDbgCheck(c)
Function parameters check.
Definition: chdebug.h:101
TIME_IMMEDIATE
#define TIME_IMMEDIATE
Zero interval specification for some functions with a timeout specification.
Definition: chtime.h:47
chSemWaitS
msg_t chSemWaitS(semaphore_t *sp)
Performs a wait operation on a semaphore.
Definition: chsem.c:191
ch_thread
Structure representing a thread.
Definition: chschd.h:134
chSemResetWithMessageI
void chSemResetWithMessageI(semaphore_t *sp, cnt_t n, msg_t msg)
Performs a reset operation on the semaphore.
Definition: chsem.c:143
chSchGoSleepS
void chSchGoSleepS(tstate_t newstate)
Puts the current thread to sleep into the specified state.
Definition: chschd.c:210
chSemObjectInit
void chSemObjectInit(semaphore_t *sp, cnt_t n)
Initializes a semaphore with the specified counter value.
Definition: chsem.c:97
ch_queue_fifo_remove
static ch_queue_t * ch_queue_fifo_remove(ch_queue_t *qp)
Removes the first-out element from a queue and returns it.
Definition: chlists.h:265
ch_semaphore
Semaphore structure.
Definition: chsem.h:52
ch_thread::u
union ch_thread::@1 u
State-specific fields.
ch_semaphore::queue
ch_queue_t queue
Queue of the threads sleeping on this semaphore.
Definition: chsem.h:53
chSchRescheduleS
void chSchRescheduleS(void)
Performs a reschedule if a higher priority thread is runnable.
Definition: chschd.c:339
ch_queue_init
static void ch_queue_init(ch_queue_t *qp)
Queue initialization.
Definition: chlists.h:207
chDbgCheckClassI
void chDbgCheckClassI(void)
I-class functions context check.
Definition: chdebug.c:233
MSG_OK
#define MSG_OK
Normal wakeup message.
Definition: chschd.h:39
ch_semaphore::cnt
cnt_t cnt
The semaphore counter.
Definition: chsem.h:55
ch_queue_lifo_remove
static ch_queue_t * ch_queue_lifo_remove(ch_queue_t *qp)
Removes the last-out element from a queue and returns it.
Definition: chlists.h:284
sysinterval_t
uint64_t sysinterval_t
Type of time interval.
Definition: chtime.h:119
chSemWaitTimeout
msg_t chSemWaitTimeout(semaphore_t *sp, sysinterval_t timeout)
Performs a wait operation on a semaphore with timeout specification.
Definition: chsem.c:229
chSemResetWithMessage
void chSemResetWithMessage(semaphore_t *sp, cnt_t n, msg_t msg)
Performs a reset operation on the semaphore.
Definition: chsem.c:118
ch_thread::wtsemp
struct ch_semaphore * wtsemp
Pointer to a generic semaphore object.
Definition: chschd.h:236
ch.h
ChibiOS/RT main include file.
MSG_TIMEOUT
#define MSG_TIMEOUT
Wakeup caused by a timeout condition.
Definition: chschd.h:40
chSysUnlock
static void chSysUnlock(void)
Leaves the kernel lock state.
Definition: chsys.h:367
chSemSignalWait
msg_t chSemSignalWait(semaphore_t *sps, semaphore_t *spw)
Performs atomic signal and wait operations on two semaphores.
Definition: chsem.c:372
chDbgCheckClassS
void chDbgCheckClassS(void)
S-class functions context check.
Definition: chdebug.c:248
chSemWaitTimeoutS
msg_t chSemWaitTimeoutS(semaphore_t *sp, sysinterval_t timeout)
Performs a wait operation on a semaphore with timeout specification.
Definition: chsem.c:258
chSemSignalI
void chSemSignalI(semaphore_t *sp)
Performs a signal operation on a semaphore.
Definition: chsem.c:313
CH_STATE_WTSEM
#define CH_STATE_WTSEM
On a semaphore.
Definition: chschd.h:68
chSchWakeupS
void chSchWakeupS(thread_t *ntp, msg_t msg)
Wakes up a thread.
Definition: chschd.c:295