exploiting purity for atomicity
play

Exploiting Purity for Atomicity 1 Busy Acquire atomic void - PowerPoint PPT Presentation

Exploiting Purity for Atomicity 1 Busy Acquire atomic void busy_acquire() { while (true) { if (CAS(m,0,1)) break; } } if (m == 0) { m = 1; return true; } else { return false; } 2 Busy Acquire atomic void busy_acquire() { while (true)


  1. Exploiting Purity for Atomicity 1

  2. Busy Acquire atomic void busy_acquire() { while (true) { if (CAS(m,0,1)) break; } } if (m == 0) { m = 1; return true; } else { return false; } 2

  3. Busy Acquire atomic void busy_acquire() { while (true) { if (CAS(m,0,1)) break; } } CAS(m,0,1) CAS(m,0,1) CAS(m,0,1) (fails) (succeeds) (fails) 3

  4.  Non-Serial Execution: CAS(m,0,1) CAS(m,0,1) CAS(m,0,1) (fails) (succeeds) (fails)  Serial Execution: CAS(m,0,1) (succeeds)  Atomic but not reducible 4

  5. alloc boolean b[MAX]; // b[i]==true iff block i is free Lock m[MAX]; atomic int alloc() { int i = 0; while (i < MAX) { acquire(m[i]); if (b[i]) { b[i] = false; release(m[i]); return i; } release(m[i]); i++; } return -1; } 5

  6. alloc acq(m[0]) rel(m[0]) test(b[0]) acq(m[1]) test(b[1]) b[1]=false rel(m[1]) 6

  7. alloc is not Atomic  There are non-serial executions with no equivalent serial executions 7

  8. m[0] = m[1] = 0; b[0] = b[1] = false; t = alloc(); || free(0); free(1); void free(int i) { acquire(m[i]); b[i] = true; release(m[i]); } 8

  9. m[0] = m[1] = 0; b[0] = b[1] = false; t = alloc(); || free(0); free(1);  Non-Serial Execution: loop for b[0] free(0) free(1) loop for b[1] t = 1  Serial Executions: loop for b[0] loop for b[1] free(0) free(1) t = -1 free(0) free(1) loop for b[0] t = 0 free(0) loop for b[0] free(1) t = 0 9

  10. Extending Atomicity  Atomicity doesn't always hold for methods that are "intuitively atomic" – serializable but not reducible (busy_acquire) – not serializable (alloc)  Examples – caches – initialization – commit/retry transactions – resource allocation – wait/notify 10

  11. Pure Code Blocks  Pure block: pure { E } – E is reducible in normally terminating executions – If E terminates normally, it does not update state visible outside of E  Example while (true) { pure { acquire(mx); if (x == 0) { x = 1; release(mx); break; } release(mx); } } 11

  12. Purity and Abstraction acq(m) rel(m) test(x) acq(m) test(x) rel(m) skip  Abstract execution semantics: – treat normal execution of pure blocks as the skip statement 12

  13. Abstraction  A bstract semantics that admits more behaviors – pure blocks can be skipped – hides "irrelevant" details (ie, failed loop iters)  Program must still be (sequentially) correct in abstract semantics  Abstract semantics make reduction possible 13

  14. Busy Acquire atomic void busy_acquire() { while (true) { pure { if (CAS(m,0,1)) break; } } } 14

  15. Abstract Execution of Busy Acquire atomic void busy_acquire() { while (true) { pure { if (CAS(m,0,1)) break; } } } CAS(m,0,1) CAS(m,0,1) CAS(m,0,1) (Concrete) skip skip CAS(m,0,1) (Abstract) (Reduced Abstract) 15

  16. alloc atomic int alloc() { int i = 0; while (i < MAX) { pure { acquire(m[i]); if (b[i]) { b[i] = false; release(m[i]); return i; } release(m[i]); } i++; } return -1; } 16

  17. Abstract Execution of alloc acq(m[0]) rel(m[0]) test(b[0]) acq(m[1]) test(b[1]) b[1]=false rel(m[1]) skip acq(m[1]) test(b[1]) b[1]=false rel(m[1]) (Abstract) (Reduced Abstract) 17

  18. Abstraction  Abstract semantics admits more executions free(0) free(1) skip acq(m[1]) test(b[1]) b[1]=false rel(m[1]) (Abstract)  Can still reason about important properties – " alloc returns either the index of a freshly allocated block or -1" – cannot guarantee " alloc returns smallest possible index" • but what does this really mean anyway??? 18

  19. Type Checking atomic void deposit(int n) { R acquire(this); B int j = bal; ((R;B);B);L = B bal = j + n; (R;B);L = L release(this); R;L = } A atomic void depositLoop() { while (true) { (A)* = C ⇒ ERROR A deposit(10); } } 19

  20. alloc boolean b[MAX]; Lock m[MAX]; atomic int alloc() { int i = 0; while (i < MAX) { acquire(m[i]); if (b[i]) { b[i] = false; release(m[i]); A* = C A return i; } release(m[i]); i++; } return -1; } 20

  21. Type Checking with Purity atomic int alloc() { int i = 0; while (i < MAX) { pure { acquire(m[i]); if (b[i]) { (B ↑ A)* = b[i] = false; A ↑ A B ↑ A B* ↑ (B*;A) = release(m[i]); return i; B ↑ A } release(m[i]); } i++; } return -1; } 21

  22. Double Checked Initialization atomic void init() { if (x != null) return; acquire(l); if (x == null) x = new(); release(l); } 22

  23. Double Checked Initialization atomic void init() { if (x != null) return; conflicting accesses acquire(l); if (x == null) x = new(); release(l); } test(x) test(x) x=new() rel(l) acq(l) (Concrete) 23

  24. Double Checked Initialization atomic void init() { A ↑ A if (x != null) return; acquire(l); if (x == null) x = new(); A ↑ _ release(l); } test(x) test(x) x=new() rel(l) acq(l) (Concrete) 24

  25. Double Checked Initialization atomic void init() { B ↑ A pure { if (x != null) return; } B;A ↑ A acquire(l); = A ↑ A if (x == null) x = new(); A ↑ _ release(l); } test(x) test(x) x=new() rel(l) acq(l) (Concrete) test(x) x=new() rel(l) skip acq(l) (Abstract) (Reduced Abstract) 25

  26. Modifying local variables in pure blocks  Partition variables into global and local variables  Allow modification of local variables pure { acq(m); rel(m); } ≅ skip local x; pure { acq(m); x = z; rel(m); } global z; ≅ pure { x = z; } ≅ x = z0; local x1, x2; pure { acq(m); x1 = z; x2 = z; rel(m); } global z; ≅ pure { x1 = z; x2 = z; } ≅ x1 = z0; x2 = z0 26

  27. Transaction retry atomic void apply_f() { int x, fx; while (true) { acq(m); A ↑ _ x = z; rel(m); A;A ↑ A;A = C ↑ C fx = f(x); A ↑ A acq(m); if (x == z) { z = fx; rel(m); break; } rel(m); } } 27

  28. Transaction retry atomic void apply_f() { int x, fx; while (true) { pure { acq(m); B ↑ _ x = z; rel(m); B;B ↑ B;A } (B ↑ A)* = B ↑ A = B ↑ A fx = f(x); B ↑ A pure { acq(m); if (x == z) { z = fx; rel(m); break; } rel(m); 28 }

  29. Transaction retry atomic void apply_f() { int x, fx;  The pure blocks allow us to prove while (true) { apply_f abstractly atomic pure {  We can prove on the abstraction acq(m); x = z; that z is updated to f(z) atomically rel(m); } fx = f(x); pure { acq(m); if (x == z) { z = fx; rel(m); break; } rel(m); 29 }

  30. Transaction retry atomic void apply_f() { atomic void apply_f() { int x, fx; int x, fx; while (true) { while (true) { pure { acq(m); x = z; skip; rel(m); } fx = f(x); fx = f(x); pure { if (*) acq(m); // normal execution if (x == z) { z = fx; rel(m); break; } skip; rel(m); else } // exceptional execution } acq(m); assume(x==z); } z =fx; rel(m); break; 30 }

  31. Lock-free synchronization  Load-linked: x = LL(z) – loads the value of z into x  Store-conditional: f = SC(z,v) – if no SC has happened since the last LL by this thread • store the value of v into z and set f to true – otherwise • set f to false 31

  32. Scenarios x = LL(z) f = SC(z,v) Success f = SC(z,v) Failure f’ = SC(z,v’) f = SC(z,v) x = LL(z) Failure x = LL(z) f’ = SC(z,v’) f = SC(z,v) Failure 32

  33. Lock-free atomic increment atomic void increment() { int x; while (true) { x = LL(z); x = x + 1; if (SC(z,x)) break; } } 33

  34. Modeling LL-SC  Global variable zSet initialized to { } – contains ids of threads who have performed the operation LL(z) since the last SC(z,v) x = LL(z) ≅ x = z; zSet = zSet ∪ { tid }; f = SC(z,v) ≅ if (tid ∈ zSet) { z = v; zSet = { }; f = true; } else { f = false; } 34

  35. Modeling LL-SC  Global variable zSet initialized to { } – contains the id of the unique thread that has performed the operation LL(z) since the last SC(z,v) and whose SC(z,v’) is destined to succeed x = LL(z) ≅ if (*) LL-Success(x,z) { assume(zSet = {}); zSet = { tid }; x = z; } else { x = z; } f = SC(z,v) ≅ if (tid ∈ zSet) { z = v; zSet = { }; f = true; } else 35 { f = false; }

  36. Modeling LL-SC  Global variable zSet initialized to { } – contains the id of the unique thread that has performed the operation LL(z) since the last SC(z,v) and whose SC(z,v’) is destined to succeed  LL-Success(x,z) is a x = LL(z) ≅ if (*) right mover LL-Success(x,z);  SC(z,v) is a left else mover x = z; … provided stores to z f = SC(z,v) ≅ performed only through SC [Wang-Stoller 2005] if (tid ∈ zSet) { z = v; zSet = { }; f = true; } else 36

  37. Lock-free atomic increment atomic void increment() { atomic void increment() { int x; int x; while (true) { while (true) { if (*) x = LL(z); LL-Success(x,z); x = x + 1; else if (SC(z,x)) break; x = z; } x = x + 1; } if (SC(z,x)) break; } } 37

  38. Lock-free atomic increment atomic void increment() { atomic void increment() { int x; int x; while (true) { while (true) { pure { pure { if (*) if (*) LL-Success(x,z); LL-Success(x,z); else x = x + 1; B ↑ (R;B;L) (B ↑ A)* x = z; // SC succeeds =B ↑ A = B ↑ A x = x + 1; SC(z,x); break; if (SC(z,x)) break; else } x = z; x = x + 1; } // SC fails } } } } 38

  39. Atomicity and Purity Effect System  Enforces properties for abstract semantics – pure blocks are reducible and side-effect free – atomic blocks are reducible  Leverages other analyses – race-freedom – control-flow – side-effect 39

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