ChibiOS/RT
2.5.1
chschd.c
Go to the documentation of this file.
00001 /*
00002     ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
00003                  2011,2012 Giovanni Di Sirio.
00004 
00005     This file is part of ChibiOS/RT.
00006 
00007     ChibiOS/RT is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 3 of the License, or
00010     (at your option) any later version.
00011 
00012     ChibiOS/RT is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 */
00020 
00021 /**
00022  * @file    chschd.c
00023  * @brief   Scheduler code.
00024  *
00025  * @addtogroup scheduler
00026  * @details This module provides the default portable scheduler code,
00027  *          scheduler functions can be individually captured by the port
00028  *          layer in order to provide architecture optimized equivalents.
00029  *          When a function is captured its default code is not built into
00030  *          the OS image, the optimized version is included instead.
00031  * @{
00032  */
00033 
00034 #include "ch.h"
00035 
00036 /**
00037  * @brief   Ready list header.
00038  */
00039 #if !defined(PORT_OPTIMIZED_RLIST_VAR) || defined(__DOXYGEN__)
00040 ReadyList rlist;
00041 #endif /* !defined(PORT_OPTIMIZED_RLIST_VAR) */
00042 
00043 /**
00044  * @brief   Scheduler initialization.
00045  *
00046  * @notapi
00047  */
00048 void _scheduler_init(void) {
00049 
00050   queue_init(&rlist.r_queue);
00051   rlist.r_prio = NOPRIO;
00052 #if CH_USE_REGISTRY
00053   rlist.r_newer = rlist.r_older = (Thread *)&rlist;
00054 #endif
00055 }
00056 
00057 /**
00058  * @brief   Inserts a thread in the Ready List.
00059  * @details The thread is positioned behind all threads with higher or equal
00060  *          priority.
00061  * @pre     The thread must not be already inserted in any list through its
00062  *          @p p_next and @p p_prev or list corruption would occur.
00063  * @post    This function does not reschedule so a call to a rescheduling
00064  *          function must be performed before unlocking the kernel. Note that
00065  *          interrupt handlers always reschedule on exit so an explicit
00066  *          reschedule must not be performed in ISRs.
00067  *
00068  * @param[in] tp        the thread to be made ready
00069  * @return              The thread pointer.
00070  *
00071  * @iclass
00072  */
00073 #if !defined(PORT_OPTIMIZED_READYI) || defined(__DOXYGEN__)
00074 Thread *chSchReadyI(Thread *tp) {
00075   Thread *cp;
00076 
00077   chDbgCheckClassI();
00078 
00079   /* Integrity checks.*/
00080   chDbgAssert((tp->p_state != THD_STATE_READY) &&
00081               (tp->p_state != THD_STATE_FINAL),
00082               "chSchReadyI(), #1",
00083               "invalid state");
00084 
00085   tp->p_state = THD_STATE_READY;
00086   cp = (Thread *)&rlist.r_queue;
00087   do {
00088     cp = cp->p_next;
00089   } while (cp->p_prio >= tp->p_prio);
00090   /* Insertion on p_prev.*/
00091   tp->p_next = cp;
00092   tp->p_prev = cp->p_prev;
00093   tp->p_prev->p_next = cp->p_prev = tp;
00094   return tp;
00095 }
00096 #endif /* !defined(PORT_OPTIMIZED_READYI) */
00097 
00098 /**
00099  * @brief   Puts the current thread to sleep into the specified state.
00100  * @details The thread goes into a sleeping state. The possible
00101  *          @ref thread_states are defined into @p threads.h.
00102  *
00103  * @param[in] newstate  the new thread state
00104  *
00105  * @sclass
00106  */
00107 #if !defined(PORT_OPTIMIZED_GOSLEEPS) || defined(__DOXYGEN__)
00108 void chSchGoSleepS(tstate_t newstate) {
00109   Thread *otp;
00110 
00111   chDbgCheckClassS();
00112 
00113   (otp = currp)->p_state = newstate;
00114 #if CH_TIME_QUANTUM > 0
00115   /* The thread is renouncing its remaining time slices so it will have a new
00116      time quantum when it will wakeup.*/
00117   otp->p_preempt = CH_TIME_QUANTUM;
00118 #endif
00119   setcurrp(fifo_remove(&rlist.r_queue));
00120   currp->p_state = THD_STATE_CURRENT;
00121   chSysSwitch(currp, otp);
00122 }
00123 #endif /* !defined(PORT_OPTIMIZED_GOSLEEPS) */
00124 
00125 #if !defined(PORT_OPTIMIZED_GOSLEEPTIMEOUTS) || defined(__DOXYGEN__)
00126 /*
00127  * Timeout wakeup callback.
00128  */
00129 static void wakeup(void *p) {
00130   Thread *tp = (Thread *)p;
00131 
00132   chSysLockFromIsr();
00133   switch (tp->p_state) {
00134   case THD_STATE_READY:
00135     /* Handling the special case where the thread has been made ready by
00136        another thread with higher priority.*/
00137     chSysUnlockFromIsr();
00138     return;
00139 #if CH_USE_SEMAPHORES || CH_USE_QUEUES ||                                   \
00140     (CH_USE_CONDVARS && CH_USE_CONDVARS_TIMEOUT)
00141 #if CH_USE_SEMAPHORES
00142   case THD_STATE_WTSEM:
00143     chSemFastSignalI((Semaphore *)tp->p_u.wtobjp);
00144     /* Falls into, intentional. */
00145 #endif
00146 #if CH_USE_QUEUES
00147   case THD_STATE_WTQUEUE:
00148 #endif
00149 #if CH_USE_CONDVARS && CH_USE_CONDVARS_TIMEOUT
00150   case THD_STATE_WTCOND:
00151 #endif
00152     /* States requiring dequeuing.*/
00153     dequeue(tp);
00154 #endif
00155   }
00156   tp->p_u.rdymsg = RDY_TIMEOUT;
00157   chSchReadyI(tp);
00158   chSysUnlockFromIsr();
00159 }
00160 
00161 /**
00162  * @brief   Puts the current thread to sleep into the specified state with
00163  *          timeout specification.
00164  * @details The thread goes into a sleeping state, if it is not awakened
00165  *          explicitly within the specified timeout then it is forcibly
00166  *          awakened with a @p RDY_TIMEOUT low level message. The possible
00167  *          @ref thread_states are defined into @p threads.h.
00168  *
00169  * @param[in] newstate  the new thread state
00170  * @param[in] time      the number of ticks before the operation timeouts, the
00171  *                      special values are handled as follow:
00172  *                      - @a TIME_INFINITE the thread enters an infinite sleep
00173  *                        state, this is equivalent to invoking
00174  *                        @p chSchGoSleepS() but, of course, less efficient.
00175  *                      - @a TIME_IMMEDIATE this value is not allowed.
00176  *                      .
00177  * @return              The wakeup message.
00178  * @retval RDY_TIMEOUT if a timeout occurs.
00179  *
00180  * @sclass
00181  */
00182 msg_t chSchGoSleepTimeoutS(tstate_t newstate, systime_t time) {
00183 
00184   chDbgCheckClassS();
00185 
00186   if (TIME_INFINITE != time) {
00187     VirtualTimer vt;
00188 
00189     chVTSetI(&vt, time, wakeup, currp);
00190     chSchGoSleepS(newstate);
00191     if (chVTIsArmedI(&vt))
00192       chVTResetI(&vt);
00193   }
00194   else
00195     chSchGoSleepS(newstate);
00196   return currp->p_u.rdymsg;
00197 }
00198 #endif /* !defined(PORT_OPTIMIZED_GOSLEEPTIMEOUTS) */
00199 
00200 /**
00201  * @brief   Wakes up a thread.
00202  * @details The thread is inserted into the ready list or immediately made
00203  *          running depending on its relative priority compared to the current
00204  *          thread.
00205  * @pre     The thread must not be already inserted in any list through its
00206  *          @p p_next and @p p_prev or list corruption would occur.
00207  * @note    It is equivalent to a @p chSchReadyI() followed by a
00208  *          @p chSchRescheduleS() but much more efficient.
00209  * @note    The function assumes that the current thread has the highest
00210  *          priority.
00211  *
00212  * @param[in] ntp       the Thread to be made ready
00213  * @param[in] msg       message to the awakened thread
00214  *
00215  * @sclass
00216  */
00217 #if !defined(PORT_OPTIMIZED_WAKEUPS) || defined(__DOXYGEN__)
00218 void chSchWakeupS(Thread *ntp, msg_t msg) {
00219 
00220   chDbgCheckClassS();
00221 
00222   ntp->p_u.rdymsg = msg;
00223   /* If the waken thread has a not-greater priority than the current
00224      one then it is just inserted in the ready list else it made
00225      running immediately and the invoking thread goes in the ready
00226      list instead.*/
00227   if (ntp->p_prio <= currp->p_prio)
00228     chSchReadyI(ntp);
00229   else {
00230     Thread *otp = chSchReadyI(currp);
00231     setcurrp(ntp);
00232     ntp->p_state = THD_STATE_CURRENT;
00233     chSysSwitch(ntp, otp);
00234   }
00235 }
00236 #endif /* !defined(PORT_OPTIMIZED_WAKEUPS) */
00237 
00238 /**
00239  * @brief   Performs a reschedule if a higher priority thread is runnable.
00240  * @details If a thread with a higher priority than the current thread is in
00241  *          the ready list then make the higher priority thread running.
00242  *
00243  * @sclass
00244  */
00245 #if !defined(PORT_OPTIMIZED_RESCHEDULES) || defined(__DOXYGEN__)
00246 void chSchRescheduleS(void) {
00247 
00248   chDbgCheckClassS();
00249 
00250   if (chSchIsRescRequiredI())
00251     chSchDoRescheduleAhead();
00252 }
00253 #endif /* !defined(PORT_OPTIMIZED_RESCHEDULES) */
00254 
00255 /**
00256  * @brief   Evaluates if preemption is required.
00257  * @details The decision is taken by comparing the relative priorities and
00258  *          depending on the state of the round robin timeout counter.
00259  * @note    Not a user function, it is meant to be invoked by the scheduler
00260  *          itself or from within the port layer.
00261  *
00262  * @retval TRUE         if there is a thread that must go in running state
00263  *                      immediately.
00264  * @retval FALSE        if preemption is not required.
00265  *
00266  * @special
00267  */
00268 #if !defined(PORT_OPTIMIZED_ISPREEMPTIONREQUIRED) || defined(__DOXYGEN__)
00269 bool_t chSchIsPreemptionRequired(void) {
00270   tprio_t p1 = firstprio(&rlist.r_queue);
00271   tprio_t p2 = currp->p_prio;
00272 #if CH_TIME_QUANTUM > 0
00273   /* If the running thread has not reached its time quantum, reschedule only
00274      if the first thread on the ready queue has a higher priority.
00275      Otherwise, if the running thread has used up its time quantum, reschedule
00276      if the first thread on the ready queue has equal or higher priority.*/
00277   return currp->p_preempt ? p1 > p2 : p1 >= p2;
00278 #else
00279   /* If the round robin preemption feature is not enabled then performs a
00280      simpler comparison.*/
00281   return p1 > p2;
00282 #endif
00283 }
00284 #endif /* !defined(PORT_OPTIMIZED_ISPREEMPTIONREQUIRED) */
00285 
00286 /**
00287  * @brief   Switches to the first thread on the runnable queue.
00288  * @details The current thread is positioned in the ready list behind all
00289  *          threads having the same priority. The thread regains its time
00290  *          quantum.
00291  * @note    Not a user function, it is meant to be invoked by the scheduler
00292  *          itself or from within the port layer.
00293  *
00294  * @special
00295  */
00296 #if !defined(PORT_OPTIMIZED_DORESCHEDULEBEHIND) || defined(__DOXYGEN__)
00297 void chSchDoRescheduleBehind(void) {
00298   Thread *otp;
00299 
00300   otp = currp;
00301   /* Picks the first thread from the ready queue and makes it current.*/
00302   setcurrp(fifo_remove(&rlist.r_queue));
00303   currp->p_state = THD_STATE_CURRENT;
00304 #if CH_TIME_QUANTUM > 0
00305   otp->p_preempt = CH_TIME_QUANTUM;
00306 #endif
00307   chSchReadyI(otp);
00308   chSysSwitch(currp, otp);
00309 }
00310 #endif /* !defined(PORT_OPTIMIZED_DORESCHEDULEBEHIND) */
00311 
00312 /**
00313  * @brief   Switches to the first thread on the runnable queue.
00314  * @details The current thread is positioned in the ready list ahead of all
00315  *          threads having the same priority.
00316  * @note    Not a user function, it is meant to be invoked by the scheduler
00317  *          itself or from within the port layer.
00318  *
00319  * @special
00320  */
00321 #if !defined(PORT_OPTIMIZED_DORESCHEDULEAHEAD) || defined(__DOXYGEN__)
00322 void chSchDoRescheduleAhead(void) {
00323   Thread *otp, *cp;
00324 
00325   otp = currp;
00326   /* Picks the first thread from the ready queue and makes it current.*/
00327   setcurrp(fifo_remove(&rlist.r_queue));
00328   currp->p_state = THD_STATE_CURRENT;
00329 
00330   otp->p_state = THD_STATE_READY;
00331   cp = (Thread *)&rlist.r_queue;
00332   do {
00333     cp = cp->p_next;
00334   } while (cp->p_prio > otp->p_prio);
00335   /* Insertion on p_prev.*/
00336   otp->p_next = cp;
00337   otp->p_prev = cp->p_prev;
00338   otp->p_prev->p_next = cp->p_prev = otp;
00339 
00340   chSysSwitch(currp, otp);
00341 }
00342 #endif /* !defined(PORT_OPTIMIZED_DORESCHEDULEAHEAD) */
00343 
00344 /**
00345  * @brief   Switches to the first thread on the runnable queue.
00346  * @details The current thread is positioned in the ready list behind or
00347  *          ahead of all threads having the same priority depending on
00348  *          if it used its whole time slice.
00349  * @note    Not a user function, it is meant to be invoked by the scheduler
00350  *          itself or from within the port layer.
00351  *
00352  * @special
00353  */
00354 #if !defined(PORT_OPTIMIZED_DORESCHEDULE) || defined(__DOXYGEN__)
00355 void chSchDoReschedule(void) {
00356 
00357 #if CH_TIME_QUANTUM > 0
00358   /* If CH_TIME_QUANTUM is enabled then there are two different scenarios to
00359      handle on preemption: time quantum elapsed or not.*/
00360   if (currp->p_preempt == 0) {
00361     /* The thread consumed its time quantum so it is enqueued behind threads
00362        with same priority level, however, it acquires a new time quantum.*/
00363     chSchDoRescheduleBehind();
00364   }
00365   else {
00366     /* The thread didn't consume all its time quantum so it is put ahead of
00367        threads with equal priority and does not acquire a new time quantum.*/
00368     chSchDoRescheduleAhead();
00369   }
00370 #else /* !(CH_TIME_QUANTUM > 0) */
00371   /* If the round-robin mechanism is disabled then the thread goes always
00372      ahead of its peers.*/
00373   chSchDoRescheduleAhead();
00374 #endif /* !(CH_TIME_QUANTUM > 0) */
00375 }
00376 #endif /* !defined(PORT_OPTIMIZED_DORESCHEDULE) */
00377 
00378 /** @} */