diff options
Diffstat (limited to 'lib/chibios/os/rt/src/chsem.c')
-rw-r--r-- | lib/chibios/os/rt/src/chsem.c | 405 |
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 | */ | ||
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); | ||
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 | */ | ||
143 | void 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 | */ | ||
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 | */ | ||
191 | msg_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 | */ | ||
229 | msg_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 | */ | ||
258 | msg_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 | */ | ||
288 | void 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 | */ | ||
313 | void 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 | */ | ||
343 | void 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 | */ | ||
372 | msg_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 | /** @} */ | ||