reference capabilities for concurrency control• code that needs concurrency control is...

32
Reference Capabilities for Concurrency Control Elias Castegren, Tobias Wrigstad ECOOP’16 sa Structured Aliasing

Upload: others

Post on 10-Jan-2020

6 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Reference Capabilities for Concurrency Control

Elias Castegren, Tobias Wrigstad

ECOOP’16

sa

Structured Aliasing

Page 2: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Concurrency Imposes Many Concerns

List l = … ; l.add(x);

Is it aliased?

Is it accessed concurrently?

Is it thread-safe?

Are its subtypes thread-safe?Is

synchronisation implicit or explicit?

Does it encapsulate its representation?

2

Page 3: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

© Hyperbole and a half

Aliasing and Concurrency Control

assert c1.value() == 42; c1.inc();

assert c1.value() == 43;

May alias? c2.inc();

T1 T2

Properly synchronised?

3

Page 4: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

© Hyperbole and a half

Aliasing and Concurrency Control

assert c1.value() == 42; c1.inc();

assert c1.value() == 43;

May alias? c2.inc();

T1 T2

Over-synchronisation

hurts performance

Properly synchronised?

Under-synchronisation

hurts correctness

3

Page 5: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Aliasing and Parallelism

Data parallelism (a parallel map)

Task parallelism (map f1 || map f2)

Can two nodes alias?

Can two elements share

data?

Is f1(e) || f2(e) safe?

Can two elements alias?

4

Page 6: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

class SynchronizedList<T> var list : List<T>

def add(elem : T) : void lock(); this.list.add(elem); unlock();

def remove(i : int) : T lock(); this.list.remove(i); unlock();

def lookup(i : int) : T lock(); this.list.remove(i); unlock(); …

Concurrency and Code Reuse

class List<T> var first : Link<T>

def add(elem : T) : void …

def remove(i : int) : T …

def lookup(i : int) : T …

class SynchronizedList<T> var list : List<T>

def add(elem : T) : void lock(); this.list.add(elem); unlock();

def remove(i : int) : T lock(); this.list.remove(i); unlock();

def lookup(i : int) : T lock(); this.list.remove(i); unlock(); …

5

var list : List<T>

def add(elem : T) : voidlock();this.list.add(elem);unlock();

def remove(i : int) : Tlock();tmp = this.list.remove(i);unlock();return tmp;

def lookup(i : int) : Tlock();tmp = this.list.remove(i);unlock();return tmp

Page 7: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Summary of Motivation

• Code that needs concurrency control is indistinguishable from code that does not

Correct synchronisation warrants program-wide aliasing analysis

• Concurrency control varies across different usage scenarios

Building concurrency control into a data-structure generates overhead

• Business logic and concurrency control are often orthogonal concerns

lock(); …; unlock();

c2.inc(); Thread-safe?

6

Page 8: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Reference Capabilities

• A capability grants access to some resource

• The type of a capability defines the interface to its object

• A capability assumes exclusive access

Thread-safety ⇒ No data-races

• How thread-safety is achieved is controlled by the capability’s mode

referenceobject

x : Tlinear

7

Page 9: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Modes of Concurrency Control

• Exclusive modes

• Safe modes

linear thread

read

Globally unique Thread-local

Precludes mutating aliases

locked

Implicit locking

!

8

Page 10: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Modes of Concurrency Control

Dominating modes

linear

thread

read

Precludes mutating aliases

lockedSubordinate mode

subordinate

Guarantees mutual exclusion

Encapsulated 9

⎫ ⎬ ⎭

Page 11: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Capability = Trait + Mode

• Capabilities are introduced via traits

• Modes control why they are safe

trait Inc require var cnt : int def inc() : void this.cnt++;

Inclinear — Globally unique increment capability

Inclocked — Implicitly synchronised increment capability

Incread — A read-only increment capability

trait Get require val cnt : int def value() : int return this.cnt;

Getread — A read-only capability for getting the value

If included in a class with a field cnt : int…

…I will provide a method inc()

10

Page 12: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Classes are Composed by Capabilities

class Counter = Inc ⊕ Get { var cnt : int }

linear

thread

locked

read

subordinate

11

Page 13: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Aliasing and Concurrency Control (revisited)

Inc ⊕ threadclass LocalCounter = read Get

Inc ⊕ lockedclass SharedCounter = read Get

assert c1.value() == 42; c1.inc();

assert c1.value() == 43;

c2.inc();

T1 T2May not alias!

Properly synchronised!

Implemented by a readers-writer lock12

Page 14: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Aliasing and Concurrency Control (revisited)

Inc ⊕ threadclass LocalCounter = read Get

Inc ⊕ lockedclass SharedCounter = read Get

assert c1.value() == 42; c1.inc();

assert c1.value() == 43;

c2.inc();

T1 T2May not alias!

Properly synchronised!

Implemented by a readers-writer lock

Also in the paper!

⊕linear read

⊕linear readreadread

⊕linear read12

Page 15: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

• A capability disjunction A ⊕ B can be used as A or B, but not in parallel

• Capabilities that do not share data should be usable in parallel…

• A capability conjunction A ⊗ B can be used as A and B, possibly in parallel

trait Fst { require var fst : int … }

trait Snd { require var snd : int … }

Composite Capabilities

trait Fst { require var fst : int … }

trait Snd { require var snd : int … }

class Pair = Fst ⊗ Snd { var fst : int var snd : int }

linear linear

13

Page 16: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Packing and Unpacking

Fst Sndlet p = new Pair(); let f, s = consume p; finish{ async{f.set(x)} async{s.set(y)} } p = consume f + consume s

