
Review: Synchronization problem with Threads CS162 • One thread per transaction, each running: Operating Systems and Deposit(acctId, amount) { acct = GetAccount(actId); /* May use disk I/O */ Systems Programming acct->balance += amount; Lecture 8 StoreAccount(acct); /* Involves disk I/O */ } • Unfortunately, shared state can get corrupted: Semaphores, Monitors, and Thread 1 Thread 2 Readers/Writers load r1, acct->balance load r1, acct->balance add r1, amount2 February 18th, 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 2/18/15 Kubiatowicz CS162 ©UCB Spring 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 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.3 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.4 Recall: Better Implementation of Locks Goals for Today by Disabling Interrupts • Continue with Synchronization Abstractions • Key idea: maintain a lock variable and impose mutual exclusion only during operations on that variable – Semaphores, Monitors, and Condition variables int mylock = FREE; • Readers-Writers problem and solution Acquire(&mylock) – wait until lock is free, then grab • Introduction to scheduling Release(&mylock) – Unlock, waking up anyone waiting Acquire(int *lock) { Release(int *lock) { disable interrupts; disable interrupts; if (*lock == BUSY) { if (anyone on wait queue) { put thread on wait queue; take thread off wait queue Go to sleep(); Place on ready queue; } else { // Enable interrupts? *lock = FREE; } else { } *lock = BUSY; enable interrupts; } } Note: Some slides and/or pictures in the following are enable interrupts; adapted from slides ©2005 Silberschatz, Galvin, and GagneGagne. } Many slides generated from my lecture notes by Kubiatowicz. • Really only works in kernel – why? 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.5 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.6 Recall: How to Re-enable After Sleep()? Review: Examples of Read-Modify-Write • test&set (&address) { /* most architectures */ • Interrupts are disabled when you call sleep: result = M[address]; – Responsibility of the next thread to re-enable ints M[address] = 1; return result; – When the sleeping thread wakes up, returns to acquire } and re-enables interrupts • swap (&address, register) { /* x86 */ temp = M[address]; Thread A Thread B M[address] = register; . register = temp; . } disable ints • compare&swap (&address, reg1, reg2) { /* 68000 */ sleep if (reg1 == M[address]) { sleep return M[address] = reg2; return success; enable ints } else { . return failure; . } . } disable int • load-linked&store conditional(&address) { sleep /* R4000, alpha */ loop: sleep return ll r1, M[address]; enable ints movi r2, 1; /* Can do arbitrary comp */ . sc r2, M[address]; . beqz r2, loop; • Why must Interrupts be disabled during context switch? } 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.7 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.8 Implementing Locks with test&set Problem: Busy-Waiting for Lock • A flawed, but simple solution (that works at user-level!) • Positives for this solution – Machine can receive interrupts int mylock = 0; // Free – User code can use this lock Acquire() { while (test&set(&mylock)); // while busy – Works on a multiprocessor } • Negatives Release() { – This is very inefficient because the busy-waiting mylock = 0; thread will consume cycles waiting } – Waiting thread may take cycles away from thread • Simple explanation: holding lock (no one wins!) – If lock is free, test&set reads 0 and sets value=1, so lock – Priority Inversion: If busy-waiting thread has higher is now busy. It returns 0 so while exits. priority than thread holding lock no progress! – If lock is busy, test&set reads 1 and sets value=1 (no • Priority Inversion problem with original Martian rover change). It returns 1, so while loop continues • For semaphores and monitors, waiting thread may – When we set value = 0, someone else can get lock wait for an arbitrary length of time! • Issues with this solution – Thus even if busy-waiting was OK for locks, definitely not ok for other primitives – Busy-Waiting: thread consumes cycles while waiting – Homework/exam solutions should not have busy-waiting! – Does not take advantage of multi-core/processor caches! 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.9 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.10 Multiprocessor Spin Locks: test&test&set Better Locks using test&set • A better solution for multiprocessors: • Can we build test&set locks without busy-waiting? int mylock = 0; // Free – Can’t entirely, but can minimize! Acquire() { – Idea: only busy-wait to atomically check lock value do { int guard = 0; int mylock = FREE; while(mylock); // Wait until might be free } while(test&set(&mylock)); // exit if get lock Acquire(&mylock) – wait until lock is free, then grab Release(&mylock) – Unlock, waking up anyone waiting } Acquire(int *lock) { Release(int *lock) { Release() { // Short busy-wait time // Short busy-wait time mylock = 0; while (test&set(&guard)); while (test&set(&guard)); } if (*lock == BUSY) { if anyone on wait queue { • Simple explanation: put thread on wait queue; take thread off wait queue – Wait until lock might be free (only reading – stays in cache) go to sleep() & guard = 0; Place on ready queue; } else { } else { – Then, try to grab lock with test&set *lock = FREE; *lock = BUSY; – Repeat if fail to actually get lock } guard = 0; guard = 0; • Issues with this solution: } – Busy-Waiting: thread still consumes cycles while waiting } » However, it does not impact other processors! • Note: sleep has to be sure to reset the guard variable – Why can’t we do it just before or just after the sleep? 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.11 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.12 Administrivia Using of Compare&Swap for queues • compare&swap (&address, reg1, reg2) { /* 68000 */ • First Checkpoint due this Friday 11:59pm PST if (reg1 == M[address]) { – Yes this is graded! M[address] = reg2; return success; – Assume design document is high level! } else { » You should think of this as a document for a manager return failure; (your TA) } } • Do your own work! – Please do not try to find solutions from previous terms Here is an atomic add to linked-list function: – We will be look out for this… addToQueue(&object) { do { // repeat until no conflict • Basic semaphores work in PintOS! ld r1, M[root] // Get ptr to current head – However, you will need to implement priority scheduling st r1, M[object] // Save link in new object behavior both in semaphore and ready queue } until (compare&swap(&root,r1,object)); } • Still could use more folks in Thursday 12-1 and root next next Friday 10-1 sections! – Much better next – Try to attend the section with your project TA…? New Object 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.13 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.14 Higher-level Primitives than Locks Semaphores • Goal of last couple of lectures: • Semaphores are a kind of generalized lock – What is the right abstraction for synchronizing threads – First defined by Dijkstra in late 60s that share memory? – Main synchronization primitive used in original UNIX – Want as high a level primitive as possible • Definition: a Semaphore has a non-negative integer • Good primitives and practices important! value and supports the following two operations: – Since execution is not entirely sequential, really hard to – P(): an atomic operation that waits for semaphore to find bugs, since they happen rarely become 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 – V(): an atomic operation that increments the semaphore crash every week or so – concurrency bugs by 1, waking up a waiting P, if any • Synchronization is a way of coordinating multiple » This of this as the signal() operation concurrent activities that are using shared state – Note that P() stands for “proberen” (to test) and V() stands for “verhogen” (to increment) in Dutch – This lecture and the next presents a couple of ways of structuring the sharing 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.15 2/18/15 Kubiatowicz CS162 ©UCB Spring 2015 Lec 8.16 Semaphores Like Integers Except Two Uses of Semaphores • Semaphores are like integers, except • Mutual Exclusion (initial value = 1) – No negative values – Also called “Binary Semaphore”.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages15 Page
-
File Size-