Class 6 CS 202 17 February 2015 On the board ------------ 1. Last time 2. Practice with concurrent programming 3. Implementation of locks: spinlocks, mutexes --------------------------------------------------------------------------- 1. Last time condition variables monitors standards for concurrent programming advice on concurrent programming teaser question for today: how are locks/mutexes implemented? 2. Practice with concurrent programming --sleeping barber question posted (as today's reading). use it as practice if you haven't already. (practice = trying it on your own WITHOUT looking at the solution.) --we guarantee to test concurrent programming in this course --today, we work a different example: --workers interact with a database --motivation: banking, airlines, etc. --readers never modify database --writers read and modify data --using only a single mutex lock would be overly restrictive. Instead, want --many readers at the same time --only one writer at a time --let's follow the concurrency advice ..... 1. Getting started a. what are units of concurrency? [readers/writers] b. what are shared chunks of state? [database] c. what does the main function look like? read() check in -- wait until no writers access DB check out -- wake up waiting writer, if appropriate write() check in -- wait until no readers or writers access DB check out -- wake up waiting readers or writers 2. and 3. Synchronization constraints and objects --reader can access DB when no writers (condition: okToRead) --writer can access DB when no other readers or writers (condition: okToWrite) --only one thread manipulates shared variables at a time. NOTE: **this does not mean only one thread in the DB at a time** (mutex) 4. write the methods --inspiration required: int AR = 0; // # active readers int AW = 0; // # active writers int WR = 0; // # waiting readers int WW = 0; // # waiting writers --see handout for the code --QUESTION: why not just hold the lock all the way through "Execute req"? (Answer: the whole point was to expose more concurrency, i.e., to move away from exclusive access.) --QUESTION: what if we had shared locks? The implementation of shared locks is given on the handout 3. Recap last lecture and a half --what is concurrency? --strategies for managing it: --provide atomicity --more generally, protect critical sections. how? Mutexes! (and below we will see how mutexes themselves are implemented) --another synchronization primitive: condition variables! conceptually is a queue a bit confusing, but: --a waiter is waiting for something to happen --a signaler expresses that that something has happened --unfortunately for programmability, the waiter has to be prepared to wake up even if nothing happened (or if something happened, but the core condition is no longer true) --both mutexes and condition variables are designed when multiple execution contexts (usually threads) share memory. --a good way to express programs in this environment is with monitors --we covered standards, advice, and practice for programming with monitors 4. Implementation of mutexes Going to continue to assume sequential consistency... How might we provide the lock()/unlock() abstraction? (a) Peterson's algorithm.... --...solves critical section in that it satisfies mutual exclusion, progress, bounded waiting --but expensive (busy waiting), requires number of threads to be fixed statically, and assumes sequential consistency --(see a textbook) (b) disable interrupts? --works only on a single CPU --cannot expose to user processes (c) spinlocks --see handout * buggy approach: what's wrong with this? * non-buggy approach: why does this work? --works in multi-CPU environment --but issue: a spinlock is no good for cases when time-to-acquire-lock expected to be long (for example, waiting for disk accesses to complete). this is because of busy waiting and the fact that waiting chews cycles that could have been spent on another task (in the kernel or in user space). --for more about spinlocks in Linux, see: https://www.kernel.org/doc/Documentation/locking/spinlocks.txt --NOTE: the spinlocks that we presented (test-and-set, or test-and-test-and-set) can introduce performance issues when there is a lot of contention. These performance issues arise even if the programmer is using spinlocks correctly. The performance issues result from cross-talk among CPUs (which undermines caching and generates traffic on the memory bus). If we have time later, we will study a remediation of this issue (or search the Web for "MCS locks"). --In everyday application-level programming, spinlocks will not be something you use. Mainly matters inside kernel. But you should know what these are for technical literacy, and to see where the mutual exclusion is truly enforced on modern hardware. (d) mutexes: spinlock + a queue --textbook describes one implementation --see handout for another [thanks to Mike Dahlin for content in portions of this lecture.]