Semaphores & Condition Variables

Slides:



Advertisements
Similar presentations
Operating Systems Semaphores II
Advertisements

Ch 7 B.
Ch. 7 Process Synchronization (1/2) I Background F Producer - Consumer process :  Compiler, Assembler, Loader, · · · · · · F Bounded buffer.
1 Semaphores and Monitors CIS450 Winter 2003 Professor Jinhua Guo.
Monitors Chapter 7. The semaphore is a low-level primitive because it is unstructured. If we were to build a large system using semaphores alone, the.
1 Semaphores and Monitors: High-level Synchronization Constructs.
Enforcing Mutual Exclusion, Semaphores. Four different approaches Hardware support Disable interrupts Special instructions Software-defined approaches.
5.6 Semaphores Semaphores –Software construct that can be used to enforce mutual exclusion –Contains a protected variable Can be accessed only via wait.
Semaphores. Announcements No CS 415 Section this Friday Tom Roeder will hold office hours Homework 2 is due today.
Language Support for Concurrency. 2 Common programming errors Process i P(S) CS P(S) Process j V(S) CS V(S) Process k P(S) CS.
5.6.2 Thread Synchronization with Semaphores Semaphores can be used to notify other threads that events have occurred –Producer-consumer relationship Producer.
Synchronization Principles. Race Conditions Race Conditions: An Example spooler directory out in 4 7 somefile.txt list.c scores.txt Process.
Monitors CSCI 444/544 Operating Systems Fall 2008.
Semaphores CSCI 444/544 Operating Systems Fall 2008.
Race Conditions CS550 Operating Systems. Review So far, we have discussed Processes and Threads and talked about multithreading and MPI processes by example.
CS Introduction to Operating Systems
CS 3214 Computer Systems Godmar Back Lecture 22. Announcements Project 4 due Nov 10 Exercise 9 due Nov 11 CS 3214 Fall 2010.
4061 Session 21 (4/3). Today Thread Synchronization –Condition Variables –Monitors –Read-Write Locks.
CSC321 Concurrent Programming: §5 Monitors 1 Section 5 Monitors.
ICS 313: Programming Language Theory Chapter 13: Concurrency.
CS 3204 Operating Systems Godmar Back Lecture 7. 12/12/2015CS 3204 Fall Announcements Project 1 due on Sep 29, 11:59pm Reading: –Read carefully.
1 Synchronization Threads communicate to ensure consistency If not: race condition (non-deterministic result) Accomplished by synchronization operations.
1 Condition Variables CS 241 Prof. Brighten Godfrey March 16, 2012 University of Illinois.
13/03/07Week 21 CENG334 Introduction to Operating Systems Erol Sahin Dept of Computer Eng. Middle East Technical University Ankara, TURKEY URL:
Problems with Semaphores Used for 2 independent purposes –Mutual exclusion –Condition synchronization Hard to get right –Small mistake easily leads to.
1 Previous Lecture Overview  semaphores provide the first high-level synchronization abstraction that is possible to implement efficiently in OS. This.
PThread Synchronization. Thread Mechanisms Birrell identifies four mechanisms commonly used in threading systems –Thread creation –Mutual exclusion (mutex)
Dining Philosophers & Monitors Questions answered in this lecture: How to synchronize dining philosophers? What are monitors and condition variables? What.
CSCI1600: Embedded and Real Time Software Lecture 17: Concurrent Programming Steven Reiss, Fall 2015.
Semaphores Reference –text: Tanenbaum ch
CS 3204 Operating Systems Godmar Back Lecture 10.
CS162 Section 2. True/False A thread needs to own a semaphore, meaning the thread has called semaphore.P(), before it can call semaphore.V() False: Any.
CS 311/350/550 Semaphores. Semaphores – General Idea Allows two or more concurrent threads to coordinate through signaling/waiting Has four main operations.
Case Study: Pthread Synchronization Dr. Yingwu Zhu.
Chapter 5 Concurrency: Mutual Exclusion and Synchronization Operating Systems: Internals and Design Principles, 6/E William Stallings Patricia Roy Manatee.
Interprocess Communication Race Conditions
CS703 - Advanced Operating Systems
Semaphores Reference text: Tanenbaum ch
CS703 – Advanced Operating Systems
Chapter 5: Process Synchronization – Part 3
CS 3214 Computer Systems Lecture 20 Godmar Back.
CS 3214 Computer Systems Lecture 22 Godmar Back.
Operating Systems CMPSC 473
CS 3214 Introduction to Computer Systems
CS 3214 Computer Systems.
CS 3214 Computer Systems.
CSCI 511 Operating Systems Chapter 5 (Part C) Monitor
Monitors Chapter 7.
Threading And Parallel Programming Constructs
Producer-Consumer Problem
Critical section problem
Chapter 30 Condition Variables
Monitors Chapter 7.
Subject : T0152 – Programming Language Concept
CSCI1600: Embedded and Real Time Software
CSE 451: Operating Systems Autumn Lecture 8 Semaphores and Monitors
Monitors Chapter 7.
CSE 451: Operating Systems Autumn Lecture 7 Semaphores and Monitors
Chapter 6 Synchronization Principles
CSE 451: Operating Systems Winter Module 7 Semaphores and Monitors
CSE 153 Design of Operating Systems Winter 19
CSE 153 Design of Operating Systems Winter 2019
CS333 Intro to Operating Systems
Chapter 6: Synchronization Tools
CSCI1600: Embedded and Real Time Software
Semaphores Reference text: Tanenbaum ch
“The Little Book on Semaphores” Allen B. Downey
Monitors and Inter-Process Communication
Don Porter Portions courtesy Emmett Witchel
Review The Critical Section problem Peterson’s Algorithm
Presentation transcript:

