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

exploiting purity for atomicity
SMART_READER_LITE
LIVE PREVIEW

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)


slide-1
SLIDE 1

1

Exploiting Purity for Atomicity

slide-2
SLIDE 2

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; }

slide-3
SLIDE 3

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) (fails) (succeeds)

slide-4
SLIDE 4

4

 Non-Serial Execution:  Serial Execution:  Atomic but not reducible

CAS(m,0,1) CAS(m,0,1) CAS(m,0,1) (fails) (fails) (succeeds) CAS(m,0,1) (succeeds)

slide-5
SLIDE 5

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; }

slide-6
SLIDE 6

6

alloc

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

slide-7
SLIDE 7

7

alloc is not Atomic

 There are non-serial executions with no equivalent serial executions

slide-8
SLIDE 8

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]); }

slide-9
SLIDE 9

9

 Non-Serial Execution:  Serial Executions:

loop for b[0] t = 1 free(0) loop for b[1] free(1) loop for b[0] free(0) loop for b[1] free(1) loop for b[0] free(0) free(1) t = -1 t = 0 loop for b[0] free(0) free(1) t = 0

m[0] = m[1] = 0; b[0] = b[1] = false; t = alloc(); || free(0); free(1);

slide-10
SLIDE 10

10

Extending Atomicity

 Atomicity doesn't always hold for methods that are "intuitively atomic"

– serializable but not reducible (busy_acquire) – not serializable (alloc)

 Examples

– initialization – resource allocation – wait/notify – caches – commit/retry transactions

slide-11
SLIDE 11

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); } }

slide-12
SLIDE 12

12

 Abstract execution semantics:

– treat normal execution of pure blocks as the skip statement

Purity and Abstraction

acq(m) test(x) rel(m) acq(m) test(x) rel(m) skip

slide-13
SLIDE 13

13

Abstraction

 Abstract 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

slide-14
SLIDE 14

14

Busy Acquire

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

slide-15
SLIDE 15

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) skip skip CAS(m,0,1)

(Concrete) (Abstract) (Reduced Abstract)

slide-16
SLIDE 16

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; }

slide-17
SLIDE 17

17

Abstract Execution of alloc

acq(m[0]) test(b[0]) rel(m[0]) acq(m[1]) test(b[1]) rel(m[1]) b[1]=false skip acq(m[1]) test(b[1]) rel(m[1]) b[1]=false (Abstract) (Reduced Abstract)

slide-18
SLIDE 18

18

 Abstract semantics admits more executions  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???

Abstraction

skip acq(m[1]) test(b[1]) rel(m[1]) b[1]=false (Abstract) free(0) free(1)

slide-19
SLIDE 19

19

Type Checking

atomic void deposit(int n) { acquire(this); int j = bal; bal = j + n; release(this); }

R B B L

((R;B);B);L = (R;B);L = R;L = A atomic void depositLoop() { while (true) { deposit(10); } } (A)* = C ⇒ ERROR

A

slide-20
SLIDE 20

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]); return i; } release(m[i]); i++; } return -1; }

A A* = C

slide-21
SLIDE 21

21

Type Checking with Purity

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; }

A↑A B↑A (B↑A)* = B*↑(B*;A) = B↑A

slide-22
SLIDE 22

22

Double Checked Initialization

atomic void init() { if (x != null) return; acquire(l); if (x == null) x = new(); release(l); }

slide-23
SLIDE 23

23

Double Checked Initialization

atomic void init() { if (x != null) return; acquire(l); if (x == null) x = new(); release(l); } conflicting accesses

test(x) acq(l) test(x) x=new() rel(l)

(Concrete)

slide-24
SLIDE 24

24

Double Checked Initialization

atomic void init() { if (x != null) return; acquire(l); if (x == null) x = new(); release(l); }

test(x) acq(l) test(x) x=new() rel(l)

(Concrete)

A↑A A↑_

slide-25
SLIDE 25

25

Double Checked Initialization

atomic void init() { pure { if (x != null) return; } acquire(l); if (x == null) x = new(); release(l); }

test(x) acq(l) test(x) x=new() rel(l) skip acq(l) test(x) x=new() rel(l)

(Abstract) (Reduced Abstract) (Concrete)

B↑A A↑_ B;A↑A = A↑A

slide-26
SLIDE 26

26

Modifying local variables in pure blocks

 Partition variables into global and local variables  Allow modification of local variables

pure { acq(m); x = z; rel(m); } ≅ pure { x = z; } ≅ x = z0; local x; global z; pure { acq(m); x1 = z; x2 = z; rel(m); } ≅ pure { x1 = z; x2 = z; } ≅ x1 = z0; x2 = z0 local x1, x2; global z; pure { acq(m); rel(m); } ≅ skip

slide-27
SLIDE 27

27

Transaction retry

atomic void apply_f() { int x, fx; while (true) { acq(m); x = z; rel(m); fx = f(x); acq(m); if (x == z) { z = fx; rel(m); break; } rel(m); } }

A↑_ A↑A A;A↑A;A = C↑C

slide-28
SLIDE 28

28

Transaction retry

atomic void apply_f() { int x, fx; while (true) { pure { acq(m); x = z; rel(m); } fx = f(x); pure { acq(m); if (x == z) { z = fx; rel(m); break; } rel(m); }

B↑_ B↑A (B↑A)* = B↑A B;B↑B;A = B↑A

slide-29
SLIDE 29

29

Transaction retry

atomic void apply_f() { int x, fx; while (true) { pure { acq(m); x = z; rel(m); } fx = f(x); pure { acq(m); if (x == z) { z = fx; rel(m); break; } rel(m); }

 The pure blocks allow us to prove apply_f abstractly atomic  We can prove on the abstraction that z is updated to f(z) atomically

slide-30
SLIDE 30

30

Transaction retry

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

slide-31
SLIDE 31

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
slide-32
SLIDE 32

32

Scenarios

x = LL(z) f = SC(z,v)

Success

f = SC(z,v)

Failure

x = LL(z) f = SC(z,v)

Failure

f’ = SC(z,v’) f’ = SC(z,v’) x = LL(z) f = SC(z,v)

Failure

slide-33
SLIDE 33

33

Lock-free atomic increment

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

slide-34
SLIDE 34

34

Modeling LL-SC

x = LL(z) ≅ x = z; zSet = zSet ∪ { tid }; f = SC(z,v) ≅ if (tid ∈ zSet) { z = v; zSet = { }; f = true; } else { f = false; }

 Global variable zSet initialized to { }

– contains ids of threads who have performed the operation LL(z) since the last SC(z,v)

slide-35
SLIDE 35

35

Modeling LL-SC

x = LL(z) ≅ if (*) { assume(zSet = {}); zSet = { tid }; x = z; } else { x = z; }  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)

f = SC(z,v) ≅ if (tid ∈ zSet) { z = v; zSet = { }; f = true; } else { f = false; }

slide-36
SLIDE 36

36

Modeling LL-SC

x = LL(z) ≅ if (*) LL-Success(x,z); else x = z; f = SC(z,v) ≅ if (tid ∈ zSet) { z = v; zSet = { }; f = true; } else  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 right mover  SC(z,v) is a left mover

… provided stores to z performed only through SC [Wang-Stoller 2005]

slide-37
SLIDE 37

37

Lock-free atomic increment

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

slide-38
SLIDE 38

38

Lock-free atomic increment

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

slide-39
SLIDE 39

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

slide-40
SLIDE 40

40

Summary

 Atomicity

– enables sequential analysis – common in practice

 Purity enables reasoning about atomicity at an abstract level

– matches programmer intuition – more effective checkers