// thread.h // Data structures for managing threads. A thread represents // sequential execution of code within a program. // So the state of a thread includes the program counter, // the processor registers, and the execution stack. // // Note that because we allocate a fixed size stack for each // thread, it is possible to overflow the stack -- for instance, // by recursing to too deep a level. The most common reason // for this occuring is allocating large data structures // on the stack. For instance, this will cause problems: // // void foo() { int buf[1000]; ...} // // Instead, you should allocate all data structures dynamically: // // void foo() { int *buf = new int[1000]; ...} // // // Bad things happen if you overflow the stack, and in the worst // case, the problem may not be caught explicitly. Instead, // the only symptom may be bizarre segmentation faults. (Of course, // other problems can cause seg faults, so that isn't a sure sign // that your thread stacks are too small.) // // One thing to try if you find yourself with seg faults is to // increase the size of thread stack -- ThreadStackSize. // // In this interface, forking a thread takes two steps. // We must first allocate a data structure for it: "t = new Thread". // Only then can we do the fork: "t->fork(f, arg)". // // Copyright (c) 1992-1993 The Regents of the University of California. // All rights reserved. See copyright.h for copyright notice and limitation // of liability and disclaimer of warranty provisions. #ifndef THREAD_H #define THREAD_H #include "copyright.h" #include "utility.h" #ifdef USER_PROGRAM #include "machine.h" #include "addrspace.h" #endif // CPU register state to be saved on context switch. // The SPARC and MIPS only need 10 registers, but the Snake needs 18. // For simplicity, this is just the max over all architectures. #define MachineStateSize 18 // Size of the thread's private execution stack. // WATCH OUT IF THIS ISN'T BIG ENOUGH!!!!! #define StackSize (4 * 1024) // in words // Thread state enum ThreadStatus { JUST_CREATED, RUNNING, READY, BLOCKED }; // external function, dummy routine whose sole job is to call Thread::Print extern void ThreadPrint(int arg); // The following class defines a "thread control block" -- which // represents a single thread of execution. // // Every thread has: // an execution stack for activation records ("stackTop" and "stack") // space to save CPU registers while not running ("machineState") // a "status" (running/ready/blocked) // // Some threads also belong to a user address space; threads // that only run in the kernel have a NULL address space. class Thread { private: // NOTE: DO NOT CHANGE the order of these first two members. // THEY MUST be in this position for SWITCH to work. int* stackTop; // the current stack pointer int machineState[MachineStateSize]; // all registers except for stackTop public: Thread(char* debugName); // initialize a Thread ~Thread(); // deallocate a Thread // NOTE -- thread being deleted // must not be running when delete // is called // basic thread operations void Fork(VoidFunctionPtr func, int arg); // Make thread run (*func)(arg) void Yield(); // Relinquish the CPU if any // other thread is runnable void Sleep(); // Put the thread to sleep and // relinquish the processor void Finish(); // The thread is done executing void CheckOverflow(); // Check if thread has // overflowed its stack void setStatus(ThreadStatus st) { status = st; } char* getName() { return (name); } void Print() { printf("%s, ", name); } private: // some of the private data for this class is listed above int* stack; // Bottom of the stack // NULL if this is the main thread // (If NULL, don't deallocate stack) ThreadStatus status; // ready, running or blocked char* name; void StackAllocate(VoidFunctionPtr func, int arg); // Allocate a stack for thread. // Used internally by Fork() #ifdef USER_PROGRAM // A thread running a user program actually has *two* sets of CPU registers -- // one for its state while executing user code, one for its state // while executing kernel code. int userRegisters[NumTotalRegs]; // user-level CPU register state public: void SaveUserState(); // save user-level register state void RestoreUserState(); // restore user-level register state AddrSpace *space; // User code this thread is running. #endif }; // Magical machine-dependent routines, defined in switch.s extern "C" { // First frame on thread execution stack; // enable interrupts // call "func" // (when func returns, if ever) call ThreadFinish() void ThreadRoot(); // Stop running oldThread and start running newThread void SWITCH(Thread *oldThread, Thread *newThread); } #endif // THREAD_H