230 likes | 242 Views
This text discusses the concepts of locks, channels, and deadlocks in the context of ensuring progress and freedom. It explores the locking order, object life cycle, and reordering of locks. It also covers channels, progress problems, and credit accounting. The text provides examples and syntax for using locks and channels effectively.
E N D
Locks, channels, deadlock freedom, progress K. Rustan M. Leino Peter Müller IFIP WG 2.3 meeting 49 10 June 2009 Boston, MA
Chalice object life cycle (abridged) share thread local shared,available • Locks are objects, objects are locks new release shared,locked acquire
Locking order in Chalice • Every lock has an associated locking level • Locks have to be acquired in ascending order, which avoids deadlocks
Locking order details • Locking order is a dense semi-lattice(Mu, <<, , ) • << is the strict version of << • The locking level of an object o is stored in a mutable ghost field o.mu • Accessing o.mu requires appropriate permissions
Example method M(o: C)requiresrd(o.mu) maxlock << o.mu …{acquire o …release o} (lHeld l.mu)
Semantics (defined by pseudo code) share thread local shared,available o := new C ≡ … o.mu := … share o between L and H ≡assertCanWrite(o,mu) o.mu = ;assert L << H;havocμ; assume L << μ << H;o.mu := μ;ExhaleMonitorInv(o); acquire o ≡ assertCanRead(o,mu);assertmaxlock << o.mu;Held := Held {o};InhaleMonitorInv(o); release o ≡ assert o Held;ExhaleMonitorInv(o);Held := Held – {o}; new release shared,locked acquire
Lock reordering reorder o between L and H ≡ assertCanWrite(o,mu) o.mu ≠ ; assert L << H; assert o Held; havocμ; assume L << μ << H; o.mu := μ; method M(o: C)requiresrd(o.mu) maxlock << o.mu …{acquire o …release o} (lHeld l.mu)
Threads • forktk := o.M() • jointk
Join deadlock Thread 0: • forktk := o.M()acquire pjointkrelease p Thread 1: • method M() …{acquire p …release p}
Avoiding join deadlocks • Tokens record the initial maxlock of the new thread • forktk := o.M() ≡tk.forkMaxlock := maxlock; … • jointk ≡assertmaxlock<<tk.forkMaxlock; …
Example corrected Thread 0: • forktk := o.M()acquire pjointkrelease p Thread 1: • method M()requiresrd(p.mu) maxlock << p.mu{acquire p …release p}
Channels • channelName(signature) wherepredicate • Example:channel Ch(t: T) whereacc(t.x) 0 ≤ t.x < 100 • Channels have unbounded slack • that is: sends are non-blocking • ch := new Ch • sendch(E) ≡ Exhale Where(ch); • receive x := ch ≡ Inhale Where(ch);
Progress problems Thread 0: • receive x := ch • acquire oreceive x := ch Thread 1: • /* No send. Ever. */ • acquire osendch(E) or:
Credits • Channels have associated credits • cf. memory locations have associated permissions • receive requires a credit • send produces a credit • Specification syntax: credit(ch, n)where n is an integer, denotes n credits for channel ch • If omitted, n defaults to 1 • Negative credits are debits
Encoding of credits • Introduce a per-activation-record credits counter C : channel int • cf. per-activation-record permissions mask P in Peter’s talk • Inhalecredit(ch,n) ≡ C[ch] := C[ch] + n • Exhalecredit(ch,n) ≡ C[ch] := C[ch] – n
Credit accounting sell where clause,obtain 1 credit • ch := new Ch ≡ … C[ch] := 0; … • sendch(E) ≡Inhalecredit(ch,1); Exhale Where(ch); • receive x := E ≡assert C[ch] > 0;Inhale Where(ch); Exhalecredit(ch,1); pay 1 credit,receive where clause
Ensuring progress • Associate a locking level also with every channel ch, recorded in a field ch.mu • ch := new Ch between L and H ≡ … • receive x := ch ≡ assertCanRead(ch,mu) maxlock << ch.mu;… // as before • sendch(E) ≡ assertCanRead(ch,mu); ?… // as before • reorder – not yet worked out
Going into debt • maxlock ≡(lHeld l.mu) (ch | C[ch] < 0 ch.mu) • Inhalecredit(ch,n) ≡ Exhalecredit(ch,-n) • Exhalecredit(ch,n) ≡ assert C[ch] – n < 0 ≤ C[ch] maxlock << ch.mu; C[ch] := C[ch] – n;
Credit-leak checking • At the end of every activation record: assert(ch 0 ≤ C[ch]);
Producer / Consumer example class Cell { varval: int; … } channel Ch(x: Cell) where x ≠ nullacc(x.val) 0 ≤ x.val credit(this) class Program { method Main() …method Producer(ch: Ch) …method Consumer(ch: Ch) …}
P/C: Main method Main() { varch := new Ch fork tk0 := Producer(ch) fork tk1 := Consumer(ch) join tk0 join tk1 }
P/C: Producer method Producer(ch: Ch)requiresrd(ch.mu)ensurescredit(ch, 1) { while (…)invariantrd(ch.mu){var x := …sendch(x)} sendch(null)} or: requiresrd(ch.mu) requirescredit(ch, -1) ensurestrue
P/C: Consumer method Consumer(ch: Ch)requiresrd(ch.mu) maxlock << ch.murequirescredit(ch)ensuresrd(ch.mu) {receive x := chwhile (x ≠ null)invariant x ≠ nullcredit(ch){ …receive x := ch } }