Deadlock - University of Cincinnatigauss.ececs.uc.edu/Courses/c4029/code/deadlock/deadlock.pdf ·...

Preview:

Citation preview

Deadlock

DeadlockSee 00-deadlock.c for an example

See Deadlock1/Deadlock.java to play around with this idea

See Deadlock2/DeadLock.java for a java version of 00-deadlock.c

See Deadlock2/DeadlockFixed.java for a fix

See 01-deadlock.c for a try at fixing 00-deadlock.c the same way

DeadlockFour conditions need to hold for a deadlock to occur:

• Mutual exclusion: threads claim exclusive control of resources that they require (e.g., a thread grabs a lock).

• Hold-and-wait: threads hold resources allocated to them (e.g., locks that they have already acquired) while waiting for additional resources (e.g., locks that they wish to acquire).

• No preemption: resources (e.g., locks) cannot be forcibly removed from threads that are holding them.

• Circular wait: there exists a circular chain of threads such that each thread holds one or more resources (e.g., locks) that are being requested by the next thread in the chain.

If any of these four conditions are not met, deadlock cannot occur.

DeadlockPrevention - circular wait:

• provide a total ordering on lock acquisition

• example: if there are three locks, L1, L2, and L3, in the system, deadlock may occur if three threads each need two locks to finish

L1,L2 L2,L3

L3,L1 Deadlock since A has L2 which isneeded by B; B has L3 which isneeded by C; C has L1 which isneeded by A

A

C

B

DeadlockPrevention - circular wait:

• provide a total ordering on lock acquisition

• example: if there are three locks, L1, L2, and L3, in the system, deadlock will not occur if L1 is acquired first, then L2, then L3

L1,L2 L2,L3

L3,L1 No deadlock if L1 is alwaysacquired first, then L2, then L3

C does not get L1 because A has itdoes not ask for L3

A

C

B

DeadlockPrevention - circular wait:

• provide a total ordering on lock acquisition

• example: if there are three locks, L1, L2, and L3, in the system, deadlock will not occur if L1 is acquired first, then L2, then L3

L1,L2 L2,L3

L3,L1 No deadlock if L1 is alwaysacquired first, then L2, then L3

B get L3, C still waiting, A still needsL2

A

C

B

DeadlockPrevention - circular wait:

• provide a total ordering on lock acquisition

• example: if there are three locks, L1, L2, and L3, in the system, deadlock will not occur if L1 is acquired first, then L2, then L3

L1,L2

L3,L1 No deadlock if L1 is alwaysacquired first, then L2, then L3

B completes, releases L2, L3 C still waiting for L1, A gets L2

A

C

B

DeadlockPrevention - circular wait:

• provide a total ordering on lock acquisition

• example: if there are three locks, L1, L2, and L3, in the system, deadlock will not occur if L1 is acquired first, then L2, then L3

L3,L1 No deadlock if L1 is alwaysacquired first, then L2, then L3

A completes, releases L1, L2 C gets L1, then gets L3

A

C

B

DeadlockPrevention - circular wait:

• provide a total ordering on lock acquisition

• example: if there are three locks, L1, L2, and L3, in the system, deadlock will not occur if L1 is acquired first, then L2, then L3

• Unfortunately, this is really hard to control and a single simple mistake could result in violating the total ordering principle

No deadlock if L1 is alwaysacquired first, then L2, then L3

C completes

A

C

B

DeadlockPrevention - hold and wait:

• acquire all locks at once, atomically

• example: lock(prevention) lock(L1) lock(L2) ... unlock(prevention)

No untimely thread switch can occur in midst of lock acquisition

requires: thread must grab global prevention lock first. Then it's OK if thread tries to grab locks L1 and L2 in a different order

requires: know which locks must be held and acquire them ahead of time when calling a routine

• decreases concurrency: all locks must be acquired early on instead of when they are truly needed.

DeadlockPrevention - no preemption:

• use a trylock – returns immediately if lock cannot be grabbed

• example: top:   lock(L1)   if (trylock(L2) == ­1) {     unlock(L1)               goto top            }

above provides a deadlock-free, ordering-robust lock acquisition protocol for grabbing two locks – can be extended to more

• problem: if one of these locks is buried in some routine that is getting called, the jump back to the beginning can be hard to implement.

• example: if after acquiring L1, the code had allocated some memory, it would have to release that memory upon failure to acquire L2, before jumping back to the top to try the entire sequence again

DeadlockLivelock - no preemption:

• threads are not deadlocked but still are unable to get resources

• example: top:   lock(L1)   if (trylock(L2) == ­1) {     unlock(L1)               goto top            }

