Tips on Writing the Preemptive Minithreads System
Here are some basic tips (as usual, passed on through generations of
CSE 451 TAs) for writing the preemptive minithreads system. Also some answers
to questions you asked are also here.
-
The clock ticks will not be called until you call minithread_clock_init
and install an interrupt service routine. This routine will not be called
until you enable interrupts with enable_interrupts().
-
Once enabled, the clock interrupt handler can be called at ANY TIME (to
change the clock period to something less than the 500 microseconds it
is currently set to, change the #define in clock.h).
-
Consequently, you will need to ensure that everything you do while scheduling
threads, updating global structures etc. must be protected in some way,
either by disabling interrupts or by using mutex's. However, note that
enabling or disabling interrupts too many times, is bad, so avoid doing
so. Use some synchronization primitives to protect kernel structures wherever
possible.
-
You may find it useful to implement spinlocks. Spinlocks are really test-and-set
locks where you continually test and set the lock until you've acquired
it. This means that the caller blocks until they acquire the lock.
-
Remember that semaphore_P and semaphore_V need to run
atomically.
-
printf's will get messy (unless you put a semaphore around them,
but if you're trying to debug semaphores...). Remember to fflush(stdout)
after each printf, otherwise things will get even more confusing
because the printf's won't necessarily print out stuff when you
expect them to.
-
Run your program a LOT of times, because each run will be different, and
new bugs may manifest themselves.
FAQ for Spring 1998
1. I realize we are building our own thread system. But
do we use any of
the UNIX thread commands anywhere in our definitions?
For example, when
we call our minithread_fork(), do we implement the UNIX command
thread_fork() inside that function?
No, you are not allowed to use any of the thread management functions
on Unix. Your program should run without the thread package. The idea
is to implement functions similar to pthread_fork() yourself.
2. If we are not using the UNIX thread commands, I'm not clear
how we can
switch_context to get the PC pointed at the right place and
start
execution of a different thread.
You will be using minithread_switch() which is declared in
minithread_public.h for making a context switch. This function
calls context_switch() which is an assembly function in sys.S
Here is something about minithread_switch() which might not
be mentioned in the docs.
void minithread_switch(stack_pointer_t *old_thread_sp,
stack_pointer_t *new_thread_sp);
Both parameters are pointers to stack pointers, which points
to the malloced stacks. The first parameter is an ouput parameter
while the second one is an input parameter.
So you call minithread_switch() as follows:
stack_pointer_t
oldsp, newsp;
/* Set newsp to
the stack of the new thread to be run */
/* oldsp is u
initialised */
newsp = ....
minithread_switch(&oldsp,
&newsp);
/* This saves
the context into the current stack, changes
the stack to newsp and restores the context of that thread.
The function returns in the new thread (because the
new thread, when it blocked, called context switch).
When it returns, oldsp contains the stack pointer
of the thread which called minithread_switch() */
/* Note that there
is an exception to this when the
thread
is scheduled for the first time. It will start
executing
from minithread_root which is defined in sys.S */
3. I am wondering if someone is to use this minithread package,
can I
assume that the client would always call minithread_initialize(...)
to
start the program?
They will call minithread_system_initialize(...)
4. Also, in the example you gave us last week (quiz 3), the
p_thread
package has this pthread_join(...) function to make sure that
the main()
thread wouldn't exit before the threads created by it finish,
right? I've
tried taking out those join statements and the result is some
threads
didn't get to print out their stuff. So in our minithread
package, do we
have to always make sure that the main thread (main()) doesn't
exit until
all the other threads are done??
When you call minithread_system_initialize(..) the thread system starts
up. The function never returns. It starts up the first thread. The
process
exit()s when all the threads have died (because you cannot create another
thread if all the threads terminated.)
5. And I'm also a bit confused
about the minithread.h interface. Is
minithread_fork() basically the same as calling minithread_create()
and
then minithread_start()? Or actually, they're supposed
to do different
things?
You are right. minithread_fork() is same as minithread_create()
followed by minithread_start().
6. Finally, about minithread_stop(), when we stop a
thread, are we
supposed to free it? I guess what I really want to ask
is what is the
difference between minithread_yield and minithread_stop, 'cause
in some
cases, we might want to stop a thread while still want to
use it later...
like when there is a semaphore blocking?? Am I thinking
this right?
minithread_stop() should only remove the thread from the ready queue.
It is put back into the ready queue only when another thread
calls minithread_start(). minithread_yield() removes the current
thread from the processor and puts it at the end of the ready queue.
So, calling yield doesn't block the thread.
A thread is deallocated when it returns from the function.
You could also add a function called void minithread_exit()
to minithread.h which kills off the thread.
7. Since proc_t is of the type:
typedef int (*proc_t)();
How do the arguments get sent into functions of type proc_t?
It looks
like the function takes no arguments, but we send in arguments
when we
initialize the stack:
minithread_initialize_stack(stacktop,
initial_proc, initial_arg,
body_proc, body_arg,
finally_proc, finally_arg)
Actually, proc_t should be defined as
typedef int (*proc_t)(void *);
But the compiler doesnot complain because, the
functions are called from minithread_root which is
written in assembly
8. The interface in minithread_public.h says function final_proc
shouldn't
return. How do we write a function that doesn't
return? Should we call
minithread_swith() and give the control to scheduler?
final_proc() shouldn't return. You should do a minithread_switch()
and make sure that the thread is not put back in the readyQ
(maybe you should put it in a deleteQ to be deleted later on
by some other thread (eg, scheduler))
cse451-TA@cs.washington.edu