ChibiOS/RT  6.0.3
chsem.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 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; either version 3 of the License, or
9  (at your option) any later version.
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 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) queue_prio_insert(tp, qp)
80 #else
81 #define sem_insert(tp, qp) queue_insert(tp, 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  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  * @note The released threads can recognize they were waked up by a reset
111  * rather than a signal because the @p chSemWait() will return
112  * @p MSG_RESET instead of @p MSG_OK.
113  *
114  * @param[in] sp pointer to a @p semaphore_t structure
115  * @param[in] n the new value of the semaphore counter. The value must
116  * be non-negative.
117  *
118  * @api
119  */
120 void chSemReset(semaphore_t *sp, cnt_t n) {
121 
122  chSysLock();
123  chSemResetI(sp, n);
125  chSysUnlock();
126 }
127 
128 /**
129  * @brief Performs a reset operation on the semaphore.
130  * @post After invoking this function all the threads waiting on the
131  * semaphore, if any, are released and the semaphore counter is set
132  * to the specified, non negative, value.
133  * @post This function does not reschedule so a call to a rescheduling
134  * function must be performed before unlocking the kernel. Note that
135  * interrupt handlers always reschedule on exit so an explicit
136  * reschedule must not be performed in ISRs.
137  * @note The released threads can recognize they were waked up by a reset
138  * rather than a signal because the @p chSemWait() will return
139  * @p MSG_RESET instead of @p MSG_OK.
140  *
141  * @param[in] sp pointer to a @p semaphore_t structure
142  * @param[in] n the new value of the semaphore counter. The value must
143  * be non-negative.
144  *
145  * @iclass
146  */
147 void chSemResetI(semaphore_t *sp, cnt_t n) {
148  cnt_t cnt;
149 
151  chDbgCheck((sp != NULL) && (n >= (cnt_t)0));
152  chDbgAssert(((sp->cnt >= (cnt_t)0) && queue_isempty(&sp->queue)) ||
153  ((sp->cnt < (cnt_t)0) && queue_notempty(&sp->queue)),
154  "inconsistent semaphore");
155 
156  cnt = sp->cnt;
157  sp->cnt = n;
158  while (++cnt <= (cnt_t)0) {
160  }
161 }
162 
163 /**
164  * @brief Performs a wait operation on a semaphore.
165  *
166  * @param[in] sp pointer to a @p semaphore_t structure
167  * @return A message specifying how the invoking thread has been
168  * released from the semaphore.
169  * @retval MSG_OK if the thread has not stopped on the semaphore or the
170  * semaphore has been signaled.
171  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
172  *
173  * @api
174  */
175 msg_t chSemWait(semaphore_t *sp) {
176  msg_t msg;
177 
178  chSysLock();
179  msg = chSemWaitS(sp);
180  chSysUnlock();
181 
182  return msg;
183 }
184 
185 /**
186  * @brief Performs a wait operation on a semaphore.
187  *
188  * @param[in] sp pointer to a @p semaphore_t structure
189  * @return A message specifying how the invoking thread has been
190  * released from the semaphore.
191  * @retval MSG_OK if the thread has not stopped on the semaphore or the
192  * semaphore has been signaled.
193  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
194  *
195  * @sclass
196  */
198 
200  chDbgCheck(sp != NULL);
201  chDbgAssert(((sp->cnt >= (cnt_t)0) && queue_isempty(&sp->queue)) ||
202  ((sp->cnt < (cnt_t)0) && queue_notempty(&sp->queue)),
203  "inconsistent semaphore");
204 
205  if (--sp->cnt < (cnt_t)0) {
206  currp->u.wtsemp = sp;
207  sem_insert(currp, &sp->queue);
209 
210  return currp->u.rdymsg;
211  }
212 
213  return MSG_OK;
214 }
215 
216 /**
217  * @brief Performs a wait operation on a semaphore with timeout specification.
218  *
219  * @param[in] sp pointer to a @p semaphore_t structure
220  * @param[in] timeout the number of ticks before the operation timeouts,
221  * the following special values are allowed:
222  * - @a TIME_IMMEDIATE immediate timeout.
223  * - @a TIME_INFINITE no timeout.
224  * .
225  * @return A message specifying how the invoking thread has been
226  * released from the semaphore.
227  * @retval MSG_OK if the thread has not stopped on the semaphore or the
228  * semaphore has been signaled.
229  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
230  * @retval MSG_TIMEOUT if the semaphore has not been signaled or reset within
231  * the specified timeout.
232  *
233  * @api
234  */
236  msg_t msg;
237 
238  chSysLock();
239  msg = chSemWaitTimeoutS(sp, timeout);
240  chSysUnlock();
241 
242  return msg;
243 }
244 
245 /**
246  * @brief Performs a wait operation on a semaphore with timeout specification.
247  *
248  * @param[in] sp pointer to a @p semaphore_t structure
249  * @param[in] timeout the number of ticks before the operation timeouts,
250  * the following special values are allowed:
251  * - @a TIME_IMMEDIATE immediate timeout.
252  * - @a TIME_INFINITE no timeout.
253  * .
254  * @return A message specifying how the invoking thread has been
255  * released from the semaphore.
256  * @retval MSG_OK if the thread has not stopped on the semaphore or the
257  * semaphore has been signaled.
258  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
259  * @retval MSG_TIMEOUT if the semaphore has not been signaled or reset within
260  * the specified timeout.
261  *
262  * @sclass
263  */
265 
267  chDbgCheck(sp != NULL);
268  chDbgAssert(((sp->cnt >= (cnt_t)0) && queue_isempty(&sp->queue)) ||
269  ((sp->cnt < (cnt_t)0) && queue_notempty(&sp->queue)),
270  "inconsistent semaphore");
271 
272  if (--sp->cnt < (cnt_t)0) {
273  if (TIME_IMMEDIATE == timeout) {
274  sp->cnt++;
275 
276  return MSG_TIMEOUT;
277  }
278  currp->u.wtsemp = sp;
279  sem_insert(currp, &sp->queue);
280 
281  return chSchGoSleepTimeoutS(CH_STATE_WTSEM, timeout);
282  }
283 
284  return MSG_OK;
285 }
286 
287 /**
288  * @brief Performs a signal operation on a semaphore.
289  *
290  * @param[in] sp pointer to a @p semaphore_t structure
291  *
292  * @api
293  */
295 
296  chDbgCheck(sp != NULL);
297 
298  chSysLock();
299  chDbgAssert(((sp->cnt >= (cnt_t)0) && queue_isempty(&sp->queue)) ||
300  ((sp->cnt < (cnt_t)0) && queue_notempty(&sp->queue)),
301  "inconsistent semaphore");
302  if (++sp->cnt <= (cnt_t)0) {
304  }
305  chSysUnlock();
306 }
307 
308 /**
309  * @brief Performs a signal operation on a semaphore.
310  * @post This function does not reschedule so a call to a rescheduling
311  * function must be performed before unlocking the kernel. Note that
312  * interrupt handlers always reschedule on exit so an explicit
313  * reschedule must not be performed in ISRs.
314  *
315  * @param[in] sp pointer to a @p semaphore_t structure
316  *
317  * @iclass
318  */
320 
322  chDbgCheck(sp != NULL);
323  chDbgAssert(((sp->cnt >= (cnt_t)0) && queue_isempty(&sp->queue)) ||
324  ((sp->cnt < (cnt_t)0) && queue_notempty(&sp->queue)),
325  "inconsistent semaphore");
326 
327  if (++sp->cnt <= (cnt_t)0) {
328  /* Note, it is done this way in order to allow a tail call on
329  chSchReadyI().*/
330  thread_t *tp = queue_fifo_remove(&sp->queue);
331  tp->u.rdymsg = MSG_OK;
332  (void) chSchReadyI(tp);
333  }
334 }
335 
336 /**
337  * @brief Adds the specified value to the semaphore counter.
338  * @post This function does not reschedule so a call to a rescheduling
339  * function must be performed before unlocking the kernel. Note that
340  * interrupt handlers always reschedule on exit so an explicit
341  * reschedule must not be performed in ISRs.
342  *
343  * @param[in] sp pointer to a @p semaphore_t structure
344  * @param[in] n value to be added to the semaphore counter. The value
345  * must be positive.
346  *
347  * @iclass
348  */
349 void chSemAddCounterI(semaphore_t *sp, cnt_t n) {
350 
352  chDbgCheck((sp != NULL) && (n > (cnt_t)0));
353  chDbgAssert(((sp->cnt >= (cnt_t)0) && queue_isempty(&sp->queue)) ||
354  ((sp->cnt < (cnt_t)0) && queue_notempty(&sp->queue)),
355  "inconsistent semaphore");
356 
357  while (n > (cnt_t)0) {
358  if (++sp->cnt <= (cnt_t)0) {
360  }
361  n--;
362  }
363 }
364 
365 /**
366  * @brief Performs atomic signal and wait operations on two semaphores.
367  *
368  * @param[in] sps pointer to a @p semaphore_t structure to be signaled
369  * @param[in] spw pointer to a @p semaphore_t structure to wait on
370  * @return A message specifying how the invoking thread has been
371  * released from the semaphore.
372  * @retval MSG_OK if the thread has not stopped on the semaphore or the
373  * semaphore has been signaled.
374  * @retval MSG_RESET if the semaphore has been reset using @p chSemReset().
375  *
376  * @api
377  */
379  msg_t msg;
380 
381  chDbgCheck((sps != NULL) && (spw != NULL));
382 
383  chSysLock();
384  chDbgAssert(((sps->cnt >= (cnt_t)0) && queue_isempty(&sps->queue)) ||
385  ((sps->cnt < (cnt_t)0) && queue_notempty(&sps->queue)),
386  "inconsistent semaphore");
387  chDbgAssert(((spw->cnt >= (cnt_t)0) && queue_isempty(&spw->queue)) ||
388  ((spw->cnt < (cnt_t)0) && queue_notempty(&spw->queue)),
389  "inconsistent semaphore");
390  if (++sps->cnt <= (cnt_t)0) {
392  }
393  if (--spw->cnt < (cnt_t)0) {
394  thread_t *ctp = currp;
395  sem_insert(ctp, &spw->queue);
396  ctp->u.wtsemp = spw;
398  msg = ctp->u.rdymsg;
399  }
400  else {
402  msg = MSG_OK;
403  }
404  chSysUnlock();
405 
406  return msg;
407 }
408 
409 #endif /* CH_CFG_USE_SEMAPHORES == TRUE */
410 
411 /** @} */
msg_t chSemWait(semaphore_t *sp)
Performs a wait operation on a semaphore.
Definition: chsem.c:175
cnt_t cnt
The semaphore counter.
Definition: chsem.h:55
void chSemAddCounterI(semaphore_t *sp, cnt_t n)
Adds the specified value to the semaphore counter.
Definition: chsem.c:349
uint64_t sysinterval_t
Type of time interval.
Definition: chtime.h:119
static void chSysLock(void)
Enters the kernel lock state.
Definition: chsys.h:353
msg_t chSemWaitTimeoutS(semaphore_t *sp, sysinterval_t timeout)
Performs a wait operation on a semaphore with timeout specification.
Definition: chsem.c:264
msg_t chSemWaitS(semaphore_t *sp)
Performs a wait operation on a semaphore.
Definition: chsem.c:197
thread_t * chSchReadyI(thread_t *tp)
Inserts a thread in the Ready List placing it behind its peers.
Definition: chschd.c:218
void chSemSignal(semaphore_t *sp)
Performs a signal operation on a semaphore.
Definition: chsem.c:294
#define currp
Current thread pointer access macro.
Definition: chschd.h:459
msg_t rdymsg
Thread wakeup code.
Definition: chschd.h:216
static void chSysUnlock(void)
Leaves the kernel lock state.
Definition: chsys.h:365
#define MSG_TIMEOUT
Wakeup caused by a timeout condition.
Definition: chschd.h:40
void chSchRescheduleS(void)
Performs a reschedule if a higher priority thread is runnable.
Definition: chschd.c:456
void chSemReset(semaphore_t *sp, cnt_t n)
Performs a reset operation on the semaphore.
Definition: chsem.c:120
#define chDbgCheck(c)
Function parameters check.
Definition: chdebug.h:101
void chSchGoSleepS(tstate_t newstate)
Puts the current thread to sleep into the specified state.
Definition: chschd.c:289
static void queue_init(threads_queue_t *tqp)
Threads queue initialization.
Definition: chschd.h:548
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:375
Semaphore structure.
Definition: chsem.h:52
#define MSG_OK
Normal wakeup message.
Definition: chschd.h:39
threads_queue_t queue
Queue of the threads sleeping on this semaphore.
Definition: chsem.h:53
thread_t * queue_lifo_remove(threads_queue_t *tqp)
Removes the last-out thread from a queue and returns it.
Definition: chschd.c:143
static bool queue_isempty(const threads_queue_t *tqp)
Evaluates to true if the specified threads queue is empty.
Definition: chschd.h:562
msg_t chSemSignalWait(semaphore_t *sps, semaphore_t *spw)
Performs atomic signal and wait operations on two semaphores.
Definition: chsem.c:378
void chSemObjectInit(semaphore_t *sp, cnt_t n)
Initializes a semaphore with the specified counter value.
Definition: chsem.c:97
#define CH_STATE_WTSEM
On a semaphore.
Definition: chschd.h:72
union ch_thread::@0 u
State-specific fields.
msg_t chSemWaitTimeout(semaphore_t *sp, sysinterval_t timeout)
Performs a wait operation on a semaphore with timeout specification.
Definition: chsem.c:235
struct ch_semaphore * wtsemp
Pointer to a generic semaphore object.
Definition: chschd.h:251
#define TIME_IMMEDIATE
Zero interval specification for some functions with a timeout specification.
Definition: chtime.h:47
void chDbgCheckClassI(void)
I-class functions context check.
Definition: chdebug.c:233
static bool queue_notempty(const threads_queue_t *tqp)
Evaluates to true if the specified threads queue is not empty.
Definition: chschd.h:575
void chSchWakeupS(thread_t *ntp, msg_t msg)
Wakes up a thread.
Definition: chschd.c:412
#define chDbgAssert(c, r)
Condition assertion.
Definition: chdebug.h:127
void chDbgCheckClassS(void)
S-class functions context check.
Definition: chdebug.c:248
ChibiOS/RT main include file.
void chSemSignalI(semaphore_t *sp)
Performs a signal operation on a semaphore.
Definition: chsem.c:319
thread_t * queue_fifo_remove(threads_queue_t *tqp)
Removes the first-out thread from a queue and returns it.
Definition: chschd.c:124
void chSemResetI(semaphore_t *sp, cnt_t n)
Performs a reset operation on the semaphore.
Definition: chsem.c:147
#define MSG_RESET
Wakeup caused by a reset condition.
Definition: chschd.h:43
Structure representing a thread.
Definition: chschd.h:153