Review: Too Much Milk Solution #3 • Here is a possible two-note solution: CS162 A Thread B Operating Systems and leave note A; leave note B; while (note B) {\\X if (noNote A) {\\Y Systems Programming do nothing; if (noMilk) { }buy milk; Lecture 8 if (noMilk) {} buy milk; } } remove note B; Locks, Semaphores, Monitors remove note A; • Does this work? Yes. Both can guarantee that: – It is safe to buy, or February 14th, 2019 – Other will buy, ok to quit •At X: Prof. John Kubiatowicz – If no note B, safe for A to buy, http://cs162.eecs.Berkeley.edu – Otherwise wait to find out what will happen •At Y: – If no note A, safe for B to buy – Otherwise, A is either buying or waiting for B to quit 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.2

Review: Solution #3 discussion Too Much Milk: Solution #4 • Our solution protects a single “Critical-Section” piece of • Suppose we have some sort of implementation of a code for each thread: – lock.Acquire() – wait until lock is free, then grab if (noMilk) { buy milk; – lock.Release() – Unlock, waking up anyone waiting } – These must be atomic operations – if two threads are waiting • Solution #3 works, but it’s really unsatisfactory for the lock and both see it’s free, only one succeeds to grab – Really complex – even for this simple an example the lock » Hard to convince yourself that this really works • Then, our milk problem is easy: – A’s code is different from B’s – what if lots of threads? milklock.Acquire(); » Code would have to be slightly different for each thread if (nomilk) – While A is waiting, it is consuming CPU time buy milk; » This is called “busy-waiting” milklock.Release(); • There’s a better way • Once again, section of code between Acquire() and – Have hardware provide higher-level primitives than atomic Release() called a “Critical Section” load & store • Of course, you can make this even simpler: suppose you – Build even higher-level programming abstractions on this hardware support are out of ice cream instead of milk – Skip the test since you always need more ice cream ;-) 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.3 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.4 Where are we going with synchronization? Goals for Today

• We are going to implement various higher-level • Explore several implementations of locks synchronization primitives using atomic operations • Continue with Synchronization Abstractions – Everything is pretty painful if only atomic primitives are load and store – Semaphores, Monitors, and Condition variables – Need to provide primitives useful at user-level • Very Quick Introduction to scheduling

Programs Shared Programs

Higher- level Locks Semaphores Monitors Send/Receive API Load/Store Disable Ints Test&Set Hardware Note: Some slides and/or pictures in the following are Compare&Swap adapted from slides ©2005 Silberschatz, Galvin, and GagneGagne.

2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.5 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.6

How to Implement Locks? Naïve use of Interrupt Enable/Disable • Lock: prevents someone from doing something • How can we build multi-instruction atomic operations? – Lock before entering critical section and – Recall: dispatcher gets control in two ways. before accessing shared data » Internal: Thread does something to relinquish the CPU – Unlock when leaving, after accessing shared data » External: Interrupts cause dispatcher to take CPU – On a uniprocessor, can avoid context-switching by: – Wait if locked » Avoiding internal events (although virtual memory tricky) » Important idea: all synchronization involves waiting » Preventing external events by disabling interrupts » Should if waiting for a long time • Consequently, naïve Implementation of locks: • Atomic Load/Store: get solution like Milk #3 LockAcquire { disable Ints; } – Pretty complex and error prone LockRelease { enable Ints; } • Problems with this approach: • Hardware Lock instruction – Can’t let user do this! Consider following: – Is this a good idea? LockAcquire(); – What about putting a task to sleep? While(TRUE) {;} » What is the interface between the hardware and scheduler? – Real-Time system—no guarantees on timing! » Critical Sections might be arbitrarily long – Complexity? – What happens with I/O or other important events? » Done in the Intel 432 » “Reactor about to meltdown. Help?” » Each feature makes HW more complex and slow 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.7 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.8 Better Implementation of Locks by Disabling Interrupts New Lock Implementation: Discussion • Why do we need to disable interrupts at all? • Key idea: maintain a lock variable and impose mutual – Avoid interruption between checking and setting lock value exclusion only during operations on that variable – Otherwise two threads could think that they both have lock Acquire() { int value = FREE; disable interrupts; if (value == BUSY) { Acquire() { Release() { put thread on wait queue; disable interrupts; disable interrupts; Go to sleep(); Critical if (value == BUSY) { if (anyone on wait queue) { // Enable interrupts? put thread on wait queue; take thread off wait queue } else { Section Go to sleep(); Place on ready queue; value = BUSY; // Enable interrupts? } else { } value = FREE; } else { enable interrupts; } value = BUSY; enable interrupts; } } } • Note: unlike previous solution, the critical section (inside enable interrupts; Acquire()) is very short } – User of lock can take as long as they like in their own critical section: doesn’t impact global machine behavior – Critical interrupts taken in time! 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.9 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.10

