e
Theory of OS - threads
Theory of OS - Thread Lab
The purpose of this lab is for you to gain mastery of thread
synchronization. I have provided to you a tiny thread system called
"tthreads" (gzipped tarball at tthreads.tgz).
A tour of the code
Most of the code
is in files named tthreads.c and tthreads.h. Below is a short
description of potentially non-intuitive components of this library.
Context Switches: setjmp/longjmp
To implement context switches, tthreads
uses the "C" functions setjmp() and longjmp(), which are available under
unix, cygwin, and most other runtime environments.
Setjmp() saves a process' registers in a "jmp_buf" structure. The
return value of setjmp(jmp_buf) is zero when first called.
Longjmp(jmp_buf, retval) restores those registers, effectively returning from
setjmp() a second time, but this time setjmp() will return the value "retval"
provided to longjmp(). Thus, a thread can save its context using setjmp,
and another thread can "context switch" back to it using longjmp. A
small demo program using setjmp is provided named "setJmpDemo.c".
I encourage students to think about why a jmp_buf's saved context is
invalid once the routine that called setjump() returns.
More info on longjmp/setjmp (thanks to Ryan Knotts).
Thread and List Structures
The Thread struct (see tthreads.h) contains a jmp_buf that stores
execution context and a linked list "next" pointer. TaskQ structures
are used for queues storing (1) ready processes (readyQ) and (2)
uninitialized threads (freshQ). These queues are likely to be useful
to implement other synchronization primitives. Three queue
maipulation functions are also provided:
- enqueueThread
- dequeueThread
- initThreadQ
Creating Thread Contexts
Each tthreads thread is allocated approximately 64k of stack space by
a functyion _makeThreads1() which calls _makeThreads2(). _makethreads2()
saves its "context" using setjmp. To allocate a group of threads,
these routines call each other in a mutually recursive fashion. Since
a jmp_buf is invalid if the routine that called setjmp() returns, the pair of recursive
makeThreads functions instead longjmp back to main()'s context.
Demo program: threadTest.c
ThreadTest recursively creates three threads, each which is assigned a
workerNum in the range of (0..2). They iterate through a short loop 3*workerNum
times, yielding the cpu (using the yield() function) before
terminating.
Mutual exclusion: mutex.c, mutex.h, mutexDemo.c
I wrote a a simple mutex, its utilization is demonstrated in mutexDemo.c.
Your Project
Implement a trivial producer-consumer system in
tthreads with
four producers and one consumer. Each producer will enqueue a
sequence of 100 integer values (0..99) into a bounded production queue
of capacity 10. The consumer will delete values from this queue, and
when done, print their sum. To control access to the queues, you should utilizebinary and counting semaphores. You are free to use the mutual
exclusion algorithm in mutex.c/mutex.h as a binary semaphore, and to
extend it to a counting semaphore.
Extra Credit Component (REQUIRED FOR GRAD STUDENTS)
Extend the thread system so that each thread has a priority
between 0 and 255. A thread's default priority will be 128. You must
define an inteface that will allow a thread to adjust its priority.
The scheduler's behavior should be modified so that, when scheduling
decisions are made, if threads with
multiple priorities are ready, a thread with the lowest priority
number will be scheduled. You should demonstrate that this mechanism
works correctly by setting giving greater priority (lower priority
value) to threads that produce than consume and checking that the
scheduling decisions are appropriate.