CS 3214 Computer Systems Lecture 22 Godmar Back.

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.
Chapter 6: Process Synchronization
Silberschatz, Galvin and Gagne ©2009 Operating System Concepts – 8 th Edition, Chapter 6: Process Synchronization.
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.
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.
CS444/CS544 Operating Systems Synchronization 2/21/2007 Prof. Searleman
Semaphores CSCI 444/544 Operating Systems Fall 2008.
CS533 Concepts of Operating Systems Class 3 Monitors.
Race Conditions CS550 Operating Systems. Review So far, we have discussed Processes and Threads and talked about multithreading and MPI processes by example.
02/19/2007CSCI 315 Operating Systems Design1 Process Synchronization Notice: The slides for this lecture have been largely based on those accompanying.
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.
Chapter 6 – Process Synchronisation (Pgs 225 – 267)
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.
CS399 New Beginnings Jonathan Walpole. 2 Concurrent Programming & Synchronization Primitives.
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:
COSC 3407: Operating Systems Lecture 9: Readers-Writers and Language Support for Synchronization.
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.
CS 3204 Operating Systems Godmar Back Lecture 10.
Mutual Exclusion -- Addendum. Mutual Exclusion in Critical Sections.
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.
Case Study: Pthread Synchronization Dr. Yingwu Zhu.
Semaphores & Condition Variables
CS703 - Advanced Operating Systems
CSE 120 Principles of Operating
CS703 – Advanced Operating Systems
Chapter 5: Process Synchronization – Part 3
Background on the need for Synchronization
CS 3214 Computer Systems Lecture 20 Godmar Back.
Operating Systems CMPSC 473
CS 3214 Introduction to Computer Systems
CS 3214 Computer Systems.
CS 3214 Computer Systems.
CS510 Operating System Foundations
CSCI 511 Operating Systems Chapter 5 (Part C) Monitor
Monitors Chapter 7.
Threading And Parallel Programming Constructs
Producer-Consumer Problem
Critical section problem
Chapter 7: Synchronization Examples
Chapter 30 Condition Variables
Monitors Chapter 7.
Concurrency: Mutual Exclusion and Process Synchronization
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 451: Operating Systems Autumn 2003 Lecture 7 Synchronization
CSE 451: Operating Systems Autumn 2005 Lecture 7 Synchronization
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
“The Little Book on Semaphores” Allen B. Downey
Monitors and Inter-Process Communication
CSE 542: Operating Systems
Don Porter Portions courtesy Emmett Witchel
Review The Critical Section problem Peterson’s Algorithm
Presentation transcript:

CS 3214 Computer Systems Lecture 22 Godmar Back

Announcements Exercise 10 due Apr 19 Project 5 out later today CS 3214 Spring 2010 7/22/2018

MULTI-THREADING CS 3214 Spring 2010 7/22/2018

Rules for Easy Locking Every shared variable must be protected by a lock Establish this relationship with code comments /* protected by … <lock>*/ Acquire lock before touching (reading or writing) variable Release when done, on all paths One lock may protect more than one variable, but not too many If in doubt, use fewer locks (may lead to worse efficiency, but less likely to lead to race conditions or deadlock) If manipulating multiple variables, acquire locks assigned to protecting each Acquire locks always in same order (doesn’t matter which order, but must be same) Release in opposite order Don’t release any locks before all have been acquired (two-phase locking) CS 3214 Spring 2010 7/22/2018

Mapping Locks To Variables Choosing which lock should protect which shared variable(s) is not easy – must weigh: Whether all variables are always accessed together (use one lock if so) Whether there is an atomicity requirement if multiple variables are accessed in related sequence (must hold single lock if so) Cost of multiple calls to lock/unlock (advantage of increased parallelism may be offset by those costs) Whether code inside critical section may block (if not, no throughput gain from fine-grained locking on uniprocessor) CS 3214 Spring 2010 7/22/2018

Race Detection Tools A number of tools help to detect race conditions (Helgrind, Intel Thread Checker) Dynamic analysis tools Typically based one or both of these approaches: Locksets: detect if no lock is consistently held when a given variable x is accessed “Happens-before” relationship: (intuition) if we can’t prove that access A must happen before B due to the synchronization the programmer used, then it’s a race; example: All accesses before a thread is started “happen before” all accesses by a thread; and these accesses “happen before” all accesses done after the thread is joined Typically do not detect atomicity violations CS 3214 Spring 2010 7/22/2018

Coordinating Multiple Threads Aside from coordinating access to shared items, thread 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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 case where multiple condition queues are needed CS 3214 Spring 2010 7/22/2018

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 3214 Spring 2010 7/22/2018

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 rendez-vous, 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 3214 Spring 2010 7/22/2018