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

thread.cc

Go to the documentation of this file.
00001 // thread.cc 00002 // Routines to manage threads. There are four main operations: 00003 // 00004 // Fork -- create a thread to run a procedure concurrently 00005 // with the caller (this is done in two steps -- first 00006 // allocate the Thread object, then call Fork on it) 00007 // Finish -- called when the forked procedure finishes, to clean up 00008 // Yield -- relinquish control over the CPU to another ready thread 00009 // Suspend -- relinquish control over the CPU, but thread is now blocked. 00010 // In other words, it will not run again, until explicitly 00011 // put back on the ready queue. 00012 // 00013 // Copyright (c) 1992-1993 The Regents of the University of California. 00014 // All rights reserved. See copyright.h for copyright notice and limitation 00015 // of liability and disclaimer of warranty provisions. 00016 00017 #include "copyright.h" 00018 #include "thread.h" 00019 #include "switch.h" 00020 #include "synch.h" 00021 #include "system.h" 00022 #include <unistd.h> 00023 #include <signal.h> 00024 00025 #define STACK_FENCEPOST 0xdeadbeef // this is put at the top of the 00026 // execution stack, for detecting 00027 // stack overflows 00028 00029 extern sigjmp_buf schd_jmp; 00030 extern void schedule(); 00031 00032 //---------------------------------------------------------------------- 00033 // Thread::Thread 00034 // Initialize a thread control block, so that we can then call 00035 // Thread::Fork. 00036 // 00037 // "threadName" is an arbitrary string, useful for debugging. 00038 //---------------------------------------------------------------------- 00039 00040 Thread::Thread(char* threadName) 00041 { 00042 name = threadName; 00043 stackTop = NULL; 00044 stack = NULL; 00045 status = JUST_CREATED; 00046 started = 0; 00047 #ifdef USER_PROGRAM 00048 space = NULL; 00049 #endif 00050 } 00051 00052 //---------------------------------------------------------------------- 00053 // Thread::~Thread 00054 // De-allocate a thread. 00055 // 00056 // NOTE: the current thread *cannot* delete itself directly, 00057 // since it is still running on the stack that we need to delete. 00058 // 00059 // NOTE: if this is the main thread, we can't delete the stack 00060 // because we didn't allocate it -- we got it automatically 00061 // as part of starting up Nachos. 00062 //---------------------------------------------------------------------- 00063 00064 Thread::~Thread() 00065 { 00066 DEBUG('t', "Deleting thread \"%s\"\n", name); 00067 00068 ASSERT(this != currentThread); 00069 if (stack != NULL) 00070 DeallocBoundedArray((char *) stack, StackSize * sizeof(int)); 00071 } 00072 00073 //---------------------------------------------------------------------- 00074 // Thread::Fork 00075 // Invoke (*func)(arg), allowing caller and callee to execute 00076 // concurrently. 00077 // 00078 // NOTE: although our definition allows only a single integer argument 00079 // to be passed to the procedure, it is possible to pass multiple 00080 // arguments by making them fields of a structure, and passing a pointer 00081 // to the structure as "arg". 00082 // 00083 // Implemented as the following steps: 00084 // 1. Allocate a stack 00085 // 2. Initialize the stack so that a call to SWITCH will 00086 // cause it to run the procedure 00087 // 3. Put the thread on the ready queue 00088 // 00089 // "func" is the procedure to run concurrently. 00090 // "arg" is a single argument to be passed to the procedure. 00091 //---------------------------------------------------------------------- 00092 00093 void Thread::Fork(VoidFunctionPtr func, int arg) 00094 { 00095 DEBUG('t', "Forking thread \"%s\" with func = 0x%x, arg = %d\n", name, (int) func, arg); 00096 00097 pfunc = func; 00098 StackAllocate(func, arg); 00099 00100 00101 IntStatus oldLevel = interrupt->SetLevel(IntOff); 00102 scheduler->ReadyToRun(this); 00103 (void) interrupt->SetLevel(oldLevel); 00104 } 00105 00106 //---------------------------------------------------------------------- 00107 // Thread::CheckOverflow 00108 // Check a thread's stack to see if it has overrun the space 00109 // that has been allocated for it. If we had a smarter compiler, 00110 // we wouldn't need to worry about this, but we don't. 00111 // 00112 // NOTE: Nachos will not catch all stack overflow conditions. 00113 // In other words, your program may still crash because of an overflow. 00114 // 00115 // If you get bizarre results (such as seg faults where there is no code) 00116 // then you *may* need to increase the stack size. You can avoid stack 00117 // overflows by not putting large data structures on the stack. 00118 // Don't do this: void foo() { int bigArray[10000]; ... } 00119 //---------------------------------------------------------------------- 00120 00121 void Thread::CheckOverflow() 00122 { 00123 if (stack != NULL) 00124 #ifdef HOST_SNAKE // Stacks grow upward on the Snakes 00125 ASSERT(stack[StackSize - 1] == STACK_FENCEPOST); 00126 #else 00127 ASSERT(*stack == STACK_FENCEPOST); 00128 #endif 00129 } 00130 00131 //---------------------------------------------------------------------- 00132 // Thread::Finish 00133 // Called by ThreadRoot when a thread is done executing the 00134 // forked procedure. 00135 // 00136 // NOTE: we don't immediately de-allocate the thread data structure 00137 // or the execution stack, because we're still running in the thread 00138 // and we're still on the stack! Instead, we set "threadToBeDestroyed", 00139 // so that Scheduler::Run() will call the destructor, once we're 00140 // running in the context of a different thread. 00141 // 00142 // NOTE: we disable interrupts, so that we don't get a time slice 00143 // between setting threadToBeDestroyed, and going to sleep. 00144 //---------------------------------------------------------------------- 00145 00146 // 00147 void Thread::Finish () 00148 { 00149 (void) interrupt->SetLevel(IntOff); 00150 ASSERT(this == currentThread); 00151 00152 DEBUG('t', "Finishing thread \"%s\"\n", getName()); 00153 00154 threadToBeDestroyed = currentThread; 00155 Suspend();// invokes SWITCH 00156 // not reached 00157 } 00158 00159 //---------------------------------------------------------------------- 00160 // Thread::Yield 00161 // Relinquish the CPU if any other thread is ready to run. 00162 // If so, put the thread on the end of the ready list, so that 00163 // it will eventually be re-scheduled. 00164 // 00165 // NOTE: returns immediately if no other thread on the ready queue. 00166 // Otherwise returns when the thread eventually works its way 00167 // to the front of the ready list and gets re-scheduled. 00168 // 00169 // NOTE: we disable interrupts, so that looking at the thread 00170 // on the front of the ready list, and switching to it, can be done 00171 // atomically. On return, we re-set the interrupt level to its 00172 // original state, in case we are called with interrupts disabled. 00173 // 00174 // Similar to Thread::Suspend(), but a little different. 00175 //---------------------------------------------------------------------- 00176 00177 void Thread::Yield () 00178 { 00179 IntStatus oldLevel = interrupt->SetLevel(IntOff); 00180 00181 ASSERT(this == currentThread); 00182 00183 DEBUG('t', "Yielding thread \"%s\"\n", getName()); 00184 00185 if (sigsetjmp(currentThread->cbuff, 1) == 0) // save state then jump 00186 schedule(); 00187 00188 (void) interrupt->SetLevel(oldLevel); 00189 } 00190 00191 //---------------------------------------------------------------------- 00192 // Thread::Suspend 00193 // Relinquish the CPU, because the current thread is blocked 00194 // waiting on a synchronization variable (Semaphore, Lock, or Condition). 00195 // Eventually, some thread will wake this thread up, and put it 00196 // back on the ready queue, so that it can be re-scheduled. 00197 // 00198 // NOTE: if there are no threads on the ready queue, that means 00199 // we have no thread to run. "Interrupt::Idle" is called 00200 // to signify that we should idle the CPU until the next I/O interrupt 00201 // occurs (the only thing that could cause a thread to become 00202 // ready to run). 00203 // 00204 // NOTE: we assume interrupts are already disabled, because it 00205 // is called from the synchronization routines which must 00206 // disable interrupts for atomicity. We need interrupts off 00207 // so that there can't be a time slice between pulling the first thread 00208 // off the ready list, and switching to it. 00209 //---------------------------------------------------------------------- 00210 void Thread::Suspend() 00211 { 00212 ASSERT(this == currentThread); 00213 00214 DEBUG('t', "Suspending thread \"%s\"\n", getName()); 00215 00216 status = BLOCKED; 00217 00218 while (scheduler->IsEmpty()) 00219 interrupt->Idle(); // no one to run, wait for an interrupt 00220 // call the scheduler of the main proc 00221 if (sigsetjmp(currentThread->cbuff, 1) == 0) // save state then jump 00222 { 00223 currentThread = 0; // can't be put on the ready list 00224 SCHED(); // schedule in the context of the main process 00225 } 00226 } 00227 00228 00229 void Thread::Sleep(int sec) 00230 { 00231 IntStatus oldLevel = interrupt->SetLevel(IntOff); 00232 00233 // your code goes here 00234 // change the state and go on the sleep queue delta list 00235 00236 if (sigsetjmp(currentThread->cbuff, 1) == 0) // save the current context 00237 { 00238 currentThread = 0; // nothing is executing 00239 SCHED(); // schedule in the context of the main process 00240 } 00241 00242 interrupt->SetLevel(oldLevel); 00243 } 00244 00245 void Thread::UnSleep() 00246 { 00247 IntStatus oldLevel = interrupt->SetLevel(IntOff); 00248 00249 // your code goes here 00250 // unsleep the thread and change its status 00251 interrupt->SetLevel(oldLevel); 00252 } 00253 00254 //---------------------------------------------------------------------- 00255 // ThreadFinish, InterruptEnable, ThreadPrint 00256 // Dummy functions because C++ does not allow a pointer to a member 00257 // function. So in order to do this, we create a dummy C function 00258 // (which we can pass a pointer to), that then simply calls the 00259 // member function. 00260 //---------------------------------------------------------------------- 00261 00262 static void ThreadFinish() { currentThread->Finish(); } 00263 static void InterruptEnable() 00264 { 00265 interrupt->Enable(); 00266 } 00267 00268 void ThreadPrint(int arg){ Thread *t = (Thread *)arg; t->Print(); } 00269 00270 //---------------------------------------------------------------------- 00271 // Thread::StackAllocate 00272 // Allocate and initialize an execution stack. The stack is 00273 // initialized with an initial stack frame for ThreadRoot, which: 00274 // enables interrupts 00275 // calls (*func)(arg) 00276 // calls Thread::Finish 00277 // 00278 // "func" is the procedure to be forked 00279 // "arg" is the parameter to be passed to the procedure 00280 //---------------------------------------------------------------------- 00281 00282 void Thread::StackAllocate (VoidFunctionPtr func, int arg) 00283 { 00284 stack = (int *) AllocBoundedArray(StackSize * sizeof(int)); 00285 00286 #ifdef HOST_SNAKE 00287 // HP stack works from low addresses to high addresses 00288 stackTop = stack + 16; // HP requires 64-byte frame marker 00289 stack[StackSize - 1] = STACK_FENCEPOST; 00290 #else 00291 // i386 & MIPS & SPARC stack works from high addresses to low addresses 00292 #ifdef HOST_SPARC 00293 // SPARC stack must contains at least 1 activation record to start with. 00294 stackTop = stack + StackSize - 96; 00295 #else // HOST_MIPS || HOST_i386 00296 stackTop = stack + StackSize - 4; // -4 to be on the safe side! 00297 #ifdef HOST_i386 00298 // the 80386 passes the return address on the stack. In order for 00299 // SWITCH() to go to ThreadRoot when we switch to this thread, the 00300 // return addres used in SWITCH() must be the starting address of 00301 // ThreadRoot. 00302 *(--stackTop) = (int)ThreadRoot; 00303 #endif 00304 #endif // HOST_SPARC 00305 *stack = STACK_FENCEPOST; 00306 #endif // HOST_SNAKE 00307 00308 machineState[PCState] = (int) ThreadRoot; 00309 machineState[StartupPCState] = (int) InterruptEnable; 00310 machineState[InitialPCState] = (int) func; 00311 machineState[InitialArgState] = arg; 00312 machineState[WhenDonePCState] = (int) ThreadFinish; 00313 } 00314 00315 #ifdef USER_PROGRAM 00316 #include "machine.h" 00317 00318 //---------------------------------------------------------------------- 00319 // Thread::SaveUserState 00320 // Save the CPU state of a user program on a context switch. 00321 // 00322 // Note that a user program thread has *two* sets of CPU registers -- 00323 // one for its state while executing user code, one for its state 00324 // while executing kernel code. This routine saves the former. 00325 //---------------------------------------------------------------------- 00326 00327 void Thread::SaveUserState() 00328 { 00329 for (int i = 0; i < NumTotalRegs; i++) 00330 userRegisters[i] = machine->ReadRegister(i); 00331 } 00332 00333 //---------------------------------------------------------------------- 00334 // Thread::RestoreUserState 00335 // Restore the CPU state of a user program on a context switch. 00336 // 00337 // Note that a user program thread has *two* sets of CPU registers -- 00338 // one for its state while executing user code, one for its state 00339 // while executing kernel code. This routine restores the former. 00340 //---------------------------------------------------------------------- 00341 00342 void Thread::RestoreUserState() 00343 { 00344 for (int i = 0; i < NumTotalRegs; i++) 00345 machine->WriteRegister(i, userRegisters[i]); 00346 } 00347 #endif

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