CSE332: Data Abstractions Lecture 24: Readers/Writer Locks and Condition Variables Tyler Robison Summer 2010 1.

Slides:



Advertisements
Similar presentations
Operating Systems Semaphores II
Advertisements

Operating Systems: Monitors 1 Monitors (C.A.R. Hoare) higher level construct than semaphores a package of grouped procedures, variables and data i.e. object.
– R 7 :: 1 – 0024 Spring 2010 Parallel Programming 0024 Recitation Week 7 Spring Semester 2010.
1 Chapter 5 Concurrency: Mutual Exclusion and Synchronization Principals of Concurrency Mutual Exclusion: Hardware Support Semaphores Readers/Writers Problem.
Ch 7 B.
Chapter 6: Process Synchronization
Silberschatz, Galvin and Gagne ©2009 Operating System Concepts – 8 th Edition, Chapter 6: Process Synchronization.
Monitors & Blocking Synchronization 1. Producers & Consumers Problem Two threads that communicate through a shared FIFO queue. These two threads can’t.
CS 5704 Fall 00 1 Monitors in Java Model and Examples.
CY2003 Computer Systems Lecture 05 Semaphores - Theory.
COSC 3407: Operating Systems Lecture 8: Semaphores, Monitors and Condition Variables.
CSE332: Data Abstractions Lecture 23: Programming with Locks and Critical Sections Dan Grossman Spring 2010.
A Sophomoric Introduction to Shared-Memory Parallelism and Concurrency Lecture 6 Data Races and Memory Reordering Deadlock Readers/Writer Locks Condition.
CSE332: Data Abstractions Lecture 22: Shared-Memory Concurrency and Mutual Exclusion Tyler Robison Summer
CSE332: Data Abstractions Lecture 22: Shared-Memory Concurrency and Mutual Exclusion Dan Grossman Spring 2010.
A Sophomoric Introduction to Shared-Memory Parallelism and Concurrency Lecture 5 Programming with Locks and Critical Sections Dan Grossman Last Updated:
Threading Part 4 CS221 – 4/27/09. The Final Date: 5/7 Time: 6pm Duration: 1hr 50mins Location: EPS 103 Bring: 1 sheet of paper, filled both sides with.
CSE332: Data Abstractions Lecture 23: Programming with Locks and Critical Sections Tyler Robison Summer
CSE332: Data Abstractions Lecture 24: Remaining Topics in Shared-Memory Concurrency Dan Grossman Spring 2010.
Monitors and Blocking Synchronization By Tal Walter.
Synchronization Principles. Race Conditions Race Conditions: An Example spooler directory out in 4 7 somefile.txt list.c scores.txt Process.
Concurrency: Mutual Exclusion, Synchronization, Deadlock, and Starvation in Representative Operating Systems.
Synchronization in Java Nelson Padua-Perez Bill Pugh Department of Computer Science University of Maryland, College Park.
A. Frank - P. Weisberg Operating Systems Introduction to Cooperating Processes.
CSE 332 Data Abstractions: Data Races and Memory, Reordering, Deadlock, Readers/Writer Locks, and Condition Variables (oh my!) Kate Deibel Summer 2012.
CS Introduction to Operating Systems
CSE332: Data Abstractions Lecture 26: Amortized Analysis Tyler Robison Summer
Concurrency Recitation – 2/24 Nisarg Raval Slides by Prof. Landon Cox.
Nachos Phase 1 Code -Hints and Comments
Object Oriented Analysis & Design SDL Threads. Contents 2  Processes  Thread Concepts  Creating threads  Critical sections  Synchronizing threads.
4061 Session 23 (4/10). Today Reader/Writer Locks and Semaphores Lock Files.
Operating Systems ECE344 Ashvin Goel ECE University of Toronto Mutual Exclusion.
1 Using Semaphores CS 241 March 14, 2012 University of Illinois Slides adapted in part from material accompanying Bryant & O’Hallaron, “Computer Systems:
Producer-Consumer Problem The problem describes two processes, the producer and the consumer, who share a common, fixed-size buffer used as a queue.bufferqueue.
4061 Session 21 (4/3). Today Thread Synchronization –Condition Variables –Monitors –Read-Write Locks.
Synchronized and Monitors. synchronized is a Java keyword to denote a block of code which must be executed atomically (uninterrupted). It can be applied.
CSC321 Concurrent Programming: §5 Monitors 1 Section 5 Monitors.
11/18/20151 Operating Systems Design (CS 423) Elsa L Gunter 2112 SC, UIUC Based on slides by Roy Campbell, Sam.
Kernel Locking Techniques by Robert Love presented by Scott Price.
ICS 313: Programming Language Theory Chapter 13: Concurrency.
Advanced Concurrency Topics Nelson Padua-Perez Bill Pugh Department of Computer Science University of Maryland, College Park.
Java Thread and Memory Model
1 CMSC421: Principles of Operating Systems Nilanjan Banerjee Principles of Operating Systems Acknowledgments: Some of the slides are adapted from Prof.
SPL/2010 Guarded Methods and Waiting 1. SPL/2010 Reminder! ● Concurrency problem: asynchronous modifications to object states lead to failure of thread.
CS399 New Beginnings Jonathan Walpole. 2 Concurrent Programming & Synchronization Primitives.
Monitors and Blocking Synchronization Dalia Cohn Alperovich Based on “The Art of Multiprocessor Programming” by Herlihy & Shavit, chapter 8.
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:
CS252: Systems Programming Ninghui Li Based on Slides by Prof. Gustavo Rodriguez-Rivera Topic 13: Condition Variable, Read/Write Lock, and Deadlock.
COSC 3407: Operating Systems Lecture 9: Readers-Writers and Language Support for Synchronization.
Concurrency in Shared Memory Systems Synchronization and Mutual Exclusion.
1 Previous Lecture Overview  semaphores provide the first high-level synchronization abstraction that is possible to implement efficiently in OS. This.
6.1 Silberschatz, Galvin and Gagne ©2009 Operating System Concepts with Java – 8 th Edition Module 6: Process Synchronization.
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.
Lecture 5 Page 1 CS 111 Summer 2013 Bounded Buffers A higher level abstraction than shared domains or simple messages But not quite as high level as RPC.
Mergesort example: Merge as we return from recursive calls Merge Divide 1 element 829.
Background on the need for Synchronization
Lecture 25 More Synchronized Data and Producer/Consumer Relationship
CSE 332: Locks and Deadlocks
CSE332: Data Abstractions Lecture 23: Data Races and Memory Reordering Deadlock Readers/Writer Locks Condition Variables Dan Grossman Spring 2012.
Producer-Consumer Problem
Multithreading.
CSCI1600: Embedded and Real Time Software
CSE 153 Design of Operating Systems Winter 19
CSE 153 Design of Operating Systems Winter 2019
CS333 Intro to Operating Systems
CSCI1600: Embedded and Real Time Software
Software Engineering and Architecture
Synchronization and liveness
Presentation transcript:

