
Review: Synchronization problem with Threads CS162 • One thread per transaction, each running: Operating Systems and Deposit(acctId, amount) { Systems Programming acct = GetAccount(actId); /* May use disk I/O */ acct->balance += amount; Lecture 8 StoreAccount(acct); /* Involves disk I/O */ } Locks, Semaphores, Monitors, • Unfortunately, shared state can get corrupted: and Thread 1 Thread 2 load r1, acct->balance Quick Intro to Scheduling load r1, acct->balance add r1, amount2 September 23rd, 2015 store r1, acct->balance add r1, amount1 Prof. John Kubiatowicz store r1, acct->balance http://cs162.eecs.Berkeley.edu • Atomic Operation: an operation that always runs to completion or not at all – It is indivisible: it cannot be stopped in the middle and state cannot be modified by someone else in the middle 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.2 Review: Too Much Milk Solution #3 Review: Too Much Milk: Solution #4 • Here is a possible two-note solution: • Suppose we have some sort of implementation of a Thread A Thread B lock (more in a moment). leave note A; leave note B; – Acquire(&mylock) – wait until lock is free, then grab while (note B) {\\X if (noNote A) {\\Y – Release(&mylock) – Unlock, waking up anyone waiting do nothing; if (noMilk) { } buy milk; – These must be atomic operations – if two threads are if (noMilk) { } waiting for the lock and both see it’s free, only one buy milk; } succeeds to grab the lock } remove note B; remove note A; • Then, our milk problem is easy: • Does this work? Yes. Both can guarantee that: Acquire(&milklock); – It is safe to buy, or if (nomilk) – Other will buy, ok to quit buy milk; Release(&milklock); • At X: – if no note B, safe for A to buy, • Once again, section of code between Acquire() and Release() called a “Critical Section” – otherwise wait to find out what will happen • Of course, you can make this even simpler: suppose • At Y: you are out of ice cream instead of milk – if no note A, safe for B to buy – Skip the test since you always need more ice cream. – Otherwise, A is either buying or waiting for B to quit 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.3 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.4 Goals for Today How to implement Locks? • Lock: prevents someone from doing something • Explore several implementations of locks – Lock before entering critical section and • Continue with Synchronization Abstractions before accessing shared data – Semaphores, Monitors, and Condition variables – Unlock when leaving, after accessing shared data – Wait if locked • Very Quick Introduction to scheduling » Important idea: all synchronization involves waiting » Should sleep if waiting for a long time • Atomic Load/Store: get solution like Milk #3 – Looked at this last lecture – Pretty complex and error prone • Hardware Lock instruction – Is this a good idea? – What about putting a task to sleep? » How do you handle the interface between the hardware and scheduler? Note: Some slides and/or pictures in the following are – Complexity? adapted from slides ©2005 Silberschatz, Galvin, and GagneGagne. » Done in the Intel 432 Many slides generated from my lecture notes by Kubiatowicz. » Each feature makes hardware more complex and slow 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.5 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.6 Naïve use of Interrupt Enable/Disable Better Implementation of Locks by Disabling Interrupts • How can we build multi-instruction atomic operations? • Key idea: maintain a lock variable and impose mutual – Recall: dispatcher gets control in two ways. exclusion only during operations on that variable » Internal: Thread does something to relinquish the CPU » External: Interrupts cause dispatcher to take CPU – On a uniprocessor, can avoid context-switching by: int value = FREE; » Avoiding internal events (although virtual memory tricky) » Preventing external events by disabling interrupts Acquire() { Release() { • Consequently, naïve Implementation of locks: disable interrupts; disable interrupts; if (value == BUSY) { if (anyone on wait queue) { LockAcquire { disable Ints; } put thread on wait queue; take thread off wait queue LockRelease { enable Ints; } Go to sleep(); Place on ready queue; • Problems with this approach: // Enable interrupts? } else { } else { value = FREE; – Can’t let user do this! Consider following: } LockAcquire(); value = BUSY; enable interrupts; While(TRUE) {;} } } – Real-Time system—no guarantees on timing! enable interrupts; » Critical Sections might be arbitrarily long } – What happens with I/O or other important events? » “Reactor about to meltdown. Help?” 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.7 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.8 New Lock Implementation: Discussion Interrupt re-enable in going to sleep • Why do we need to disable interrupts at all? • What about re-enabling ints when going to sleep? – Avoid interruption between checking and setting lock value Acquire() { – Otherwise two threads could think that they both have lock disable interrupts; if (value == BUSY) { Acquire() { Enable Position disable interrupts; Enable Position put thread on wait queue; if (value == BUSY) { Enable Position Go to sleep(); put thread on wait queue; } else { Go to sleep(); value = BUSY; // Enable interrupts? Critical } } else { Section enable interrupts; value = BUSY; } } • Before Putting thread on the wait queue? enable interrupts; – Release can check the queue and not wake up thread } • After putting the thread on the wait queue • Note: unlike previous solution, the critical section – Release puts the thread on the ready queue, but the (inside Acquire()) is very short thread still thinks it needs to go to sleep – User of lock can take as long as they like in their own critical section: doesn’t impact global machine behavior – Misses wakeup and still holds lock (deadlock!) – Critical interrupts taken in time! • Want to put it after sleep(). But – how? 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.9 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.10 How to Re-enable After Sleep()? Administrivia • In scheduler, since interrupts are disabled when you • First Checkpoint due this Friday 11:59pm PST call sleep: – Yes this is graded! – Responsibility of the next thread to re-enable ints – Assume design document is high level! – When the sleeping thread wakes up, returns to acquire » You should think of this as a document for a manager (your TA) and re-enables interrupts • Design review Thread A Thread B – High-level discussion of your approach . » What will you modify? . » What algorithm will you use? disable ints » How will things be linked together, etc. sleep » Do not need final design (complete with all semicolons!) sleep return – You will be asked about testing enable ints » Understand testing framework » Are there things you are doing that are not tested by the tests we . give you? . • Do your own work! disable int – Please do not try to find solutions from previous terms sleep – We will be look out for this… sleep return • Basic semaphores work in PintOS! enable ints – However, you will need to implement priority scheduling behavior . both in semaphore and ready queue . 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.11 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.12 Atomic Read-Modify-Write instructions Examples of Read-Modify-Write • test&set (&address) { /* most architectures */ • Problems with previous solution: result = M[address]; M[address] = 1; – Can’t give lock implementation to users return result; } – Doesn’t work well on multiprocessor • swap (&address, register) { /* x86 */ temp = M[address]; » Disabling interrupts on all processors requires messages M[address] = register; and would be very time consuming register = temp; } • Alternative: atomic instruction sequences • compare&swap (&address, reg1, reg2) { /* 68000 */ – These instructions read a value from memory and write if (reg1 == M[address]) { M[address] = reg2; a new value atomically return success; } else { – Hardware is responsible for implementing this correctly return failure; » on both uniprocessors (not too hard) } } » and multiprocessors (requires help from cache coherence • load-linked&store conditional(&address) { protocol) /* R4000, alpha */ loop: – Unlike disabling interrupts, can be used on both ll r1, M[address]; uniprocessors and multiprocessors movi r2, 1; /* Can do arbitrary comp */ sc r2, M[address]; beqz r2, loop; } 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.13 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.14 Using of Compare&Swap for queues Implementing Locks with test&set • compare&swap (&address, reg1, reg2) { /* 68000 */ if (reg1 == M[address]) { • Another flawed, but simple solution: M[address] = reg2; return success; int value = 0; // Free } else { return failure; Acquire() { } while (test&set(value)); // while busy } } Release() { Here is an atomic add to linked-list function: value = 0; addToQueue(&object) { } do { // repeat until no conflict • Simple explanation: ld r1, M[root] // Get ptr to current head st r1, M[object] // Save link in new object – If lock is free, test&set reads 0 and sets value=1, so } until (compare&swap(&root,r1,object)); lock is now busy. It returns 0 so while exits. } root next next – If lock is busy, test&set reads 1 and sets value=1 (no change). It returns 1, so while loop continues next – When we set value = 0, someone else can get lock New • Busy-Waiting: thread consumes cycles while waiting Object 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015 Lec 8.15 9/23/15 Kubiatowicz CS162 ©UCB Fall 2015
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages12 Page
-
File Size-