Fst Snd

p

14

Page 17: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Packing and Unpacking

Fst Snd

Fst Snd

let p = new Pair(); let f, s = consume p; finish{ async{f.set(x)} async{s.set(y)} } p = consume f + consume s

p

f s

14

Page 18: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Packing and Unpacking

Fst Snd

Fst Snd

let p = new Pair(); let f, s = consume p; finish{ async{f.set(x)} async{s.set(y)} } p = consume f + consume s

p

f s

14

Page 19: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Packing and Unpacking

Fst Snd

Fst Snd

let p = new Pair(); let f, s = consume p; finish{ async{f.set(x)} async{s.set(y)} } p = consume f + consume s

Fst Snd

p

14

Page 20: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Packing and Unpacking

Fst Snd

Fst Snd

let p = new Pair(); let f, s = consume p; finish{ async{f.set(x)} async{s.set(y)} } p = consume f + consume s

Fst Snd

p

14

Also in the paper!

Tree<Pair>

Tree<Fst> Tree<Snd>

Page 21: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Aliasing and Parallelism (revisited)

class Tree = linear Left ⊗ linear Right ⊗ …linear linear

15

Page 22: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Aliasing and Parallelism (revisited)

class Tree = linear Left ⊗ linear Right ⊗ …linear linear

15

No aliasing nodes

No unsafely aliased elements

No unsafe sharing between

elements

Page 23: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Aliasing and Parallelism (revisited)

class Tree = linear Left ⊗ linear Right ⊗ …linear linear

15

No aliasing nodes

No unsafely aliased elements

No unsafe sharing between

elements

(borrowing)

def foreach(t : S(Tree<T>), f : T -> T) : void let l : S(linear Left<T>) , r : S(linear Right<T>) , e : S(linear Element<T>) = consume t; finish { async { foreach(l.getLeft(), f) } async { foreach(r.getRight(), f) } e.apply(f); }

Page 24: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Concurrency and Code Reuse (revisited)

• Reuse traits across different concurrency scenarios

• Separate business logic from concurrency concerns

trait Add<T> { require var first : Link<T> def add(elem : T) : void { … } }

class List<T> = Add<T> ⊕ … { var first : Link<T> }

thread

class SynchronizedList<T> = Add<T> ⊕ … { var first : Link<T> }

locked

Can assume exclusive access

16

Annotations in type declarations only

No effect tracking

or ownership types

Page 25: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Meta-Theoretic Results

• Type system formalised on top of a simple object-oriented language

Finish-Async style parallelism

Reentrant readers-writer locks

• Key result: Well-formed programs preserve safe aliasing

Any two aliases are e.g. composable, synchronised, thread-local, subordinate ⇒ No data-races

17

!

Page 26: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Current and Future Work

• Implementation underway in the context of Encore

Reference capabilities for safe sharing between active objects

17

Page 27: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Current and Future Work

• Implementation underway in the context of Encore

Reference capabilities for safe sharing between active objects

17

def foreach(t : S(Tree<T>), f : T -> T) : void let l : S(linear Left<T>) , r : S(linear Right<T>) , e : S(linear Element<T>) = consume t; finish { async { foreach(l.getLeft(), f) } async { foreach(r.getRight(), f) } e.apply(f); }

Page 28: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Current and Future Work

• Implementation underway in the context of Encore

Reference capabilities for safe sharing between active objects

17

def foreach(t : S(Tree<T>), f : T -> T) : void let l : S(linear Left<T>) , r : S(linear Right<T>) , e : S(linear Element<T>) = consume t; finish { async { foreach(l.getLeft(), f) } async { foreach(r.getRight(), f) } e.apply(f); }

def foreach(t : S(Tree<T>), f : T -> T) : void let l, r, e = consume t; finish { async { foreach(l.getLeft(), f) } async { foreach(r.getRight(), f) } e.apply(f); }

Page 29: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Current and Future Work

• Implementation underway in the context of Encore

Reference capabilities for safe sharing between active objects

17

def foreach(t : S(Tree<T>), f : T -> T) : void let l : S(linear Left<T>) , r : S(linear Right<T>) , e : S(linear Element<T>) = consume t; finish { async { foreach(l.getLeft(), f) } async { foreach(r.getRight(), f) } e.apply(f); }

def foreach(t : S(Tree<T>), f : T -> T) : void let l, r, e = consume t; finish { async { foreach(l.getLeft(), f) } async { foreach(r.getRight(), f) } e.apply(f); }

def foreach(t : S(Tree<T>), f : T -> T) : void finish { async { foreach(t.getLeft(), f) } async { foreach(t.getRight(), f) } t.apply(f); }

Page 30: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Capability

Shared

Atomic Immutable

Unsafe

Lock-Free Active

Subordinate

Optimistic Pessimistic Oblivious

Thread

Locked Read

Exclusive

Linear

18

A Hierarchy of Capabilities

Safe

Page 31: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

Capability

Shared

Atomic Immutable

Unsafe

Lock-Free Active

Subordinate

Optimistic Pessimistic Oblivious

Thread

Locked Read

Exclusive

Linear

18

A Hierarchy of Capabilities

Safe

Page 32: Reference Capabilities for Concurrency Control• Code that needs concurrency control is indistinguishable from code that does not Correct synchronisation warrants program-wide aliasing

List l = … ; l.add(x);

Reference Capabilities Address Many Concerns

Is it aliased?

Is it accessed concurrently?

Is it thread-safe?

Are its subtypes thread-safe?Is

synchronisation implicit or explicit?

Does it encapsulate its representation?

✓✓

✓✓

More examples, full proofs etc. in the technical report!