CSE332: Data Abstractions Lecture 24: Readers/Writer Locks and Condition Variables Tyler Robison Summer

Concurrency: Where are we 2 Done:  Programming with locks and critical sections  Key guidelines and trade-offs Now: More on facilitating concurrent access  Readers/writer locks  Specific type of lock that can allow for more efficient access  Condition variables  More efficient access for producer/consumer relationships

Reading vs. writing 3 Which of these is a problem?  Concurrent writes of same object:  Concurrent reads of same object:  Concurrent read & write of same object:  Concurrent read/write or write/write is a data race So far:  If concurrent write/write or read/write could occur, use synchronization to ensure one-thread-at-a-time access But:  In some cases this is unnecessarily conservative  If multiple threads want to access to ‘read’, should be ok Problem Not a Problem Problem

Example 4 Consider a hashtable with one coarse-grained lock  So only one thread can perform any operation at a time  Won’t allow simultaneous reads, even though it’s ok conceptually But suppose:  There are many simultaneous lookup operations  insert operations are very rare  It’d be nice to support multiple reads; we’d do lots of waiting otherwise Assumptions: lookup doesn’t mutate shared memory, and doesn’t have some different intermediate state  Unlike our unusual peek implementation, which did a pop then a push

Readers/writer locks 5 A new synchronization ADT: The readers/writer lock  Idea: Allow any number of readers OR one writer  A lock’s states fall into three categories:  “not held”  “held for writing” by one thread  “held for reading” by one or more threads  new: make a new lock, initially “not held”  acquire_write: block if currently “held for reading” or “held for writing”, else make “held for writing”  release_write: make “not held”  acquire_read: block if currently “held for writing”, else make/keep “held for reading” and increment readers count  release_read: decrement readers count, if 0, make “not held” 0  writers  1 && 0  readers && writers * readers==0

