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.


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