synchronizing the asynchronous
play

Synchronizing the Asynchronous Bernhard Kragl IST Austria Shaz - PowerPoint PPT Presentation

Synchronizing the Asynchronous Bernhard Kragl IST Austria Shaz Qadeer Thomas A. Henzinger Microsoft IST Austria Concurrency is Ubiquitous Asynchronous Concurrency is Ubiquitous Asynchronous programs are hard to specify assert Pre(Q)


  1. Synchronizing the Asynchronous Bernhard Kragl IST Austria Shaz Qadeer Thomas A. Henzinger Microsoft IST Austria

  2. Concurrency is Ubiquitous

  3. Asynchronous Concurrency is Ubiquitous

  4. Asynchronous programs are hard to specify assert Pre(Q) assert Pre(Q) call Q async Q assume Post(Q) assume Post(Q) Asynchronous programs are hard to verify

  5. Structured program vs. Transition relation Init: 𝑞𝑑 = 𝑞𝑑 1 = 𝑞𝑑 2 = 𝑏 a: x := 0 b: acquire(l) acquire(l) Next: ′ = 𝑞𝑑 2 ′ = 𝑐 ∧ 𝑦 ′ = 0 ∧ 𝑓𝑟 𝑚, 𝑢 1 , 𝑢 2 𝑞𝑑 = 𝑏 ∧ 𝑞𝑑 ′ = 𝑞𝑑 1 c: t1 := x t2 := x ′ = 𝑑 ∧ ¬𝑚 ∧ 𝑚 ′ ∧ 𝑓𝑟 𝑞𝑑, 𝑞𝑑 2 , 𝑦, 𝑢 1 , 𝑢 2 𝑞𝑑 1 = 𝑐 ∧ 𝑞𝑑 1 d: t1 := t1+1 t2 := t2+1 ′ = 𝑒 ∧ 𝑢 1 ′ = 𝑦 ∧ 𝑓𝑟 𝑞𝑑, 𝑞𝑑 2 , 𝑚, 𝑦, 𝑢 2 𝑞𝑑 1 = 𝑑 ∧ 𝑞𝑑 1 e: x := t1 x := t2 ′ = 𝑓 ∧ 𝑢 1 ′ = 𝑢 1 + 1 ∧ 𝑓𝑟 𝑞𝑑, 𝑞𝑑 2 , 𝑚, 𝑦, 𝑢 2 𝑞𝑑 1 = 𝑒 ∧ 𝑞𝑑 1 f: release(l) release(l) ′ = 𝑔 ∧ 𝑦 ′ = 𝑢 1 ∧ 𝑓𝑟 𝑞𝑑, 𝑞𝑑 2 , 𝑚, 𝑢 1 , 𝑢 2 𝑞𝑑 1 = 𝑓 ∧ 𝑞𝑑 1 ′ = 𝑕 ∧ ¬𝑚 ′ ∧ 𝑓𝑟(𝑞𝑑, 𝑞𝑑 2 , 𝑦, 𝑢 1 , 𝑢 2 ) 𝑞𝑑 1 = 𝑔 ∧ 𝑞𝑑 1 g: assert x = 2 ′ = 𝑑 ∧ ¬𝑚 ∧ 𝑚 ′ ∧ 𝑓𝑟 𝑞𝑑, 𝑞𝑑 1 , 𝑦, 𝑢 1 , 𝑢 2 𝑞𝑑 2 = 𝑐 ∧ 𝑞𝑑 2 ′ = 𝑒 ∧ 𝑢 2 ′ = 𝑦 ∧ 𝑓𝑟 𝑞𝑑, 𝑞𝑑 1 , 𝑚, 𝑦, 𝑢 1 𝑞𝑑 2 = 𝑑 ∧ 𝑞𝑑 2 ′ = 𝑓 ∧ 𝑢 2 ′ = 𝑢 2 + 1 ∧ 𝑓𝑟 𝑞𝑑, 𝑞𝑑 1 , 𝑚, 𝑦, 𝑢 1 𝑞𝑑 2 = 𝑒 ∧ 𝑞𝑑 2 ′ = 𝑔 ∧ 𝑦 ′ = 𝑢 2 ∧ 𝑓𝑟 𝑞𝑑, 𝑞𝑑 1 , 𝑚, 𝑢 1 , 𝑢 2 𝑞𝑑 2 = 𝑓 ∧ 𝑞𝑑 2 ′ = 𝑕 ∧ ¬𝑚 ′ ∧ 𝑓𝑟(𝑞𝑑, 𝑞𝑑 1 , 𝑦, 𝑢 1 , 𝑢 2 ) 𝑞𝑑 2 = 𝑔 ∧ 𝑞𝑑 2 Procedures and dynamic thread creation 𝑞𝑑 1 = 𝑞𝑑 2 = 𝑕 ∧ 𝑞𝑑′ = 𝑕 ∧ 𝑓𝑟(𝑞𝑑 1 , 𝑞𝑑 2 , 𝑚, 𝑦, 𝑢 1 , 𝑢 2 ) complicate transition relation further! Safe: 𝑞𝑑 = 𝑕 ⇒ 𝑦 = 2

  6. Shared State in Message-Passing Programs P / P# Language • Windows 8 USB 3.0 driver • Azure cloud services • DRONA framework Problem: Monolithic proofs do not scale Question: How can structured proofs help?

  7. Idea: “ Inlining of Event Handlers ” Dispatcher ↻ Dispatcher ↻ Event Handlers Event Handlers H1: H1: … … H2: REQ : send REQ C1;C2;C3; H3: H3: … …

  8. Idea: “ Inlining of Event Handlers ” Dispatcher ↻ Dispatcher ↻ Event Handlers Event Handlers H1: H1: … … H2: REQ : send REQ C1;C2;C3; C1;C2;C3; H3: H3: … …

  9. Our Contributions Synchronization proof rule Syntax-driven and structured proofs 𝑄 1 ≼ 𝑄 2 ≼ ⋯ ≼ 𝑄 𝑜−1 ≼ 𝑄 𝑄 𝑜 is safe 𝑜 𝑄 1 is safe

  10. Reduction Theorem Sequence of (right)*(none)?(left)* is atomic. Left/right movers Commutativity (right) (both) (both) (left) lock A t := x B x := t+1 C unlock 𝑇 1 𝑇 2 𝑇 3 𝑇 4 𝑇 5 𝑇 6 𝑇 7 𝑇 8 A lock t := x x := t+1 unlock B C 𝑇 1 𝑇 2 ′ 𝑇 3 ′ 𝑇 4 ′ 𝑇 5 ′ 𝑇 6 ′ 𝑇 7 ′ 𝑇 8

  11. Lifting Reduction to Asynchronous Programs Let 𝑅 be a procedure in program 𝑄 • Reduction atomic action Q ⇝ [𝑅] ⇝ 𝐵 • Synchronization 𝑅 ⇝ [𝑡𝑧𝑜𝑑(𝑅)] ⇝ 𝐵 contains asynchronous replaces asynchronous invocations invocations with synchronous ones

  12. Synchronization Example global var x proc Main(n): Traces of x: 0 1 2 1 0 -1 -2 -1 0 ... 0 var i := 0 0 1 2 3 4 3 2 3 2 … 0 while i < n: async [x := x + 1] … async [x := x – 1] i := i + 1 global var x proc Main(n): var i := 0 Trace of x: 0 1 0 1 0 1 0 … 0 while i < n: [x := x + 1] [x := x – 1] i := i + 1 ≼ atomic Main(n): skip

  13. Termination? global var x proc Main: proc Main(n): async Foo Failure reachable var i := 0 assert false while i < n: async [x := x + 1] proc Foo: async [x := x – 1] while (true): skip i := i + 1 global var x proc Main: proc Main(n): call Foo var i := 0 assert false Failure unreachable while i < n: [x := x + 1] proc Foo: [x := x – 1] while (true): skip i := i + 1 ≼ ≼ atomic Main(n): atomic Main: skip assume false

  14. Termination? Cooperation! global var x global var x proc Main: proc Main(n): async Foo proc Main(n): var i := 0 assert false var i := 0 while i < n: while i < n: async [x := x + 1] proc Foo: async [x := x + 1] async [x := x – 1] while (true): skip async [x := x – 1] i := i + 1 if *: i := i + 1 global var x global var x proc Main: proc Main(n): proc Main(n): call Foo var i := 0 var i := 0 assert false while i < n: while i < n: [x := x + 1] [x := x + 1] proc Foo: [x := x – 1] [x := x – 1] while (true): skip if *: i := i + 1 i := i + 1 ≼ ≼ ≼ atomic Main(n): atomic Main: atomic Main(n): skip assume false skip

  15. Pending Asynchronous Calls 𝑅 ⇝ [𝑡𝑧𝑜𝑑(𝑅)] ⇝ 𝐵 contains asynchronous replaces asynchronous invocations invocations with synchronous ones Example: Lock Service global var lock : nat? proc Acquire(tid : nat) s := false while (!s) call s := CAS(lock,NIL,tid) async Callback(tid)

  16. Pending Asynchronous Calls 𝑅 ⇝ [𝑡𝑧𝑜𝑑(𝑅)] ⇝ 𝐵 contains asynchronous replaces SOME asynchronous contains invocations invocations with synchronous ones pending asyncs Example: Lock Service global var lock : nat? global var lock : nat? proc Acquire(tid : nat) atomic ACQUIRE(tid : nat) s := false assume lock == NIL while (!s) lock := tid call s := CAS(lock,NIL,tid) async Callback(tid) async Callback(tid)

  17. Example: Lock Service Server Client proc Acquire(tid: nat) proc Callback(tid: nat) s := false t := x while (!s) x := t + 1 call s := CAS(lock,NIL,tid) async Release(tid) async Callback(tid) By synchronization proc Release(tid: nat) lock := nil left CALLBACK(tid: nat) assert lock == tid x := x + 1 lock := nil By synchronization By async elimination atomic ACQUIRE(tid: nat) atomic ACQUIRE ’( tid: nat) assume lock == NIL assume lock == NIL lock := tid lock := tid async Callback(tid) x := x + 1 lock := nil left RELEASE(tid: nat) assert lock == tid By abstraction lock := nil atomic ACQUIRE ’’( tid: nat) x := x + 1

  18. Synchronizing Asynchrony II Synchronization transforms procedure Q into atomic action A 𝑩𝒖𝒑𝒏𝒋𝒅𝒋𝒖𝒛 : (1) execution steps of 𝑅 match (right)*(none)?(left)* (2) execution steps in asynchronous threads of 𝑅 match (left)*

  19. Synchronizing Asynchrony II Synchronization transforms procedure Q into atomic action A 𝑫𝒑𝒑𝒒𝒇𝒔𝒃𝒖𝒋𝒑𝒐 : partial sequential executions of 𝑅 must have some terminating extension ⋮

  20. Multi-layered Refinement Proofs 𝑄 1 ≼ 𝑄 2 ≼ ⋯ ≼ 𝑄 𝑜−1 ≼ 𝑄 𝑄 𝑜 is safe 𝑜 𝑄 1 is safe Advantages of structured proofs: Better for humans: easier to construct and maintain Better for computers: localized/small checks  easier to automate Layered Programs [Hawblitzl, Petrank, Qadeer, Tasiran 2015] [K, Qadeer 2018] Express 𝑄 1 , ⋯ , 𝑄 𝑜 (and their connection) as single entity

  21. Lock Service (Layered Program) // Global variables // Server // Primitive atomic actions var lock@[1,3] : nat? atomic ACQUIRE@[2,3](tid: nat) atomic CAS@[1,1](old, new: nat?) var x @[1,4] : int assume lock == NIL returns (s: bool) lock := tid if (lock == old) async Callback(tid) lock := new s := true else left RELEASE@[2,2](tid: nat) s := false assert lock == tid // Client lock := NIL atomic RESET@[1,1]() lock := NIL left CALLBACK@[3,3](tid: nat) proc Acquire@1(tid: nat) assert lock == tid refines ACQUIRE both READ@[1,2](tid: nat) x := x + 1 var s: bool returns (v: int) lock := NIL s := false assert lock == tid while (!s) call s := CAS(NIL, tid) v := x proc Callback@2(tid: nat) async Callback(tid) refines CALLBACK both WRITE@[1,2](tid: nat, v: int) var t: int proc Release@1(tid: nat) assert lock == tid call t := READ(tid) refines RELEASE x := v call WRITE(tid, t+1) call RESET() async Release(tid)

  22. Lock Service (Layer 1) // Global variables // Server // Primitive atomic actions var lock@[1,3] : nat? atomic ACQUIRE@[2,3](tid: nat) atomic CAS@[1,1](old, new: nat?) var x @[1,4] : int assume lock == NIL returns (s: bool) lock := tid if (lock == old) async Callback(tid) lock := new s := true else left RELEASE@[2,2](tid: nat) s := false assert lock == tid // Client lock := NIL atomic RESET@[1,1]() lock := NIL left CALLBACK@[3,3](tid: nat) proc Acquire@1(tid: nat) assert lock == tid refines ACQUIRE both READ@[1,2](tid: nat) x := x + 1 var s: bool returns (v: int) lock := NIL s := false assert lock == tid while (!s) call s := CAS(NIL, tid) v := x proc Callback@2(tid: nat) async Callback(tid) refines CALLBACK both WRITE@[1,2](tid: nat, v: int) var t: int proc Release@1(tid: nat) assert lock == tid call t := READ(tid) refines RELEASE x := v call WRITE(tid, t+1) call RESET() async Release(tid)

Recommend


More recommend