CS611
Programming Assignment 6
Fall 2003
Due Sunday Nov 23


Implement a simple threads package for the Linux Intel IA-32 architecture.

The two fundamental primitives that you should implement first are:

long thread_create(void (*func)(void*), void* info);
void thread_yield(void);

The thread_create primitive is passed two parameters. The first parameter is a function that should be executed by the newly created thread. The second parameter is a parameter to be passed to that function.

The thread_create primitive returns 0 if it cannot create the thread. Otherwise it returns a non-zero thread identifier that can be passed to other primitives to control the thread (see below).

When a thread is created, it should be given a stack of 16384 bytes.

A FIFO list of active threads should be maintained. This list should be initialized upon the first call to a thread primitive and should initially contain the "main" thread, the thread of control created "automatically" when the process was created. When thread_create is called, the newly created thread should be placed on the end of the list. Note: a newly created thread does not start executing immediately. Rather it is simply placed at the end of the list of active threads.

When thread_yield is called, the currently executing thread should be moved to the end of the list of active threads and the thread that is now at the front of the list should become the thread that is now executing. If there is only one active thread, then that thread continues executing.

There is no status associated with a call to thread_yield (i.e. it must succeed) so the primitive returns nothing.

When the main thread exits, then the process exits and all created threads are therefore terminated. When a created thread returns from its given function, then the thread is terminated, it is removed from the list of active threads, and any associated memory should be freed.

Your implementation of thread_create and thread_yield must be done first and will be worth 75% of the points for this assignment.

The other 25% of the points will be given for implementing the following primitives (each of these primitives is equal weight):

long thread_suspend(long thread_id);
long thread_continue(long thread_id);
long thread_kill(long thread_id);
long thread_self(void);

The thread_suspend primitive puts the specified thread in a suspended state. That is, the thread will not execute again until its id is passed to a call to thread_continue. The thread_suspend primitive returns 0 if the thread_id is invalid; otherwise a 1 is returned. Note that it is not an error for a suspended thread to be suspended again. This has no effect but it is not an error.

Note also that a thread can suspend itself. This will require that another thread (the next one in the FIFO list of active threads) be made the currently executing thread. If the thread being suspended is the only active thread, then an error message should be printed to stderr and the program should be halted (call exit(-1)).

The thread_continue primitive moves a suspended thread to the end of the FIFO list of active threads. If the thread_id is invalid or the thread is not suspended, then the primitive returns 0; otherwise it returns 1.

The thread_kill primitive removes a thread from the system (and frees any associated memory). The thread can be either active or suspended. However, the main thread cannot be killed (but it can be suspended). (The user should use exit() instead.) If the thread_id is invalid or indicates the main thread, then the primitive returns 0; otherwise it returns 1.

Note that a thread can kill itself. This will require that another thread (the next one in the FIFO list of active threads) be made the currently executing thread. If the thread being killed is the only active thread, then an error message should be printed to stderr and the program should be halted (call exit(-1)).

The thread_self primitive returns the thread id for the currently executing thread. For example, a thread could kill itself by executing: thread_kill(thread_self());

Your implementation should be performed using both C and Intel IA-32 assembler. Use C for as much of the code as possible and only use assembler when necessary. Place the C code in a file called thread.c and place the assembler code in a file called thr_asm.s.

The directory ~cs611/public/prog6 contains test programs that you can link with your code. Only these test cases will be used for grading this assignment.

Your program will be graded primarily by testing it for correct functionality. However, you may lose points if your program is not properly structured or adequately documented. See the mandatory guidelines given in the course overview webpage. Remember that with assembler code documentation is even more important than with C programs.

Your assignment should be submitted for grading from a CIS Linux/Intel machine (e.g. turing.unh.edu). Submit just two files, thread.c and thr_asm.s. To turn in this assignment, type:
~cs611/bin/submit prog6 thread.c thr_asm.s

Do not turn in any other files!

Submissions can be checked by typing:
~cs611/bin/scheck prog6

To receive full credit for the assignment, you must turn in your files prior to 8am on Monday November 24. Late submissions will be accepted at the penalty of 5 points per day up to one week late.

Remember: as always you are expected to do your own work on this assignment.


Last modified on November 11, 2003.

Comments and questions should be directed to hatcher@unh.edu