[ RCOS.java Home | RCOS Home | David's Home ]
Name: |
Thread |
Comment: |
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)". The following class defines a "thread control block" -- which represents a single thread of execution. Every thread has:
Some threads also belong to a user address space; threads that only run in the kernel have a NULL address space. |
Collaborators: |
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); }