|
ChibiOS/RT
2.5.1 |
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 /** @} */