Semaphores & Condition Variables CS 5204 Operating Systems Semaphores & Condition Variables Godmar Back

MULTI-THREADING CS 5204 Fall 2013

Coordinating Multiple Threads Aside from coordinating access to shared items, threads may need to communicate about events “has event A already happened in another thread?” aka “precedence constraint”, or “scheduling constraint” Do B after A Must do so Correctly (never miss that event A has occurred when in fact it has) Efficiently Don’t waste resources in the process Don’t unnecessarily delay notification of event A CS 5204 Fall 2013

int coin_flip; static void * thread1(void *_) { coin_flip = rand() % 2; printf("Thread 1: flipped coin %d\n", coin_flip); return NULL; } thread2(void *_) printf("Thread 2: flipped coin %d\n", int main() { int i, N = 2; pthread_t t[N]; srand(getpid()); pthread_create(&t[1], NULL, thread2, NULL); pthread_create(&t[0], NULL, thread1, NULL); for (i = 0; i < N; i++) pthread_join(t[i], NULL); return 0; } Q.: How can thread2 make sure that ‘coin_flip’ has occurred before printing its outcome? CS 5204 Fall 2013

Thread 2 could “busy-wait” – spin until thread 1 completes the coin flip. Exceptions not withstanding, this is practically never an acceptable solution. int coin_flip; volatile bool coin_flip_done; static void * thread1(void *_) { coin_flip = rand() % 2; coin_flip_done = true; printf("Thread 1: flipped coin %d\n", coin_flip); return NULL; } The somewhat less wasteful variant of busy-waiting: while (!coin_flip_done) sched_yield(); is not acceptable, either. wastes CPU cycles is fragile (volatile needed when using –O) does not document semantics static void * thread2(void *_) { /* Thread 2 spins, "busy-waits" until the coin flip is done. * This is an unacceptable solution. Bad for the planet, too. */ while (!coin_flip_done) continue; printf("Thread 2: flipped coin %d\n", coin_flip); return NULL; } CS 5204 Fall 2013

Semaphores Invented by Edsger Dijkstra in 1960s Source: inter.scoutnet.org Invented by Edsger Dijkstra in 1960s Counter S, initialized to some value, with two operations: P(S) or “down” or “wait” – if counter greater than zero, decrement. Else wait until greater than zero, then decrement V(S) or “up” or “signal” or “post” – increment counter, wake up any threads stuck in P. Semaphores don’t go negative: #V + InitialValue - #P >= 0 Note: direct access to counter value after initialization is not allowed Counting Semaphores vs Binary Semaphores Binary: counter can only be 0 or 1 Simple to implement, yet powerful Can be used for many synchronization problems CS 5204 Fall 2013

POSIX Semaphores int coin_flip; sem_t coin_flip_done; // semaphore for thread 1 to signal coin flip static void * thread1(void *_) { coin_flip = rand() % 2; sem_post(&coin_flip_done); // raise semaphore, increment, 'up' printf("Thread 1: flipped coin %d\n", coin_flip); } POSIX Semaphores Notice the 3rd argument of sem_init() – it gives the initial value of the semaphore: ‘0’ means the semaphore is used to express scheduling constraint static void * thread2(void *_) { // wait until semaphore is raised, // then decrement, 'down' sem_wait(&coin_flip_done); printf("Thread 2: flipped coin %d\n", coin_flip); } int main() { … sem_init(&coin_flip_done, 0, 0); pthread_create(&t[1], NULL, thread2, NULL); pthread_create(&t[0], NULL, thread1, NULL); } CS 5204 Fall 2013

Implementing Mutual Exclusion with Semaphores Semaphores can be used to build locks Must initialize semaphore with 1 to allow one thread to enter critical section This is not a recommended style, despite of what Bryant & O’Hallaron suggest – you should use a mutex instead [Cantrill & Bonwick 2008] sem_t S; sem_init(&S, 0, 1); lock_acquire() { // try to decrement, wait if 0 sem_wait (S); } lock_release() { // increment (wake up waiters if any) sem_post(S); Easily generalized to allow at most N simultaneous threads: multiplex pattern (i.e., a resource can be accessed by at most N threads) CS 5204 Fall 2013

Condition Variables - Intro Besides (and perhaps more so) than semaphores, condition variables are another widely used form to implement ‘signaling’ kinds of coordination/synchronization In POSIX Threads, Java, C# Based on the concept of a Monitor ADT that combines protected access to state and signaling Confusing terminology alert: Word ‘signal’ is overloaded 3 times Semaphore signal (V(), “up”, “post”) Monitor/Condition variable signal (“signal”, “notify”) Unix signals Word ‘wait’ is overloaded Semaphore wait (P(), “down”) Monitor/Condition variable wait Unix wait() for child process CS 5204 Fall 2013

Monitors A monitor combines a set of shared variables & operations to access them Think of a Java class with no public fields & all public methods carrying the attribute ‘synchronized’ A monitor provides implicit synchronization (only one thread can access private variables simultaneously) Single lock is used to ensure all code associated with monitor is within critical section A monitor provides a general signaling facility Wait/Signal pattern (similar to, but different from semaphores) May declare & maintain multiple signaling queues CS 5204 Fall 2013

Monitors (cont’d) Classic monitors are embedded in programming languages Invented by Hoare & Brinch-Hansen 1972/73 First used in Mesa/Cedar System @ Xerox PARC 1978 Adapted version available in Java/C# (Classic) Monitors are safer than semaphores can’t forget to lock data – compiler checks this In contemporary C, monitors are a synchronization pattern that is achieved using locks & condition variables Helps to understand monitor abstraction to use it correctly CS 5204 Fall 2013

Infinite Buffer w/ Monitor monitor buffer { /* implied: struct lock mlock;*/ private: char buffer[]; int head, tail; public: produce(item); item consume(); } buffer::produce(item i) { /* try { lock_acquire(&mlock); */ buffer[head++] = i; /* } finally {lock_release(&mlock);} */ } buffer::consume() return buffer[tail++]; Monitors provide implicit protection for their internal variables Still need to add the signaling part CS 5204 Fall 2013

Condition Variables Used by a monitor for signaling a condition a general (programmer-defined) condition, not just integer increment as with semaphores Somewhat weird: the condition is actually not stored in the variable – it’s typically some boolean predicate of monitor variables, e.g. “buffer.size > 0” the condition variable itself is better thought of as a signaling queue Monitor can have more than one condition variable Three operations: Wait(): leave monitor, wait for condition to be signaled, reenter monitor Signal(): signal one thread waiting on condition Broadcast(): signal all threads waiting on condition CS 5204 Fall 2013

Condition Variables as Queues Region of mutual exclusion Enter Exit Wait Signal A condition variable’s state is just a queue of waiters: Wait(): adds current thread to (end of queue) & block Signal(): pick one thread from queue & unblock it Broadcast(): unblock all threads Note on style: best practice is to leave monitor only once, and near the procedure’s entry. CS 5204 Fall 2013

Bounded Buffer w/ Monitor monitor buffer { condition items_avail; condition slots_avail; private: char buffer[]; int head, tail; public: produce(item); item consume(); } buffer::produce(item i) { while ((tail+1–head)%CAPACITY==0) slots_avail.wait(); buffer[head++] = i; items_avail.signal(); } buffer::consume() while (head == tail) items_avail.wait(); item i = buffer[tail++]; slots_avail.signal(); return i; CS 5204 Fall 2013

Bounded Buffer w/ Monitor monitor buffer { condition items_avail; condition slots_avail; private: char buffer[]; int head, tail; public: produce(item); item consume(); } buffer::produce(item i) { while ((tail+1–head)%CAPACITY==0) slots_avail.wait(); buffer[head++] = i; items_avail.signal(); } buffer::consume() while (head == tail) items_avail.wait(); item i = buffer[tail++]; slots_avail.signal(); return i; Q1.: How is lost update problem avoided? lock_release(&mlock); block_on(items_avail); lock_acquire(&mlock); Q2.: Why while() and not if()? CS 5204 Fall 2013

cond_signal semantics cond_signal keeps lock, so it leaves signaling thread in monitor waiter is made READY, but can’t enter until signaler gives up lock There is no guarantee whether signaled thread will enter monitor next or some other thread will (who may be already waiting!) so must always use “while()” when checking condition – cannot assume that condition set by signaling thread will still hold when monitor is reentered by signaled thread This semantics is also referred to as “Mesa-Style” after the system in which it was first used POSIX Threads, Java, and C# use this semantics CS 5204 Fall 2013

Condition Variables vs. Semaphores Signals are lost if nobody’s on the queue (e.g., nothing happens) Wait() always blocks Semaphores Signals (calls to V() or sem_post()) are remembered even if nobody’s current waiting Wait (e.g., P() or sem_wait()) may or may not block CS 5204 Fall 2013

Monitors in C POSIX Threads as well as many custom environments No compiler support, must do it manually must declare locks & condition vars must call pthread_mutex_lock/unlock when entering & leaving the monitor must use pthread_cond_wait/pthread_cond_signal to wait for/signal condition Note: pthread_cond_wait(&c, &m) takes monitor lock as parameter necessary so monitor can be left & reentered without losing signals pthread_cond_signal() does not leaving room for programmer error! CS 5204 Fall 2013

Monitors in Java synchronized block means class buffer { private char buffer[]; private int head, tail; public synchronized produce(item i) { while (buffer_full()) this.wait(); buffer[head++] = i; this.notifyAll(); } public synchronized item consume() { while (buffer_empty()) i = buffer[tail++]; return ; synchronized block means enter monitor execute block leave monitor wait()/notify() use condition variable associated with receiver Every object in Java can function as a condition variable (just like it can function as a lock) More restrictive than Pthreads/C which allow multiple condition variables (signaling conditions) to be used in connection with a lock protecting state CS 5204 Fall 2013

import java.util.concurrent.locks.*; class buffer { private ReentrantLock monitorlock = new ReentrantLock(); private Condition items_available = monitorlock.newCondition(); private Condition slots_available public /* NO SYNCHRONIZED here */ void produce(item i) { monitorlock.lock(); try { while (buffer_full()) slots_available.await(); buffer[head++] = i; items_available.signal(); } finally { monitorlock.unlock(); } } /* consume analogous */ Monitors in Java, Take 2 Previous slide (bounded buffer) is actually an example of where Java’s built-in monitors suck Needed “notifyAll()” to make sure one at least one of the right kind of threads was woken up Unacceptably inefficient Use java.util.concurrent.- locks.Condition instead in cases where multiple condition queues are needed CS 5204 Fall 2013

A ReadWrite Lock Implementation struct lock mlock; // protects rdrs & wrtrs int readers = 0, writers = 0; struct condvar canread, canwrite; void read_lock_acquire() { lock_acquire(&mlock); while (writers > 0) cond_wait(&canread, &mlock); readers++; lock_release(&mlock); } void read_lock_release() { if (--readers == 0) cond_signal(&canwrite); void write_lock_acquire() { lock_acquire(&mlock); while (readers > 0 || writers > 0) cond_wait(&canwrite, &mlock); writers++; lock_release(&mlock); } void write_lock_release() { writers--; ASSERT(writers == 0); cond_broadcast(&canread); cond_signal(&canwrite); Note: this is a naïve implementation that may lead to livelock – no guarantees a reader or writer ever enters the locked section even if every threads eventually leaves it CS 5204 Fall 2013

Summary Semaphores & Condition Variables provide signaling facilities Condition variables are loosely based on “monitor” concept Java/C# provide syntactic sugar Semaphores have “memory” But require that # of signals matches # of waits Good for rendezvous, precedence constraints – if problem lends itself to semaphore, use one Always use idiomatic “while (!cond) *_wait()” pattern when using condition variables (in C) or Object.wait() (in Java) CS 5204 Fall 2013