aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios/os/nil/src/ch.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios/os/nil/src/ch.c')
-rw-r--r--lib/chibios/os/nil/src/ch.c1080
1 files changed, 1080 insertions, 0 deletions
diff --git a/lib/chibios/os/nil/src/ch.c b/lib/chibios/os/nil/src/ch.c
new file mode 100644
index 000000000..b8c81cacb
--- /dev/null
+++ b/lib/chibios/os/nil/src/ch.c
@@ -0,0 +1,1080 @@
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 nil/src/ch.c
22 * @brief Nil RTOS main source file.
23 *
24 * @addtogroup NIL_KERNEL
25 * @{
26 */
27
28#include "ch.h"
29
30/*===========================================================================*/
31/* Module local definitions. */
32/*===========================================================================*/
33
34/*===========================================================================*/
35/* Module exported variables. */
36/*===========================================================================*/
37
38/**
39 * @brief System data structures.
40 */
41nil_system_t nil;
42
43/*===========================================================================*/
44/* Module local variables. */
45/*===========================================================================*/
46
47/*===========================================================================*/
48/* Module local functions. */
49/*===========================================================================*/
50
51/*===========================================================================*/
52/* Module interrupt handlers. */
53/*===========================================================================*/
54
55/*===========================================================================*/
56/* Module exported functions. */
57/*===========================================================================*/
58
59/**
60 * @brief Retrieves the highest priority thread in the specified state and
61 * associated to the specified object.
62 *
63 * @param[in] state thread state
64 * @param[in] p object pointer
65 * @return The pointer to the found thread.
66 * @retval NULL if the thread is not found.
67 *
68 * @notapi
69 */
70thread_t *nil_find_thread(tstate_t state, void *p) {
71 thread_t *tp = nil.threads;
72
73 while (tp < &nil.threads[CH_CFG_MAX_THREADS]) {
74 /* Is this thread matching?*/
75 if ((tp->state == state) && (tp->u1.p == p)) {
76 return tp;
77 }
78 tp++;
79 }
80 return NULL;
81}
82
83/**
84 * @brief Puts in ready state all thread matching the specified status and
85 * associated object.
86 *
87 * @param[in] p object pointer
88 * @param[in] cnt number of threads to be readied as a negative number,
89 * non negative numbers are ignored
90 * @param[in] msg the wakeup message
91 * @return The number of readied threads.
92 *
93 * @notapi
94 */
95cnt_t nil_ready_all(void *p, cnt_t cnt, msg_t msg) {
96 thread_t *tp = nil.threads;;
97
98 while (cnt < (cnt_t)0) {
99
100 chDbgAssert(tp < &nil.threads[CH_CFG_MAX_THREADS],
101 "pointer out of range");
102
103 /* Is this thread waiting on this queue?*/
104 if ((tp->state == NIL_STATE_WTQUEUE) && (tp->u1.p == p)) {
105 cnt++;
106 (void) chSchReadyI(tp, msg);
107 }
108 tp++;
109 }
110
111 return cnt;
112}
113
114#if (CH_DBG_SYSTEM_STATE_CHECK == TRUE) || defined(__DOXYGEN__)
115/**
116 * @brief Guard code for @p chSysDisable().
117 *
118 * @notapi
119 */
120void _dbg_check_disable(void) {
121
122 if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) {
123 chSysHalt("SV#1");
124 }
125}
126
127/**
128 * @brief Guard code for @p chSysSuspend().
129 *
130 * @notapi
131 */
132void _dbg_check_suspend(void) {
133
134 if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) {
135 chSysHalt("SV#2");
136 }
137}
138
139/**
140 * @brief Guard code for @p chSysEnable().
141 *
142 * @notapi
143 */
144void _dbg_check_enable(void) {
145
146 if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) {
147 chSysHalt("SV#3");
148 }
149}
150
151/**
152 * @brief Guard code for @p chSysLock().
153 *
154 * @notapi
155 */
156void _dbg_check_lock(void) {
157
158 if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) {
159 chSysHalt("SV#4");
160 }
161 _dbg_enter_lock();
162}
163
164/**
165 * @brief Guard code for @p chSysUnlock().
166 *
167 * @notapi
168 */
169void _dbg_check_unlock(void) {
170
171 if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt <= (cnt_t)0)) {
172 chSysHalt("SV#5");
173 }
174 _dbg_leave_lock();
175}
176
177/**
178 * @brief Guard code for @p chSysLockFromIsr().
179 *
180 * @notapi
181 */
182void _dbg_check_lock_from_isr(void) {
183
184 if ((nil.isr_cnt <= (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) {
185 chSysHalt("SV#6");
186 }
187 _dbg_enter_lock();
188}
189
190/**
191 * @brief Guard code for @p chSysUnlockFromIsr().
192 *
193 * @notapi
194 */
195void _dbg_check_unlock_from_isr(void) {
196
197 if ((nil.isr_cnt <= (cnt_t)0) || (nil.lock_cnt <= (cnt_t)0)) {
198 chSysHalt("SV#7");
199 }
200 _dbg_leave_lock();
201}
202
203/**
204 * @brief Guard code for @p CH_IRQ_PROLOGUE().
205 *
206 * @notapi
207 */
208void _dbg_check_enter_isr(void) {
209
210 port_lock_from_isr();
211 if ((nil.isr_cnt < (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) {
212 chSysHalt("SV#8");
213 }
214 nil.isr_cnt++;
215 port_unlock_from_isr();
216}
217
218/**
219 * @brief Guard code for @p CH_IRQ_EPILOGUE().
220 *
221 * @notapi
222 */
223void _dbg_check_leave_isr(void) {
224
225 port_lock_from_isr();
226 if ((nil.isr_cnt <= (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) {
227 chSysHalt("SV#9");
228 }
229 nil.isr_cnt--;
230 port_unlock_from_isr();
231}
232
233/**
234 * @brief I-class functions context check.
235 * @details Verifies that the system is in an appropriate state for invoking
236 * an I-class API function. A panic is generated if the state is
237 * not compatible.
238 *
239 * @api
240 */
241void chDbgCheckClassI(void) {
242
243 if ((nil.isr_cnt < (cnt_t)0) || (nil.lock_cnt <= (cnt_t)0)) {
244 chSysHalt("SV#10");
245 }
246}
247
248/**
249 * @brief S-class functions context check.
250 * @details Verifies that the system is in an appropriate state for invoking
251 * an S-class API function. A panic is generated if the state is
252 * not compatible.
253 *
254 * @api
255 */
256void chDbgCheckClassS(void) {
257
258 if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt <= (cnt_t)0)) {
259 chSysHalt("SV#11");
260 }
261}
262#endif /* CH_DBG_SYSTEM_STATE_CHECK == TRUE */
263
264/**
265 * @brief Initializes the kernel.
266 * @details Initializes the kernel structures, the current instructions flow
267 * becomes the idle thread upon return. The idle thread must not
268 * invoke any kernel primitive able to change state to not runnable.
269 * @note This function assumes that the @p nil global variable has been
270 * zeroed by the runtime environment. If this is not the case then
271 * make sure to clear it before calling this function.
272 *
273 * @special
274 */
275void chSysInit(void) {
276 const thread_descriptor_t *tdp;
277
278 /* Optional library modules.*/
279 _oslib_init();
280
281 /* Architecture layer initialization.*/
282 port_init();
283
284 /* System initialization hook.*/
285 CH_CFG_SYSTEM_INIT_HOOK();
286
287 /* Making idle the current thread, this may change after rescheduling.*/
288 nil.next = nil.current = &nil.threads[CH_CFG_MAX_THREADS];
289 nil.current->state = NIL_STATE_READY;
290
291#if CH_DBG_ENABLE_STACK_CHECK == TRUE
292 /* The idle thread is a special case because its stack is set up by the
293 runtime environment.*/
294 nil.threads[CH_CFG_MAX_THREADS].wabase = THD_IDLE_BASE;
295#endif
296
297 /* Interrupts partially enabled. It is equivalent to entering the
298 kernel critical zone.*/
299 chSysSuspend();
300#if CH_DBG_SYSTEM_STATE_CHECK == TRUE
301 nil.lock_cnt = (cnt_t)1;
302#endif
303
304#if CH_CFG_AUTOSTART_THREADS == TRUE
305 /* Iterates through the list of threads to be auto-started.*/
306 tdp = nil_thd_configs;
307 do {
308 (void) chThdCreateI(tdp);
309 tdp++;
310 } while (tdp->funcp != NULL);
311#endif
312
313 /* Starting the dance.*/
314 chSchRescheduleS();
315 chSysUnlock();
316}
317
318/**
319 * @brief Halts the system.
320 * @details This function is invoked by the operating system when an
321 * unrecoverable error is detected, for example because a programming
322 * error in the application code that triggers an assertion while
323 * in debug mode.
324 * @note Can be invoked from any system state.
325 *
326 * @param[in] reason pointer to an error string
327 *
328 * @special
329 */
330void chSysHalt(const char *reason) {
331
332 port_disable();
333
334#if NIL_DBG_ENABLED
335 nil.dbg_panic_msg = reason;
336#else
337 (void)reason;
338#endif
339
340 /* Halt hook code, usually empty.*/
341 CH_CFG_SYSTEM_HALT_HOOK(reason);
342
343 /* Harmless infinite loop.*/
344 while (true) {
345 }
346}
347
348/**
349 * @brief Time management handler.
350 * @note This handler has to be invoked by a periodic ISR in order to
351 * reschedule the waiting threads.
352 *
353 * @iclass
354 */
355void chSysTimerHandlerI(void) {
356
357 chDbgCheckClassI();
358
359#if CH_CFG_ST_TIMEDELTA == 0
360 thread_t *tp = &nil.threads[0];
361 nil.systime++;
362 do {
363 /* Is the thread in a wait state with timeout?.*/
364 if (tp->timeout > (sysinterval_t)0) {
365
366 chDbgAssert(!NIL_THD_IS_READY(tp), "is ready");
367
368 /* Did the timer reach zero?*/
369 if (--tp->timeout == (sysinterval_t)0) {
370 /* Timeout on queues/semaphores requires a special handling because
371 the counter must be incremented.*/
372 /*lint -save -e9013 [15.7] There is no else because it is not needed.*/
373#if CH_CFG_USE_SEMAPHORES == TRUE
374 if (NIL_THD_IS_WTQUEUE(tp)) {
375 tp->u1.semp->cnt++;
376 }
377 else
378#endif
379 if (NIL_THD_IS_SUSPENDED(tp)) {
380 *tp->u1.trp = NULL;
381 }
382 /*lint -restore*/
383 (void) chSchReadyI(tp, MSG_TIMEOUT);
384 }
385 }
386 /* Lock released in order to give a preemption chance on those
387 architectures supporting IRQ preemption.*/
388 chSysUnlockFromISR();
389 tp++;
390 chSysLockFromISR();
391 } while (tp < &nil.threads[CH_CFG_MAX_THREADS]);
392#else
393 thread_t *tp = &nil.threads[0];
394 sysinterval_t next = (sysinterval_t)0;
395
396 chDbgAssert(nil.nexttime == port_timer_get_alarm(), "time mismatch");
397
398 do {
399 sysinterval_t timeout = tp->timeout;
400
401 /* Is the thread in a wait state with timeout?.*/
402 if (timeout > (sysinterval_t)0) {
403
404 chDbgAssert(!NIL_THD_IS_READY(tp), "is ready");
405 chDbgAssert(timeout >= chTimeDiffX(nil.lasttime, nil.nexttime),
406 "skipped one");
407
408 /* The volatile field is updated once, here.*/
409 timeout -= chTimeDiffX(nil.lasttime, nil.nexttime);
410 tp->timeout = timeout;
411
412 if (timeout == (sysinterval_t)0) {
413 /* Timeout on thread queues requires a special handling because the
414 counter must be incremented.*/
415 if (NIL_THD_IS_WTQUEUE(tp)) {
416 tp->u1.tqp->cnt++;
417 }
418 else {
419 if (NIL_THD_IS_SUSPENDED(tp)) {
420 *tp->u1.trp = NULL;
421 }
422 }
423 (void) chSchReadyI(tp, MSG_TIMEOUT);
424 }
425 else {
426 if (timeout <= (sysinterval_t)(next - (sysinterval_t)1)) {
427 next = timeout;
428 }
429 }
430 }
431
432 /* Lock released in order to give a preemption chance on those
433 architectures supporting IRQ preemption.*/
434 chSysUnlockFromISR();
435 tp++;
436 chSysLockFromISR();
437 } while (tp < &nil.threads[CH_CFG_MAX_THREADS]);
438
439 nil.lasttime = nil.nexttime;
440 if (next > (sysinterval_t)0) {
441 nil.nexttime = chTimeAddX(nil.nexttime, next);
442 port_timer_set_alarm(nil.nexttime);
443 }
444 else {
445 /* No tick event needed.*/
446 port_timer_stop_alarm();
447 }
448#endif
449}
450
451/**
452 * @brief Unconditionally enters the kernel lock state.
453 * @note Can be called without previous knowledge of the current lock state.
454 * The final state is "s-locked".
455 *
456 * @special
457 */
458void chSysUnconditionalLock(void) {
459
460 if (port_irq_enabled(port_get_irq_status())) {
461 chSysLock();
462 }
463}
464
465/**
466 * @brief Unconditionally leaves the kernel lock state.
467 * @note Can be called without previous knowledge of the current lock state.
468 * The final state is "normal".
469 *
470 * @special
471 */
472void chSysUnconditionalUnlock(void) {
473
474 if (!port_irq_enabled(port_get_irq_status())) {
475 chSysUnlock();
476 }
477}
478
479/**
480 * @brief Returns the execution status and enters a critical zone.
481 * @details This functions enters into a critical zone and can be called
482 * from any context. Because its flexibility it is less efficient
483 * than @p chSysLock() which is preferable when the calling context
484 * is known.
485 * @post The system is in a critical zone.
486 *
487 * @return The previous system status, the encoding of this
488 * status word is architecture-dependent and opaque.
489 *
490 * @xclass
491 */
492syssts_t chSysGetStatusAndLockX(void) {
493
494 syssts_t sts = port_get_irq_status();
495 if (port_irq_enabled(sts)) {
496 if (port_is_isr_context()) {
497 chSysLockFromISR();
498 }
499 else {
500 chSysLock();
501 }
502 }
503 return sts;
504}
505
506/**
507 * @brief Restores the specified execution status and leaves a critical zone.
508 * @note A call to @p chSchRescheduleS() is automatically performed
509 * if exiting the critical zone and if not in ISR context.
510 *
511 * @param[in] sts the system status to be restored.
512 *
513 * @xclass
514 */
515void chSysRestoreStatusX(syssts_t sts) {
516
517 if (port_irq_enabled(sts)) {
518 if (port_is_isr_context()) {
519 chSysUnlockFromISR();
520 }
521 else {
522 chSchRescheduleS();
523 chSysUnlock();
524 }
525 }
526}
527
528#if (PORT_SUPPORTS_RT == TRUE) || defined(__DOXYGEN__)
529/**
530 * @brief Realtime window test.
531 * @details This function verifies if the current realtime counter value
532 * lies within the specified range or not. The test takes care
533 * of the realtime counter wrapping to zero on overflow.
534 * @note When start==end then the function returns always false because a
535 * null time range is specified.
536 * @note This function is only available if the port layer supports the
537 * option @p PORT_SUPPORTS_RT.
538 *
539 * @param[in] cnt the counter value to be tested
540 * @param[in] start the start of the time window (inclusive)
541 * @param[in] end the end of the time window (non inclusive)
542 * @retval true current time within the specified time window.
543 * @retval false current time not within the specified time window.
544 *
545 * @xclass
546 */
547bool chSysIsCounterWithinX(rtcnt_t cnt, rtcnt_t start, rtcnt_t end) {
548
549 return (bool)(((rtcnt_t)cnt - (rtcnt_t)start) <
550 ((rtcnt_t)end - (rtcnt_t)start));
551}
552
553/**
554 * @brief Polled delay.
555 * @note The real delay is always few cycles in excess of the specified
556 * value.
557 * @note This function is only available if the port layer supports the
558 * option @p PORT_SUPPORTS_RT.
559 *
560 * @param[in] cycles number of cycles
561 *
562 * @xclass
563 */
564void chSysPolledDelayX(rtcnt_t cycles) {
565 rtcnt_t start = chSysGetRealtimeCounterX();
566 rtcnt_t end = start + cycles;
567
568 while (chSysIsCounterWithinX(chSysGetRealtimeCounterX(), start, end)) {
569 }
570}
571#endif /* PORT_SUPPORTS_RT == TRUE */
572
573/**
574 * @brief Makes the specified thread ready for execution.
575 *
576 * @param[in] tp pointer to the @p thread_t object
577 * @param[in] msg the wakeup message
578 *
579 * @return The same reference passed as parameter.
580 */
581thread_t *chSchReadyI(thread_t *tp, msg_t msg) {
582
583 chDbgCheckClassI();
584 chDbgCheck((tp >= nil.threads) && (tp < &nil.threads[CH_CFG_MAX_THREADS]));
585 chDbgAssert(!NIL_THD_IS_READY(tp), "already ready");
586 chDbgAssert(nil.next <= nil.current, "priority ordering");
587
588 tp->u1.msg = msg;
589 tp->state = NIL_STATE_READY;
590 tp->timeout = (sysinterval_t)0;
591 if (tp < nil.next) {
592 nil.next = tp;
593 }
594 return tp;
595}
596
597/**
598 * @brief Evaluates if preemption is required.
599 * @details The decision is taken by comparing the relative priorities and
600 * depending on the state of the round robin timeout counter.
601 * @note Not a user function, it is meant to be invoked by the scheduler
602 * itself or from within the port layer.
603 *
604 * @retval true if there is a thread that must go in running state
605 * immediately.
606 * @retval false if preemption is not required.
607 *
608 * @special
609 */
610bool chSchIsPreemptionRequired(void) {
611
612 return chSchIsRescRequiredI();
613}
614
615/**
616 * @brief Switches to the first thread on the runnable queue.
617 * @note Not a user function, it is meant to be invoked by the scheduler
618 * itself or from within the port layer.
619 *
620 * @special
621 */
622void chSchDoReschedule(void) {
623 thread_t *otp = nil.current;
624
625 nil.current = nil.next;
626 if (otp == &nil.threads[CH_CFG_MAX_THREADS]) {
627 CH_CFG_IDLE_LEAVE_HOOK();
628 }
629 port_switch(nil.next, otp);
630}
631
632/**
633 * @brief Reschedules if needed.
634 *
635 * @sclass
636 */
637void chSchRescheduleS(void) {
638
639 chDbgCheckClassS();
640
641 if (chSchIsRescRequiredI()) {
642 chSchDoReschedule();
643 }
644}
645
646/**
647 * @brief Puts the current thread to sleep into the specified state with
648 * timeout specification.
649 * @details The thread goes into a sleeping state, if it is not awakened
650 * explicitly within the specified system time then it is forcibly
651 * awakened with a @p MSG_TIMEOUT low level message.
652 *
653 * @param[in] newstate the new thread state or a semaphore pointer
654 * @param[in] timeout the number of ticks before the operation timeouts.
655 * the following special values are allowed:
656 * - @a TIME_INFINITE no timeout.
657 * .
658 * @return The wakeup message.
659 * @retval MSG_TIMEOUT if a timeout occurred.
660 *
661 * @sclass
662 */
663msg_t chSchGoSleepTimeoutS(tstate_t newstate, sysinterval_t timeout) {
664 thread_t *ntp, *otp = nil.current;
665
666 chDbgCheckClassS();
667
668 chDbgAssert(otp != &nil.threads[CH_CFG_MAX_THREADS],
669 "idle cannot sleep");
670
671 /* Storing the wait object for the current thread.*/
672 otp->state = newstate;
673
674#if CH_CFG_ST_TIMEDELTA > 0
675 if (timeout != TIME_INFINITE) {
676 systime_t abstime;
677
678 /* TIMEDELTA makes sure to have enough time to reprogram the timer
679 before the free-running timer counter reaches the selected timeout.*/
680 if (timeout < (sysinterval_t)CH_CFG_ST_TIMEDELTA) {
681 timeout = (sysinterval_t)CH_CFG_ST_TIMEDELTA;
682 }
683
684 /* Absolute time of the timeout event.*/
685 abstime = chTimeAddX(chVTGetSystemTimeX(), timeout);
686
687 if (nil.lasttime == nil.nexttime) {
688 /* Special case, first thread asking for a timeout.*/
689 port_timer_start_alarm(abstime);
690 nil.nexttime = abstime;
691 }
692 else {
693 /* Special case, there are already other threads with a timeout
694 activated, evaluating the order.*/
695 if (chTimeIsInRangeX(abstime, nil.lasttime, nil.nexttime)) {
696 port_timer_set_alarm(abstime);
697 nil.nexttime = abstime;
698 }
699 }
700
701 /* Timeout settings.*/
702 otp->timeout = abstime - nil.lasttime;
703 }
704#else
705
706 /* Timeout settings.*/
707 otp->timeout = timeout;
708#endif
709
710 /* Scanning the whole threads array.*/
711 ntp = nil.threads;
712 while (true) {
713 /* Is this thread ready to execute?*/
714 if (NIL_THD_IS_READY(ntp)) {
715 nil.current = nil.next = ntp;
716 if (ntp == &nil.threads[CH_CFG_MAX_THREADS]) {
717 CH_CFG_IDLE_ENTER_HOOK();
718 }
719 port_switch(ntp, otp);
720 return nil.current->u1.msg;
721 }
722
723 /* Points to the next thread in lowering priority order.*/
724 ntp++;
725 chDbgAssert(ntp <= &nil.threads[CH_CFG_MAX_THREADS],
726 "pointer out of range");
727 }
728}
729
730/**
731 * @brief Checks if the specified time is within the specified time range.
732 * @note When start==end then the function returns always false because the
733 * time window has zero size.
734 *
735 * @param[in] time the time to be verified
736 * @param[in] start the start of the time window (inclusive)
737 * @param[in] end the end of the time window (non inclusive)
738 * @retval true current time within the specified time window.
739 * @retval false current time not within the specified time window.
740 *
741 * @xclass
742 */
743bool chTimeIsInRangeX(systime_t time, systime_t start, systime_t end) {
744
745 return (bool)((systime_t)((systime_t)(time) - (systime_t)(start)) <
746 (systime_t)((systime_t)(end) - (systime_t)(start)));
747}
748
749/**
750 * @brief Creates a new thread into a static memory area.
751 * @details The new thread is initialized and make ready to execute.
752 * @note A thread can terminate by calling @p chThdExit() or by simply
753 * returning from its main function.
754 *
755 * @param[out] tdp pointer to the thread descriptor structure
756 * @return The pointer to the @p thread_t structure allocated for
757 * the thread.
758 *
759 * @iclass
760 */
761thread_t *chThdCreateI(const thread_descriptor_t *tdp) {
762 thread_t *tp;
763
764 chDbgCheck((tdp->prio < (tprio_t)CH_CFG_MAX_THREADS) &&
765 (tdp->wbase != NULL) &&
766 MEM_IS_ALIGNED(tdp->wbase, PORT_WORKING_AREA_ALIGN) &&
767 (tdp->wend > tdp->wbase) &&
768 MEM_IS_ALIGNED(tdp->wbase, PORT_STACK_ALIGN) &&
769 (tdp->funcp != NULL));
770
771 chDbgCheckClassI();
772
773 /* Pointer to the thread slot to be used.*/
774 tp = &nil.threads[tdp->prio];
775 chDbgAssert(NIL_THD_IS_WTSTART(tp) || NIL_THD_IS_FINAL(tp),
776 "priority slot taken");
777
778#if CH_CFG_USE_EVENTS == TRUE
779 tp->epmask = (eventmask_t)0;
780#endif
781#if CH_DBG_ENABLE_STACK_CHECK == TRUE
782 tp->wabase = (stkalign_t *)tdp->wbase;
783#endif
784
785 /* Port dependent thread initialization.*/
786 PORT_SETUP_CONTEXT(tp, tdp->wbase, tdp->wend, tdp->funcp, tdp->arg);
787
788 /* Initialization hook.*/
789 CH_CFG_THREAD_EXT_INIT_HOOK(tp);
790
791 /* Readying up thread.*/
792 return chSchReadyI(tp, MSG_OK);
793}
794
795/**
796 * @brief Creates a new thread into a static memory area.
797 * @details The new thread is initialized and make ready to execute.
798 * @note A thread can terminate by calling @p chThdExit() or by simply
799 * returning from its main function.
800 *
801 * @param[out] tdp pointer to the thread descriptor structure
802 * @return The pointer to the @p thread_t structure allocated for
803 * the thread.
804 *
805 * @api
806 */
807thread_t *chThdCreate(const thread_descriptor_t *tdp) {
808 thread_t *tp;
809
810 chSysLock();
811 tp = chThdCreateI(tdp);
812 chSchRescheduleS();
813 chSysUnlock();
814
815 return tp;
816}
817
818/**
819 * @brief Terminates the current thread.
820 * @details The thread goes in the @p CH_STATE_FINAL state holding the
821 * specified exit status code, other threads can retrieve the
822 * exit status code by invoking the function @p chThdWait().
823 * @post Exiting a non-static thread that does not have references
824 * (detached) causes the thread to remain in the registry.
825 * It can only be removed by performing a registry scan operation.
826 * @post Eventual code after this function will never be executed,
827 * this function never returns. The compiler has no way to
828 * know this so do not assume that the compiler would remove
829 * the dead code.
830 *
831 * @param[in] msg thread exit code
832 *
833 * @api
834 */
835void chThdExit(msg_t msg) {
836
837 chSysLock();
838
839 /* Exit handler hook.*/
840 CH_CFG_THREAD_EXIT_HOOK(tp);
841
842#if CH_CFG_USE_WAITEXIT == TRUE
843 {
844 /* Waking up any waiting thread.*/
845 thread_t *tp = nil.threads;
846 while (tp < &nil.threads[CH_CFG_MAX_THREADS]) {
847 /* Is this thread waiting for current thread termination?*/
848 if ((tp->state == NIL_STATE_WTEXIT) && (tp->u1.tp == nil.current)) {
849 (void) chSchReadyI(tp, msg);
850 }
851 tp++;
852 }
853 }
854#endif
855
856 /* Going into final state with exit message stored.*/
857 nil.current->u1.msg = msg;
858 (void) chSchGoSleepTimeoutS(NIL_STATE_FINAL, TIME_INFINITE);
859
860 /* The thread never returns here.*/
861 chDbgAssert(false, "zombies apocalypse");
862}
863
864/**
865 * @brief Blocks the execution of the invoking thread until the specified
866 * thread terminates then the exit code is returned.
867 *
868 * @param[in] tp pointer to the thread
869 * @return The exit code from the terminated thread.
870 *
871 * @api
872 */
873msg_t chThdWait(thread_t *tp) {
874 msg_t msg;
875
876 chSysLock();
877 if (NIL_THD_IS_FINAL(tp)) {
878 msg = tp->u1.msg;
879 }
880 else {
881 nil.current->u1.tp = tp;
882 msg = chSchGoSleepTimeoutS(NIL_STATE_WTEXIT, TIME_INFINITE);
883 }
884 chSysUnlock();
885
886 return msg;
887}
888
889/**
890 * @brief Sends the current thread sleeping and sets a reference variable.
891 * @note This function must reschedule, it can only be called from thread
892 * context.
893 *
894 * @param[in] trp a pointer to a thread reference object
895 * @param[in] timeout the number of ticks before the operation timeouts,
896 * the following special values are allowed:
897 * - @a TIME_IMMEDIATE immediate timeout.
898 * - @a TIME_INFINITE no timeout.
899 * .
900 * @return The wake up message.
901 *
902 * @sclass
903 */
904msg_t chThdSuspendTimeoutS(thread_reference_t *trp, sysinterval_t timeout) {
905
906 chDbgAssert(*trp == NULL, "not NULL");
907
908 if (TIME_IMMEDIATE == timeout) {
909 return MSG_TIMEOUT;
910 }
911
912 *trp = nil.current;
913 nil.current->u1.trp = trp;
914 return chSchGoSleepTimeoutS(NIL_STATE_SUSPENDED, timeout);
915}
916
917/**
918 * @brief Wakes up a thread waiting on a thread reference object.
919 * @note This function must not reschedule because it can be called from
920 * ISR context.
921 *
922 * @param[in] trp a pointer to a thread reference object
923 * @param[in] msg the message code
924 *
925 * @iclass
926 */
927void chThdResumeI(thread_reference_t *trp, msg_t msg) {
928
929 if (*trp != NULL) {
930 thread_reference_t tr = *trp;
931
932 chDbgAssert(NIL_THD_IS_SUSPENDED(tr), "not suspended");
933
934 *trp = NULL;
935 (void) chSchReadyI(tr, msg);
936 }
937}
938
939/**
940 * @brief Wakes up a thread waiting on a thread reference object.
941 * @note This function must reschedule, it can only be called from thread
942 * context.
943 *
944 * @param[in] trp a pointer to a thread reference object
945 * @param[in] msg the message code
946 *
947 * @api
948 */
949void chThdResume(thread_reference_t *trp, msg_t msg) {
950
951 chSysLock();
952 chThdResumeS(trp, msg);
953 chSysUnlock();
954}
955
956/**
957 * @brief Suspends the invoking thread for the specified time.
958 *
959 * @param[in] timeout the delay in system ticks
960 *
961 * @api
962 */
963void chThdSleep(sysinterval_t timeout) {
964
965 chSysLock();
966 chThdSleepS(timeout);
967 chSysUnlock();
968}
969
970/**
971 * @brief Suspends the invoking thread until the system time arrives to the
972 * specified value.
973 *
974 * @param[in] abstime absolute system time
975 *
976 * @api
977 */
978void chThdSleepUntil(systime_t abstime) {
979
980 chSysLock();
981 chThdSleepUntilS(abstime);
982 chSysUnlock();
983}
984
985/**
986 * @brief Enqueues the caller thread on a threads queue object.
987 * @details The caller thread is enqueued and put to sleep until it is
988 * dequeued or the specified timeouts expires.
989 *
990 * @param[in] tqp pointer to the threads queue object
991 * @param[in] timeout the timeout in system ticks, the special values are
992 * handled as follow:
993 * - @a TIME_IMMEDIATE immediate timeout.
994 * - @a TIME_INFINITE no timeout.
995 * .
996 * @return The message from @p osalQueueWakeupOneI() or
997 * @p osalQueueWakeupAllI() functions.
998 * @retval MSG_TIMEOUT if the thread has not been dequeued within the
999 * specified timeout or if the function has been
1000 * invoked with @p TIME_IMMEDIATE as timeout
1001 * specification.
1002 *
1003 * @sclass
1004 */
1005msg_t chThdEnqueueTimeoutS(threads_queue_t *tqp, sysinterval_t timeout) {
1006
1007 chDbgCheckClassS();
1008 chDbgCheck(tqp != NULL);
1009
1010 chDbgAssert(tqp->cnt <= (cnt_t)0, "invalid counter");
1011
1012 if (TIME_IMMEDIATE == timeout) {
1013 return MSG_TIMEOUT;
1014 }
1015
1016 tqp->cnt--;
1017 nil.current->u1.tqp = tqp;
1018 return chSchGoSleepTimeoutS(NIL_STATE_WTQUEUE, timeout);
1019}
1020
1021/**
1022 * @brief Dequeues and wakes up one thread from the threads queue object.
1023 * @details Dequeues one thread from the queue without checking if the queue
1024 * is empty.
1025 * @pre The queue must contain at least an object.
1026 *
1027 * @param[in] tqp pointer to the threads queue object
1028 * @param[in] msg the message code
1029 *
1030 * @iclass
1031 */
1032void chThdDoDequeueNextI(threads_queue_t *tqp, msg_t msg) {
1033 thread_t *tp;
1034
1035 chDbgAssert(tqp->cnt < (cnt_t)0, "empty queue");
1036
1037 tqp->cnt++;
1038 tp = nil_find_thread(NIL_STATE_WTQUEUE, (void *)tqp);
1039
1040 chDbgAssert(tp != NULL, "thread not found");
1041
1042 (void) chSchReadyI(tp, msg);
1043}
1044
1045/**
1046 * @brief Dequeues and wakes up one thread from the threads queue object,
1047 * if any.
1048 *
1049 * @param[in] tqp pointer to the threads queue object
1050 * @param[in] msg the message code
1051 *
1052 * @iclass
1053 */
1054void chThdDequeueNextI(threads_queue_t *tqp, msg_t msg) {
1055
1056 chDbgCheckClassI();
1057 chDbgCheck(tqp != NULL);
1058
1059 if (tqp->cnt < (cnt_t)0) {
1060 chThdDoDequeueNextI(tqp, msg);
1061 }
1062}
1063
1064/**
1065 * @brief Dequeues and wakes up all threads from the threads queue object.
1066 *
1067 * @param[in] tqp pointer to the threads queue object
1068 * @param[in] msg the message code
1069 *
1070 * @iclass
1071 */
1072void chThdDequeueAllI(threads_queue_t *tqp, msg_t msg) {
1073
1074 chDbgCheckClassI();
1075 chDbgCheck(tqp != NULL);
1076
1077 tqp->cnt = nil_ready_all((void *)tqp, tqp->cnt, msg);
1078}
1079
1080/** @} */