ChibiOS/RT  6.1.4
chschd.c
Go to the documentation of this file.
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  */
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 
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.*/
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:
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);
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  */
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  */
154 
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  */
186 
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 
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.*/
226  currp->state = CH_STATE_CURRENT;
227 
228  /* Handling idle-enter hook.*/
229  if (currp->hdr.pqueue.prio == IDLEPRIO) {
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 
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 
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) {
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 
342 
343  if (chSchIsRescRequiredI()) {
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  */
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  */
391  thread_t *otp = currp;
392 
393  /* Picks the first thread from the ready queue and makes it current.*/
395  currp->state = CH_STATE_CURRENT;
396 
397  /* Handling idle-leave hook.*/
398  if (otp->hdr.pqueue.prio == IDLEPRIO) {
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  */
424  thread_t *otp = currp;
425 
426  /* Picks the first thread from the ready queue and makes it current.*/
428  currp->state = CH_STATE_CURRENT;
429 
430  /* Handling idle-leave hook.*/
431  if (otp->hdr.pqueue.prio == IDLEPRIO) {
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.*/
458  currp->state = CH_STATE_CURRENT;
459 
460  /* Handling idle-leave hook.*/
461  if (otp->hdr.pqueue.prio == IDLEPRIO) {
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 /** @} */
CH_CFG_IDLE_LEAVE_HOOK
#define CH_CFG_IDLE_LEAVE_HOOK()
Idle thread leave hook.
Definition: chconf.h:709
IDLEPRIO
#define IDLEPRIO
Idle priority.
Definition: chschd.h:52
_scheduler_init
void _scheduler_init(void)
Scheduler initialization.
Definition: chschd.c:128
ch_thread::wttrp
thread_reference_t * wttrp
Pointer to a generic thread reference object.
Definition: chschd.h:222
CH_STATE_READY
#define CH_STATE_READY
Waiting on the ready list.
Definition: chschd.h:62
ch_queue::next
ch_queue_t * next
Next in the list/queue.
Definition: chlists.h:70
chSchReadyI
thread_t * chSchReadyI(thread_t *tp)
Inserts a thread in the Ready List placing it behind its peers.
Definition: chschd.c:153
ch_thread::pqueue
ch_priority_queue_t pqueue
Threads ordered queues element.
Definition: chschd.h:138
currp
#define currp
Current thread pointer access macro.
Definition: chschd.h:462
ch_thread::rdymsg
msg_t rdymsg
Thread wakeup code.
Definition: chschd.h:201
CH_CFG_TIME_QUANTUM
#define CH_CFG_TIME_QUANTUM
Round robin interval.
Definition: chconf.h:108
chVTIsArmedI
static bool chVTIsArmedI(const virtual_timer_t *vtp)
Returns true if the specified timer is armed.
Definition: chvt.h:242
chDbgAssert
#define chDbgAssert(c, r)
Condition assertion.
Definition: chdebug.h:127
CH_CFG_IDLE_ENTER_HOOK
#define CH_CFG_IDLE_ENTER_HOOK()
Idle thread enter hook.
Definition: chconf.h:699
CH_STATE_SUSPENDED
#define CH_STATE_SUSPENDED
Suspended state.
Definition: chschd.h:66
chSysSwitch
#define chSysSwitch(ntp, otp)
Performs a context switch.
Definition: chsys.h:268
ch_sch_prio_insert
void ch_sch_prio_insert(ch_queue_t *tp, ch_queue_t *qp)
Inserts a thread into a priority ordered queue.
Definition: chschd.c:109
chSchDoRescheduleBehind
void chSchDoRescheduleBehind(void)
Switches to the first thread on the runnable queue.
Definition: chschd.c:390
CH_STATE_WTCOND
#define CH_STATE_WTCOND
On a cond.variable.
Definition: chschd.h:70
chSchGoSleepTimeoutS
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:258
chSchIsRescRequiredI
static bool chSchIsRescRequiredI(void)
Determines if the current thread must reschedule.
Definition: chschd.h:528
chSchDoReschedule
void chSchDoReschedule(void)
Switches to the first thread on the runnable queue.
Definition: chschd.c:453
chDbgCheck
#define chDbgCheck(c)
Function parameters check.
Definition: chdebug.h:101
ch_system
System data structure.
Definition: chschd.h:414
ch_pqueue_insert_ahead
static ch_priority_queue_t * ch_pqueue_insert_ahead(ch_priority_queue_t *pqp, ch_priority_queue_t *p)
Inserts an element in the priority queue placing it ahead of its peers.
Definition: chlists.h:387
ch_thread
Structure representing a thread.
Definition: chschd.h:134
chSchGoSleepS
void chSchGoSleepS(tstate_t newstate)
Puts the current thread to sleep into the specified state.
Definition: chschd.c:210
CH_STATE_CURRENT
#define CH_STATE_CURRENT
Currently running.
Definition: chschd.h:64
chSemFastSignalI
static void chSemFastSignalI(semaphore_t *sp)
Increases the semaphore counter.
Definition: chsem.h:174
ch_thread::ticks
tslices_t ticks
Number of ticks remaining to this thread.
Definition: chschd.h:180
ch_pqueue_remove_highest
static ch_priority_queue_t * ch_pqueue_remove_highest(ch_priority_queue_t *pqp)
Removes the highest priority element from a priority queue and returns it.
Definition: chlists.h:337
CH_STATE_FINAL
#define CH_STATE_FINAL
Thread terminated.
Definition: chschd.h:81
ch_pqueue_insert_behind
static ch_priority_queue_t * ch_pqueue_insert_behind(ch_priority_queue_t *pqp, ch_priority_queue_t *p)
Inserts an element in the priority queue placing it behind its peers.
Definition: chlists.h:358
ch_thread::u
union ch_thread::@1 u
State-specific fields.
ch_queue
Structure representing a generic bidirectional linked list header and element.
Definition: chlists.h:69
chSysLockFromISR
static void chSysLockFromISR(void)
Enters the kernel lock state from within an interrupt handler.
Definition: chsys.h:395
TIME_INFINITE
#define TIME_INFINITE
Infinite interval specification for all functions with a timeout specification.
Definition: chtime.h:55
chSchRescheduleS
void chSchRescheduleS(void)
Performs a reschedule if a higher priority thread is runnable.
Definition: chschd.c:339
firstprio
#define firstprio(rlp)
Returns the priority of the first thread on the given ready list.
Definition: chschd.h:455
chDbgCheckClassI
void chDbgCheckClassI(void)
I-class functions context check.
Definition: chdebug.c:233
ch_virtual_timer
Structure representing a Virtual Timer.
Definition: chschd.h:321
chSchReadyAheadI
thread_t * chSchReadyAheadI(thread_t *tp)
Inserts a thread in the Ready List placing it ahead its peers.
Definition: chschd.c:185
chSchIsPreemptionRequired
bool chSchIsPreemptionRequired(void)
Evaluates if preemption is required.
Definition: chschd.c:362
ch
ch_system_t ch
System data structures.
Definition: chschd.c:42
sysinterval_t
uint64_t sysinterval_t
Type of time interval.
Definition: chtime.h:119
chSchDoRescheduleAhead
void chSchDoRescheduleAhead(void)
Switches to the first thread on the runnable queue.
Definition: chschd.c:423
ch_pqueue_init
static void ch_pqueue_init(ch_priority_queue_t *pqp)
Priority queue initialization.
Definition: chlists.h:321
chVTDoResetI
void chVTDoResetI(virtual_timer_t *vtp)
Disables a Virtual Timer.
Definition: chvt.c:461
CH_STATE_QUEUED
#define CH_STATE_QUEUED
On a queue.
Definition: chschd.h:67
ch_thread::queue
ch_queue_t queue
Threads queues element.
Definition: chschd.h:137
ch_thread::state
tstate_t state
Current thread state.
Definition: chschd.h:165
ch_thread::wtsemp
struct ch_semaphore * wtsemp
Pointer to a generic semaphore object.
Definition: chschd.h:236
ch.h
ChibiOS/RT main include file.
MSG_TIMEOUT
#define MSG_TIMEOUT
Wakeup caused by a timeout condition.
Definition: chschd.h:40
chVTDoSetI
void chVTDoSetI(virtual_timer_t *vtp, sysinterval_t delay, vtfunc_t vtfunc, void *par)
Enables a one-shot virtual timer.
Definition: chvt.c:438
ch_system::rlist
ready_list_t rlist
Ready list header.
Definition: chschd.h:418
ch_queue_dequeue
static ch_queue_t * ch_queue_dequeue(ch_queue_t *p)
Removes an element from a queue and returns it.
Definition: chlists.h:303
chDbgCheckClassS
void chDbgCheckClassS(void)
S-class functions context check.
Definition: chdebug.c:248
chSysUnlockFromISR
static void chSysUnlockFromISR(void)
Leaves the kernel lock state from within an interrupt handler.
Definition: chsys.h:415
CH_STATE_WTSEM
#define CH_STATE_WTSEM
On a semaphore.
Definition: chschd.h:68
chSchWakeupS
void chSchWakeupS(thread_t *ntp, msg_t msg)
Wakes up a thread.
Definition: chschd.c:295