Relaxed Linear References for Lock-free Data Structures Elias - - PowerPoint PPT Presentation

relaxed linear references for lock free data structures
SMART_READER_LITE
LIVE PREVIEW

Relaxed Linear References for Lock-free Data Structures Elias - - PowerPoint PPT Presentation

Relaxed Linear References for Lock-free Data Structures Elias Castegren , Tobias Wrigstad ECOOP17, Barcelona sa Structured Aliasing http://www.kristianstadik.se/www/wordpress/wp-content/uploads/2016/06/midsommar.jpg


slide-1
SLIDE 1

Relaxed Linear References for Lock-free Data Structures

Elias Castegren, Tobias Wrigstad ECOOP’17, Barcelona

sa Structured Aliasing

slide-2
SLIDE 2

http://www.kristianstadik.se/www/wordpress/wp-content/uploads/2016/06/midsommar.jpg http://www.vikstroms.nu/cms/wp-content/uploads/2014/06/Midsommar-2014.png http://pix.tagapi.aller.se/sf.php?src=http://stilexperten.mabra.com/wp-content/uploads/sites/ 36/2016/06/383ab62d304910ab9e9bb9a0f197d918.jpg&w=640&h=400&a=c

2

slide-3
SLIDE 3

http://2.bp.blogspot.com/-ed71CELc5pY/T-h5pPLBR-I/AAAAAAAAAck/9hILp4JdDJg/s1600/annicasxperia+juni+026.jpg

3

slide-4
SLIDE 4

https://upload.wikimedia.org/wikipedia/en/e/e7/The_Swedish_Chef.jpg https://68.media.tumblr.com/5cab6229774bfb3fa6a35bf3479b6f8c/ tumblr_inline_nht61yoHMA1r0y63m.jpg https://s3.amazonaws.com/gs-geo-images/ 3738763b-2dc4-47fb-81d2-345908ddbc07_l.jpg

”Små grodorna är lustiga att se”

4

slide-5
SLIDE 5

Lock-free Programming

  • Fine-grained concurrency achieved by following some protocol
  • 1. Read a shared value (speculation)
  • 2. Perform some local computation
  • 3. If the speculation is still valid, make the local value global (publication)
  • Requires atomic operations, e.g. CAS (compare-and-swap)

def CAS(x.f, y, z) : bool { if (x.f == y) { x.f = z; return true; } else return false; }

} Performed atomically

5

slide-6
SLIDE 6

Example: A Lock-free Stack [Treiber]

struct Node { var elem : T; val next : Node; } next elem struct Stack { var top : Node; } Stack N N N T T T top

6

slide-7
SLIDE 7

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T top

7

slide-8
SLIDE 8

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

  • ldTop

top

7

slide-9
SLIDE 9

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

  • ldTop
  • ldTop

top

7

slide-10
SLIDE 10

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

  • ldTop
  • ldTop

top

7

slide-11
SLIDE 11

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

  • ldTop
  • ldTop

top

7

slide-12
SLIDE 12

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

  • ldTop
  • ldTop

top

7

slide-13
SLIDE 13

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

  • ldTop
  • ldTop

top

7

slide-14
SLIDE 14

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

  • ldTop
  • ldTop

top

7

slide-15
SLIDE 15

Example: A Lock-free Stack [Treiber]

Stack N N N def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

  • ldTop
  • ldTop

top

7

slide-16
SLIDE 16

Example: A Lock-free Queue [Michael & Scott]

struct Node { var elem : T; var next : Node; } next elem struct Queue { var first : Node; var last : Node } Queue N N N T T T first last

8

slide-17
SLIDE 17

Example: A Lock-free Queue [Michael & Scott]

def enqueue(q : Queue, x : Elem) : void { val n = new Node(x); val done = false; while(not done) { val oldLast = q.last; done = CAS(oldLast.next, null, n); q.last = oldLast.next; } } Queue N N N T T T

9

slide-18
SLIDE 18

Example: A Lock-free Queue [Michael & Scott]

def enqueue(q : Queue, x : Elem) : void { val n = new Node(x); val done = false; while(not done) { val oldLast = q.last; done = CAS(oldLast.next, null, n); q.last = oldLast.next; } } Queue N N N T T T N T n

9

slide-19
SLIDE 19

Example: A Lock-free Queue [Michael & Scott]

