tac0S
Template Affectional Command Operating System
|
The following class defines a "thread control block" – which represents a single thread of execution. More...
#include <thread.h>
Public Member Functions | |
Thread (const char *debugName) | |
Thread::Thread Initialize a thread control block, so that we can then call Thread::Fork. More... | |
~Thread () | |
Thread::~Thread De-allocate a thread. More... | |
void | Fork (VoidFunctionPtr func, int arg) |
Thread::Fork Invoke (*func)(arg), allowing caller and callee to execute concurrently. More... | |
void | Yield () |
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. More... | |
void | Sleep () |
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. More... | |
void | Finish () |
Thread::Finish Called by ThreadRoot when a thread is done executing the forked procedure. More... | |
void | CheckOverflow () |
Thread::CheckOverflow Check a thread's stack to see if it has overrun the space that has been allocated for it. If we had a smarter compiler, we wouldn't need to worry about this, but we don't. More... | |
void | setStatus (ThreadStatus st) |
Thread::setStatus Set the status as in parameter with debug info. More... | |
const char * | getName () |
void | Print () |
void | setUserThread (void *userThreadAdress) |
void * | getUserThreadAdress () |
ThreadStatus | getStatus () |
void | enterCritique () |
Thread::enterCritique Get a semaphore for "critic area" code with still having interrupt. | |
void | enterCritiqueExt () |
Thread::enterCritiqueExt From exterior of a thread Get a semaphore for "critic area" code with still having interrupt Basicly stopped the thread when the thread is'nt in critique mode No exit statut for this just exit and call ReadyToRun form the scheduler. | |
void | exitCritique () |
Thread::exitCritique Drop the semaphore for the critique zone of code, see Thread::enterCritique. | |
Public Attributes | |
bool | stopped = false |
bool | inAMutex =false |
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.
Thread::Thread | ( | const char * | threadName | ) |
Thread::Thread Initialize a thread control block, so that we can then call Thread::Fork.
"threadName" is an arbitrary string, useful for debugging.
Thread::~Thread | ( | ) |
Thread::~Thread De-allocate a thread.
NOTE: the current thread cannot delete itself directly, since it is still running on the stack that we need to delete.
NOTE: if this is the main thread, we can't delete the stack because we didn't allocate it – we got it automatically as part of starting up Nachos.
void Thread::CheckOverflow | ( | ) |
Thread::CheckOverflow Check a thread's stack to see if it has overrun the space that has been allocated for it. If we had a smarter compiler, we wouldn't need to worry about this, but we don't.
NOTE: Nachos will not catch all stack overflow conditions. In other words, your program may still crash because of an overflow.
If you get bizarre results_test (such as seg faults where there is no code) then you may need to increase the stack size. You can avoid stack overflows by not putting large data structures on the stack. Don't do this: void foo() { int bigArray[10000]; ... }
void Thread::Finish | ( | ) |
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.
void Thread::Fork | ( | VoidFunctionPtr | func, |
int | arg | ||
) |
Thread::Fork Invoke (*func)(arg), allowing caller and callee to execute concurrently.
NOTE: although our definition allows only a single integer argument to be passed to the procedure, it is possible to pass multiple arguments by making them fields of a structure, and passing a pointer to the structure as "arg".
Implemented as the following steps:
func | is the procedure to run concurrently. |
arg | is a single argument to be passed to the procedure. |
void Thread::setStatus | ( | ThreadStatus | st | ) |
Thread::setStatus Set the status as in parameter with debug info.
ThreadStatus | st |
void Thread::Sleep | ( | ) |
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.
void Thread::Yield | ( | ) |
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.