Main Page | Alphabetical List | Class List | File List | Class Members | File Members

scheduler.cc

Go to the documentation of this file.
00001 // scheduler.cc 00002 // Routines to choose the next thread to run, and to dispatch to 00003 // that thread. 00004 // 00005 // These routines assume that interrupts are already disabled. 00006 // If interrupts are disabled, we can assume mutual exclusion 00007 // (since we are on a uniprocessor). 00008 // 00009 // NOTE: We can't use Locks to provide mutual exclusion here, since 00010 // if we needed to wait for a lock, and the lock was busy, we would 00011 // end up calling FindNextToRun(), and that would put us in an 00012 // infinite loop. 00013 // 00014 // Very simple implementation -- no priorities, straight FIFO. 00015 // Might need to be improved in later assignments. 00016 // 00017 // Copyright (c) 1992-1993 The Regents of the University of California. 00018 // All rights reserved. See copyright.h for copyright notice and limitation 00019 // of liability and disclaimer of warranty provisions. 00020 00021 #include "copyright.h" 00022 #include "scheduler.h" 00023 #include "system.h" 00024 #include "thread.h" 00025 #include <signal.h> 00026 #include <time.h> 00027 #include <unistd.h> 00028 #include <setjmp.h> 00029 00030 00031 sigjmp_buf schd_jmp; 00032 Thread bogus_th("starter"); 00033 sigset_t timer_mask; 00034 timer_t timer_id; 00035 itimerspec timer_value; 00036 itimerspec timer_empty; 00037 00038 bool preempt = false; 00039 00040 //---------------------------------------------------------------------- 00041 // SetPreemption 00042 // Global Function to turn on Preemption int the code. 00043 //---------------------------------------------------------------------- 00044 00045 void SetPreemption() 00046 { 00047 preempt = true; 00048 } 00049 00050 //---------------------------------------------------------------------- 00051 // Scheduler::Scheduler 00052 // Initialize the list of ready but not running threads to empty. 00053 //---------------------------------------------------------------------- 00054 00055 Scheduler::Scheduler() 00056 { 00057 readyList = new List; 00058 } 00059 00060 //---------------------------------------------------------------------- 00061 // Scheduler::~Scheduler 00062 // De-allocate the list of ready threads. 00063 //---------------------------------------------------------------------- 00064 00065 Scheduler::~Scheduler() 00066 { 00067 delete readyList; 00068 } 00069 00070 int Scheduler::IsEmpty() 00071 { 00072 return readyList->IsEmpty(); // default*/ 00073 } 00074 00075 //---------------------------------------------------------------------- 00076 // Scheduler::ReadyToRun 00077 // Mark a thread as ready, but not running. 00078 // Put it on the ready list, for later scheduling onto the CPU. 00079 // 00080 // "thread" is the thread to be put on the ready list. 00081 //---------------------------------------------------------------------- 00082 00083 void Scheduler::ReadyToRun (Thread *thread) 00084 { 00085 thread->setStatus(READY); 00086 readyList->Append((void *)thread); 00087 } 00088 00089 //---------------------------------------------------------------------- 00090 // Scheduler::FindNextToRun 00091 // Return the next thread to be scheduled onto the CPU. 00092 // If there are no ready threads, return NULL. 00093 // Side effect: 00094 // Thread is removed from the ready list. 00095 //---------------------------------------------------------------------- 00096 Thread* Scheduler::FindNextToRun () 00097 { 00098 return (Thread *)readyList->Remove(); 00099 } 00100 00101 00102 00103 //---------------------------------------------------------------------- 00104 // Scheduler::Print 00105 // Print the scheduler state -- in other words, the contents of 00106 // the ready list. For debugging. 00107 //---------------------------------------------------------------------- 00108 void Scheduler::Print() 00109 { 00110 printf("Ready list contents:\n"); 00111 readyList->Mapcar((VoidFunctionPtr) ThreadPrint); 00112 } 00113 00114 00115 00116 //---------------------------------------------------------------------- 00117 // timer interrupt handler that is called when SIGALRM is generated 00118 //---------------------------------------------------------------------- 00119 00120 void Scheduler::timerIntH(int sig) 00121 { 00122 static int counter = 0; 00123 00124 if (!PREEMPTION && !preempt) 00125 return; 00126 00127 timerCount++; 00128 counter++; 00129 if (counter == TICKS_PER_SEC) // the timer should be called about 15 times per second 00130 { 00131 counter = 0; 00132 secCount++; // increment the elapsed seconds 00133 } 00134 // arm the timer again 00135 timer_settime(timer_id, 0, &timer_value, 0); 00136 00137 00138 if (interrupt->level == IntOff || scheduler->IsEmpty()) 00139 { 00140 if (interrupt->level == IntOff) // preempt this thread after the sys call is done 00141 { 00142 interrupt->YieldOnReturn(); 00143 } 00144 return; 00145 } 00146 00147 if (currentThread) 00148 { 00149 if (sigsetjmp(currentThread->cbuff, 1) == 0) 00150 schedule(); 00151 } 00152 else // just call schedule as nothing is running at the moment besides the main proc 00153 schedule(); 00154 00155 return; 00156 } 00157 00158 //---------------------------------------------------------------------- 00159 // Inializes the timer handler and the signal mask. then sets off the 00160 // timer. 00161 //---------------------------------------------------------------------- 00162 void timerInit() 00163 { 00164 struct sigaction tact; 00165 00166 //setup the timer handler and tell it to use an alternative signal stack 00167 memset(&tact, 0, sizeof(struct sigaction)); 00168 tact.sa_handler = Scheduler::timerIntH; 00169 tact.sa_flags = 0; 00170 sigemptyset(&tact.sa_mask); 00171 00172 sigemptyset(&timer_mask); 00173 sigaddset(&timer_mask, SIGALRM); 00174 00175 if (sigaction(SIGALRM, &tact, NULL) != 0) 00176 printf("Failed to set the alarm handler\n"); 00177 00178 timer_create(CLOCK_REALTIME, 0, &timer_id); 00179 memset(&timer_value, 0, sizeof(struct itimerspec)); 00180 memset(&timer_empty, 0, sizeof(struct itimerspec)); 00181 timer_value.it_value.tv_sec = 0; 00182 timer_value.it_value.tv_nsec = NSECS_PER_SEC / 15; // 0.0666 sec 00183 timer_settime(timer_id, 0, &timer_value, 0); 00184 } 00185 00186 // --------------------------------------------------------------- 00187 // IMPORTANT!!!!!!!!!!!!!!!!!!! 00188 // The function assumes that the current thread's context has 00189 // been ALREADY saved! (since schedule happens in the context 00190 // of the main thread). It just puts the current thread on the 00191 // ready list and selects the next one for execution and then does 00192 // a siglongjmp 00193 // 00194 // DO NOT TOUCH THIS FUNCTION: 00195 // the scheduling policy can be done by changing the FindNextToRun 00196 // function. 00197 // --------------------------------------------------------------- 00198 void schedule() 00199 { 00200 Thread *nextThread = 0; 00201 00202 // disarm the alarm 00203 timer_settime(timer_id, 0, &timer_empty, 0); 00204 00205 nextThread = scheduler->FindNextToRun(); 00206 00207 if (threadToBeDestroyed != NULL) 00208 { 00209 currentThread = 0; 00210 DEBUG('t', "Purge Thread \"%s\"\n", threadToBeDestroyed->getName()); 00211 delete threadToBeDestroyed; 00212 threadToBeDestroyed = NULL; 00213 } 00214 00215 if (nextThread != NULL) 00216 { 00217 if (currentThread) 00218 scheduler->ReadyToRun(currentThread); // set to READY 00219 00220 currentThread = nextThread; // switch to the next thread 00221 //swap over here 00222 if (!currentThread->started) 00223 { 00224 currentThread->started = 1; 00225 currentThread->setStatus(RUNNING); // set to RUNNING 00226 DEBUG('t', "^Create^ \"%s\"\n", currentThread->getName()); 00227 interrupt->Enable(); 00228 timer_settime(timer_id, 0, &timer_value, 0); 00229 // use the NachOS switch to get the thread going for the 00230 // time and then begin to use the siglongjmp way 00231 SWITCH(&bogus_th, currentThread); 00232 } 00233 else 00234 { 00235 DEBUG('t', "Sched \"%s\"\n", currentThread->getName()); 00236 currentThread->setStatus(RUNNING); // set to RUNNING 00237 interrupt->Enable(); 00238 timer_settime(timer_id, 0, &timer_value, 0); 00239 siglongjmp(currentThread->cbuff, 1); 00240 } 00241 } 00242 else if (currentThread)// only a single thread is left 00243 { 00244 DEBUG('t', "Only Thread \"%s\"\n", currentThread->getName()); 00245 currentThread->setStatus(RUNNING); // set to RUNNING 00246 interrupt->Enable(); 00247 timer_settime(timer_id, 0, &timer_value, 0); 00248 sigprocmask(SIG_UNBLOCK, &timer_mask, 0); 00249 siglongjmp(currentThread->cbuff, 1); 00250 } 00251 else 00252 { 00253 // we are spinning while something is sleeping 00254 interrupt->Enable(); 00255 timer_settime(timer_id, 0, &timer_value, 0); 00256 } 00257 } 00258

Generated on Thu Sep 16 12:33:45 2004 for NachOS by doxygen 1.3.8