I. Discussion Section Quiz 1 ============================ TA Yitao Duan (FA04) 1. What is the difference between semaphores and condition variables? 2. A simple implementation of a lock is given below. class Lock { int value = FREE; Acquire() { Disable interrupts if (value == BUSY) { put on wait queue, sleep // Enable interrupts ??? } else { value = BUSY; } Enable interrupts; } Release() { Disable interrupts; if (wait queue not empty) { take thread off wait queue, add to ready queue } else { value = FREE; } Enable interrupts; } } (1) Why must the interrupts be disabled in the Release procedure? (2) When should Acquire reenable interrupts in going to sleep? II. Condition variable ====================== From nachos.threads.Condition: "A condition variable is a synchronization primitive that does not have a value (unlike a semaphore or a lock), but threads may still be queued. sleep(): atomically release the lock and relinkquish the CPU until woken; then reacquire the lock. wake(): wake up a single thread sleeping in this condition variable, if possible. wakeAll(): wake up all threads sleeping in this condition variable. Every condition variable is associated with some lock. Multiple condition variables may be associated with the same lock. All three condition variable operations can only be used while holding the associated lock. In Nachos, condition variables are summed to obey Mesa-style semantics. When a wake() or wakeAll() wakes up another thread, the woken thread is simply put on the ready list, and it is the responsibility of the woken thread to reacquire the lock (this reacquire is taken care of in sleep()). By contrast, some implementations of condition variables obey Hoare-style semantics, where the thread that calls wake() gives up the lock and the CPU to the woken thread, which runs immediately and gives the lock and CPU back to the waker when the woken thread exits the critical section. The consequence of using Mesa-style semantics is that some other thread can acquire the lock and change data structures, before the woken thread gets a chance to run. The advance to Mesa-style semantics is that it is a lot easier to implement." III. Join ========= ...... KThread aThread = new KThread(...); aThread.fork(); aThread.join(); ...... The calling thread will wait for aThread to finish. Q: How could you implement a traditional function all with join? i.e. given code sequence: ...... B(); ...... Replace the function call B(); with join. IV. Priority Inversion ======================= If a high priority thread needs to wait for a low priority thread (for instance, for a lock held by a low priority thread), and another high priority thread is on the ready list, then the high priority thread will never get the CPU because the low priority thread will not get any CPU time. A partial fix for this problem is to have the waiting thread donate its priority to the low priority thread while it is holding the lock. See "What really happened on Mars?"