HW5 Solutions 1. Sleeping barber Waitiing Room Solution: Type Name Initial Value (if applicable) ------------------------------------------------------------- mutex lock cond cond int nfull 0 int ticketAvail 0 int ticketTurn -1 int WaitingRoom::enter() { lock.acquire(); int ret; if (nfull == NCHAIRS){ ret = WR_FULL; } else { ret = MY_TURN; myTicket = ticketAvail++; nfull++; while (myTicket > ticketTurn) { cond.wait(&lock); } nfull--; } lock.release(); return ret; } int WaitingRoom::callNextCustomer() { lock.acquire(); ticketTurn++; if (nfull == 0) { ret = EMPTY; } else { ret = BUSY; cond.broadcast(); } lock.release(); return ret; } Barber Chair Solution: Type Name Initial Value (if applicable) ------------------------------------------------------------- mutex lock cond custUp cond barberGetUp cond sitDown cond seatFree cond cutDone int state EMPTY int custWalkedIn 0 void BarberChair::wakeBarber() { lock.acquire(); custWalkedIn = 1; barberGetUp.signal(&lock); lock.release() } void BarberChair::sitInChair() { lock.acquire() while (state != EMPTY) { seatFree.wait(&lock); } state = LONG_HAIR_CUSTOMER_IN_CHAIR; sitDown.signal(&lock); while (state != SHORT_HAIR_CUSTOMER_IN_CHAIR) { cutDone.wait(&lock); } state = EMPTY; custUp.signal(&lock); lock.release(); } void BarberChair::napInChair() { lock.acquire(); if(custWalkedIn == 0) { // Cust could arrive before I sit down state = BARBER_IN_CHAIR; } while(custWalkedIn == 0) { barberGetUp.wait(&lock); } custWalkedIn = 0; if (state == BARBER_IN_CHAIR) { // Cust could have beaten us state = EMPTY seatFree.signal(&lock); } lock.release(); } void BarberChair::cutHair() { lock.acquire(); while(state != LONG_HAIR_CUSTOMER_IN_CHAIR) { sitDown.wait(&lock); } state = SHORT_HAIR_CUSTOMER_IN_CHAIR; cutDone.signal(&lock); lock.release(); } void BarberChair::tellCustomerDone() { lock.acquire(); while(state != EMPTY){ // NOTE: No other cust can arrive until I call call_next_cust() custUp.wait(&lock); } lock.release(); } 2.1 T1 calls transfer(0, 1, 100) T2 calls transfer(1, 0, 100) T1: lock(&mutex[0]) T2: lock(&mutex[1]) T1: try to lock(&mutex[1]) T2: try to lock(&mutex[0]) 2.2 bool transfer(int from, int to, double trans) { if (from < to) { smutex_lock(&mtx[from]); smutex_lock(&mtx[to]); } else { smutex_lock(&mtx[to]); smutex_lock(&mutex[from]); } /* * continue as before. for good style, the unlock() should be * done in the corresponding order, though that is not * required. */ (or, if we know that there are only two mutexes, we could do: smutex_lock(&mtx[0]); smutex_lock(&mtx[1]); ) 3. Acquire the locks in some partial order, say in order of array index. This requires some logic before an operation to sort the order of requested items. 4. 4.1: The code is designed to place box2 inside of box1, and box3 inside of box2. The expected output is: 37 - 12 - - 19 4.2: The program's behavior is undefined, as print_box() will deference an undefined pointer. Possible outputs include an infinite loop, printing of garbage values, or a segmentation fault. 4.3: When a function is invoked with a call-by-value parameter, the parameter is copied and the function executes with a local copy. In this case, insert_box() makes a copy of inner; insert_box() then sets outer->inner_box to the address of this copy. But where does the copy live? It lives on the _stack_ and will go out of scope when the function returns. Thus, when the function returns outer->inner_box points to invalid memory (concretely, it is pointing to a location on the stack that will be used for something else). 4.4: Change insert_box to use a pointer for inner: void insert_box(struct box* outer, struct box* inner) { printf("insert box: placing id %d inside id %d\n", inner->id, outer->id); outer->inner_box = inner; } Also, change main() to correctly invoke the modified function: int main() { ... insert_box(&box1, &box2); insert_box(&box2, &box3); ... } 5. void reader_release(sharedlock* lock) { atomic_decrement(&lock->value); } void writer_acquire(sharedlock* lock) { /* * a common error here is not spinning, i.e., not including * the while() around the cmpxchg. note that spinning is what keeps * the writer from entering the critical section before it's * supposed to */ while (cmpxchg(&lock->value, 0, -1) != 0) {} } void writer_release(sharedlock* lock) { xchg_val(&lock->value, 0); /* xchg_val is from class notes */ /* * two other options: * 1. cmpxchg(&lock->value, -1, 0); * 2. lock->value = 0; * option 2. works because the question said to * assume sequential consistency. Without sequential * consistency, option 2. might not work because it might * not guarantee that all of the instructions before or after it * would appear in program order to the other CPUs in the machine. */ }