UNIVERSITY OF WISCONSIN-MADISON
Computer Sciences Department
CS 537
Fall 2007
A. Arpaci-Dusseau
Quiz #5: Take-home Due Oct 18
Name:

Critical Sections

The following four code samples each specify different implementations for a lock; in all cases, the lock structure is defined, as well as the lock_init(), lock_acquire(), and lock_release() procedures. You should assume that the application calls lock_init() before using the lock and calls lock_acquire() before entering the critical section and lock_release() after exiting the critical section. For the implementations that require a tid (i.e., thread id), you can assume that the tid of each thread is either 0 or 1 and that this is available to the function. Assume a uniprocessor.

For each of the four code sample, does the code correctly protect a critical section? If the implementation is not correct, list a critical section requirement that is violated (at least one of three) and show a sample interleaving of instructions (across two threads) that leads to that problem. Clearly designate which thread is executing each stream of instructions. Hint: Remember to consider how variables are initialized!

typedef struct {
  int lock[2];
} lock_t;
lock_init(lock_t *myl) {
    myl->lock[0] = myl->lock[1] = 0;
}
void lock_acquire(lock_t *myl) {
    myl->lock[tid] = 1;
    while (myl->lock[1-tid]);
}
void lock_release(lock_t *myl) {
    myl->lock[tid] = 0;
}
typedef struct {
   int lock;
} lock_t;
lock_init(lock_t *myl) {
   myl->lock = 0;
}
void lock_acquire(lock_t *myl) {
    int key = 1;
    do {
        // atomically swaps contents of lock and key
        AtomicSwap(myl->lock, key); 
    } while (key == 1);
}
void lock_releaselock_t *myl) {
    myl->lock = 0;
}
typedef struct {
   int turn;
   int lock[2];
} lock_t;

lock_init(lock_t *myl) {
   myl->turn = 0;
   myl->lock[0] = myl->lock[1] = 1;
}

void lock_acquire(lock_t *myl) {
   myl->lock[tid] = 1;
   myl->turn = 1 - tid;
   while (myl->lock[1-tid] && myl->turn == (1 - tid));
}

void lock_release(lock_t *myl) {
   myl->lock[tid] = 0;
}

typedef struct {
   int turn;
   int lock[2];
} lock_t;

lock_init(lock_t *myl) {
   myl->turn = 0;
   myl->lock[0] = myl->lock[1] = 0;
}

void lock_acquire(lock_t *myl) {
   myl->lock[tid] = 1;
   if (tid == 1) {
       myl->turn = 1;
       while (myl->lock[1-tid]);
    } else { /* tid == 0 */
       while (myl->lock[1-tid] && myl->turn == 1);
    }
}

void lock_release(lock_t *myl) {
   myl->turn = 0;
   myl->lock[tid] = 0;
}












Dining Philosophers with Semaphores

Below is code that solves the Dining Philosopher problem such that it is live as well as safe; this is done with the addition of a state variable for each philosopher, designating whether they are THINKING, EATING, or HUNGRY. This is the same code presented in class and works correctly (assuming it is initialized appropriately). Assume that each philosopher thread alternates between thinking, calling take_chopsticks, eating, and calling put_chopsticks. Assume that each thread has a variable tid that is set correctly.

sem_t mayEat[5]; // How is this initialized?
sem_t mutex;     // How is this initialized?
volatile int state[5];        // Initialized to THINKING

void take_chopsticks() {
     sem_wait(&mutex);
     state[tid] = HUNGRY;
     test(tid);
     sem_signal(&mutex);
     sem_wait(&mayEat[tid]);
}

void put_chopsticks() {
     sem_wait(&mutex);
     state[tid] = THINKING;
     test(tid+1%5);
     test(tid+4%5);
     sem_signal(&mutex);
}

void test(int i) {
     if (state[i]==HUNGRY && state[i+1%5]!=EATING && state[i+4%5]!=EATING) {
          state[i] = EATING;
          sem_signal(&mayEat[i]);
     }
}

How should the Semaphore elements of mayEat be initialized?


How should the Semaphore mutex be initialized?


What is the maximum number of Philosophers that can be waiting on a Semaphore element mayEat[i] at any given time?


What is the maximum number of Philosophers that can be waiting on mutex at any given time?


Does the code work correctly if mutex is not declared as a global variable? Briefly describe why or why not.







Does the code work correctly if the statement sem_wait(&mayEat[tidi]) is moved before sem_signal(&mutex) in take_chopsticks()? Briefly describe why or why not.








Does the code work correctly if the order of the two statements test(i+1%5) and test(i+4%5) are switched in put_chopsticks()? Briefly describe why or why not.







Does the code work correctly if a new assignment of state[tid]=HUNGRY is added before the statement state[tid]=THINKING in put_chopsticks()? Briefly describe why or why not.





Does the code work correctly if the statements test(i+1%5) and test(i+4%5) are moved before state[i]=THINKING() in put_chopsticks()? Briefly describe why or why not.