Pseudocode example (not Java) 6 class Hashtable { … // coarse-grained, one lock for table RWLock lk = new RWLock(); V lookup(K key) { int bucket = hasher(key); lk.acquire_read(); … read array[bucket] … lk.release_read(); } void insert(K key, V val) { int bucket = hasher(key); lk.acquire_write(); … read array[bucket] … lk.release_write(); }

Readers/writer lock details 7  A readers/writer lock implementation (“not our problem”) usually gives priority to writers:  Once a writer blocks, no readers arriving later will get the lock before the writer  Otherwise an insert could starve  That is, it could wait indefinitely because of continuous stream of read requests  Side note: Notion of starvation used in other places: scheduling threads, scheduling hard-drive accesses, etc.  Re-entrant? Mostly an orthogonal issue  Some libraries support upgrading from reader to writer  Once held for reading, can grab for writing once other readers release  Why not use readers/writer locks with more fine-grained locking, like on each bucket?  Not wrong, but likely not worth it due to low contention

Readers/writer Locks in Java 8 Java’s synchronized statement does not support readers/writer Instead, use this class: java.util.concurrent.locks.ReentrantReadWriteLock  Notes:  Our pseudo-code used acquire_read, release_read, acquire_write & release_write  In Java, methods readLock and writeLock return objects that themselves have lock and unlock methods  Does not have writer priority or reader-to-writer upgrading

Motivating Condition Variables: Producers and Consumers 9 Another means of allowing concurrent access is the condition variable; before we get into that though, lets look at a situation where we’d need one:  Imagine we have several producer threads and several consumer threads  Producers do work, toss their results into a buffer  Consumers take results off of buffer as they come and process them  Ex: Multi-step computation fedc buffer back front producer(s) enqueue consumer(s) dequeue

Motivating Condition Variables: Producers and Consumers 10  Cooking analogy: Team one peels potatoes, team two takes those and slices them up  When a member of team one finishes peeling, they toss the potato into a tub  Members of team two pull potatoes out of the tub and dice them up fedc buffer back front producer(s) enqueue consumer(s) dequeue

Motivating Condition Variables: Producers and Consumers 11  If the buffer is empty, consumers have to wait for producers to produce more data  If buffer gets full, producers have to wait for consumers to consume some data and clear space  We’ll need to synchronize access; why?  Data race; simultaneous read/write or write/write to back/front fedc buffer back front producer(s) enqueue consumer(s) dequeue

First attempt 12 class Buffer { E[] array = (E[])new Object[SIZE]; … // front, back fields, isEmpty, isFull methods synchronized void enqueue(E elt) { if(isFull()) ??? else … add to array and adjust back … } synchronized E dequeue() { if(isEmpty()) { ??? else … take from array and adjust front … }  One approach; if buffer is full on enqueue, or empty on dequeue, throw an exception  Not what we want here; w/ multiple threads taking & giving, these will be common occurrences – should not handle like errors  Common, and only temporary; will only be empty/full briefly  Instead, we want threads to be pause until it can proceed

Pausing 13  enqueue to a full buffer should not raise an exception  Wait until there is room  dequeue from an empty buffer should not raise an exception  Wait until there is data One approach to pausing: spin the lock: loop, checking until buffer is no longer full (for enqueue case)  Hold the lock for the check, then release and loop Spinning works… but is very wasteful:  We’re using a processor just for looping & checking  We’re holding the lock a good deal of the time for that checking  Cooking analogy: When waiting for work, team two members reach into tub every few seconds to see if another potato is in there void enqueue(E elt) { while(true) { synchronized(this) { if(isFull()) continue; … add to array and adjust back … return; }}} // dequeue similar

What we want 14  Better would be for a thread to wait until it can proceed  Be notified when it should try again  Thread suspended until then; in meantime, other threads run  While waiting, lock is released; will be re-acquired later by one notified thread  Upon being notified, thread just drops in to see what condition it’s condition is in  Team two members work on something else until they’re told more potatoes are ready  Less contention for lock, and time waiting spent more efficiently

Condition Variables 15  Like locks & threads, not something you can implement on your own  Language or library gives it to you  An ADT that supports this: condition variable  Informs waiting thread(s) when the condition that causes it/them to wait has varied  Terminology not completely standard; will mostly stick with Java

Java approach: right idea; some problems in the details 16 class Buffer { … synchronized void enqueue(E elt) { if(isFull()) this.wait(); // releases lock and waits add to array and adjust back if(buffer was empty) this.notify(); // wake somebody up } synchronized E dequeue() { if(isEmpty()) { this.wait(); // releases lock and waits take from array and adjust front if(buffer was full) this.notify(); // wake somebody up } }

Key ideas 17  Condition variables: A Thread can wait, suspending operation and relinquishing the lock, until it is notified  wait:  “Register” running thread as interested in being woken up  Then atomically: release the lock and block  When execution resumes after notify, thread again holds the lock  notify:  Pick one waiting thread and wake them up  No guarantee woken up thread runs next, just that it is no longer blocked on the condition – now waits for the lock  If no thread is waiting, then do nothing  Java weirdness: every object “is” a condition variable (and a lock)  Just like how we can synchronize on any object  Other languages/libraries often make them separate

Bug #1 18 Between the time a thread is notified and when it re-acquires the lock, the condition can become false again! synchronized void enqueue(E elt){ if(isFull()) this.wait(); add to array and adjust back … } if(isFull()) this.wait(); add to array Time Thread 2 (dequeue) Thread 1 (enqueue) take from array if(was full) this.notify(); enqueue; full again Thread 3 (enqueue)

Bug fix #1 19 Guideline: Always re-check the condition after re-gaining the lock  If condition still not met, go back to waiting  In fact, for obscure reasons, Java is technically allowed to notify a thread for no reason synchronized void enqueue(E elt) { while(isFull()) this.wait(); … } synchronized E dequeue() { while(isEmpty()) { this.wait(); … }

Bug #2 20  If multiple threads are waiting, currently we only wake up one  Works for the most part, but what if 2 are waiting to enqueue, and two quick dequeues occur before either gets to go?  We’d only notify once; other thread would wait forever if(isFull()) this.wait(); … Time Thread 2 (enqueue) Thread 1 (enqueue) if(isFull()) this.wait(); // dequeue #1 if(buffer was full) this.notify(); // dequeue #2 if(buffer was full) this.notify(); Thread 3 (dequeues)

Bug fix #2 21 notifyAll wakes up all current waiters on the condition variable Guideline: If in any doubt, use notifyAll  Wasteful waking is better than never waking up  So why does notify exist?  Well, it is faster when correct… synchronized void enqueue(E elt) { … if(buffer was empty) this.notifyAll(); // wake everybody up } synchronized E dequeue() { … if(buffer was full) this.notifyAll(); // wake everybody up }

Alternate approach 22  An alternative is to call notify (not notifyAll ) on every enqueue / dequeue, not just when the buffer was empty / full  Easy to implement: just remove the if statement  Alas, makes our code subtly wrong since it’s technically possible that an enqueue and a dequeue are both waiting  Idea: Under extreme cases, the fact that producers and consumers share a condition variable can result in each waiting for the other  Details for the curious (not on the final):  Buffer is full and so a huge # of enqueues (>SIZE) have to wait  So each dequeue wakes up one enqueue, but say so many dequeue calls happen so fast that the buffer is empty and a dequeue call waits  The final notify may wake up a dequeue, which immediately has to wait again, and now everybody will wait forever  We can fix it; it just involves using a different condition variable for producers and consumers – they still share the same lock though

Last condition-variable comments 23  notify/notifyAll often called signal/broadcast  Condition variables are subtle and harder to use than locks  Not as common as locks  But when you need them, you need them  Spinning and other work-arounds don’t work well  Fortunately, like most things in CSE332, the common use-cases are already provided efficiently in libraries  Example: java.util.concurrent.ArrayBlockingQueue  All uses of condition variables hidden in the library; client just calls put and take