Interrupt Re-enable in Going to Sleep Interrupt Re-enable in Going to Sleep • What about re-enabling ints when going to sleep? • What about re-enabling ints when going to sleep? Acquire() { Acquire() { disable interrupts; disable interrupts; if (value == BUSY) { if (value == BUSY) { put thread on wait queue; Enable Position put thread on wait queue; Go to sleep(); Go to sleep(); } else { } else { value = BUSY; value = BUSY; } } enable interrupts; enable interrupts; } } • Before Putting thread on the wait queue?

2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.11 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.12 Interrupt Re-enable in Going to Sleep Interrupt Re-enable in Going to Sleep • What about re-enabling ints when going to sleep? • What about re-enabling ints when going to sleep? Acquire() { Acquire() { disable interrupts; disable interrupts; if (value == BUSY) { if (value == BUSY) { Enable Position put thread on wait queue; Enable Position put thread on wait queue; Go to sleep(); Go to sleep(); } else { } else { value = BUSY; value = BUSY; } } enable interrupts; enable interrupts; } } • Before Putting thread on the wait queue? • Before Putting thread on the wait queue? – Release can check the queue and not wake up thread – Release can check the queue and not wake up thread • After putting the thread on the wait queue

2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.13 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.14

Interrupt Re-enable in Going to Sleep Interrupt Re-enable in Going to Sleep • What about re-enabling ints when going to sleep? • What about re-enabling ints when going to sleep? Acquire() { Acquire() { disable interrupts; disable interrupts; if (value == BUSY) { if (value == BUSY) { Enable Position put thread on wait queue; put thread on wait queue; Go to sleep(); Enable Position Go to sleep(); } else { } else { value = BUSY; value = BUSY; } } enable interrupts; enable interrupts; } } • Before Putting thread on the wait queue? • Before Putting thread on the wait queue? – Release can check the queue and not wake up thread – Release can check the queue and not wake up thread • After putting the thread on the wait queue • After putting the thread on the wait queue – Release puts the thread on the ready queue, but the thread – Release puts the thread on the ready queue, but the thread still thinks it needs to go to sleep still thinks it needs to go to sleep – Misses wakeup and still holds lock (deadlock!) – Misses wakeup and still holds lock (deadlock!) • Want to put it after sleep(). But – how? 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.15 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.16 How to Re-enable After Sleep()? Administrivia • In scheduler, since interrupts are disabled when you call • Midterm Thursday 2/28 sleep: – No class on day of midterm – Responsibility of the next thread to re-enable ints – 8-10PM – no conflict with data science! – When the sleeping thread wakes up, returns to acquire and • Project 1 Design Document due next Wednesday 2/20 re-enables interrupts Thread A Thread B • Project 1 Design reviews upcoming . – High-level discussion of your approach . » What will you modify? disable ints » What algorithm will you use? sleep » How will things be linked together, etc. sleep return enable ints » Do not need final design (complete with all semicolons!) . – You will be asked about testing . » Understand testing framework . » Are there things you are doing that are not tested by tests we give disable int you? sleep • Do your own work! sleep return enable ints – Please do not try to find solutions from previous terms . – We will be on the look out for anyone doing this…today . 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.17 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.18

