📄 thread.cc
字号:
// overflows by not putting large data structures on the stack.// Don't do this: void foo() { int bigArray[10000]; ... }//----------------------------------------------------------------------voidThread::CheckOverflow(){ if (stack != NULL)#ifdef HOST_SNAKE // Stacks grow upward on the Snakes ASSERT(stack[StackSize - 1] == STACK_FENCEPOST);#else ASSERT(*stack == STACK_FENCEPOST);#endif}//----------------------------------------------------------------------// CHANGED// Thread::Finish// Called by ThreadRoot when a thread is done executing the // forked procedure.//// NOTE: we don't immediately de-allocate the thread data structure // or the execution stack, because we're still running in the thread // and we're still on the stack! Instead, we set "threadToBeDestroyed", // so that Scheduler::Run() will call the destructor, once we're// running in the context of a different thread.//// NOTE: we disable interrupts, so that we don't get a time slice // between setting threadToBeDestroyed, and going to sleep.//----------------------------------------------------------------------//voidThread::Finish (){ (void) interrupt->SetLevel(IntOff); ASSERT(this == currentThread); DEBUG('t', "Finishing thread \"%s\"\n", getName()); // Return all the threads that executed a join on this // back to the READY LIST. if (joinList) { Thread *joinThread; while ( (joinThread = joinList->Remove()) ) { joinThread->setStatus(READY); scheduler->ReadyToRun(joinThread); } } threadToBeDestroyed = currentThread; Sleep(); // invokes SWITCH // not reached}//----------------------------------------------------------------------// Thread::Yield// Relinquish the CPU if any other thread is ready to run.// If so, put the thread on the end of the ready list, so that// it will eventually be re-scheduled.//// NOTE: returns immediately if no other thread on the ready queue.// Otherwise returns when the thread eventually works its way// to the front of the ready list and gets re-scheduled.//// NOTE: we disable interrupts, so that looking at the thread// on the front of the ready list, and switching to it, can be done// atomically. On return, we re-set the interrupt level to its// original state, in case we are called with interrupts disabled. //// Similar to Thread::Sleep(), but a little different.// // NOTE: This method falsifies the invariant that the priorities// of all threads in ready queue are less than that of currently// executing thread. The invariant does not hold until the next // event when the scheduler is invoked.//----------------------------------------------------------------------voidThread::Yield (){ Thread *nextThread; IntStatus oldLevel = interrupt->SetLevel(IntOff); ASSERT(this == currentThread); DEBUG('t', "Yielding thread \"%s\"\n", getName()); nextThread = scheduler->FindNextToRun(); if (nextThread != NULL) { scheduler->ReadyToRun(this); scheduler->Run(nextThread); } (void) interrupt->SetLevel(oldLevel);}//----------------------------------------------------------------------// Thread::Sleep// Relinquish the CPU, because the current thread is blocked// waiting on a synchronization variable (Semaphore, Lock, or Condition).// Eventually, some thread will wake this thread up, and put it// back on the ready queue, so that it can be re-scheduled.//// NOTE: if there are no threads on the ready queue, that means// we have no thread to run. "Interrupt::Idle" is called// to signify that we should idle the CPU until the next I/O interrupt// occurs (the only thing that could cause a thread to become// ready to run).//// NOTE: we assume interrupts are already disabled, because it// is called from the synchronization routines which must// disable interrupts for atomicity. We need interrupts off // so that there can't be a time slice between pulling the first thread// off the ready list, and switching to it.//----------------------------------------------------------------------voidThread::Sleep (){ Thread *nextThread; ASSERT(this == currentThread); ASSERT(interrupt->getLevel() == IntOff); DEBUG('t', "Sleeping thread \"%s\"\n", getName()); status = BLOCKED; while ((nextThread = scheduler->FindNextToRun()) == NULL) interrupt->Idle(); // no one to run, wait for an interrupt scheduler->Run(nextThread); // returns when we've been signalled}//----------------------------------------------------------------------// ThreadFinish, InterruptEnable, ThreadPrint// Dummy functions because C++ does not allow a pointer to a member// function. So in order to do this, we create a dummy C function// (which we can pass a pointer to), that then simply calls the // member function.//----------------------------------------------------------------------static void ThreadFinish() { currentThread->Finish(); }static void InterruptEnable() { interrupt->Enable(); }void ThreadPrint(int arg){ Thread *t = (Thread *)arg; t->Print(); }//----------------------------------------------------------------------// Thread::StackAllocate// Allocate and initialize an execution stack. The stack is// initialized with an initial stack frame for ThreadRoot, which:// enables interrupts// calls (*func)(arg)// calls Thread::Finish//// "func" is the procedure to be forked// "arg" is the parameter to be passed to the procedure//----------------------------------------------------------------------voidThread::StackAllocate (VoidFunctionPtr func, int arg){ stack = (int *) AllocBoundedArray(StackSize * sizeof(int));#ifdef HOST_SNAKE // HP stack works from low addresses to high addresses stackTop = stack + 16; // HP requires 64-byte frame marker stack[StackSize - 1] = STACK_FENCEPOST;#else // i386 & MIPS & SPARC stack works from high addresses to low addresses#ifdef HOST_SPARC // SPARC stack must contains at least 1 activation record to start with. stackTop = stack + StackSize - 96;#else // HOST_MIPS || HOST_i386 stackTop = stack + StackSize - 4; // -4 to be on the safe side!#ifdef HOST_i386 // the 80386 passes the return address on the stack. In order for // SWITCH() to go to ThreadRoot when we switch to this thread, the // return addres used in SWITCH() must be the starting address of // ThreadRoot. *(--stackTop) = (int)ThreadRoot;#endif#endif // HOST_SPARC *stack = STACK_FENCEPOST;#endif // HOST_SNAKE machineState[PCState] = (int) ThreadRoot; machineState[StartupPCState] = (int) InterruptEnable; machineState[InitialPCState] = (int) func; machineState[InitialArgState] = arg; machineState[WhenDonePCState] = (int) ThreadFinish;}#ifdef USER_PROGRAM#include "machine.h"//----------------------------------------------------------------------// Thread::SaveUserState// Save the CPU state of a user program on a context switch.//// Note that a user program thread has *two* sets of CPU registers -- // one for its state while executing user code, one for its state // while executing kernel code. This routine saves the former.//----------------------------------------------------------------------voidThread::SaveUserState(){ for (int i = 0; i < NumTotalRegs; i++) userRegisters[i] = machine->ReadRegister(i);}//----------------------------------------------------------------------// Thread::RestoreUserState// Restore the CPU state of a user program on a context switch.//// Note that a user program thread has *two* sets of CPU registers -- // one for its state while executing user code, one for its state // while executing kernel code. This routine restores the former.//----------------------------------------------------------------------voidThread::RestoreUserState(){ for (int i = 0; i < NumTotalRegs; i++) machine->WriteRegister(i, userRegisters[i]);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -