Extend your threads package for the Linux Intel IA-32 architecture to include support for preemptive scheduling and synchronization.
Step 1 is to finish your implementation of thread_create and thread_yield from Program 5 if they still have problems. You do not need implementations of the other primitives (thread_suspend, thread_continue, thread_kill and thread_self).
Step 2 is to use your Lab 11 work 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.
Step 3 is to add support for a thread_mutex_t type and the following primitives:
int thread_mutex_init(thread_mutex_t *mutex);
int thread_mutex_lock(thread_mutex_t *mutex);
int thread_mutex_unlock(thread_mutex_t *mutex);
The thread_mutex_init primitive shall initialize the mutex referenced by its argument. Attempting to initialize an already initialized mutex results in undefined behavior, as does operating on a mutex that is not yet initialized. If successful, the primitive shall return 1. The primitive fails if passed a NULL pointer.
A call to thread_mutex_lock shall lock the mutex object referenced by its argument. If the mutex is already locked, the calling thread shall block until the mutex becomes available. This primitive shall return with the mutex object in the locked state with the calling thread as its owner. If successful, the primitive shall return 1. The primitive fails if passed a NULL pointer or if the referenced mutex is already locked by the calling thread.
The thread_mutex_unlock primitive shall release the mutex object referenced by its argument. If there are threads blocked on the mutex object when thread_mutex_unlock is called, resulting in the mutex becoming available, the longest waiting thread shall get the lock. That is, the wait queue for the mutex should be a FIFO queue. The thread that now owns the lock should be moved to the end of the ready-to-run list. If successful, the primitive shall return 1. The primitive fails if passed a NULL pointer or if the mutex is not in a locked state or if the caller does not own the mutex.
Step 4 is to add support for a thread_cond_t type and the following primitives:
int thread_cond_init(thread_cond_t *cond);
int thread_cond_wait(thread_cond_t *cond, thread_mutex_t *mutex);
int thread_cond_signal(thread_cond_t *cond);
The thread_cond_init primitive shall initialize the condition variable referenced by its argument. Attempting to initialize an already initialized condition variable results in undefined behavior, as does operating on a condition variable that is not yet initialized. If successful, the primitive shall return 1. The primitive fails if passed a NULL pointer.
The thread_cond_wait primitive shall be called with the mutex referenced by its second argument locked by the calling thread. The primitive shall release the mutex and cause the calling thread to block on the condition variable referenced by its first argument. Upon successful return, the mutex shall have been locked and shall be owned by the calling thread. If successful, the primitive shall return 1. The primitive fails if passed a NULL pointer or if the referenced mutex is not locked by the calling thread.
The thread_cond_signal primitive shall unblock a thread blocked on the condition variable referenced by its argument, if any threads are blocked. The longest waiting thread will be unblocked. That is, the queue associated with the condition variable is a FIFO queue. Before proceeding to execute, the unblocked thread will contend for the mutex that it associated with the condition variable when it executed the earlier wait that caused it to block. That is, if the mutex is locked, then the unblocked thread will be placed on the end of the queue of threads waiting for the mutex. If the mutex is unlocked, then the unblocked thread is given the mutex and is put on the end of the ready-to-run queue. The primitive shall have no effect if there are no threads currently blocked on the condition variable. If successful, the primitive shall return 1. The primitive fails if passed a NULL pointer.
Points will be assigned for this assignment in the following manner:
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. As in Program 5, place all your C code in a file called thread.c and place all your the assembler code in a file called thr_asm.s. You will also need to provide a header file called thread.h to contain your definitions of the thread_mutex_t and thread_cond_t types, as well as the prototypes for all the primitives. A sample thread.h file, with stubs for the two types, is provided in ~cs520/public/prog6.
The directory ~cs520/public/prog6 also contains test programs that you can link with your code. Use these files to start your testing, but you will probably 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. See the mandatory guidelines given in the course overview webpage. Remember that with assembler code documentation is even more important than with C programs.
There are two labs associated with this program: Lab 11 on April 20 and Lab 12 on April 27.
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 prog6 thread.c thr_asm.s thread.h
Submissions can be checked by typing:
~cs520/bin/scheck prog6
This assignment is due Tuesday May 1. 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.
Comments and questions should be directed to hatcher@unh.edu