Atomic Read-Modify-Write Instructions Examples of Read-Modify-Write • test&set (&address) { /* most architectures */ • Problems with previous solution: result = M[address]; // return result from “address” and – Can’t give lock implementation to users M[address] = 1; // set value at “address” to 1 return result; – Doesn’t work well on multiprocessor } » Disabling interrupts on all processors requires messages and • swap (&address, register) { /* x86 */ would be very time consuming temp = M[address]; // swap register’s value to M[address] = register; // value at “address” register = temp; • Alternative: atomic instruction sequences } – These instructions read a value and write a new value atomically • compare&swap (&address, reg1, reg2) { /* 68000 */ if (reg1 == M[address]) { // If memory still == reg1, – Hardware is responsible for implementing this correctly M[address] = reg2; // then put reg2 => memory return success; » on both uniprocessors (not too hard) } else { // Otherwise do not change memory » and multiprocessors (requires help from cache coherence return failure; protocol) } } – Unlike disabling interrupts, can be used on both uniprocessors and • load-linked&store-conditional(&address) { /* R4000, alpha */ multiprocessors loop: ll r1, M[address]; movi r2, 1; // Can do arbitrary computation sc r2, M[address]; beqz r2, loop; } 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.19 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.20 Using of Compare&Swap for queues Implementing Locks with test&set • compare&swap (&address, reg1, reg2) { /* 68000 */ • Another flawed, but simple solution: if (reg1 == M[address]) { M[address] = reg2; int value = 0; // Free return success; Acquire() { } else { return failure; while (test&set(value)); // while busy } } } Release() { value = 0; Here is an atomic add to linked-list function: } addToQueue(&object) { • Simple explanation: do { // repeat until no conflict ld r1, M[root] // Get ptr to current head – If lock is free, test&set reads 0 and sets value=1, so lock is st r1, M[object] // Save link in new object now busy. It returns 0 so while exits. } until (compare&swap(&root,r1,object)); } – If lock is busy, test&set reads 1 and sets value=1 (no change) root next next It returns 1, so while loop continues. – When we set value = 0, someone else can get lock. next • Busy-Waiting: thread consumes cycles while waiting New – For multiprocessors: every test&set() is a write, which makes Object value ping-pong around in cache (using lots of network BW) 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.21 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.22

Problem: Busy-Waiting for Lock Multiprocessor Spin Locks: test&test&set • Positives for this solution • A better solution for multiprocessors: – Machine can receive interrupts int mylock = 0; // Free – User code can use this lock Acquire() { – Works on a multiprocessor do { while(mylock); // Wait until might be free • Negatives } while(test&set(&mylock)); // exit if get lock – This is very inefficient as thread will consume cycles waiting } – Waiting thread may take cycles away from thread holding lock (no one wins!) Release() { mylock = 0; – Priority Inversion: If busy-waiting thread has higher priority } than thread holding lock no progress!  • Simple explanation: • Priority Inversion problem with original Martian rover – Wait until lock might be free (only reading – stays in cache) • For semaphores and monitors, waiting thread may wait for – Then, try to grab lock with test&set an arbitrary long time! – Repeat if fail to actually get lock – Thus even if busy-waiting was OK for locks, definitely not ok • Issues with this solution: for other primitives – Busy-Waiting: thread still consumes cycles while waiting – Homework/exam solutions should avoid busy-waiting! » However, it does not impact other processors!

2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.23 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.24 Better Locks using test&set Recall: Locks using Interrupts vs. test&set • Can we build test&set locks without busy-waiting? Compare to “disable interrupt” solution – Can’t entirely, but can minimize! – Idea: only busy-wait to atomically check lock value int value = FREE; int guard = 0; Acquire() { Release() { int value = FREE; disable interrupts; disable interrupts; if (value == BUSY) { if (anyone on wait queue) { Release() { Acquire() { put thread on wait queue; take thread off wait queue // Short busy‐wait time // Short busy‐wait time Go to sleep(); Place on ready queue; while (test&set(guard)); while (test&set(guard)); } else { if anyone on wait queue { // Enable interrupts? if (value == BUSY) { } else { value = FREE; take thread off wait queue } put thread on wait queue; Place on ready queue; value = BUSY; go to sleep() & guard = 0; enable interrupts; } else { } } } else { value = FREE; enable interrupts; value = BUSY; } } guard = 0; guard = 0; } } Basically we replaced: • Note: sleep has to be sure to reset the guard variable – disable interrupts  while (test&set(guard)); – Why can’t we do it just before or just after the sleep? – enable interrupts  guard = 0; 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.25 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.26

Recap: Locks using interrupts Recap: Locks using test & set int guard = 0; int value = 0; int value = 0; Acquire() { Acquire() { // Short busy-wait time // Short busy-wait time Acquire() { disable interrupts; int value = 0; while(test&set(guard)); disable interrupts; if (value == 1) { Acquire() { if (value == 1) { } put thread on wait-queue; while(test&set(value)); put thread on wait-queue; go to sleep() //?? } go to sleep()& guard = 0; lock.Acquire(); } else { lock.Acquire(); } else { … value = 1; … value = 1; enable interrupts; critical section; critical section; guard = 0; } } … } … } lock.Release(); lock.Release();

Release() { Release() { Release() { Release() { enable interrupts; // Short busy-wait time value = 0; // Short busy-wait time } disable interrupts; } while (test&set(guard)); if anyone on wait queue { if anyone on wait queue { take thread off wait-queue take thread off wait-queue If one thread in critical Place on ready queue; Place on ready queue; section, no other } else { Threads waiting to } else { value = 0; enter critical section value = 0; activity (including OS) } } can run! enable interrupts; busy-wait guard = 0; } }

2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.27 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.28 Higher-level Primitives than Locks Semaphores • Goal of last couple of lectures: • Semaphores are a kind of generalized lock – What is right abstraction for synchronizing threads that – First defined by Dijkstra in late 60s share memory? – Main synchronization primitive used in original UNIX – Want as high a level primitive as possible • Definition: a has a non-negative integer value • Good primitives and practices important! and supports the following two operations: – Since execution is not entirely sequential, really hard to find – P(): an atomic operation that waits for semaphore to become bugs, since they happen rarely positive, then decrements it by 1 – UNIX is pretty stable now, but up until about mid-80s » Think of this as the wait() operation (10 years after started), systems running UNIX would crash – V(): an atomic operation that increments the semaphore by 1, every week or so – concurrency bugs waking up a waiting P, if any • Synchronization is a way of coordinating multiple » This of this as the () operation concurrent activities that are using shared state – Note that P() stands for “proberen” (to test) and V() stands for – This lecture and the next presents a some ways of “verhogen” (to increment) in Dutch structuring sharing

2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.29 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.30

Semaphores Like Integers Except Two Uses of Semaphores • Semaphores are like integers, except (initial value = 1) – No negative values • Also called “Binary Semaphore”. – Only operations allowed are P and V – can’t read or write • Can be used for mutual exclusion: value, except to set it initially semaphore.P(); // Critical section goes here – Operations must be atomic semaphore.V(); » Two P’s together can’t decrement value below zero Scheduling Constraints (initial value = 0) » Similarly, thread going to sleep in P won’t miss wakeup from V – • Allow thread 1 to wait for a signal from thread 2 even if they both happen at same time – thread 2 schedules thread 1 when a given event occurs • Semaphore from railway analogy • Example: suppose you had to implement ThreadJoin which – Here is a semaphore initialized to 2 for resource control: must wait for thread to terminate: Initial value of semaphore = 0 ThreadJoin { semaphore.P(); } ThreadFinish { Value=2Value=1Value=0 semaphore.V(); } 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.31 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.32 Producer-Consumer with a Bounded Buffer Correctness constraints for solution • Correctness Constraints: Producer Buffer Consumer – Consumer must wait for producer to fill buffers, if none full • Problem Definition (scheduling constraint) – Producer puts things into a shared buffer – Producer must wait for consumer to empty buffers, if all full – Consumer takes them out (scheduling constraint) – Need synchronization to coordinate producer/consumer – Only one thread can manipulate buffer queue at a time (mutual exclusion) • Don’t want producer and consumer to have to work in • Remember why we need mutual exclusion lockstep, so put a fixed-size buffer between them – Because computers are stupid – Need to synchronize access to this buffer – Producer needs to wait if buffer is full – Imagine if in real life: the delivery person is filling the machine and somebody comes up and tries to stick their money into the – Consumer needs to wait if buffer is empty machine • General rule of thumb: • Example 1: GCC compiler Use a separate semaphore for each constraint – cpp | cc1 | cc2 | as | ld – Semaphore fullBuffers; // consumer’s constraint • Example 2: Coke machine – Semaphore emptyBuffers;// producer’s constraint – Producer can put limited number of Cokes in machine – Consumer can’t take Cokes out if machine is empty – Semaphore mutex; // mutual exclusion 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.33 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.34

Full Solution to Bounded Buffer Discussion about Solution Semaphore fullSlots = 0; // Initially, no coke Decrease # of Increase # of Semaphore emptySlots = bufSize; • Why asymmetry? empty slots occupied slots // Initially, num empty slots – Producer does: emptyBuffer.P(), fullBuffer.V() Semaphore mutex = 1; // No one using machine – Consumer does: fullBuffer.P(), emptyBuffer.V()

Producer(item) { Decrease # of Increase # of emptySlots.P(); // Wait until space occupied slots empty slots mutex.P(); // Wait until machine free Enqueue(item); mutex.V(); Producer(item) { fullSlots.V(); // Tell consumers there is • Is order of P’s important? mutex.P(); // more coke – Yes! Can cause deadlock emptySlots.P(); } Enqueue(item); • Is order of V’s important? mutex.V(); Consumer() { fullSlots.V(); fullSlots.P(); // Check if there’s a coke – No, except that it might } mutex.P(); // Wait until machine free affect scheduling efficiency Consumer() { item = Dequeue(); fullSlots.P(); mutex.V(); • What if we have 2 producers mutex.P(); emptySlots.V(); // tell producer need more or 2 consumers? item = Dequeue(); mutex.V(); return item; – Do we need to change anything? } emptySlots.V(); return item; } 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.35 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.36 Motivation for Monitors and Condition Variables Monitor with Condition Variables • Semaphores are a huge step up; just think of trying to do the bounded buffer with only loads and stores – Problem is that semaphores are dual purpose: » They are used for both mutex and scheduling constraints » Example: the fact that flipping of P’s in bounded buffer gives deadlock is not immediately obvious. How do you prove correctness to someone? • Cleaner idea: Use locks for mutual exclusion and condition variables for scheduling constraints • Lock: the lock provides mutual exclusion to shared data – Always acquire before accessing shared data structure • Definition: Monitor: a lock and zero or more condition – Always release after finishing with shared data variables for managing concurrent access to shared data – Lock initially free – Some languages like Java provide this natively • Condition Variable: a queue of threads waiting for something – Most others use actual locks and condition variables 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 – Contrast to semaphores: Can’t wait inside critical section 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.37 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.38

Simple Monitor Example (version 1) Condition Variables • Here is an (infinite) synchronized queue • How do we change the RemoveFromQueue() routine to wait Lock lock; until something is on the queue? Queue queue; – Could do this by keeping a count of the number of things on the queue (with semaphores), but error prone AddToQueue(item) { • Condition Variable: a queue of threads waiting for something lock.Acquire(); // Lock shared data inside a critical section queue.enqueue(item); // Add item lock.Release(); // Release Lock – Key idea: allow sleeping inside critical section by atomically } releasing lock at time we go to sleep – Contrast to semaphores: Can’t wait inside critical section RemoveFromQueue() { lock.Acquire(); // Lock shared data • Operations: item = queue.dequeue();// Get next item or null – Wait(&lock): Atomically release lock and go to sleep. Re- lock.Release(); // Release Lock acquire lock later, before returning. return(item); // Might return null } – Signal(): Wake up one waiter, if any • Not very interesting use of “Monitor” – Broadcast(): Wake up all waiters – It only uses a lock with no condition variables • Rule: Must hold lock when doing condition variable ops! – In Birrell paper, he says can perform signal() outside of lock – – Cannot put consumer to sleep if no work! IGNORE HIM (this is only an optimization)

2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.39 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.40 Complete Monitor Example (with condition variable) Mesa vs. Hoare monitors • Here is an (infinite) synchronized queue • Need to be careful about precise definition of signal and wait. Lock lock; Consider a piece of our dequeue code: Condition dataready; while (queue.isEmpty()) { Queue queue; dataready.wait(&lock); // If nothing, sleep } item = queue.dequeue(); // Get next item AddToQueue(item) { – Why didn’t we do this? lock.Acquire(); // Get Lock queue.enqueue(item); // Add item if (queue.isEmpty()) { dataready.signal(); // Signal any waiters dataready.wait(&lock); // If nothing, sleep lock.Release(); // Release Lock } } item = queue.dequeue(); // Get next item • Answer: depends on the type of scheduling RemoveFromQueue() { – Hoare-style (most textbooks): lock.Acquire(); // Get Lock » Signaler gives lock, CPU to waiter; waiter runs immediately while (queue.isEmpty()) { dataready.wait(&lock); // If nothing, sleep » Waiter gives up lock, processor back to signaler when it exits } critical section or if it waits again item = queue.dequeue(); // Get next item – Mesa-style (most real operating systems): lock.Release(); // Release Lock » Signaler keeps lock and processor return(item); } » Waiter placed on ready queue with no special priority » Practically, need to check condition again after wait 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.41 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.42

Summary (1/2) Summary (2/2) • Important concept: Atomic Operations • Semaphores: Like integers with restricted interface – An operation that runs to completion or not at all – Two operations: – These are the primitives on which to construct various »P(): Wait if zero; decrement when becomes non-zero synchronization primitives »V(): Increment and wake a sleeping task (if exists) • Talked about hardware atomicity primitives: » Can initialize value to any non-negative value – Disabling of Interrupts, test&set, swap, compare&swap, – Use separate semaphore for each constraint load-locked & store-conditional • Monitors: A lock plus one or more condition variables • Showed several constructions of Locks – Always acquire lock before accessing shared data – Must be very careful not to waste/tie up machine resources – Use condition variables to wait inside critical section » Shouldn’t disable interrupts for long » Three Operations: Wait(), Signal(), and Broadcast() » Shouldn’t spin wait for long – Key idea: Separate lock variable, use hardware mechanisms to protect modifications of that variable

2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.43 2/14/19 Kubiatowicz CS162 ©UCB Spring 2019 Lec 8.44