CPS110: Deadlock Landon Cox February 4, 2009 Concurrency so far Mostly tried to constrain orderings Locks, monitors, semaphores It is possible to over-constrain too A must run before B B must run before A This can lead to deadlock Definitions Resource Thing needed by a thread to do its job Threads wait for resources E.g. locks, disk space, memory, CPU Deadlock Circular waiting for resources Leaves threads unable to make progress Example deadlock Both CPS108 and CPS110 are full You want to switch from 110 to 108 Someone else wants to switch from 108 to 110 Algorithm for switching Wait for spot in new class to open up Add new class Drop old class Problem: must add before dropping Deadlock and starvation Deadlock starvation Starvation = one process waits forever Thread A Thread B lock (x) lock (y) … unlock (y) unlock (x) lock (y) lock (x) … unlock(x) unlock(y) Can deadlock occur? Deadlock and starvation Deadlock starvation Starvation = one process waits forever Thread A Thread B lock (x) lock (y) lock (y) // wait for B lock (x) // wait for A WillCan deadlock deadlock always occur? occur? Common thread work pattern Phase 1. Phase 2. while (not done) { get some resources (block if necessary) work // assume finite } release all resources Has anyone taken CPS116 (databases)? This is called “two-phase” locking Ensures “conflict-serializability” Still deadlock-prone Dining philosophers Philosopher algorithm 1) Wait for right chopstick 2) Pick up right chopstick 3) Wait for left chopstick 4) Pick up left chopstick 5) Eat 6) Put both chopsticks down How can deadlock occur? A E B D C Conditions for deadlock 1. Limited resource Not enough for all threads simultaneously 2. Hold-and-wait Hold one resource, while waiting for another 3. No pre-emption Cannot force threads to give up resources 4. Circular chain of requests Circular chain of requests Arrows Thread resource it’s waiting for Resource thread that’s holding it Thread A Resource 1 Thread B acquires Resource 1 Thread B waits for Resource 2 Thread A acquires Resource 2 Thread A waits for Resource 1 Resource 2 Thread B Called a wait-for graph Deadlock What should we do about them? 1. Ignore them Common OS-level solution for programs (OS has no control over user programs) 2. Detect-and-fix 3. Prevent Detect-and-fix First part: detect This is easy; just scan the wait-for graph Second part: fix 1. Kill first thread, take back resources by force Why might this be unsafe? Can expose inconsistent state 2. Roll-back actions of 1 or more thread, retry Often used in databases Not always possible (some actions can’t be undone) E.g. can’t unsend a network message Detect-and-fix Retrying during fix phase can be tricky If holding R and will wait for L, drop R and try again. What could happen? Everyone picks up R Everyone drops R Everyone picks up R Everyone drops R (and so on) This is called “livelock.” A E B D C Detect-and-fix Retrying during fix phase can be tricky If holding R and will wait for L, drop R and try again. How to prevent livelock? Choose a winner. How to choose a winner? E First to start/pick up? What if we have priorities? (called “priority inversion”) e.g. Queen waiting for joker Should Queen always win? No, could starve the joker A B D C Deadlock prevention Idea: remove one of four pre-conditions 1. Make resources unlimited Best solution, but often impossible Can still increase number, reduce likelihood (larger 108 and 110 classes) Not clear this works for locks Deadlock prevention 2. Eliminate hold-and-wait Idea: move resource acquisition to beginning Phase 1a. get all resources Phase 1b. while (not done) { work // assume finite } Phase 2. release all resources Two ways to avoid holding while waiting Eliminating hold-and-wait 1. Wait for everything to be free, then grab them all at once Philosopher algorithm lock while (left chopstick busy || right chopstick busy) { wait } pick up right chopstick pick up left chopstick unlock eat lock drop chopsticks unlock Any potential problems? Can induce starvation (neighbors alternate eating). Eliminating hold-and-wait 2. If you find a resource busy, drop everything, and try again Philosopher algorithm lock while (1) { while (right chopstick busy) { wait } pick up right chopstick if (left chopstick busy) { drop right chopstick } else { pick up left chopstick break } } unlock eat lock drop chopsticks unlock Issues? Must predict what you need in advance. Hold reservations longer than needed Requires changes to applications. Deadlock prevention 3. Enable pre-emption Can pre-empt the CPU (context switch) Can pre-empt memory (swap to disk) Not everything can be pre-empted Can locks be pre-empted? Probably not Must ensure that data is in consistent state Deadlock prevention 4. Eliminate circular chain of requests How can we get rid of these cycles? Impose an ordering on resource acquisition E.g. must always acquire A before B In dining philosophers? Number chopsticks Pick up lower-numbered chopstick first Dining philosophers Philosopher algorithm 1) Wait for lower chopstick 2) Pick up lower chopstick 3) Wait for higher chopstick 4) Pick up higher chopstick 5) Eat 6) Put both chopsticks down A 1 2 E B 5 Try to create a deadlock 3 D C 4 Universal ordering Why does this work? At some point in time T holds highest-numbered acquired resource T is guaranteed to make progress. Why? If T needs a higher-numbered resource, it must be free If T needs a lower-numbered resource, it already has it If T can make progress, it will eventually release all of its resources What if another thread acquires a higher-numbered resource? They just become T (in which case, the same reasoning as above holds) Dining philosophers Philosopher algorithm 1) Wait for lower chopstick 2) Pick up lower chopstick 3) Wait for higher chopstick 4) Pick up higher chopstick 5) Eat 6) Put both chopsticks down A 1 2 E B 5 What if E only needs one chopstick? Only have trouble if E acquires lowernumbered chopstick before 5. (but this is a violation of our rule) 3 D C 4 Deadlock avoidance Issue with imposing global orderings Must change the application Common technique Requires programming discipline Course administration Project 1 Due February 18th (two weeks) 6 groups are done with 1d No one has submitted 1t yet (!) Should be done with 1d by end of the week 1t is a lot more work, don’t keep putting it off! Compiling g++ -o disk disk.cc thread.o Course administration Remember to work on your thread library test suite too For each of your test cases Compare output of your library with thread.o Writing test cases Read through spec Write test cases to stress each required part E.g. lock() blocks if lock is already held Use yield() to create the right interleavings Read through your code Write test cases to exercise all lines of code E.g. each clause of an if/else statement Micro-tests are better for debugging than macro-tests Banker’s algorithm Like acquiring all resources first (but more efficient) Phase 1a. state max resources needed Phase 1b. while (not done) { get some resources (blocking if not SAFE) work // assume finite } Phase 2. release all resources Comparing banker’s algorithm Original Acquired resources if available (deadlock) Previous solution (no “hold-and-wait”) Acquired all resources or none Must acquire max resources to do work All threads doing work own their max Thus, all threads doing work can complete Banker’s algorithm Give out resources at request time (1b) Request granted if it is safe Can grant max requests of all threads in some sequential order Sequential order One thread gets its max resources, finishes, and releases its resources Another thread gets its max resources, finishes, and releases, etc Phase 1a. state max resources needed Phase 1b. while (not done) { get some resources (blocking if not SAFE) work // assume finite } Phase 2. release all resources Example Bank Has $6000 to loan Customers Establish credit limits (max resources) Borrow money (up to their limit) Return all money at the end Example: Solution 1 Bank gives money on request, if available For example Ann asks for credit line of $2000 Bob asks for credit line of $4000 Cat asks for credit line of $6000 Can bank approve each of these lines? (assuming it gives money, if available) Example: Solution 1 No. Consider: Ann takes out $1000 (bank has $5000) Bob takes out $2000 (bank has $3000) Cat takes out $3000 (bank is empty) Bank has no money Ann, Bob, and Cat could all ask for $ None would be able to finish (deadlock) Example: Solution 1 This only works if we wait to approve credit lines For example Ann asks for credit line of $2000 Bank approves Bob asks for credit line of $4000 Bank approves Cat asks for credit line of $6000 Bank must make Cat wait until Ann or Bob finish Sum of all max resource needs of all current threads Must not exceed the total resources Example: Solution 2 Bank says ok to all credit line requests Customers may wait for resources Bank ensures no deadlocks For example Ann asks for credit limit of $2000 Bob asks for credit limit of $4000 Cat asks for credit limit of $6000 Bank approves all credit limits Example: Solution 2 Ann takes out $1000 (bank has $5000) Bob takes out $2000 (bank has $3000) Cat wants to take out $2000 Is this allowed? Bank would be left with $1000 Ann would still be guaranteed to finish (could take out another $1000) On finish, bank would have $2k This is enough to ensure that Bob can finish Example: Solution 2 Ann takes out $1000 (bank has $5000) Bob takes out $2000 (bank has $3000) Cat wants to take out $2500 Is this allowed? Bank would be left with $500 Can’t guarantee that any threads will finish Banker’s algorithm Allows resources to be overcommitted How can we apply this to dining philosophers? Put all chopsticks in the middle of the table Max resource need for each philosopher is 2 Grant requests, unless There is only one chopstick left and nobody has 2 Nice algorithm, but few people use it. Why? You rarely know what you’ll need in advance Doesn’t really make sense for locks (no “generic lock”)