diff options
Diffstat (limited to 'lib/chibios/os/rt/src/chschd.c')
-rw-r--r-- | lib/chibios/os/rt/src/chschd.c | 493 |
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 | */ | ||
42 | ch_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 | */ | ||
59 | static 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 | */ | ||
109 | void 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 | */ | ||
128 | void _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 | */ | ||
153 | thread_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 | */ | ||
185 | thread_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 | */ | ||
210 | void 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 | */ | ||
258 | msg_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 | */ | ||
295 | void 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 | */ | ||
339 | void 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 | */ | ||
362 | bool 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 | */ | ||
390 | void 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 | */ | ||
423 | void 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 | */ | ||
453 | void 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 | /** @} */ | ||