CS520
Programming Assignment 5
Spring 2015
Due Sunday April 19


Implement a threads package for the Linux Intel 64 architecture.

The first step (60 points) is to implement these two primitives:

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.

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. Normally the next thread in the list of active threads should now execute. If there are no other active threads, then an error message should be printed to stderr and the program should be halted (call exit(-1)).

The second step (20 additional points) of this assignment is to modify your implementation to support preemptive scheduling of the threads.

This means that thread_yield will be called by the handler for a timer signal. When the thread_create primitive is initially called, initialize an interval timer using the setitimer function (see man setitimer). Request a virtual timer (ITIMER_VIRTUAL) and use a time slice of 10 milliseconds.

Specify a handler for the virtual timer alarm signal (SIGVTALRM) using the sigaction function (see man sigaction). You should use the SA_NODEFER flag to allow multiple handlers to be alive at the same time.

With preemptive scheduling you must be careful since now there will be asynchronous events. That is, the timer may go off at any time. For instance, consider a user program calling thread_yield and then a timer signal is generated. What might go wrong? You will need to develop a technique for disabling timer signals (or ignoring them) when you are in critical sections of code.

We will practice with timers, signals and signal handlers during Lab 9.

The third step (20 additional points) of this assignment is to add the following primitives:

long thread_self(void);
int thread_join(long thread_ID);

The thread_self primitive returns the thread ID of the calling thread.

The thread_join primitive waits for the thread specified by its parameter to terminate. If that thread has already terminated, then thread_join returns immediately.

When "waking up" a thread that has been waiting to join, put the waking thread on the end of the ready list.

On success, pthread_join returns 0; on error, it returns an error code:

If multiple threads simultaneously try to join with the same thread, the results are undefined.

Joining with a thread that has previously been joined results in undefined behavior.

Your implementation should be performed using both C and Intel 64 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 thread_asm.s.

The directory ~cs520/public/prog5 contains test programs that you can link with your code. Use these files to start your testing, but you will need to create other files in order to do thorough testing.

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. Remember that with assembly language, documentation is even more important than with C programs.

Please turn-off any debugging code before you submit your program.

Your programs should be submitted for grading from agate.cs.unh.edu. To turn in this assignment, type:
~cs520/bin/submit prog5 thread.c thread_asm.s

Submissions can be checked by typing:
~cs520/bin/scheck prog5

This assignment is due Sunday April 19. The standard late policy concerning late submissions will be in effect. See the course overview webpage.

Remember: as always you are expected to do your own work on this assignment. Copying code from another student or from sites on the internet is explicitly forbidden!


Last modified on April 2, 2015.

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