aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios/os/rt/src/chsem.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios/os/rt/src/chsem.c')
-rw-r--r--lib/chibios/os/rt/src/chsem.c405
1 files changed, 405 insertions, 0 deletions
diff --git a/lib/chibios/os/rt/src/chsem.c b/lib/chibios/os/rt/src/chsem.c
new file mode 100644
index 000000000..5f2ca8e21
--- /dev/null
+++ b/lib/chibios/os/rt/src/chsem.c
@@ -0,0 +1,405 @@
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 */
97void 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 */
118void chSemResetWithMessage(semaphore_t *sp, cnt_t n, msg_t msg) {
119
120 chSysLock();
121 chSemResetWithMessageI(sp, n, msg);
122 chSchRescheduleS();
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 */
143void chSemResetWithMessageI(semaphore_t *sp, cnt_t n, msg_t msg) {
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)) {
153 chSchReadyI((thread_t *)ch_queue_lifo_remove(&sp->queue))->u.rdymsg = msg;
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 */
169msg_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 */
191msg_t chSemWaitS(semaphore_t *sp) {
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 currp->u.wtsemp = sp;
201 sem_insert(currp, &sp->queue);
202 chSchGoSleepS(CH_STATE_WTSEM);
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 */
229msg_t chSemWaitTimeout(semaphore_t *sp, sysinterval_t timeout) {
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 */
258msg_t chSemWaitTimeoutS(semaphore_t *sp, sysinterval_t timeout) {
259
260 chDbgCheckClassS();
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 */
288void chSemSignal(semaphore_t *sp) {
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) {
297 chSchWakeupS((thread_t *)ch_queue_fifo_remove(&sp->queue), MSG_OK);
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 */
313void chSemSignalI(semaphore_t *sp) {
314
315 chDbgCheckClassI();
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().*/
324 thread_t *tp = (thread_t *)ch_queue_fifo_remove(&sp->queue);
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 */
343void chSemAddCounterI(semaphore_t *sp, cnt_t n) {
344
345 chDbgCheckClassI();
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) {
353 chSchReadyI((thread_t *)ch_queue_fifo_remove(&sp->queue))->u.rdymsg = MSG_OK;
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 */
372msg_t chSemSignalWait(semaphore_t *sps, semaphore_t *spw) {
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) {
385 chSchReadyI((thread_t *)ch_queue_fifo_remove(&sps->queue))->u.rdymsg = MSG_OK;
386 }
387 if (--spw->cnt < (cnt_t)0) {
388 thread_t *ctp = currp;
389 sem_insert(ctp, &spw->queue);
390 ctp->u.wtsemp = spw;
391 chSchGoSleepS(CH_STATE_WTSEM);
392 msg = ctp->u.rdymsg;
393 }
394 else {
395 chSchRescheduleS();
396 msg = MSG_OK;
397 }
398 chSysUnlock();
399
400 return msg;
401}
402
403#endif /* CH_CFG_USE_SEMAPHORES == TRUE */
404
405/** @} */