Synchronization: Locks, Semaphores
Total Page:16
File Type:pdf, Size:1020Kb
Review: Too Much Milk Solution #3 • Here is a possible two-note solution: CS162 Thread 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 lock 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 sleep 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