In this lecture, I went over in some more detail over how the user level threads package would work. In our simple design, we have only
thread_spawn(void (*fn)(void *), void *arg, void *stack); thread_yield(void); thread_exit(void); thread_wait(void);The big picture is that a process would initially have the main thread only, and that thread may call thread_spawn to spawn new threads, perhaps in some initialization code or as part of a work loop, and eventually the main thread will call thread_wait to await the termination of all other threads. Meanwhile, these other spawned threads may in turn spawn other threads, and context switch occurs only on calls to thread_yield. thread_exit is how a non-main thread exits.
We first look at thread_yield, which is responsible for the context switching.
The per-thread data, sketched out as C code, would look like:
struct thread { unsigned int reg[32]; /* actually fewer */ int alive; } thread_array[128];where we have a compile-time limit of a maximum of 128 threads. (This is not the best threads package design; instead, we are trying to keep it simple for pedagogical reasons.)
thread_yield must save all the registers for the current thread and load the register set of the next thread to context switch. A global variable cur_thread would be used for round-robin scheduling: we scan down the thread array until we find the next alive thread, and make that the current thread, loading its registers.
I noted that the registers need not include the $t registers, nor the $pc. Upon a reload of the other registers, thread_yield will return to $ra per standard calling convention, so it will return to where that thread had last called thread_yield.
There are some design decisions that come up when we look at the birth and death of a thread. Do we allow the startup function for a thread to return? A reasonable design would be to permit it to do so, which would implicitly be a call to thread_exit. Do we allow the main thread to context switch using thread_yield? If not, it could make some of the thread management a little simpler: the main thread is a special case, and when thread_yield scans for a next available thread to run, it can detect the no-living-threads condition a little simpler. The no-living-threads condition causes the main thread's call to thread_wait to return.
In any case, thread_spawn is responsible for making sure that the $pc, $sp, $a0 and $ra registers have reasonable values in them ($a0 has the arg parameter). $ra would contain thread_exit or the address of code that will call it, so that when the thread function fn returns, the appropriate cleanup will happen. thread_exit is responsible for marking the thread as dead (and the thread_array slot available) and then context switching using thread_yield to the next runnable thread.
bsy@cse.ucsd.edu, last updated