ChibiOS 21.11.4
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(qp, &tp->hdr.queue)
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
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
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
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
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
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/** @} */
#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:144
#define chDbgCheck(c)
Function parameters check.
Definition chdebug.h:118
#define chDbgCheckClassS()
Definition chdebug.h:100
#define chDbgCheckClassI()
Definition chdebug.h:99
static void ch_queue_init(ch_queue_t *qp)
Queue initialization.
Definition chlists.h:222
static bool ch_queue_notempty(const ch_queue_t *qp)
Evaluates to true if the specified queue is not empty.
Definition chlists.h:249
static bool ch_queue_isempty(const ch_queue_t *qp)
Evaluates to true if the specified queue is empty.
Definition chlists.h:236
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
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
#define threadref(p)
Safe cast of a queue pointer to a thread pointer.
Definition chearly.h:206
int32_t cnt_t
Definition chearly.h:92
int32_t msg_t
Definition chearly.h:88
#define unlikely(x)
Marks a boolean expression as likely false.
Definition chearly.h:191
struct ch_thread thread_t
Type of a thread structure.
Definition chearly.h:133
void chSchRescheduleS(void)
Performs a reschedule if a higher priority thread is runnable.
Definition chschd.c:458
#define MSG_OK
Normal wakeup message.
Definition chschd.h:39
thread_t * chSchReadyI(thread_t *tp)
Inserts a thread in the Ready List placing it behind its peers.
Definition chschd.c:280
#define MSG_TIMEOUT
Wakeup caused by a timeout condition.
Definition chschd.h:40
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:359
#define CH_STATE_WTSEM
On a semaphore.
Definition chschd.h:68
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:47
uint64_t sysinterval_t
Type of time interval.
Definition chtime.h:119
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:242
struct ch_semaphore * wtsemp
Pointer to a generic semaphore object.
Definition chobjects.h:277
union ch_thread::@250330312022121344252011223135034045240103044261 u
State-specific fields.