def enqueue(q : Queue, x : Elem) : void { val n = new Node(x); val done = false; while(not done) { val oldLast = q.last; done = CAS(oldLast.next, null, n); q.last = oldLast.next; } } Queue N N N T T T N T n

  • ldLast

9

slide-20
SLIDE 20

Example: A Lock-free Queue [Michael & Scott]

def enqueue(q : Queue, x : Elem) : void { val n = new Node(x); val done = false; while(not done) { val oldLast = q.last; done = CAS(oldLast.next, null, n); q.last = oldLast.next; } } Queue N N N T T T N T n

  • ldLast

9

slide-21
SLIDE 21

Example: A Lock-free Queue [Michael & Scott]

def enqueue(q : Queue, x : Elem) : void { val n = new Node(x); val done = false; while(not done) { val oldLast = q.last; done = CAS(oldLast.next, null, n); q.last = oldLast.next; } } Queue N N N T T T N T n

  • ldLast
  • ldLast

9

slide-22
SLIDE 22

Example: A Lock-free Queue [Michael & Scott]

def enqueue(q : Queue, x : Elem) : void { val n = new Node(x); val done = false; while(not done) { val oldLast = q.last; done = CAS(oldLast.next, null, n); q.last = oldLast.next; } } Queue N N N T T T N T n

  • ldLast
  • ldLast

9

slide-23
SLIDE 23
  • Speculation
  • Publication
  • Acquisition

def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } }

Finding Patterns in Lock-free Programming

def enqueue(q : Queue, x : Elem) : void { val n = new Node(x); val done = false; while(not done) { val oldLast = q.last; done = CAS(oldLast.next, null, n); q.last = oldLast.next; } }

10

def push(s : Stack, x : T) : void { ... } def dequeue(q : Queue) : T { ... }

slide-24
SLIDE 24
  • Speculation
  • Publication
  • Acquisition

def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } }

Finding Patterns in Lock-free Programming

def enqueue(q : Queue, x : Elem) : void { val n = new Node(x); val done = false; while(not done) { val oldLast = q.last; done = CAS(oldLast.next, null, n); q.last = oldLast.next; } } Field ’last’ is subject to races and must be written to with a CAS.

10

def push(s : Stack, x : T) : void { ... } def dequeue(q : Queue) : T { ... }

Our idea: Give these concepts meaningful types!

slide-25
SLIDE 25

Why Types?

  • Why not model checking, concurrent separation logic, etc.?
  • Modularity: no need for inter-procedural analysis
  • Language expressivity: allow concurrent data structures storing linear references

X X X

11

slide-26
SLIDE 26

Why Types?

  • Why not model checking, concurrent separation logic, etc.?
  • Modularity: no need for inter-procedural analysis
  • Language expressivity: allow concurrent data structures storing linear references

X X X

12

slide-27
SLIDE 27

Linear References for Ownership Transfer

  • A linear reference is the only reference to an object
  • Prohibits shared access to objects
  • Makes ownership explicit

X

13

slide-28
SLIDE 28

Linear References for Ownership Transfer

  • A linear reference is the only reference to an object
  • Prohibits shared access to objects
  • Makes ownership explicit
  • A correct lock-free data-structure can guarantee exclusive ownership of an element

— Can we use linear references to capture this?

X

13

slide-29
SLIDE 29

Linear References and Lock-free Programming

14

def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } Aliasing! Aliasing! Stack N N N T T T Sharing!

slide-30
SLIDE 30

Relaxed Linear References

  • Separate aliasing from ownership
  • Unbounded aliasing, linear ownership
  • Ownership can be atomically transferred between aliases

15

1 1 1

slide-31
SLIDE 31

Relaxed Linear References

  • Separate aliasing from ownership
  • Unbounded aliasing, linear ownership
  • Ownership can be atomically transferred between aliases

16

1 1 1

slide-32
SLIDE 32

Relaxed Linear References

  • Separate aliasing from ownership
  • Unbounded aliasing, linear ownership
  • Ownership can be atomically transferred between aliases

17

1 1 1

slide-33
SLIDE 33

Atomic Ownership Transfer with CAS

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f”

19

A B1 C1 1 x y f B2 C2 z 1

CAT

slide-34
SLIDE 34

Atomic Ownership Transfer with CAS

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f”

19

A B1 C1 1 x y f B2 C2 z 1

CAT

slide-35
SLIDE 35

Ownership Transfer in Action

20

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T 1 1

slide-36
SLIDE 36

