Synchronization 2 Monitors and Language Support for Concurrency

  • Slides: 108
Download presentation
Synchronization 2: Monitors and Language Support for Concurrency Sam Kumar CS 162: Operating Systems

Synchronization 2: Monitors and Language Support for Concurrency Sam Kumar CS 162: Operating Systems and System Programming Lecture 9 https: //inst. eecs. berkeley. edu/~cs 162/su 20 Read: A&D 5. 4 -6 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 1

Recall: Layers… User Application / Service High Level I/O Low Level I/O Syscall length

Recall: Layers… User Application / Service High Level I/O Low Level I/O Syscall length = read(input_fd, buffer, BUFFER_SIZE); User Library ssize_t read(int, void *, size_t){ marshal args into registers issue syscall register result of syscall to rtn value }; Exception U K, interrupt processing void syscall_handler (struct intr_frame *f) { unmarshall call#, args from regs dispatch : handlers[call#](args) marshal results fo syscall ret File System I/O Driver } ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { User Process/File System relationship call device driver to do the work } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 Device Driver 2

User Program Kernel I/O Subsystem Device Driver Top Half Device Driver Bottom Half Device

User Program Kernel I/O Subsystem Device Driver Top Half Device Driver Bottom Half Device Hardware 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 3

Recall: Microkernels • Split OS into separate processes • Example: File System, Network Driver

Recall: Microkernels • Split OS into separate processes • Example: File System, Network Driver are processes outside of the kernel • Pass messages among these components (e. g. , via RPC) instead of system calls App file system VM App Windowing Networking Threads Monolithic Structure 9/6/2021 App File sys windows address spaces threads RPC Microkernel Structure Kumar CS 162 at UC Berkeley, Summer 2020 4

 • Consider the following code blocks: func A() { B(); } func B()

• Consider the following code blocks: func A() { B(); } func B() { while(TRUE) { yield(); } } • Two threads, S and T, each run A 9/6/2021 Stack growth Recall: Switching Threads Thread S Thread T A A B(while) yield run_new_thread switch Thread S's switch returns to Thread T's (and vice versa) Kumar CS 162 at UC Berkeley, Summer 2020 Pintos: switch. S 5

Recall: Interrupt Management Priority Encoder Interrupt Mask Timer Software Interrupt Int. ID Interrupt CPU

Recall: Interrupt Management Priority Encoder Interrupt Mask Timer Software Interrupt Int. ID Interrupt CPU Int Disable Control NMI • Interrupt controller chooses interrupt request to honor • Interrupt identity specified with ID line • Software Interrupt Set/Cleared by Software • CPU can disable all interrupts with internal flag • Non-Maskable Interrupt line (NMI) can’t be disabled 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 6

Recall: Preempting a Thread Interrupt Timer. Interrupt run_new_thread switch Stack growth Some Routine •

Recall: Preempting a Thread Interrupt Timer. Interrupt run_new_thread switch Stack growth Some Routine • Timer Interrupt routine: Timer. Interrupt() { Do. Periodic. House. Keeping(); run_new_thread(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 7

Creating a New Thread Other Thread Stack growth Thread. Root A B(while) yield run_new_thread

Creating a New Thread Other Thread Stack growth Thread. Root A B(while) yield run_new_thread switch 9/6/2021 New Thread. Root stub • Let Thread. Root be the routine that the thread should start out running • We need to set up the thread state so that, another thread can “return” into the beginning of Thread. Root • This really starts the new thread Kumar CS 162 at UC Berkeley, Summer 2020 8

Bootstrapping Threads Thread. Root() { Do. Startup. Housekeeping(); User. Mode. Switch(); /* enter user

Bootstrapping Threads Thread. Root() { Do. Startup. Housekeeping(); User. Mode. Switch(); /* enter user mode */ call fcn. Ptr(fcn. Arg. Ptr); Thread. Finish(); } • Stack will grow and shrink with execution of thread • Thread. Root() never returns • Thread. Finish() destroys thread, invokes scheduler 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 9

Aside: SMT/Hyperthreading • Hardware technique • Superscalar processors try to execute multiple independent instructions

Aside: SMT/Hyperthreading • Hardware technique • Superscalar processors try to execute multiple independent instructions in parallel • Hyperthreading allows a single core to process multiple instructions streams at once • But, speedup is sub-linear • Originally called “Simultaneous Multithreading” • http: //www. cs. washington. edu/research/smt/index. html • Intel, SPARC, Power (IBM) Colored blocks show Instructions executed • From the OS perspective, this just looks like multiple cores 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 10

Aside: SMT/Hyperthreading Process 1 Process N threads … CPU state threads Mem. IO state

Aside: SMT/Hyperthreading Process 1 Process N threads … CPU state threads Mem. IO state … … CPU state CPU sched. 9/6/2021 Core 2 • Same proc: low • Different proc: high OS 8 threads at a time Core 3 • Same process: low • Different proc. : high • Protection CPU Core 1 • Switch overhead: • Sharing overhead • Same proc: low • Different proc: high Core 4 Kumar CS 162 at UC Berkeley, Summer 2020 11

Recall: Race Conditions • What are the possible values of x below? • Initially

Recall: Race Conditions • What are the possible values of x below? • Initially x == 0 and y == 0 Thread B Thread A x = y + 1; y = 2; y = y * 2; • 1 or 3 or 5 (non-deterministic) • Race Condition: Thread A races against Thread B 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 12

Recall: Locks • Locks provide two atomic operations: • Lock. acquire() – wait until

Recall: Locks • Locks provide two atomic operations: • Lock. acquire() – wait until lock is free; then mark it as busy • After this returns, we say the calling thread holds the lock • Lock. release() – mark lock as free • Should only be called by a thread that currently holds the lock • After this returns, the calling thread no longer holds the lock • Provides mutual exclusion between two or more threads 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 13

Mutual Exclusion between Thread and Interrupt Handler • Interrupt handler runs to completion •

Mutual Exclusion between Thread and Interrupt Handler • Interrupt handler runs to completion • Can’t acquire a lock in an interrupt handler (why? ) • Solution: Disable interrupts and restore them afterwards int state = intr_disable(); <code manipulating shared data> intr_restore(state); 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 14

Is Mutual Exclusion Enough? No… 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020

Is Mutual Exclusion Enough? No… 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 15

Recall: Relevant Definitions • Synchronization: Coordination among threads, usually regarding shared data • Mutual

Recall: Relevant Definitions • Synchronization: Coordination among threads, usually regarding shared data • Mutual Exclusion: Ensuring only one thread does a particular thing at a time (one thread excludes the others) • Type of synchronization • Critical Section: Code exactly one thread can execute at once • Result of mutual exclusion • Lock: An object only one thread can hold at a time • Provides mutual exclusion 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 16

The Producer-Consumer Problem • Some processes/threads produce output that is consumed as input by

The Producer-Consumer Problem • Some processes/threads produce output that is consumed as input by other processes/threads • Where have we seen this? • Pipes • Sockets • GCC compiler – simple 1 -1 • cpp | cc 1 | cc 2 | as | ld 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 17

Producer-Consumer with a Bounded Buffer • Problem Definition • Producers puts things into a

Producer-Consumer with a Bounded Buffer • Problem Definition • Producers puts things into a shared buffer • Consumers takes them out Producer Buffer Consumer • Don’t want producers and consumers to have to work in lockstep, so put a buffer (bounded) between them • Need synchronization to maintain integrity of the data structure and coordinate producers/consumers • Producer needs to wait if buffer is full • Consumer needs to wait if buffer is empty 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 18

Circular Buffer Data Structure (Sequential Case) typedef struct buf { int write_index; int read_index;

Circular Buffer Data Structure (Sequential Case) typedef struct buf { int write_index; int read_index; <type> *entries[BUFSIZE]; } buf_t; w r di+2 9/6/2021 • Insert: write & bump write ptr (enqueue) • Remove: read & bump read ptr (dequeue) • How to tell if Full (on insert)? • How to tell if Empty (on remove)? • And what do you do if it is? • What needs to be atomic? di di+1 Kumar CS 162 at UC Berkeley, Summer 2020 19

Producer-Consumer: Correctness • Mutual exclusion: • Only one thread manipulates the buffer data structure

Producer-Consumer: Correctness • Mutual exclusion: • Only one thread manipulates the buffer data structure at a time • Synchronization requirements other than mutual exclusion: • If buffer is empty, consumer waits for the producer • If buffer is full, producer waits for consumer 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 20

Circular Buffer: Attempt #1 mutex buf_lock = <initially unlocked> Producer(item) { acquire(&buf_lock); while (buffer

Circular Buffer: Attempt #1 mutex buf_lock = <initially unlocked> Producer(item) { acquire(&buf_lock); while (buffer full) {} // Wait for a free slot enqueue(item); release(&buf_lock); } Will we ever come out of the wait loop? Consumer() { acquire(&buf_lock); while (buffer empty) {} // Wait for a used slot item = dequeue(); release(&buf_lock); return item; } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 21

 Circular Buffer: Attempt #2 mutex buf_lock = <initially unlocked> Producer(item) { acquire(&buf_lock); while

Circular Buffer: Attempt #2 mutex buf_lock = <initially unlocked> Producer(item) { acquire(&buf_lock); while (buffer full) { release(&buf_lock); acquire(&buf_lock); } enqueue(item); release(&buf_lock); } What happens when one is waiting for the other? Consumer() { acquire(&buf_lock); while (buffer empty) { release(&buf_lock); acquire(&buf_lock); } item = dequeue(); release(&buf_lock); return item; } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 22

Recall: Semaphore • Semaphores are a kind of generalized lock • First defined by

Recall: Semaphore • Semaphores are a kind of generalized lock • First defined by Dijkstra in late 60 s • Main synchronization primitive used in original UNIX (& Pintos) • Definition: a Semaphore has a non-negative integer value and supports the following two operations: • P() or down(): atomic operation that waits for semaphore to become positive, then decrements it by 1 • V() or up(): an atomic operation that increments the semaphore by 1, waking up a waiting P, if any P() stands for “proberen” (to test) and V() stands for “verhogen” (to increment) in Dutch 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 23

Recall: Two Important Semaphore Patterns • Mutual Exclusion: (Like lock) • Called a "binary

Recall: Two Important Semaphore Patterns • Mutual Exclusion: (Like lock) • Called a "binary semaphore" initial value of semaphore = 1; semaphore. down(); // Critical section goes here semaphore. up(); • Signaling other threads, e. g. Thread. Join Initial value of semaphore = 0 Thread. Join { semaphore. down(); } 9/6/2021 Thread. Finish { semaphore. up(); } Kumar CS 162 at UC Berkeley, Summer 2020 24

Producer-Consumer Synchronization • Mutual exclusion: • Only one thread manipulates the buffer data structure

Producer-Consumer Synchronization • Mutual exclusion: • Only one thread manipulates the buffer data structure at a time • Lock mutex; • Synchronization requirements other than mutual exclusion: • If buffer is empty, consumer waits for the producer • Semaphore used. Slots; • If buffer is full, producer waits for consumer • Semaphore free. Slots; • Rule of thumb: use a separate semaphore for each constraint 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 25

Producer-Consumer Code Semaphore used. Slots = 0; // No slots used Semaphore free. Slots

Producer-Consumer Code Semaphore used. Slots = 0; // No slots used Semaphore free. Slots = buf. Size; // All slots free Lock mutex = <initially unlocked>; // Nobody in critical sec. Producer(item) { free. Slots. P(); mutex. acquire(); Enqueue(item); mutex. release(); used. Slots. V(); } 9/6/2021 Consumer() { used. Slots. P(); mutex. acquire(); item = Dequeue(); mutex. release(); free. Slots. V(); return item; } Kumar CS 162 at UC Berkeley, Summer 2020 26

Discussion • What if we wrote the following? Producer(item) { mutex. acquire(); free. Slots.

Discussion • What if we wrote the following? Producer(item) { mutex. acquire(); free. Slots. P(); Enqueue(item); mutex. release(); used. Slots. V(); } Consumer() { used. Slots. P(); mutex. acquire(); item = Dequeue(); mutex. release(); free. Slots. V(); return item; } Deadlock… more on this later 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 27

Discussion • What if we wrote the following? Producer(item) { free. Slots. P(); mutex.

Discussion • What if we wrote the following? Producer(item) { free. Slots. P(); mutex. acquire(); Enqueue(item); used. Slots. V(); mutex. release(); } Consumer() { used. Slots. P(); mutex. acquire(); item = Dequeue(); mutex. release(); free. Slots. V(); return item; } Still correct! 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 28

Announcements • Congrats on finishing Quiz 1! • Project 1 design docs due tonight

Announcements • Congrats on finishing Quiz 1! • Project 1 design docs due tonight • Homework 3 will be released soon 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 29

Problems with Semaphores • More powerful (and primitive) than locks • Argument: Clearer to

Problems with Semaphores • More powerful (and primitive) than locks • Argument: Clearer to have separate constructs for • Mutual Exclusion: One thread can do something at a time • Waiting for a condition to become true • Need to make sure a thread calls P() for every V() • Other tools are more flexible than this 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 30

Two Distinct Uses of Semaphores “During system conception it transpired that we used the

Two Distinct Uses of Semaphores “During system conception it transpired that we used the semaphores in two completely different ways. The difference is so marked that, looking back, one wonders whether it was really fair to present the two ways as uses of the very same primitives. On the one hand, we have the semaphores used for mutual exclusion, on the other hand, the private semaphores. ” — Dijkstra, The Structure of the “THE” Multiprogramming System, 1968 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 31

Condition Variables • Queue of threads waiting inside a critical section • Typically, waiting

Condition Variables • Queue of threads waiting inside a critical section • Typically, waiting until a condition on some variables becomes true • Variables typically are protected by a mutex • Operations: • wait(&lock): Atomically release lock and go to sleep until condition variable is signaled. Re-acquire the lock before returning. • signal(): Wake up one waiting thread (if there is one) • broadcast(): Wake up all waiting threads • Rule: Hold lock when using a condition variable 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 32

Monitors • A monitor consists of a lock and zero or more condition variables

Monitors • A monitor consists of a lock and zero or more condition variables used for managing concurrent access to shared data • Lock: the lock provides mutual exclusion to shared data • Condition Variable: a queue of threads waiting for something inside a critical section • Key idea: make it possible to go to sleep inside critical section by atomically releasing lock at time we go to sleep 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 33

Producer-Consumer with Condition Variables mutex buf_lock = <initially unlocked> condvar no_longer_empty = <initially empty>

Producer-Consumer with Condition Variables mutex buf_lock = <initially unlocked> condvar no_longer_empty = <initially empty> condvar no_longer_full = <initially empty> Producer(item) { acquire(&buf_lock); while (buffer full) { cond_wait(&no_longer_full, &buf_lock); } enqueue(item); cond_signal(&no_longer_empty); release(&buf_lock); } Consumer() { acquire(&buf_lock); while (buffer empty) { cond_wait(&no_longer_empty, &buf_lock); } item = dequeue(); cond_signal(&no_longer_full); release(&buf_lock); return item; } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 34

Why the while Loop? • When a thread is woken up by signal(), it

Why the while Loop? • When a thread is woken up by signal(), it is simply marked as eligible to run • It may or may not reacquire the lock immediately! • Another thread could be scheduled and “sneak in” make the condition it’s waiting for no longer true • Need a loop to re-check condition on wakeup • This is called Mesa Scheduling (Mesa-style Monitors) • Most operating systems use Mesa-style Monitors! 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 35

Why the while Loop? (Example) Thread A (Consumer) Thread B (Producer) Thread C (Consumer)

Why the while Loop? (Example) Thread A (Consumer) Thread B (Producer) Thread C (Consumer) acquire(&buf_lock); while (buffer empty) { cond_wait(&not_empty, &buf_lock); acquire(&buf_lock) enqueue(item) cond_signal(&not_empty); release(&buf_lock); // while loop checks condition // again, goes back to sleep } 9/6/2021 This is why the while loop is necessary! Kumar CS 162 at UC Berkeley, Summer 2020 acquire(&buf_lock); while (buffer empty) dequeue(); release(&buf_lock); 36

Mesa Monitors • Signaler keeps lock and CPU • Waiter placed on ready queue

Mesa Monitors • Signaler keeps lock and CPU • Waiter placed on ready queue with no special priority Put waiting thread on ready queue … acquire(&buf_lock) … cond_signal(&not_empty); … ad e r h release(&buf_lock)); le t ater!) u d l e sch etime (som acquire(&buf_lock); … while (is. Empty(&queue)) { cond_wait(&not_empty, &buf_lock); } … lock. Release(); • Practically, need to check condition again after wait • By the time the waiter gets scheduled, condition may be false again – so, just check again with the “while” loop • Most real operating systems do this! • Efficient, easy to implement 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 37

Alternative: Hoare Monitors • Named after British logician Tony Hoare • When a thread

Alternative: Hoare Monitors • Named after British logician Tony Hoare • When a thread call signal(): • It releases the lock and the OS context-switches to the waiter, which acquires the lock immediately • When waiter releases lock, the OS switches back to signaler … acquire(&buf_lock); … cond_signal(&buf_CV); … release(&buf_lock); Lock, CPU Lock , CPU acquire(&buf_lock); … if (is. Empty(&queue)) { cond_wait(&buf_CV, &buf_lock); } … release(&buf_lock); • Academically interesting, but not necessary! • Introduces complexity into the scheduler • Adds additional context switches 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 38

Mesa Monitors vs. Hoare Monitors Mesa Monitor Hoare Monitor while (buffer empty) { if

Mesa Monitors vs. Hoare Monitors Mesa Monitor Hoare Monitor while (buffer empty) { if (buffer empty) { cond_wait(&not_empty, &buf_lock); } • In practice, almost all OSes implement Mesa monitors 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 39

Summary: Monitors • Monitors represent the logic of the program • Wait if necessary

Summary: Monitors • Monitors represent the logic of the program • Wait if necessary • Signal when change something so any waiting threads can proceed • Basic structure of monitor-based program: lock while (need to wait) { condvar. wait(); } unlock do something so no need to wait lock condvar. signal(); unlock 9/6/2021 Check and/or update state variables Wait if necessary Check and/or update state variables Kumar CS 162 at UC Berkeley, Summer 2020 40

Break 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 41

Break 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 41

Programming Language Support for Concurrency and Synchronization • Synchronization operations • Exceptional conditions 9/6/2021

Programming Language Support for Concurrency and Synchronization • Synchronization operations • Exceptional conditions 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 42

Concurrency and Synchronization in C • Standard approach: use pthreads, protect access to shared

Concurrency and Synchronization in C • Standard approach: use pthreads, protect access to shared data structures • One pitfall: consistently unlocking a mutex int Rtn() { lock. acquire(); … if (error) { lock. release(); return err. Code; } … lock. release(); return OK; } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 43

Concurrency and Synchronization in C • Harder with more locks • Is goto a

Concurrency and Synchronization in C • Harder with more locks • Is goto a solution? ? ? void Rtn() { lock 1. acquire(); … if (error) { lock 1. release(); return; } … lock 2. acquire(); … if (error) { lock 2. release() lock 1. release(); return; } … lock 2. release(); lock 1. release(); } void Rtn() { lock 1. acquire(); … if (error) { goto release_lock 1_and_return; } … lock 2. acquire(); … if (error) { goto release_both_and_return; } … release_both_and_return: lock 2. release(); release_lock 1_and_return: lock 1. release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 44

C++ Lock Guards #include <mutex> int global_i = 0; std: : mutex global_mutex; void

C++ Lock Guards #include <mutex> int global_i = 0; std: : mutex global_mutex; void safe_increment() { std: : lock_guard<std: : mutex> lock(global_mutex); … global_i++; // Mutex released when ‘lock’ goes out of scope } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 45

Python with Keyword • More versatile than we’ll show here (can be used to

Python with Keyword • More versatile than we’ll show here (can be used to close files, database connections, etc. ) lock = threading. Lock() … with lock: # Automatically calls acquire() some_var += 1 … # release() called however we leave block 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 46

Java synchronized Keyword • Every Java object has an associated lock: • Lock is

Java synchronized Keyword • Every Java object has an associated lock: • Lock is acquired on entry and released on exit from a synchronized method • Lock is properly released if exception occurs inside a synchronized method • Mutex execution of synchronized methods (beware deadlock) class Account { private int balance; } 9/6/2021 // object constructor public Account (int initial. Balance) { balance = initial. Balance; } public synchronized int get. Balance() { return balance; } public synchronized void deposit(int amount) { balance += amount; } Kumar CS 162 at UC Berkeley, Summer 2020 47

Java Support for Monitors • Along with a lock, every object has a single

Java Support for Monitors • Along with a lock, every object has a single condition variable associated with it • To wait inside a synchronized method: • void wait(); • void wait(long timeout); • To signal while in a synchronized method: • void notify(); • void notify. All(); 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 48

Go Language Support for Concurrency • Go was designed with concurrent applications in mind

Go Language Support for Concurrency • Go was designed with concurrent applications in mind • Some language aspects we’ll talk about today: • defer keyword • Channels • Some language aspects we won’t talk about today (but may revisit): • Goroutines • select keyword • Contexts 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 49

Go defer Keyword func Rtn() { lock. Lock() … if error { lock. Unlock()

Go defer Keyword func Rtn() { lock. Lock() … if error { lock. Unlock() return } … lock. Unlock() return } 9/6/2021 • Solution: use defer func Rtn() { lock. Lock() defer lock. Unlock() … if error { return } … return } Kumar CS 162 at UC Berkeley, Summer 2020 50

Go defer Keyword • The queue of “deferred” calls is maintained dynamically func Rtn()

Go defer Keyword • The queue of “deferred” calls is maintained dynamically func Rtn() { lock 1. Lock() defer lock 1. Lock() … if condition { lock 2. Lock() • lock 1 is always unlocked here defer lock 2. Unlock() • lock 2 is unlocked here only if } … the condition was true earlier return } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 51

Go Channels • A channel is a bounded buffer in userspace • Writes block

Go Channels • A channel is a bounded buffer in userspace • Writes block if buffer is full • Reads block if buffer is empty • “Do not communicate by sharing memory; instead, share memory by communicating. ” • From Effective Go • Channels are the preferred mechanism for synchronization • Mutexes and condition variables are still supported, as in pthreads 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 52

Go Channels • Semantics similar to pipes, with the following differences: • Used within

Go Channels • Semantics similar to pipes, with the following differences: • Used within a single process (not across processes) • Carries language objects/structs, not bytes (no marshalling/unmarshalling) var x chan int = make(chan int, 5) x <- 162 y : = <- x fmt. Println(y) // Prints 162 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 53

Conclusion • We studied synchronization primitives to wait until an event • Mutual exclusion

Conclusion • We studied synchronization primitives to wait until an event • Mutual exclusion isn’t enough! • Monitors: A lock plus one or more condition variables • Always acquire lock before accessing shared data • Use condition variables to wait inside critical section • Three Operations: Wait(), Signal(), and Broadcast() • Some languages support monitors directly • Monitors represent the logic of the program • Wait if necessary • Signal when change something so any waiting threads can proceed 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 54

Readers-Writers Problem • Consider a shared database • Two classes of users: W R

Readers-Writers Problem • Consider a shared database • Two classes of users: W R R R • Readers – never modify DB • Writers – read and modify DB • Is using a single lock on the whole DB sufficient? • Yes, but not ideal • Want to allow multiple concurrent readers 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 55

Reader-Writer Correctness • Readers can access when no writers • Writers can access when

Reader-Writer Correctness • Readers can access when no writers • Writers can access when no readers and no other writers • A lock will satisfy these requirements • But we want to allow multiple readers • Better efficiency 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 56

Reader-Writer with Monitors Reader() { Wait until no active writers Access database Maybe wake

Reader-Writer with Monitors Reader() { Wait until no active writers Access database Maybe wake up a writer } Writer() { Wait until no active readers or writers Access database Maybe wakeup reader or writer } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Lock (for mutual exclusion) • int active. Readers • condvar ok. To. Read • int active. Writers • condvar ok. To. Write 57

Reader Version 1 Reader() { // First check self into system lock. Acquire(); while

Reader Version 1 Reader() { // First check self into system lock. Acquire(); while (AW > 0) { // Is it safe to read? ok. To. Read. wait(&lock); // Sleep on cond var } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • AR = “Active Readers” • AW = “Active Writers” 58

Writer Version 1 Writer() { // First check self into system lock. Acquire(); while

Writer Version 1 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? ok. To. Write. wait(&lock); // Sleep on cond var } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active ok. To. Write. signal(); // Wake up one writer ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • AR = “Active Readers” • AW = “Active Writers” 59

Writer Version 1: Starvation Writer() { // First check self into system lock. Acquire();

Writer Version 1: Starvation Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? ok. To. Write. wait(&lock); // Sleep on cond var } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active ok. To. Write. signal(); // Wake up one writer ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 If there always readers, this is always true! Writer starves 60

Writer Version 1: Conflict Writer() { // First check self into system lock. Acquire();

Writer Version 1: Conflict Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? ok. To. Write. wait(&lock); // Sleep on cond var } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); If a writer gets the lock, all AW--; // No longer active the readers wake up anyway, ok. To. Write. signal(); // Wake up one writer re-check the condition, and ok. To. Read. broadcast(); // Wake up all readers lock. Release(); go to sleep } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 61

Reader-Writer with Monitors, Version 2 Reader() { Wait until no active or waiting writers

Reader-Writer with Monitors, Version 2 Reader() { Wait until no active or waiting writers Access database Maybe wake up a writer } Writer() { Wait until no active readers or writers Access database If waiting writer, wake it up Otherwise, wakeup readers } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 62

Reader Version 2 Reader() { // First check self into system lock. Acquire(); while

Reader Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • AR = “Active Readers” • AW = “Active Writers” • WR = “Waiting Readers” • WW = “Waiting Writers” 63

Writer Version 2 Writer() { // First check self into system lock. Acquire(); while

Writer Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • AR = “Active Readers” • AW = “Active Writers” • WR = “Waiting Readers” • WW = “Waiting Writers” 64

Reader-Writer Design Choices • Reader starvation: while (AW > 0 || WW > 0)

Reader-Writer Design Choices • Reader starvation: while (AW > 0 || WW > 0) { // Safe to read? ok. To. Read. wait(&lock); // Sleep on cond var } • “Writer-biased” Lock • Can favor readers by changing conditions on wait loops • Other possibilities, e. g. track readers waiting since before current writer started 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 65

Fair Solution to the Reader-Writer Problem? • Ideas? 9/6/2021 Kumar CS 162 at UC

Fair Solution to the Reader-Writer Problem? • Ideas? 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 66

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 0, AW = 0, WW = 0 • R 1 comes along (nobody waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 67

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 0, AW = 0, WW = 0 • R 1 comes along (nobody waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 68

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 1 comes along (nobody waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 69

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 1 comes along (nobody waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 70

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 1 accessing DB } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 71

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 2 comes along (R 1 accessing DB) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 72

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 2 comes along (R 1 accessing DB) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 73

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 0, AW = 0, WW = 0 • R 2 comes along (R 1 accessing DB) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 74

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 0, AW = 0, WW = 0 • R 2 comes along (R 1 accessing DB) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 75

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR++; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 0, AW = 0, WW = 0 • R 1 & R 2 accessing DB } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 76

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 0, AW = 0, WW = 0 • W 1 comes along (R 1 & R 2 accessing DB) 77

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 0, AW = 0, WW = 0 • W 1 comes along (R 1 & R 2 accessing DB) 78

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 0, AW = 0, WW = 1 • W 1 comes along (R 1 & R 2 accessing DB) 79

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 0, AW = 0, WW = 1 • R 3 comes along (R 1 & R 2 accessing DB, W 1 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 80

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 0, AW = 0, WW = 1 • R 3 comes along (R 1 & R 2 accessing DB, W 1 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 81

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 1, AW = 0, WW = 1 • R 1 & R 2 accessing DB, W 1 & R 3 waiting } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 82

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 2, WR = 1, AW = 0, WW = 1 • R 2 finishes (R 1 accessing DB, W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 83

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 1, AW = 0, WW = 1 • R 2 finishes (R 1 accessing DB, W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 84

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 1, AW = 0, WW = 1 • R 2 finishes (R 1 accessing DB, W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 85

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 1, AW = 0, WW = 1 • R 2 finishes (R 1 accessing DB, W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 86

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 1, AW = 0, WW = 1 • R 1 finishes (W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 87

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 1 • R 1 finishes (W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 88

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 1 • R 1 finishes (W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 89

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 1 • R 1 finishes (W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 90

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 1 • R 1 finishes (W 1 & R 3 waiting) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 91

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 0 • W 1 is awakened (R 3 waiting) 92

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 1, WW = 0 • W 1 is awakened (R 3 waiting) 93

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 1, WW = 0 • W 1 is awakened (R 3 waiting) 94

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 1, WW = 0 • W 1 accessing DB (R 3 waiting) 95

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 1, WW = 0 • W 1 finishes (R 3 waiting) 96

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 0 • W 1 finishes (R 3 waiting) 97

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 0 • W 1 finishes (R 3 waiting) 98

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 0 • W 1 finishes (R 3 waiting) 99

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Writer() { // First check self into system lock. Acquire(); while (AR > 0 || AW > 0) { // Is it safe to write? WW++; ok. To. Write. wait(&lock); // Sleep on cond var WW--; } AW++; // Now we are active! lock. release(); // Perform actual read/write access Access. Database(Read. Write); // Now, check out of system lock. Acquire(); AW--; // No longer active if (WW > 0) ok. To. Write. signal(); // Wake up one writer else ok. To. Read. broadcast(); // Wake up all readers lock. Release(); } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 1, AW = 0, WW = 0 • W 1 finishes (R 3 waiting) 100

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 0, AW = 0, WW = 0 • R 3 is awakened (no other threads) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 101

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 3 is awakened (no other threads) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 102

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 3 is awakened (no other threads) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 103

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 3 accessing the DB (no other threads) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 104

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 1, WR = 0, AW = 0, WW = 0 • R 3 finishes (no other threads) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 105

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 0, AW = 0, WW = 0 • R 3 finishes (no other threads) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 106

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 0, AW = 0, WW = 0 • R 3 finishes (no other threads) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 107

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock.

Simulation of Reader-Writer, Version 2 Reader() { // First check self into system lock. Acquire(); while (AW > 0 || WW > 0) { // Is it safe to read? WR++; ok. To. Read. wait(&lock); // Sleep on cond var WR--; } AR++; // Now we are active! lock. release(); // Perform actual read-only access Access. Database(Read. Only); // Now, check out of system lock. Acquire(); AR--; // No longer active if (AR == 0 && WW > 0) // No other active readers ok. To. Write. signal(); // Wake up one writer lock. Release(); • Sequence of arrivals: R 1, R 2, W 1, R 3 • AR = 0, WR = 0, AW = 0, WW = 0 • R 3 finishes (no other threads) } 9/6/2021 Kumar CS 162 at UC Berkeley, Summer 2020 108