thread modular reasoning for lock free data structures
play

Thread-Modular Reasoning for Lock-Free Data Structures Roland Meyer - PowerPoint PPT Presentation

Thread-Modular Reasoning for Lock-Free Data Structures Roland Meyer based on joint work with Luk Holk, Tom Vojnar, and Sebastian Wol ff . Lock-Free Data Structures Key Take Aways: e ffi cient but complex correctness =


  1. Thread-Modular Reasoning 
 for Lock-Free Data Structures Roland Meyer based on joint work with Luká š Holík, Tomá š Vojnar, and Sebastian Wol ff .

  2. Lock-Free Data Structures Key Take Aways: • e ffi cient but complex • correctness = linearizability • checking linearizability reduces to reachability http://www.braunschweig-fotograf.de/mein-braunschweig/

  3. Concept • avoid locks ➡ critical section cannot exist • single commands are atomic ➡ compare-and-swap (CAS) CAS(src, cmp, dst) := atomic { if (src != cmp) return false; src = dst; return true; }

  4. Example: Treiber’s Stack push(val): pop(): 1 node = new Node(val); while (true) { while (true) { top = ToS; top = ToS; if (top == NULL) node.next = top; return EMPTY; if (CAS(ToS, top, node)) next = top.next; return; if (CAS(ToS, top, next)) } return top.data; } ToS top node

  5. Example: Treiber’s Stack push(val): pop(): node = new Node(val); while (true) { while (true) { top = ToS; top = ToS; if (top == NULL) node.next = top; return EMPTY; if (CAS(ToS, top, node)) next = top.next; return; if (CAS(ToS, top, next)) 1 } return top.data; } ToS top node top next

  6. Example: Treiber’s Stack push(val): pop(): 2 node = new Node(val); while (true) { while (true) { top = ToS; top = ToS; if (top == NULL) node.next = top; return EMPTY; if (CAS(ToS, top, node)) next = top.next; 1 return; if (CAS(ToS, top, next)) } return top.data; } ToS top top next next top 2 next 2

  7. Correctness and Concurrency • pre/post conditions meaningless ➡ other correctness criteria required • linearizability ➡ every concurrent run must coincide with a sequential run ➡ most common for lock-free data structures ➡ illusion of sequentiality [ Filipovi ć et al. ESOP’09 ]: linearizable ⟺ sequential and concurrent implementation 
 are observationally equivalent

  8. Checking Linearizability • check sequentiality illusion ➡ sufficient: sequence of linearization points is valid [ Abdulla et al. TACAS’13 ] 
 (intuitively: linearization point = change of data structure takes effect) concurrent ( DS ) | = sequential ( DS ) ⇒ linp ( DS ) ⊆ sequential ( DS ) ⇐ ⇒ linp ( DS ) ∩ sequential ( DS ) = ∅ ⇐ ⇒ linp ( DS ) ∩ observer ( DS ) = ∅ ⇐ ➡ checking linearizability is a reachability problem

  9. Overview 1. thread-modular reasoning 2. ownership 3. summaries

  10. Thread-Modular Reasoning [Qadeer, Flanagan SPIN’03] Key Take Aways: • compute reachability • interference is key to scalability

  11. Concept • view abstraction ➡ split states into set of views ➡ views capture perception of 1 thread (abstract from correlation) • state exploration ➡ fixed-point computation: X = X ∪ sequential ( X ) ∪ interference ( X )

  12. Example: View Abstraction X = X ∪ sequential ( X ) ∪ interference ( X ) ToS ToS CAS(ToS, top, next) 1 Note: both views are equal . CAS(ToS, top, next) 2 top 1 next 1 top 2 next 2

  13. Example: Sequential Step X = X ∪ sequential ( X ) ∪ interference ( X ) ToS CAS(ToS, top, next) 1 top 1 next 1 No concurrent behavior.

  14. Example: Interference Step X = X ∪ sequential ( X ) ∪ interference ( X ) ToS CAS(ToS, top, next) 1 top 1 next 1 ToS CAS(ToS, top, next) 2 top 2 next 2 1. combine

  15. Example: Interference Step X = X ∪ sequential ( X ) ∪ interference ( X ) ToS CAS(ToS, top, next) 1 CAS(ToS, top, next) 2 top 1 next 1 top 2 next 2 1. combine 2. step 3. project

  16. Challenges with Interference • number of possible combinations is enormous ➡ not all combinations are reasonable • need pruning to make the approach practical ➡ precision ➡ performance • pruning must be sound

  17. Pruning Interferences two types • matching ➡ Is it possible to combine at all? Skip if not. • correlation ➡ Which nodes should coincide?

  18. Matching: Complication • matching gets harder due to finite abstraction • we use reachability predicates (shape analysis): • 0-step: = ToS • 1-step: � node • n-step: ⤏ ToS // ToS ⤏ NULL • unreach: ⋈ // node � ToS node

  19. Matching: Example ToS Subgraph top next isomorphism: NP-complete! logical stack content ToS node

  20. Correlation: Example ToS node 1 … ToS … next 2 node ?? top 2 Exponentially many! ToS … ToS top next … top 2 next 2 node 1

  21. Practicality is about Interference • interference ➡ quadratic in size of state space • matching poor scalability ➡ subgraph isomorphism (NP) fight imprecision 
 (false-positives) • correlation ➡ exponential

  22. Ownership Key Take Aways: • ownership saves the day • even under explicit memory management

  23. Concept partition allocated heap into • owned ➡ exclusive access for a single thread ➡ granted upon allocation • shared ➡ accessible by every thread ➡ by publishing (e.g. making accessible via shared variables)

  24. Ownership in Thread-Modular Reasoning [Gotsman et al. PLDI’07] • track ownership ➡ small overhead • matching ➡ owned cells not contained • correlation ➡ owned cells not merged with other nodes

  25. Ownership and Correlation ToS ToS node 1 … own … next 2 node node ?? top 2 ToS … ToS … top next top 2 next 2 node 1

  26. Ownership in Thread-Modular Reasoning • helps a lot with Only for garbage ➡ matching collection (GC)! ➡ correlation • makes thread-modular reasoning 
 practical What about explicit ➡ prunes false-positives memory management (MM)?

  27. Problem with MM Ownership does not exist under 
 explicit memory management. 
 — folklore • almost true • indeed no exclusivity ➡ dangling pointers • we introduced weak ownership in VMCAI’16

  28. Weak Ownership [ VMCAI’16 ] • write exclusivity ➡ only owners may write owned dangling … • no read exclusivity … ➡ dangling readers allowed ➡ dangling reads unsafe ➡ only owner may rely on memory contents

  29. Weak Ownership in Thread-Modular Reasoning [ VMCAI’16 ] • track dangling pointers ➡ small overhead • matching: like normal ownership • correlation ➡ -owned cells referenced by only via dangling pointers 1 2 • dangling write accesses may be unsafe ➡ report as bug

  30. Performance Impact [ VMCAI’16 ] MM with 
 MM without 
 ownership ownership 944s 25.5s :37 Treiber’s stack :36 #116776 #3175 false positive 11700s Michael&Scott’s queue impractical > #69000 #19742

  31. Accomplishments • ownership helps with matching and correlation • low overhead tracking additional info • deeming unsafe accesses as bugs reflects programming practice • performance improvements for analysis • but: not practical yet ➡ interference still computationally complex

  32. Summaries Key Take Aways: • copy-and-check blocks • statelessness • e ffi cient interference

  33. Observation • lock-freedom relies on 
 copy-and-check blocks push(val): node = new Node(val); 1. create local copy of shared data while (true) { top = ToS; 1 2. make changes locally node.next = top; 2 if (CAS(ToS, top, node)) 3 3. publish changes if copy up-to-date 
 return; } or retry otherwise ➡ updates appear atomically

  34. Insight Threads cannot observe the local behavior of other threads. 
 — SAS’17 So why do interference for all intermediate steps? ➡ instead: apply updates in one shot ➡ potentially unsound: stay tuned

  35. Example: Summary for pop 1. make atomic atomic { while (true) { 2. remove noise top = ToS; if (top == NULL) return ; EMPTY next = top.next; if (CAS(ToS, top, next)) top.data ; return } }

  36. Example: Summary for pop 1. make atomic atomic { while (true) { 2. remove noise top = ToS; if (top == NULL) 3. copy propagation return; next = top.next; if (CAS(ToS, top, next)) return; } }

  37. Example: Summary for pop 1. make atomic atomic { while (true) { 2. remove noise top = ; ToS ToS ToS if ( == NULL) top 3. copy propagation return; top next = .next; top if (CAS(ToS, , next)) return; } }

  38. Example: Summary for pop 1. make atomic atomic { while (true) { 2. remove noise if (ToS == NULL) 3. copy propagation return; 4. remove noise if (CAS(ToS, ToS, ToS.next)) 5. rewrite CAS return; } }

  39. Example: Summary for pop 1. make atomic atomic { 2. remove noise if (ToS == NULL) 3. copy propagation return; 4. remove noise if (CAS(ToS, ToS, ToS.next)) ToS = ToS.next; return; 5. rewrite CAS return; }

  40. Example: Summary for pop 1. make atomic atomic { 2. remove noise assume(ToS != NULL); if (ToS == NULL) 3. copy propagation return; 4. remove noise ToS = ToS.next; return; 5. rewrite CAS 6. rewrite guard }

  41. Example: Summary for pop 1. make atomic atomic { 2. remove noise assume(ToS != NULL); 3. copy propagation 4. remove noise ToS = ToS.next; 5. rewrite CAS 6. rewrite guard }

  42. Example: Summary for pop 1. make atomic atomic { assume(ToS != NULL); 2. remove noise ToS = ToS.next; } 3. copy propagation • easy to compute 4. remove noise ➡ similar for push 5. rewrite CAS • compact form beneficial for analysis 
 (and understandability) 6. rewrite guard

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend