CS170: Project 3 - Thread Synchronization (20% of project score)Project GoalsThe goals of this project are:
Administrative InformationThe project is an individual project. It is due on Wednesday, 11.11.2009, 23:59:59 PST (no deadline extensions or late turn ins). Adding synchronization to your Linux thread libraryIn the previous project, we have seen how we can start threads, let them perform computations, and then exit. However, we have not considered (intentional or unintentional) interactions between threads. In this project, we want to address this issue and add support so that multiple threads can safely co-exist. The project consists of three parts:
ImplementationIn this project, you will build upon the thread library that you developed for the previous project. Thus, as a first step, make sure that everything works well. Then, copy your code from Project 2 into the new directory and extend your library as outlined above. Adding the lock and the unlock functions is straightforward. To lock, you just need a way to make sure that the currently running thread can no longer be interrupted by an alarm signal. For this, you can make use of the sigprocmask function. To unlock, simply re-enable (unblock) the alarm signal, again using sigprocmask. Once you have lock and unlock, use them to protect all accesses to global data structures. You will definitely need to call these functions when implementing the semaphore routines. To implement the pthread_join function, you will probably need to introduce a BLOCKED status for your threads. Whenever a thread is blocked, it cannot be selected by the scheduler. A thread becomes blocked when it attempts to join a thread that is still running. In addition to blocking threads that wait for (attempt to join) active processes, you might also need to modify pthread_exit. In particular, whenever a thread exits, you cannot immediately clean up everything that is related to it. Instead, you need to retain its return value, because other threads might later want to get this return value by calling pthread_join. That is, once a thread exits, it is not immediately gone, but becomes a "zombie" thread (very similar to the situation with processes). Once a thread's exit value is collected via a call to pthread_join, you can free all resources related to this thread. One question that might arise is how you can obtain the return (exit) value of a thread that does not call pthread_exit explicitly. We know that, in this case, we have to use the return value of the thread's start function (man pthread_exit). A function normally passes its return value back to the caller (the calling function) via the $eax register. To access this value, some assembler code is needed. In the following, I assume that you have set up the stack of every new thread so that it returns to the function pthread_exit_wrapper. Note: This is different from returning directly into pthread_exit. You can then use the following code for this wrapper function:
void pthread_exit_wrapper()
{
unsigned int res;
asm("movl %%eax, %0\n":"=r"(res));
pthread_exit((void *) res);
}
You can see that this wrapper function uses inline assembler to load the return value of the thread's start function (from register eax) into the res variable. Then, it performs the desired call to pthread_exit, using the return value of the thread's start function as the argument. When implementing semaphores, recall that you should first include the appropriate header file semaphore.h. This file includes /usr/include/bits/semaphore.h, where you can find the definition of the type sem_t. You will notice that this struct/union is likely not sufficient to store all relevant information for your semaphores. Thus, you might want to create your own semaphore structure that stores the current value, a pointer to a queue for threats that are waiting, and a flag that indicates whether the semaphore is initialized. Then, you can use one of the fields of the sem_t struct (for example, __align) to hold a reference to your own semaphore. Once you have your semaphore data structure, just go ahead and implement the semaphore functions as described above. Make sure that you test a scenario where a semaphore is initialized with a value of 1, and multiple threads use this semaphore to manage access to a critical code section that requires mutual exclusion (i.e., they invoke sem_wait before entering the critical section). When using your semaphore, only a single thread should be in the critical section at any time. The other threads need to wait until the first thread exits and calls sem_post. At this point, the next thread can proceed. DeliverablesPlease follow the instructions below exactly!
Use this command to submit your work: turnin proj3@cs170 sync |