aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios/os/rt/src/chschd.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios/os/rt/src/chschd.c')
-rw-r--r--lib/chibios/os/rt/src/chschd.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/lib/chibios/os/rt/src/chschd.c b/lib/chibios/os/rt/src/chschd.c
new file mode 100644
index 000000000..f10f8308b
--- /dev/null
+++ b/lib/chibios/os/rt/src/chschd.c
@@ -0,0 +1,493 @@
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/chschd.c
22 * @brief Scheduler code.
23 *
24 * @addtogroup scheduler
25 * @details This module provides the default portable scheduler code.
26 * @{
27 */
28
29#include "ch.h"
30
31/*===========================================================================*/
32/* Module local definitions. */
33/*===========================================================================*/
34
35/*===========================================================================*/
36/* Module exported variables. */
37/*===========================================================================*/
38
39/**
40 * @brief System data structures.
41 */
42ch_system_t ch;
43
44/*===========================================================================*/
45/* Module local types. */
46/*===========================================================================*/
47
48/*===========================================================================*/
49/* Module local variables. */
50/*===========================================================================*/
51
52/*===========================================================================*/
53/* Module local functions. */
54/*===========================================================================*/
55
56/*
57 * Timeout wakeup callback.
58 */
59static void wakeup(void *p) {
60 thread_t *tp = (thread_t *)p;
61
62 chSysLockFromISR();
63 switch (tp->state) {
64 case CH_STATE_READY:
65 /* Handling the special case where the thread has been made ready by
66 another thread with higher priority.*/
67 chSysUnlockFromISR();
68 return;
69 case CH_STATE_SUSPENDED:
70 *tp->u.wttrp = NULL;
71 break;
72#if CH_CFG_USE_SEMAPHORES == TRUE
73 case CH_STATE_WTSEM:
74 chSemFastSignalI(tp->u.wtsemp);
75#endif
76 /* Falls through.*/
77 case CH_STATE_QUEUED:
78 /* Falls through.*/
79#if (CH_CFG_USE_CONDVARS == TRUE) && (CH_CFG_USE_CONDVARS_TIMEOUT == TRUE)
80 case CH_STATE_WTCOND:
81#endif
82 /* States requiring dequeuing.*/
83 (void) ch_queue_dequeue(&tp->hdr.queue);
84 break;
85 default:
86 /* Any other state, nothing to do.*/
87 break;
88 }
89 tp->u.rdymsg = MSG_TIMEOUT;
90 (void) chSchReadyI(tp);
91 chSysUnlockFromISR();
92}
93
94/*===========================================================================*/
95/* Module exported functions. */
96/*===========================================================================*/
97
98#if (CH_CFG_OPTIMIZE_SPEED == FALSE) || defined(__DOXYGEN__)
99/**
100 * @brief Inserts a thread into a priority ordered queue.
101 * @note The insertion is done by scanning the list from the highest
102 * priority toward the lowest.
103 *
104 * @param[in] tp the pointer to the thread to be inserted in the list
105 * @param[in] qp the pointer to the threads list header
106 *
107 * @notapi
108 */
109void ch_sch_prio_insert(ch_queue_t *tp, ch_queue_t *qp) {
110
111 ch_queue_t *cp = qp;
112 do {
113 cp = cp->next;
114 } while ((cp != qp) &&
115 (((thread_t *)cp)->hdr.pqueue.prio >= ((thread_t *)tp)->hdr.pqueue.prio));
116 tp->next = cp;
117 tp->prev = cp->prev;
118 tp->prev->next = tp;
119 cp->prev = tp;
120}
121#endif /* CH_CFG_OPTIMIZE_SPEED */
122
123/**
124 * @brief Scheduler initialization.
125 *
126 * @notapi
127 */
128void _scheduler_init(void) {
129
130 ch_pqueue_init(&ch.rlist.pqueue);
131#if CH_CFG_USE_REGISTRY == TRUE
132 ch.rlist.newer = (thread_t *)&ch.rlist;
133 ch.rlist.older = (thread_t *)&ch.rlist;
134#endif
135}
136
137/**
138 * @brief Inserts a thread in the Ready List placing it behind its peers.
139 * @details The thread is positioned behind all threads with higher or equal
140 * priority.
141 * @pre The thread must not be already inserted in any list through its
142 * @p next and @p prev or list corruption would occur.
143 * @post This function does not reschedule so a call to a rescheduling
144 * function must be performed before unlocking the kernel. Note that
145 * interrupt handlers always reschedule on exit so an explicit
146 * reschedule must not be performed in ISRs.
147 *
148 * @param[in] tp the thread to be made ready
149 * @return The thread pointer.
150 *
151 * @iclass
152 */
153thread_t *chSchReadyI(thread_t *tp) {
154
155 chDbgCheckClassI();
156 chDbgCheck(tp != NULL);
157 chDbgAssert((tp->state != CH_STATE_READY) &&
158 (tp->state != CH_STATE_FINAL),
159 "invalid state");
160
161 /* The thread is marked ready.*/
162 tp->state = CH_STATE_READY;
163
164 /* Insertion in the priority queue.*/
165 return (thread_t *)ch_pqueue_insert_behind(&ch.rlist.pqueue,
166 &tp->hdr.pqueue);
167}
168
169/**
170 * @brief Inserts a thread in the Ready List placing it ahead its peers.
171 * @details The thread is positioned ahead all threads with higher or equal
172 * priority.
173 * @pre The thread must not be already inserted in any list through its
174 * @p next and @p prev or list corruption would occur.
175 * @post This function does not reschedule so a call to a rescheduling
176 * function must be performed before unlocking the kernel. Note that
177 * interrupt handlers always reschedule on exit so an explicit
178 * reschedule must not be performed in ISRs.
179 *
180 * @param[in] tp the thread to be made ready
181 * @return The thread pointer.
182 *
183 * @iclass
184 */
185thread_t *chSchReadyAheadI(thread_t *tp) {
186
187 chDbgCheckClassI();
188 chDbgCheck(tp != NULL);
189 chDbgAssert((tp->state != CH_STATE_READY) &&
190 (tp->state != CH_STATE_FINAL),
191 "invalid state");
192
193 /* The thread is marked ready.*/
194 tp->state = CH_STATE_READY;
195
196 /* Insertion in the priority queue.*/
197 return (thread_t *)ch_pqueue_insert_ahead(&ch.rlist.pqueue,
198 &tp->hdr.pqueue);
199}
200
201/**
202 * @brief Puts the current thread to sleep into the specified state.
203 * @details The thread goes into a sleeping state. The possible
204 * @ref thread_states are defined into @p threads.h.
205 *
206 * @param[in] newstate the new thread state
207 *
208 * @sclass
209 */
210void chSchGoSleepS(tstate_t newstate) {
211 thread_t *otp = currp;
212
213 chDbgCheckClassS();
214
215 /* New state.*/
216 otp->state = newstate;
217
218#if CH_CFG_TIME_QUANTUM > 0
219 /* The thread is renouncing its remaining time slices so it will have a new
220 time quantum when it will wakeup.*/
221 otp->ticks = (tslices_t)CH_CFG_TIME_QUANTUM;
222#endif
223
224 /* Next thread in ready list becomes current.*/
225 currp = (thread_t *)ch_pqueue_remove_highest(&ch.rlist.pqueue);
226 currp->state = CH_STATE_CURRENT;
227
228 /* Handling idle-enter hook.*/
229 if (currp->hdr.pqueue.prio == IDLEPRIO) {
230 CH_CFG_IDLE_ENTER_HOOK();
231 }
232
233 /* Swap operation as tail call.*/
234 chSysSwitch(currp, otp);
235}
236
237/**
238 * @brief Puts the current thread to sleep into the specified state with
239 * timeout specification.
240 * @details The thread goes into a sleeping state, if it is not awakened
241 * explicitly within the specified timeout then it is forcibly
242 * awakened with a @p MSG_TIMEOUT low level message. The possible
243 * @ref thread_states are defined into @p threads.h.
244 *
245 * @param[in] newstate the new thread state
246 * @param[in] timeout the number of ticks before the operation timeouts, the
247 * special values are handled as follow:
248 * - @a TIME_INFINITE the thread enters an infinite sleep
249 * state, this is equivalent to invoking
250 * @p chSchGoSleepS() but, of course, less efficient.
251 * - @a TIME_IMMEDIATE this value is not allowed.
252 * .
253 * @return The wakeup message.
254 * @retval MSG_TIMEOUT if a timeout occurs.
255 *
256 * @sclass
257 */
258msg_t chSchGoSleepTimeoutS(tstate_t newstate, sysinterval_t timeout) {
259
260 chDbgCheckClassS();
261
262 if (TIME_INFINITE != timeout) {
263 virtual_timer_t vt;
264
265 chVTDoSetI(&vt, timeout, wakeup, currp);
266 chSchGoSleepS(newstate);
267 if (chVTIsArmedI(&vt)) {
268 chVTDoResetI(&vt);
269 }
270 }
271 else {
272 chSchGoSleepS(newstate);
273 }
274
275 return currp->u.rdymsg;
276}
277
278/**
279 * @brief Wakes up a thread.
280 * @details The thread is inserted into the ready list or immediately made
281 * running depending on its relative priority compared to the current
282 * thread.
283 * @pre The thread must not be already inserted in any list through its
284 * @p next and @p prev or list corruption would occur.
285 * @note It is equivalent to a @p chSchReadyI() followed by a
286 * @p chSchRescheduleS() but much more efficient.
287 * @note The function assumes that the current thread has the highest
288 * priority.
289 *
290 * @param[in] ntp the thread to be made ready
291 * @param[in] msg the wakeup message
292 *
293 * @sclass
294 */
295void chSchWakeupS(thread_t *ntp, msg_t msg) {
296 thread_t *otp = currp;
297
298 chDbgCheckClassS();
299
300 chDbgAssert((ch.rlist.pqueue.next == &ch.rlist.pqueue) ||
301 (ch.rlist.current->hdr.pqueue.prio >= ch.rlist.pqueue.next->prio),
302 "priority order violation");
303
304 /* Storing the message to be retrieved by the target thread when it will
305 restart execution.*/
306 ntp->u.rdymsg = msg;
307
308 /* If the waken thread has a not-greater priority than the current
309 one then it is just inserted in the ready list else it made
310 running immediately and the invoking thread goes in the ready
311 list instead.*/
312 if (ntp->hdr.pqueue.prio <= otp->hdr.pqueue.prio) {
313 (void) chSchReadyI(ntp);
314 }
315 else {
316 otp = chSchReadyAheadI(otp);
317
318 /* Handling idle-leave hook.*/
319 if (otp->hdr.pqueue.prio == IDLEPRIO) {
320 CH_CFG_IDLE_LEAVE_HOOK();
321 }
322
323 /* The extracted thread is marked as current.*/
324 currp = ntp;
325 ntp->state = CH_STATE_CURRENT;
326
327 /* Swap operation as tail call.*/
328 chSysSwitch(ntp, otp);
329 }
330}
331
332/**
333 * @brief Performs a reschedule if a higher priority thread is runnable.
334 * @details If a thread with a higher priority than the current thread is in
335 * the ready list then make the higher priority thread running.
336 *
337 * @sclass
338 */
339void chSchRescheduleS(void) {
340
341 chDbgCheckClassS();
342
343 if (chSchIsRescRequiredI()) {
344 chSchDoRescheduleAhead();
345 }
346}
347
348#if !defined(CH_SCH_IS_PREEMPTION_REQUIRED_HOOKED)
349/**
350 * @brief Evaluates if preemption is required.
351 * @details The decision is taken by comparing the relative priorities and
352 * depending on the state of the round robin timeout counter.
353 * @note Not a user function, it is meant to be invoked by the scheduler
354 * itself or from within the port layer.
355 *
356 * @retval true if there is a thread that must go in running state
357 * immediately.
358 * @retval false if preemption is not required.
359 *
360 * @special
361 */
362bool chSchIsPreemptionRequired(void) {
363 tprio_t p1 = firstprio(&ch.rlist.pqueue);
364 tprio_t p2 = currp->hdr.pqueue.prio;
365
366#if CH_CFG_TIME_QUANTUM > 0
367 /* If the running thread has not reached its time quantum, reschedule only
368 if the first thread on the ready queue has a higher priority.
369 Otherwise, if the running thread has used up its time quantum, reschedule
370 if the first thread on the ready queue has equal or higher priority.*/
371 return (currp->ticks > (tslices_t)0) ? (p1 > p2) : (p1 >= p2);
372#else
373 /* If the round robin preemption feature is not enabled then performs a
374 simpler comparison.*/
375 return p1 > p2;
376#endif
377}
378#endif /* !defined(CH_SCH_IS_PREEMPTION_REQUIRED_HOOKED) */
379
380/**
381 * @brief Switches to the first thread on the runnable queue.
382 * @details The current thread is positioned in the ready list behind all
383 * threads having the same priority. The thread regains its time
384 * quantum.
385 * @note Not a user function, it is meant to be invoked by the scheduler
386 * itself.
387 *
388 * @special
389 */
390void chSchDoRescheduleBehind(void) {
391 thread_t *otp = currp;
392
393 /* Picks the first thread from the ready queue and makes it current.*/
394 currp = (thread_t *)ch_pqueue_remove_highest(&ch.rlist.pqueue);
395 currp->state = CH_STATE_CURRENT;
396
397 /* Handling idle-leave hook.*/
398 if (otp->hdr.pqueue.prio == IDLEPRIO) {
399 CH_CFG_IDLE_LEAVE_HOOK();
400 }
401
402#if CH_CFG_TIME_QUANTUM > 0
403 /* It went behind peers so it gets a new time quantum.*/
404 otp->ticks = (tslices_t)CH_CFG_TIME_QUANTUM;
405#endif
406
407 /* Placing in ready list behind peers.*/
408 otp = chSchReadyI(otp);
409
410 /* Swap operation as tail call.*/
411 chSysSwitch(currp, otp);
412}
413
414/**
415 * @brief Switches to the first thread on the runnable queue.
416 * @details The current thread is positioned in the ready list ahead of all
417 * threads having the same priority.
418 * @note Not a user function, it is meant to be invoked by the scheduler
419 * itself.
420 *
421 * @special
422 */
423void chSchDoRescheduleAhead(void) {
424 thread_t *otp = currp;
425
426 /* Picks the first thread from the ready queue and makes it current.*/
427 currp = (thread_t *)ch_pqueue_remove_highest(&ch.rlist.pqueue);
428 currp->state = CH_STATE_CURRENT;
429
430 /* Handling idle-leave hook.*/
431 if (otp->hdr.pqueue.prio == IDLEPRIO) {
432 CH_CFG_IDLE_LEAVE_HOOK();
433 }
434
435 /* Placing in ready list ahead of peers.*/
436 otp = chSchReadyAheadI(otp);
437
438 /* Swap operation as tail call.*/
439 chSysSwitch(currp, otp);
440}
441
442#if !defined(CH_SCH_DO_RESCHEDULE_HOOKED)
443/**
444 * @brief Switches to the first thread on the runnable queue.
445 * @details The current thread is positioned in the ready list behind or
446 * ahead of all threads having the same priority depending on
447 * if it used its whole time slice.
448 * @note Not a user function, it is meant to be invoked by the scheduler
449 * itself or from within the port layer.
450 *
451 * @special
452 */
453void chSchDoReschedule(void) {
454 thread_t *otp = currp;
455
456 /* Picks the first thread from the ready queue and makes it current.*/
457 currp = (thread_t *)ch_pqueue_remove_highest(&ch.rlist.pqueue);
458 currp->state = CH_STATE_CURRENT;
459
460 /* Handling idle-leave hook.*/
461 if (otp->hdr.pqueue.prio == IDLEPRIO) {
462 CH_CFG_IDLE_LEAVE_HOOK();
463 }
464
465#if CH_CFG_TIME_QUANTUM > 0
466 /* If CH_CFG_TIME_QUANTUM is enabled then there are two different scenarios
467 to handle on preemption: time quantum elapsed or not.*/
468 if (otp->ticks == (tslices_t)0) {
469
470 /* The thread consumed its time quantum so it is enqueued behind threads
471 with same priority level, however, it acquires a new time quantum.*/
472 otp = chSchReadyI(otp);
473
474 /* The thread being swapped out receives a new time quantum.*/
475 otp->ticks = (tslices_t)CH_CFG_TIME_QUANTUM;
476 }
477 else {
478 /* The thread didn't consume all its time quantum so it is put ahead of
479 threads with equal priority and does not acquire a new time quantum.*/
480 otp = chSchReadyAheadI(otp);
481 }
482#else /* !(CH_CFG_TIME_QUANTUM > 0) */
483 /* If the round-robin mechanism is disabled then the thread goes always
484 ahead of its peers.*/
485 otp = chSchReadyAheadI(otp);
486#endif /* !(CH_CFG_TIME_QUANTUM > 0) */
487
488 /* Swap operation as tail call.*/
489 chSysSwitch(currp, otp);
490}
491#endif /* !defined(CH_SCH_DO_RESCHEDULE_HOOKED) */
492
493/** @} */