ChibiOS  21.6.0
rt/src/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(qp, tp) ch_sch_prio_insert(&tp->hdr.queue, qp)
80 #else
81 #define sem_insert(qp, tp) ch_queue_insert(qp, &tp->hdr.queue)
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  */
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  */
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  */
144 
145  chDbgCheckClassI();
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  */
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 
193  chDbgCheckClassS();
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  thread_t *currtp = chThdGetSelfX();
201  currtp->u.wtsemp = sp;
202  sem_insert(&sp->queue, currtp);
204 
205  return currtp->u.rdymsg;
206  }
207 
208  return MSG_OK;
209 }
210 
211 /**
212  * @brief Performs a wait operation on a semaphore with timeout specification.
213  *
214  * @param[in] sp pointer to a @p semaphore_t structure
215  * @param[in] timeout the number of ticks before the operation timeouts,
216  * the following special values are allowed:
217  * - @a TIME_IMMEDIATE immediate timeout.
218  * - @a TIME_INFINITE no timeout.
219  * .
220  * @return A message specifying how the invoking thread has been
221  * released from the semaphore.
222  * @retval MSG_OK if the thread has not stopped on the semaphore or the
223  * semaphore has been signaled.
224  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
225  * @retval MSG_TIMEOUT if the semaphore has not been signaled or reset within
226  * the specified timeout.
227  *
228  * @api
229  */
231  msg_t msg;
232 
233  chSysLock();
234  msg = chSemWaitTimeoutS(sp, timeout);
235  chSysUnlock();
236 
237  return msg;
238 }
239 
240 /**
241  * @brief Performs a wait operation on a semaphore with timeout specification.
242  *
243  * @param[in] sp pointer to a @p semaphore_t structure
244  * @param[in] timeout the number of ticks before the operation timeouts,
245  * the following special values are allowed:
246  * - @a TIME_IMMEDIATE immediate timeout.
247  * - @a TIME_INFINITE no timeout.
248  * .
249  * @return A message specifying how the invoking thread has been
250  * released from the semaphore.
251  * @retval MSG_OK if the thread has not stopped on the semaphore or the
252  * semaphore has been signaled.
253  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
254  * @retval MSG_TIMEOUT if the semaphore has not been signaled or reset within
255  * the specified timeout.
256  *
257  * @sclass
258  */
260 
261  chDbgCheckClassS();
262  chDbgCheck(sp != NULL);
263  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
264  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
265  "inconsistent semaphore");
266 
267  if (--sp->cnt < (cnt_t)0) {
268  if (unlikely(TIME_IMMEDIATE == timeout)) {
269  sp->cnt++;
270 
271  return MSG_TIMEOUT;
272  }
273  thread_t *currtp = chThdGetSelfX();
274  currtp->u.wtsemp = sp;
275  sem_insert(&sp->queue, currtp);
276 
277  return chSchGoSleepTimeoutS(CH_STATE_WTSEM, timeout);
278  }
279 
280  return MSG_OK;
281 }
282 
283 /**
284  * @brief Performs a signal operation on a semaphore.
285  *
286  * @param[in] sp pointer to a @p semaphore_t structure
287  *
288  * @api
289  */
291 
292  chDbgCheck(sp != NULL);
293 
294  chSysLock();
295  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
296  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
297  "inconsistent semaphore");
298  if (++sp->cnt <= (cnt_t)0) {
300  }
301  chSysUnlock();
302 }
303 
304 /**
305  * @brief Performs a signal operation on a semaphore.
306  * @post This function does not reschedule so a call to a rescheduling
307  * function must be performed before unlocking the kernel. Note that
308  * interrupt handlers always reschedule on exit so an explicit
309  * reschedule must not be performed in ISRs.
310  *
311  * @param[in] sp pointer to a @p semaphore_t structure
312  *
313  * @iclass
314  */
316 
317  chDbgCheckClassI();
318  chDbgCheck(sp != NULL);
319  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
320  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
321  "inconsistent semaphore");
322 
323  if (++sp->cnt <= (cnt_t)0) {
324  /* Note, it is done this way in order to allow a tail call on
325  chSchReadyI().*/
327  tp->u.rdymsg = MSG_OK;
328  (void) chSchReadyI(tp);
329  }
330 }
331 
332 /**
333  * @brief Adds the specified value to the semaphore counter.
334  * @post This function does not reschedule so a call to a rescheduling
335  * function must be performed before unlocking the kernel. Note that
336  * interrupt handlers always reschedule on exit so an explicit
337  * reschedule must not be performed in ISRs.
338  *
339  * @param[in] sp pointer to a @p semaphore_t structure
340  * @param[in] n value to be added to the semaphore counter. The value
341  * must be positive.
342  *
343  * @iclass
344  */
346 
347  chDbgCheckClassI();
348  chDbgCheck((sp != NULL) && (n > (cnt_t)0));
349  chDbgAssert(((sp->cnt >= (cnt_t)0) && ch_queue_isempty(&sp->queue)) ||
350  ((sp->cnt < (cnt_t)0) && ch_queue_notempty(&sp->queue)),
351  "inconsistent semaphore");
352 
353  while (n > (cnt_t)0) {
354  if (++sp->cnt <= (cnt_t)0) {
356  }
357  n--;
358  }
359 }
360 
361 /**
362  * @brief Performs atomic signal and wait operations on two semaphores.
363  *
364  * @param[in] sps pointer to a @p semaphore_t structure to be signaled
365  * @param[in] spw pointer to a @p semaphore_t structure to wait on
366  * @return A message specifying how the invoking thread has been
367  * released from the semaphore.
368  * @retval MSG_OK if the thread has not stopped on the semaphore or the
369  * semaphore has been signaled.
370  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
371  *
372  * @api
373  */
375  msg_t msg;
376 
377  chDbgCheck((sps != NULL) && (spw != NULL));
378 
379  chSysLock();
380  chDbgAssert(((sps->cnt >= (cnt_t)0) && ch_queue_isempty(&sps->queue)) ||
381  ((sps->cnt < (cnt_t)0) && ch_queue_notempty(&sps->queue)),
382  "inconsistent semaphore");
383  chDbgAssert(((spw->cnt >= (cnt_t)0) && ch_queue_isempty(&spw->queue)) ||
384  ((spw->cnt < (cnt_t)0) && ch_queue_notempty(&spw->queue)),
385  "inconsistent semaphore");
386  if (++sps->cnt <= (cnt_t)0) {
388  }
389  if (--spw->cnt < (cnt_t)0) {
390  thread_t *currtp = chThdGetSelfX();
391  sem_insert(&spw->queue, currtp);
392  currtp->u.wtsemp = spw;
394  msg = currtp->u.rdymsg;
395  }
396  else {
398  msg = MSG_OK;
399  }
400  chSysUnlock();
401 
402  return msg;
403 }
404 
405 #endif /* CH_CFG_USE_SEMAPHORES == TRUE */
406 
407 /** @} */
chSchReadyI
thread_t * chSchReadyI(thread_t *tp)
Inserts a thread in the Ready List placing it behind its peers.
Definition: chschd.c:276
ch_thread::rdymsg
msg_t rdymsg
Thread wakeup code.
Definition: chobjects.h:242
chSemAddCounterI
void chSemAddCounterI(semaphore_t *sp, cnt_t n)
Adds the specified value to the semaphore counter.
Definition: rt/src/chsem.c:345
chDbgAssert
#define chDbgAssert(c, r)
Condition assertion.
Definition: chdebug.h:144
chSemSignal
void chSemSignal(semaphore_t *sp)
Performs a signal operation on a semaphore.
Definition: rt/src/chsem.c:290
chSemWait
msg_t chSemWait(semaphore_t *sp)
Performs a wait operation on a semaphore.
Definition: rt/src/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:249
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:236
msg_t
int32_t msg_t
Definition: chearly.h:88
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:355
chDbgCheck
#define chDbgCheck(c)
Function parameters check.
Definition: chdebug.h:118
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: rt/src/chsem.c:191
ch_thread
Structure representing a thread.
Definition: chobjects.h:156
chSemResetWithMessageI
void chSemResetWithMessageI(semaphore_t *sp, cnt_t n, msg_t msg)
Performs a reset operation on the semaphore.
Definition: rt/src/chsem.c:143
chSchGoSleepS
void chSchGoSleepS(tstate_t newstate)
Puts the current thread to sleep into the specified state.
Definition: chschd.c:301
cnt_t
int32_t cnt_t
Definition: chearly.h:92
chSemObjectInit
void chSemObjectInit(semaphore_t *sp, cnt_t n)
Initializes a semaphore with the specified counter value.
Definition: rt/src/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:280
ch_semaphore
Semaphore structure.
Definition: rt/include/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: rt/include/chsem.h:53
chSchRescheduleS
void chSchRescheduleS(void)
Performs a reschedule if a higher priority thread is runnable.
Definition: chschd.c:454
ch_queue_init
static void ch_queue_init(ch_queue_t *qp)
Queue initialization.
Definition: chlists.h:222
MSG_OK
#define MSG_OK
Normal wakeup message.
Definition: chschd.h:39
ch_semaphore::cnt
cnt_t cnt
The semaphore counter.
Definition: rt/include/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:299
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: rt/src/chsem.c:230
chSemResetWithMessage
void chSemResetWithMessage(semaphore_t *sp, cnt_t n, msg_t msg)
Performs a reset operation on the semaphore.
Definition: rt/src/chsem.c:118
ch_thread::wtsemp
struct ch_semaphore * wtsemp
Pointer to a generic semaphore object.
Definition: chobjects.h:277
unlikely
#define unlikely(x)
Marks a boolean expression as likely false.
Definition: chearly.h:191
MSG_TIMEOUT
#define MSG_TIMEOUT
Wakeup caused by a timeout condition.
Definition: chschd.h:40
chSemSignalWait
msg_t chSemSignalWait(semaphore_t *sps, semaphore_t *spw)
Performs atomic signal and wait operations on two semaphores.
Definition: rt/src/chsem.c:374
chSemWaitTimeoutS
msg_t chSemWaitTimeoutS(semaphore_t *sp, sysinterval_t timeout)
Performs a wait operation on a semaphore with timeout specification.
Definition: rt/src/chsem.c:259
chSysUnlock
#define chSysUnlock()
Leaves the kernel lock state.
Definition: nil/include/ch.h:1053
chSemSignalI
void chSemSignalI(semaphore_t *sp)
Performs a signal operation on a semaphore.
Definition: rt/src/chsem.c:315
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:393
chThdGetSelfX
#define chThdGetSelfX()
Returns a pointer to the current thread_t.
Definition: nil/include/ch.h:1132
chSysLock
#define chSysLock()
Enters the kernel lock state.
Definition: nil/include/ch.h:1043