Ownership Transfer in Action

20

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T 1 1

slide-37
SLIDE 37

Ownership Transfer in Action

20

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T 1 1

slide-38
SLIDE 38

Ownership Transfer in Action

20

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T 1 1

slide-39
SLIDE 39

Ownership Transfer in Action

20

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T 1 1

slide-40
SLIDE 40

Ownership Transfer in Action

21

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T 1

slide-41
SLIDE 41

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f” Unlink from a chain of objects:
 CAT(x.f, y, y.g) — ”Transfer ownership of x.f to y and of y.g to x.f”

22

A B C 1 1 x y f g

Atomic Ownership Transfer with CAS CAT

slide-42
SLIDE 42

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f” Unlink from a chain of objects:
 CAT(x.f, y, y.g) — ”Transfer ownership of x.f to y and of y.g to x.f”

22

A B C 1 1 x y f g

Atomic Ownership Transfer with CAS CAT

slide-43
SLIDE 43

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f” Unlink from a chain of objects:
 CAT(x.f, y, y.g) — ”Transfer ownership of x.f to y and of y.g to x.f”

22

A B C 1 1 x y f g

Atomic Ownership Transfer with CAS CAT

slide-44
SLIDE 44

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f” Unlink from a chain of objects: 
 CAT(x.f, y, y.g) — ”Transfer ownership of x.f to y and of y.g to x.f” Link into a chain of objects:
 CAT(x.f, y.g, y) — ”Transfer ownership of x.f to y.g and of y to x.f”

23

A B C 1 1 x y f g y

Atomic Ownership Transfer with CAS CAT

slide-45
SLIDE 45

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f” Unlink from a chain of objects: 
 CAT(x.f, y, y.g) — ”Transfer ownership of x.f to y and of y.g to x.f” Link into a chain of objects:
 CAT(x.f, y.g, y) — ”Transfer ownership of x.f to y.g and of y to x.f”

23

A B C 1 1 x y f g y

Atomic Ownership Transfer with CAS CAT

slide-46
SLIDE 46

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f” Unlink from a chain of objects: 
 CAT(x.f, y, y.g) — ”Transfer ownership of x.f to y and of y.g to x.f” Link into a chain of objects:
 CAT(x.f, y.g, y) — ”Transfer ownership of x.f to y.g and of y to x.f”

23

A B C 1 1 x y f g y

Atomic Ownership Transfer with CAS CAT

slide-47
SLIDE 47

CAT(x.f, y, z) — ”Transfer ownership of x.f to y and of z to x.f” Unlink from a chain of objects: 
 CAT(x.f, y, y.g) — ”Transfer ownership of x.f to y and of y.g to x.f” Link into a chain of objects:
 CAT(x.f, y.g, y) — ”Transfer ownership of x.f to y.g and of y to x.f”

24

A B C x y f g y

Atomic Ownership Transfer with CAS CAT

Three kinds of CATs

slide-48
SLIDE 48

inear wnership with ock-free L O L CAT

slide-49
SLIDE 49

L O L CAT

slide-50
SLIDE 50

Ownership in LOLCAT

  • References do not own objects, but rather the (mutable) fields of the objects

27

struct Node { var elem : T; val next : Node; } N N T T next elem elem Stack struct Stack { spec top : Node; } top Contended field References which own the var field elem

slide-51
SLIDE 51

Ownership Tracked using Flow-sensitive Types

28

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T

slide-52
SLIDE 52

Ownership Tracked using Flow-sensitive Types

28

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T Node | elem Node | elem

slide-53
SLIDE 53

Ownership Tracked using Flow-sensitive Types

28

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T Node | elem Node | elem Node | elem

slide-54
SLIDE 54

Ownership Tracked using Flow-sensitive Types

28

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T Node | elem Node | elem Node | elem

slide-55
SLIDE 55

Ownership Tracked using Flow-sensitive Types

28

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T Node | elem Node ~ next

  • ldTop : Node ~ next

Node | elem

slide-56
SLIDE 56

Ownership Tracked using Flow-sensitive Types

28

Stack N N N top def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } T T T Node | elem Node ~ next

  • ldTop : Node ~ next

Node | elem

slide-57
SLIDE 57

Restricted References

  • A reference of type Node can access the full node

29

struct Node { var elem : T; val next : Node; } n : Node N T

slide-58
SLIDE 58

Restricted References

  • A reference of type Node can access the full node
  • A reference of type Node | elem cannot access the var field elem

