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