Page 1 CS162 Operating Systems and Systems Programming Lecture 8 Readers-Writers Language Support for Synchronization Friday 11, 2010 Ion Stoica http://inst.eecs.berkeley.edu/~cs162 Lec 8.2 2/11/10 CS162 ©UCB Fall 2009 Review: Implementation of Locks by Disabling Interrupts • Key idea: maintain a lock variable and impose mutual exclusion only during operations on that variable int value = FREE; Acquire() { disable interrupts; if (value == BUSY) { put thread on wait queue; Go to sleep(); // Enable interrupts? } else { value = BUSY; } enable interrupts; } Release() { disable interrupts; if (anyone on wait queue) { take thread off wait queue Place on ready queue; } else { value = FREE; } enable interrupts; } Lec 8.3 2/11/10 CS162 ©UCB Fall 2009 Review: Implementation of Locks by Disabling Interrupts Acquire() { disable interrupts; if (value == BUSY) { … } else { value = BUSY; } enable interrupts; } Thread A Thread B Acquire() { disable interrupts; if (value == BUSY) { put thread on wait queue; Go to sleep(); // Enable interrupts? } … enable interrupts; } value = BUSY; value = FREE; . . . Wait Queue Thread B Lec 8.4 2/11/10 CS162 ©UCB Fall 2009 Review: Implementation of Locks by Disabling Interrupts Thread A Thread B value = FREE; Ready Queue Thread B Release() { disable interrupts; if (anyone on wait queue) { take thread off wait queue; Place on ready queue; } else { … } enable interrupts; } Release() { disable interrupts; if (anyone on wait queue) { … } else { value = FREE; } enable interrupts; }Page 2 Lec 8.5 2/11/10 CS162 ©UCB Fall 2009 Review: How to Re-enable After Sleep()? • In Nachos, since ints are disabled when you call sleep: – Responsibility of the next thread to re-enable ints – When the sleeping thread wakes up, returns to acquire and re-enables interrupts Thread A Thread B . . disable ints sleep sleep return enable ints . . . disable int sleep sleep return enable ints . . context switch context switch Lec 8.6 2/11/10 CS162 ©UCB Fall 2009 Review: Locks using test&set • Can we build test&set locks without busy-waiting? – Can’t entirely, but can minimize! – Idea: only busy-wait to atomically check lock value • Note: sleep has to be sure to reset the guard variable – Why can’t we do it just before or just after the sleep? Release() { // Short busy-wait time while (test&set(guard)); if anyone on wait queue { take thread off wait queue Place on ready queue; } else { value = FREE; } guard = 0; int guard = 0; int value = FREE; Acquire() { // Short busy-wait time while (test&set(guard)); if (value == BUSY) { put thread on wait queue; go to sleep() & guard = 0; } else { value = BUSY; guard = 0; } } Lec 8.7 2/11/10 CS162 ©UCB Fall 2009 Review: Semaphores • Definition: a Semaphore has a non-negative integer value and supports the following two operations: – P(): an atomic operation that waits for semaphore to become positive, then decrements it by 1 » Think of this as the wait() operation – V(): an atomic operation that increments the semaphore by 1, waking up a waiting P, if any » This of this as the signal() operation – Only time can set integer directly is at initialization time • Semaphore from railway analogy – Here is a semaphore initialized to 2 for resource control: Value=2 Value=1 Value=0 Value=1 Value=0 Value=2 Lec 8.8 2/11/10 CS162 ©UCB Fall 2009 Goals for Today • Continue with Synchronization Abstractions – Monitors and condition variables • Readers-Writers problem and solution • Language Support for Synchronization Note: Some slides and/or pictures in the following are adapted from slides ©2005 Silberschatz, Galvin, and Gagne Note: Some slides and/or pictures in the following are adapted from slides ©2005 Silberschatz, Galvin, and Gagne. Many slides generated from lecture notes by Kubiatowicz.Page 3 Lec 8.9 2/11/10 CS162 ©UCB Fall 2009 Review: Full Solution to Bounded Buffer Semaphore fullBuffer = 0; // Initially, no coke Semaphore emptyBuffers = numBuffers; // Initially, num empty slots Semaphore mutex = 1; // No one using machine Producer(item) { emptyBuffers.P(); // Wait until space mutex.P(); // Wait until buffer free Enqueue(item); mutex.V(); fullBuffers.V(); // Tell consumers there is // more coke } Consumer() { fullBuffers.P(); // Check if there’s a coke mutex.P(); // Wait until machine free item = Dequeue(); mutex.V(); emptyBuffers.V(); // tell producer need more return item; } Lec 8.10 2/11/10 CS162 ©UCB Fall 2009 Discussion about Solution • Why asymmetry? – Producer does: emptyBuffer.P(), fullBuffer.V() – Consumer does: fullBuffer.P(), emptyBuffer.V() • Is order of P’s important? – Yes! Can cause deadlock: Producer(item) { mutex.P(); // Wait until buffer free emptyBuffers.P(); // Could wait forever! Enqueue(item); mutex.V(); fullBuffers.V(); // Tell consumers more coke } • Is order of V’s important? – No, except that it might affect scheduling efficiency • What if we have 2 producers or 2 consumers? – Do we need to change anything? Lec 8.11 2/11/10 CS162 ©UCB Fall 2009 Motivation for Monitors and Condition Variables • Semaphores are a huge step up, but: – They are confusing because they are dual purpose: » Both mutual exclusion and scheduling constraints » Example: the fact that flipping of P’s in bounded buffer gives deadlock is not immediately obvious – Cleaner idea: Use locks for mutual exclusion and condition variables for scheduling constraints • Definition: Monitor: a lock and zero or more condition variables for managing concurrent access to shared data – Use of Monitors is a programming paradigm – Some languages like Java provide monitors in the language • The lock provides mutual exclusion to shared data: – Always acquire before accessing shared data structure – Always release after finishing with shared data – Lock initially free Lec 8.12 2/11/10 CS162 ©UCB Fall 2009 Simple Monitor Example (version 1) • Here is an (infinite) synchronized queue Lock lock; Queue queue; AddToQueue(item) { lock.Acquire(); // Lock shared data queue.enqueue(item); //
View Full Document