29

struct Node { var elem : T; val next : Node; } n : Node N T n : Node | elem

X

slide-59
SLIDE 59

Restricted References

  • A reference of type Node can access the full node
  • A reference of type Node | elem cannot access the var field elem
  • A reference of type Node ~ next can access the var field elem, but 


has a val field next without ownership

29

struct Node { var elem : T; val next : Node; } N T n : Node | elem N N n : Node ~ next Main invariant: For all aliases of a Node, at most

  • ne alias has ownership of

the var field elem

slide-60
SLIDE 60

A Lock-free Stack

30

struct Node { var elem : T; val next : Node; } struct Stack { var top : Node; } def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAS(s.top, oldTop, oldTop.next)) return oldTop.elem; } } def push(s : Stack, x : T) : void { val newTop = new Node(x); newTop.next = s.top; while(true) { if(CAS(s.top, newTop.next, newTop)) return; newTop.next = s.top; } } next elem Stack N N N T T T top

slide-61
SLIDE 61

A Lock-free Stack in LOLCAT

31

struct Node { var elem : T; val next : Node; } struct Stack { spec top : Node; } def pop(s : Stack) : T { while(true) { val oldTop = s.top; if(CAT(s.top, oldTop, oldTop.next)) return oldTop.elem; } } def push(s : Stack, x : T) : void { val newTop = new Node(x); newTop.next = s.top; while(true) { if(CAT(s.top, newTop.next, newTop)) return; newTop.next = s.top; } } next elem Stack N N N T T T top

slide-62
SLIDE 62

A Lock-free Stack in LOLCAT (more explicit)

32

struct Node { var elem : T; val next : Node; } struct Stack { spec top : Node; } def pop(s : Stack) : T { while(true) { val oldTop = speculate s.top; if(CAT(s.top, oldTop, oldTop.next)) return consume oldTop.elem; } } def push(s : Stack, x : T) : void { val newTop = new Node(consume x); newTop.next = speculate s.top; while(true) { if(CAT(s.top, newTop.next, newTop)) return; newTop.next = speculate s.top; } } next elem Stack N N N T T T top

slide-63
SLIDE 63

Also in the Paper

  • LOLCAT formalized and proven sound

— Linear ownership: at most one alias may access the var fields of an object

  • Three fundamental lock-free data-structures

— Treiber Stack
 — Michael—Scott Queue
 — Tim Harris List

  • Prototype implementation in Encore

— OO support

slide-64
SLIDE 64

Not in the Paper

  • Additional examples (implemented in Encore)

— Skip list [Fomitchev & Ruppert]
 — BST [Ellen et al.]
 — Lazy list-based set [Heller et al.]
 — Spin-locks

  • Type-Assisted Automatic Garbage Collection for Lock-Free Data Structures


[Yang & Wrigstad, ISMM’17] — Pluggable concurrent garbage collector
 — Relies on LOLCAT types to track when objects may be collected

34

slide-65
SLIDE 65

Summary

  • Relaxed linearity: unbounded aliasing with linear ownership

— Access to mutable (var) fields is always exclusive
 — Compare-and-swap for atomic ownership transfer
 — Controlled sharing of certain fields (val and spec)
 — Guarantee absence of (uncontrolled) data-races in lock-free data structures

  • LOLCAT provides meaningful types for common patterns in lock-free programming

— Speculation (Node | elem)
 — Publication (CAT(x.f, y, z))
 — Acquisition (Node ~ next)
 — Types inferred (modulo struct declarations)

  • Prototype implementation in Encore

— Open source: try it yourself!

35

slide-66
SLIDE 66

http://www.kristianstadik.se/www/wordpress/wp-content/uploads/2016/06/midsommar.jpg

Glad Midsommar!

slide-67
SLIDE 67

Summary

  • Relaxed linearity: unbounded aliasing with linear ownership

— Access to mutable (var) fields is always exclusive
 — Compare-and-swap for atomic ownership transfer
 — Controlled sharing of certain fields (val and spec)
 — Guarantee absence of (uncontrolled) data-races in lock-free data structures

  • LOLCAT provides meaningful types for common patterns in lock-free programming

— Speculation (Node | elem)
 — Publication (CAT(x.f, y, z))
 — Acquisition (Node ~ next)
 — Types inferred (modulo struct declarations)

  • Prototype implementation in Encore

— Open source: try it yourself!