two threads could repeatedly attempt the above sequence, with locks interchanged, and repeatedly fail to acquire both locks

• solution: add a random delay before looping back (yuck)

DeadlockPrevention – mutual exclusion:

• use control structures that do not require explicit locking

• example:    void AtomicIncr(int *value, int amount) {     do {       int old = *value;     } while (CompareAndSwap(value,old,old+amount)==0);   }

above uses atomic hardware instruction to update instead of aqcuiring a lock before updating – CompareAndSwap fails if the expected value of *value is wrong, otherwise updates

DeadlockPrevention – mutual exclusion:

• example:    void insert(int value) {     node_t *n = malloc(sizeof(node_t));     assert(n != NULL);     n­>value = value;     n­>next = head;     head = n;   }

above performs a simple insertion, but if called by multiple threads at the “same time”, has a race condition

A possible solution is on the next slide

DeadlockPrevention – mutual exclusion:

• example:    void insert(int value) {     node_t *n = malloc(sizeof(node_t));     assert(n != NULL);     n­>value = value;     lock(listlock); // begin critical section     n­>next = head;     head = n;     unlock(listlock); // end of critical section   }

above uses a lock to prevent the list from being mangled

A lock-free solution is on the next slide

DeadlockPrevention – mutual exclusion:

• example:    void insert(int value) {     node_t *n = malloc(sizeof(node_t));     assert(n != NULL);     n­>value = value;     do {       n­>next = head;     } while (CompareAndSwap(&head, n­>next, n));   }

A lock-free solution using hardware atomic instructionss

DeadlockAvoidance – scheduling:

• use global knowledge of which locks various threads might grab during execution and schedule the threads to prevent deadlock

• example 1: assume - 2 processors CPU1-2 and 4 threads, T1-T4 T1 grabs locks L1 and L2 in some order during its execution T2 grabs L1 and L2 as well T3 grabs just L2 T4 grabs no locks at all

A deadlock-free schedule is:

CPU1: T3 T4

CPU2: T1 T2

observe: although T3 grabs lock L2, it can never cause a deadlock by running concurrently with T1 because it only grabs one lock.

DeadlockAvoidance – scheduling:

• use global knowledge of which locks various threads might grab during execution and schedule the threads to prevent deadlock

• example 2: assume - 2 processors CPU1-2 and 4 threads, T1-T4 T1 grabs locks L1 and L2 in some order during its execution T2 grabs L1 and L2 as well T3 grabs L1 and L2 as well T4 grabs no locks at all

A deadlock-free schedule is:

CPU1: T4

CPU2: T1 T2 T3

observe: this is a really conservative approach and may result in a significant performance decrease

DeadlockBanker's algorithm – detection and avoidance:

• only useful in very limited environments such as in an embedded system where one has full knowledge of the entire set of tasks that must be run and the locks that they need.

• algorithm sketch: started when a process requests permission to use a resource

tests for safety by simulating the allocation of predetermined maximum possible amounts of all resources – denies request if allocation requested is greater than the maximum for that resource, waits if number available is less than number requested

makes state check to test for possible deadlock conditions for all other pending activities – denies if deadlock possible

If the algorithm gets this far, permission is granted

• resources tracked: memory, semaphores, interface access, etc.

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    1    2    2    1 Process 2 (alloc):     1    0    3    3 Process 3 (alloc):     1    2    1    0

Available resources:    3    1    1    2

Safe state: where a hypothetical set of requests would allow each process to acquire its maximum resources and then terminate

Process 3 request of 2 of C is denied because then its total 3 > max 2 Process 3 request of 1 of C continues to next step

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    1    2    2    1 Process 2 (alloc):     1    0    3    3 Process 3 (alloc):     1    2    1    0

Available resources:    3    1    1    2

Check for safe state: Process 3 requests 1 of C

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    1    2    2    1 Process 2 (alloc):     1    0    3    3 Process 3 (alloc):     1    2    2    0

Available resources:    3    1    0    2

Check for safe state: Process 3 requests 1 of C

New hypothetical state – OK, continue

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    1    2    2    1 Process 2 (alloc):     1    0    3    3 Process 3 (alloc):     1    2    1    0

Available resources:    3    1    1    2

Check for safe state: Process 3 requests 1 of C

If Process 1 gets 2 A, 1 B, 1 D (to reach all its maximums)

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    3    3    2    2 Process 2 (alloc):     1    0    3    3 Process 3 (alloc):     1    2    1    0

Available resources:    1    0    1    1

Check for safe state: Process 3 requests 1 of C

If Process 1 gets 2 A, 1 B, 1 D - OK

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    0    0    0    0 Process 2 (alloc):     1    0    3    3 Process 3 (alloc):     1    2    1    0

Available resources:    4    3    3    3

Check for safe state: Process 3 requests 1 of C

Now Process 1 terminates

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    0    0    0    0 Process 2 (alloc):     1    0    3    3 Process 3 (alloc):     1    2    1    0

Available resources:    4    3    3    3

Check for safe state: Process 3 requests 1 of C

If Process 2 gets 2 B, 1 D (to reach its maximums)

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    0    0    0    0 Process 2 (alloc):     1    2    3    4 Process 3 (alloc):     1    2    1    0

Available resources:    4    1    3    2

Check for safe state: Process 3 requests 1 of C

If Process 2 gets 2 B, 1 D - OK

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    0    0    0    0 Process 2 (alloc):     0    0    0    0 Process 3 (alloc):     1    2    1    0

Available resources:    5    3    6    6

Check for safe state: Process 3 requests 1 of C

Now Process 2 terminates

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    0    0    0    0 Process 2 (alloc):     0    0    0    0 Process 3 (alloc):     1    2    1    0

Available resources:    5    3    6    6

Check for safe state: Process 3 requests 1 of C

If Process 3 gets 1 B, 4 C (to reach its maximums)

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    0    0    0    0 Process 2 (alloc):     0    0    0    0 Process 3 (alloc):     1    3    5    0

Available resources:    5    2    2    6

Check for safe state: Process 3 requests 1 of C

If Process 3 gets 1 B, 4 C - OK

DeadlockBanker's algorithm – detection and avoidance:

• example: A    B    C    D Total resources:    6    5    7    6

Process 1 (max):      3    3    2    2 Process 2 (max):      1    2    3    4 Process 3 (max):      1    3    5    0

Process 1 (alloc):    0    0    0    0 Process 2 (alloc):     0    0    0    0 Process 3 (alloc):     0    0    0    0

Available resources:    6    5    7    6

Check for safe state: Process 3 requests 1 of C

Now Process 3 terminates - Grant Request!

DeadlockDetect and recover – ouch:

• Allow deadlocks to occasionally happen, then take action once a deadlock is detected – this may involve killing a thread or even restarting the operating system

• see Deadlocker.java for an example

DeadlockTwo Phase Locking – guarantees serializability: • transaction: a sequence of memory reads and writes (ops)

• concurrency control: lots of threads acting on a set of transactions at the same time – for efficiency

• serializability: a transaction schedule is serializable if its resulting memory state is the same as the result of executing the transactions sequentially: that is, without overlapping in time.

• rollback: the effects of a transaction are erased if a rollback is executed

• commit: execution of a commit makes the effects of a transaction permanent (transaction executes atomically)

• recoverability: committed transactions have not read data written by aborted transactions

• conflicting operations: two ops are conflicting if they are of different non-committed transactions on the same memory item and at least one of them is write.

DeadlockTwo Phase Locking – guarantees serializability: anomalous behavior possibilities when two transactions conflict: uncommitted data is read (WR – write T1 then read T2):

a read may not be repeated (RW – read T1 then write T2):

uncommitted data can be overwritten (WW):

A transaction T2 could read a database object X that has been modified by another transaction T1, which has not yet committed (hence the wrong (old) value may be read by T2).

A transaction T2 could change the value of an object X that has been read by a transaction T1, while T1 is still is progress (hence T1 may have read the wrong (old) value).

A transaction T2 could overwrite the value of an object X, which has already been modified by a transaction T1, while T1 is still in progress (who knows what value any threadshould have?).

DeadlockTwo Phase Locking – guarantees serializability: • shared lock: a transaction acquires a shared lock in order to read an item from memory – more than one transaction may acquire a shared lock but all transactions intending to write to a locked item must wait until the lock is released

• exclusive lock: only one transaction can acquire an exclusive lock for writing to a memory item

• 2PL protocol: all reads are handled through shared locks a transaction handles its locks in two phases: expanding: locks are acquired and none are released shrinking: locks are released and none are acquired

• end of phase 1: may be determined by some synchronization or atomic commitment point – but this is typically expensive because more than one rollback may be required. Hence it is usually the case that all locks are held until after the commit.

Deadlock

Two-Phase Locking: Example – no good

T1 T2 Result

read_lock (Y); read_lock (X); Initial values: X=20; Y=30read_item (Y); read_item (X); Result of serial executionunlock (Y); unlock (X); T1 followed by T2 write_lock (X); write_lock (Y); X=50, Y=80.read_item (X); read_item (Y); Result of serial executionX:=X+Y; Y:=X+Y; T2 followed by T1 write_item (X); write_item (Y); X=70, Y=50unlock (X); unlock (Y);

Deadlock

Two-Phase Locking: Example – no good

T1 T2 Resultread_lock (Y); X=50; Y=50read_item (Y); Nonserializable and itunlock (Y); violates two-phase policy.

read_lock (X); read_item (X); unlock (X); write_lock (Y);read_item (Y);Y:=X+Y;write_item (Y);unlock (Y);

write_lock (X);read_item (X);X:=X+Y;write_item (X);unlock (X);

Deadlock

Two-Phase Locking: Example

T1' T2'

read_lock (Y); read_lock (X); T1 and T2 follow two-phaseread_item (Y); read_item (X); policy write_lock (X); write_lock (Y);unlock (Y); unlock (X);read_item (X); read_item (Y);X:=X+Y; Y:=X+Y; write_item (X); write_item (Y);unlock (X); unlock (Y);

Deadlock

Two-Phase Locking: Example

T1' T2'

read_lock (Y); If T1' gets read_lock(Y) first read_item (Y); then T2' cannot get write_lock(Y) write_lock (X); until T1' unlocks(Y). By that time, unlock (Y); if T2' had not grabbed read_lock(X), read_item (X); T1' has write_lock(X). T1' then has X:=X+Y; X=20 and Y=30 and computes write_item (X); X=50. T1' unlocks(X), allows T2' to unlock (X); read X=50, computes Y=80. read_lock(X); read_item(X); write_lock(Y); unlock(Y); read_item(Y); Y := X+Y; write_item(Y); unlock(Y);

Deadlock

Two-Phase Locking: Example

T1' T2'

read_lock (Y);read_item (Y);

read_lock(X); read_item(X); write_lock (X); unlock (Y); read_item (X);

X:=X+Y; write_item (X);unlock (X);

T2' cannot get the write_lock(Y)Because T1' has read_lock(Y) andT1' cannot get the write_lock(X) Because T2' has read_lock(X)

DeadlockPrecedence Graph: Schedule compliance with conflict serializability can be tested with the precedence graph for committed transactions of the schedule. It is the directed graph representing precedence of transactions in the schedule, as reflected by precedence of conflicting operations in the transactions.

A schedule is conflict-serializable if and only if its precedence graph of committed transactions is acyclic.

Cycles of committed transactions can be prevented by aborting an undecided transaction on each cycle in the precedence graph of all the transactions, which can otherwise turn into a cycle of committed transactions.

DeadlockPrecedence Graph:The precedence graph for a schedule S contains: - nodes represent transactions - an edge from transaction Ti to Tj exists if Ti reads or writes X before Tj writes X, or Ti writes X before reads Tj X

The precedence graphs for schedules corresponding,respectively, to (i), (ii), (iii):

(i) (ii)

(iii)

T1 T2 T1 T2

T1 T2

T3

Deadlock T1 T2

Xlock(A) Xlock(B) read(A) read(B) write(A)

(ii) T1 T2 T1 T2 T3

Slock(A) Slock(A) read(A) read(A) Xlock(B) write(B) Slock(A) Slock(B) read(A) read(B) Xlock(C) write(B) read(C) read(A) write(C) write(A) write(A) (i) Stuff (iii)

DeadlockApproximation of the precedence graph to keep it acyclic: • Timestamp-based concurrency control (roughly)

Each transaction Ti gets a timestamp ts(Ti) before its first action

rts(X): the max of ts(Ti) of all uncommitted Ti that read X wts(X): the max of ts(Ti) of all uncommitted Ti that wrote to X

ReadTi(X): if ts(Ti) < wts(X) then abort Ti;

otherwise, read(X); rts(X) = max(rts(X), ts(Ti));

WriteTi(X): if ts(Ti) < wts(X) or ts(Ti) < rts(X) then abort Ti;

otherwise, write(X); wts(X) = ts(Ti);

Aborted transactions are restarted with a higher timestamp so they will eventually succeed Note: the abort is used preemptively to avoid deadlock

DeadlockA waits-for graph may also be kept acyclic: • Detect and resolve deadlocks (roughly)

Maintain a waits-for graph: Ti waits on Tj is an edge in the graph from node Ti to node Tj

If Ti read-locks or write-locks X then Tj tries to write-lock X, or Ti write-locks X then Tj tries to read-lock X

When a cycle is detected: Abort at least one transaction so that no cycles exist

Which transactions should be aborted to break a cycle? one holding fewest locks? one waiting on fewest locks? one early in its execution? one that has done the most work so far?

Aborted transactions are restarted later when they will succeed Note: the abort is used preemptively to avoid deadlock

Recommended