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

Generated on Mon Feb 10 09:54:48 2003 for nachos by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002
The University of Southern California does not screen or control the content on this website and thus does not guarantee the accuracy, integrity, or quality of such content. All content on this website is provided by and is the sole responsibility of the person from which such content originated, and such content does not necessarily reflect the opinions of the University administration or the Board of Trustees