We discussed the solution to the third homework. Points to take away
from the discussion:
- You have to initialize the semaphore values. For instance, if
your "mutex" semaphore was incorrectly initialized to
0, none of the threads would have been able to proceed.
- For this graded question, you had one semaphore for mutual
exclusion and two more semaphores for ordering.
- You had to wake up all the waiting women/men. If you called up()
onces in the leave() function, only one of the waiting threads will
wake up.
- You had to increment/decrement the active/waiting counters in the
leave() function. I went over an example showing you why it is
necessary.
- Remember not to call down() on the ordering semaphore if you you
called down() of a mutual exclusion semaphore!
- mutex.down()
- ....
- women.down()
You should always release the mutual
exclusion lock
- mutex.down()
- ...
- mutex.up()
- women.down()
- In the non-graded question, I demonstrated how semaphores can
lead to an elegant solution.
We spent half of the discussion time going over the thread library
- Keep track of the "runnable" threads in a queue.
- Keep track of the currently running thread.
- Keep track of the threads that are done running and their
resources need to be cleaned up. Remember that the currently running
thread can't cleanup after itself. It can't deallocate it's own stack.
But don't accumulate "dead" threads until the exit condition of the
thread library!
struct/class thread_t {
....
ucontext_t thread_context;
}
thread_libinit() {
initialize internal data structures (locks,
condition variables
/* remember that this function has to be called
exactly onces */
create_thread() {
thread_t *t = malloc() /* create a new
data structure */
/* code below is from your project
handout */
/* first, you need initialize the
ucontext_t data structure before it can be used.
* it is done by calling
"getcontext". You MUST call this function for every new
* new thread that you create!
*/
getcontext(&t->thread_context);
/* Now you need to allocate memory for
the stack for this new thread.
* ucontext_t data structure has a
field that points to the memory of the stack.
* also, you need to tell how much
memory you've allocated for the stack.
*/
t->thread_context.uc_stack.ss_sp =
malloc(262144) /* assign allocated memory */
t->thread_context.uc_stack.ss_size =
262144; /* this is size */
.... /* look what else you need to do
here in the project handout */
/* Now you need to initialize another
field inside the ucontext_t data structure.
* and you do so by calling the
function "makecontext". You specify where you
* would like this thread to start
executing
*/
makecontext(&t->thread_context,
start_function, 2, arg1, arg2);
/* the new thread will start in the
function "start_function(arg1, arg2)" */
/* we talked about where you need to
start executing your new thread */
/* now you've created and initialize
your new thread. To execute your new thread, you can
* either use "setcontext" or
"swapcontext"
*/
....
add new thread to the ready queue
}
you've created you new thread. for instance, to
start a thread you can do
setcontext(&t->thread_context);
}
schedule_next() {
if ready queue is empty, exit;
else
old =put current thread on the ready
queue
new = take a thread from the ready queue
/* while setcontext() just starts
execution of the new ucontext_t data structure,
* swapcontext() saves the
information about the currently running (ucontext_t) thread
* then is start execution
specified by the 2nd argument to the function.
*/
swapcontext(old, new);
}
Remember that some function don't give control of the currently
executing thread while others do.