Attached and Detached Closures in Actors Dave Clarke, Kiko - - PowerPoint PPT Presentation

attached and detached closures in actors
SMART_READER_LITE
LIVE PREVIEW

Attached and Detached Closures in Actors Dave Clarke, Kiko - - PowerPoint PPT Presentation

Attached and Detached Closures in Actors Dave Clarke, Kiko Fernandez-Reyes, Elias Castegren Tobias Wrigstad, Albert Mingkun Yang KTH Royal Institute of Uppsala University Technology What Tobias Said The Encore programming language


slide-1
SLIDE 1

Attached and Detached Closures in Actors

Elias Castegren Dave Clarke, Kiko Fernandez-Reyes, Tobias Wrigstad, Albert Mingkun Yang

KTH Royal Institute of Technology Uppsala University

slide-2
SLIDE 2

What Tobias Said

  • The Encore programming language
  • Object orientation + actors
  • Guarantees safe sharing of objects between actors
  • Handling both concurrency and parallelism in the actor model
  • Lessons learned & Open questions

!2

slide-3
SLIDE 3

This Talk

  • State-capturing closures in an actor-setting
  • Current and future solutions in Encore
  • Terminology for discussing closure semantics

!3

slide-4
SLIDE 4

We All Like Actors

!4

slide-5
SLIDE 5

We All Like Actors

!5

slide-6
SLIDE 6

Some of Us Like Functional Programming

  • Functional programming plays nicely with the actor model
  • Algebraic data-types
  • Immutability
  • Higher-order functions
  • Examples include Erlang and Elixir

!6

data List a = | Nil | Cons a (List a) 흺 x : t.x + 42

slide-7
SLIDE 7

Some of Us Also Like Object Orientation

  • Actor programming is familiar to OO programmers
  • Actors can be thought of as ”active” objects
  • Sending Messages ≈ Calling Methods
  • OO relies heavily on mutable state and aliasing

⇒Sharing ⇒

!7

slide-8
SLIDE 8

Some of Us Also Like Object Orientation

  • Actor programming is familiar to OO programmers
  • Actors can be thought of as ”active” objects
  • Sending Messages ≈ Calling Methods
  • OO relies heavily on mutable state and aliasing

⇒Sharing ⇒Data-races ⇒Loss of actor isolation!

!8

slide-9
SLIDE 9

Making Actors and OO Play Nice

  • Capability-based languages/systems, type systems
  • Encore
  • Pony [Clebsch et al.]
  • LaCasa (for Scala) [Haller & Loiko]
  • Joelle [Östlund et al.]
  • Relying on delegation of method calls
  • e.g. far references in AmbientTalk [Dedecker et al.]
  • Relying on copying of (passive) objects
  • e.g. Proactive [Caromel et al.]

!9

slide-10
SLIDE 10

Encore Primer/Reminder

!10

active class Actor var count : int val other : Actor def work() : unit val fut = this.other ! compute() val result = get fut this.print(result) end def print(v : Data) : unit this.count += 1 … // Print the value end … end

Actors introduced via classes Message passing Synchronisation via futures

slide-11
SLIDE 11

Capabilities for Concurrency Control

  • Every reference carries a capability (tracked by the type system)
  • linear — No aliases, transfer semantics
  • local — Local to its creating actor
  • read — Read-only reference (no mutable aliases)
  • active — Actor reference (asynchronous communication)

!11

slide-12
SLIDE 12

Capabilities for Concurrency Control

  • Every reference carries a capability (tracked by the type system)
  • linear — No aliases, transfer semantics
  • local — Local to its creating actor
  • read — Read-only reference (no mutable aliases)
  • active — Actor reference (asynchronous communication)

!11

local class Counter var cnt : int … end linear class List var first : Node … end var c = new Counter actor ! foo(c) var l = new List actor ! bar(consume l)

Can’t share local object

slide-13
SLIDE 13

Avoiding Blocking on Futures (Chaining)

!12

active class Actor var count : int val other : Actor def work() : unit val fut = this.other ! compute() val result = get fut this.print(result) end def print(v : Data) : unit this.count += 1 … // Print the value end … end def work_noblock() : unit val fut = this.other ! compute() fut ~~> fun (v : Data) => this.print(v) end

Who runs this closure? Induces waiting times

slide-14
SLIDE 14

Who Runs a Closure?

!13

def work_noblock() : unit val fut = this.other ! compute() fut ~~> fun (v : Data) => this.print(v) end

F

slide-15
SLIDE 15

Who Runs a Closure?

!14

def work_noblock() : unit val fut = this.other ! compute() fut ~~> fun (v : Data) => this.print(v) end

F

def work_noblock2() : unit val fut = this.other ! compute() fut ~~> fun (v : Data) => this ! print(v) end

slide-16
SLIDE 16

Attached and Detached Closures

  • An attached closure is always run by its creating actor
  • A detached closure can be run by any actor

!15

slide-17
SLIDE 17

Attached and Detached Closures

  • An attached closure is always run by its creating actor
  • A detached closure can be run by any actor

!15

slide-18
SLIDE 18

Attached and Detached Closures

  • An attached closure is always run by its creating actor
  • A detached closure can be run by any actor

!15

fun (v : Data) => this.print(v)

slide-19
SLIDE 19

Closures and Capabilities in Encore

  • A closure mirrors the (non-sharable) capabilities it captures

!16

fun (v : Data) => this.print(v) : (Data -> unit) local

slide-20
SLIDE 20

Closures and Capabilities in Encore

  • A closure mirrors the (non-sharable) capabilities it captures

!16

fun (v : Data) => this.print(v) : (Data -> unit) local

slide-21
SLIDE 21

Closures and Capabilities in Encore

  • A closure mirrors the (non-sharable) capabilities it captures

!17

fun (v : Data) => this.print(v) : (Data -> unit) local fun (v : Data) => this ! print(v) : (Data -> unit) active

slide-22
SLIDE 22

Closures and Capabilities in Encore

  • A closure mirrors the (non-sharable) capabilities it captures

!17

fun (v : Data) => this.print(v) : (Data -> unit) local fun (v : Data) => this ! print(v) : (Data -> unit) active

slide-23
SLIDE 23

Closures and Capabilities in Encore

  • A closure mirrors the (non-sharable) capabilities it captures

!17

fun (v : Data) => this.print(v) : (Data -> unit) local fun (v : Data) => this ! print(v) : (Data -> unit)

slide-24
SLIDE 24

Labeling Closures as Attached/Detached

!18

def work_noblock() : unit val fut = this.other ! compute() fut ~~> fun (v : Data) => this.print(v) end def work_noblock2() : unit val fut = this.other ! compute() fut ~~> fun (v : Data) => this ! print(v) end

Captures local state: must be attached! Only captures safe state: can be detached!

slide-25
SLIDE 25

Categorising Closures

  • Tetheredness ∈ {attached, detached}
  • Execution ∈ {synchronous, asynchronous}
  • Sharability ∈ {sharable, unsharable}

!19

Tetheredness Execution Sharability

slide-26
SLIDE 26

Categorising Closures

!20

Tetheredness Execution Sharability Comment Attached Synchronous Sharable Explicitly pass back closure to owner Attached Synchronous Unsharable Current Encore implementation Attached Asynchronous Sharable Encore, when chaining Attached Asynchronous Unsharable Delaying operations Detached Synchronous Sharable Safe ”normal” closures in Encore Detached Synchronous Unsharable Not useful? Detached Asynchronous Sharable Task paralellism Detached Asynchronous Unsharable Not useful?

slide-27
SLIDE 27

Categorising Closures

!20

Tetheredness Execution Sharability Comment Attached Synchronous Sharable Explicitly pass back closure to owner Attached Synchronous Unsharable Current Encore implementation Attached Asynchronous Sharable Encore, when chaining Attached Asynchronous Unsharable Delaying operations Detached Synchronous Sharable Safe ”normal” closures in Encore Detached Synchronous Unsharable Not useful? Detached Asynchronous Sharable Task paralellism Detached Asynchronous Unsharable Not useful? fun (v : Data) => v.foo() + 1 fun (v : Data) => this ! print(v)

slide-28
SLIDE 28

Categorising Closures

!21

Tetheredness Execution Sharability Comment Attached Synchronous Sharable Explicitly pass back closure to owner Attached Synchronous Unsharable Current Encore implementation Attached Asynchronous Sharable Encore, when chaining Attached Asynchronous Unsharable Delaying operations Detached Synchronous Sharable Safe ”normal” closures in Encore Detached Synchronous Unsharable Not useful? Detached Asynchronous Sharable Task paralellism Detached Asynchronous Unsharable Not useful? fun (v : Data) => this.print(v)

slide-29
SLIDE 29

Categorising Closures

!22

Tetheredness Execution Sharability Comment Attached Synchronous Sharable Explicitly pass back closure to owner Attached Synchronous Unsharable Current Encore implementation Attached Asynchronous Sharable Encore, when chaining Attached Asynchronous Unsharable Delaying operations Detached Synchronous Sharable Safe ”normal” closures in Encore Detached Synchronous Unsharable Not useful? Detached Asynchronous Sharable Task paralellism Detached Asynchronous Unsharable Not useful? fut ~~> fun (v : Data) => this.print(v)

slide-30
SLIDE 30

Categorising Closures

!23

Tetheredness Execution Sharability Comment Attached Synchronous Sharable Explicitly pass back closure to owner Attached Synchronous Unsharable Current Encore implementation Attached Asynchronous Sharable Encore, when chaining Attached Asynchronous Unsharable Delaying operations Detached Synchronous Sharable Safe ”normal” closures in Encore Detached Synchronous Unsharable Not useful? Detached Asynchronous Sharable Task paralellism Detached Asynchronous Unsharable Not useful? async (x.foo())

slide-31
SLIDE 31

Categorising Closures

!24

Tetheredness Execution Sharability Comment Attached Synchronous Sharable Explicitly pass back closure to owner Attached Synchronous Unsharable Current Encore implementation Attached Asynchronous Sharable Encore, when chaining Attached Asynchronous Unsharable Delaying operations Detached Synchronous Sharable Safe ”normal” closures in Encore Detached Synchronous Unsharable Not useful? Detached Asynchronous Sharable Task paralellism Detached Asynchronous Unsharable Not useful?

slide-32
SLIDE 32

Categorising Closures

!25

Tetheredness Execution Sharability Comment Attached Synchronous Sharable Explicitly pass back closure to owner Attached Synchronous Unsharable Current Encore implementation Attached Asynchronous Sharable Encore, when chaining Attached Asynchronous Unsharable Delaying operations Detached Synchronous Sharable Safe ”normal” closures in Encore Detached Synchronous Unsharable Not useful? Detached Asynchronous Sharable Task paralellism Detached Asynchronous Unsharable Not useful?

slide-33
SLIDE 33

Related Work (closures)

!26

Scala/Akka All closures detached, synchronous and sharable (unsafe) Pony Synchronous, detached/sharable or attached/unsharable AmbientTalk All closures attached, far references are asynchronous ProActive Attached, synchronous and sharable (deep copy) Erlang No mutable state ABS No closures (functions passed by name)

slide-34
SLIDE 34

Open Questions

  • Sharing attached closures
  • Deadlocking on attached closures
  • Reasoning about timing and scheduling

!27

def run(fn : int -> int) : int fn(42) end def deadlock(a : Actor) : unit var fut = a ! msg() ~~> fun(v) => … var value = get fut end

slide-35
SLIDE 35

Open Questions

  • Sharing attached closures
  • Deadlocking on attached closures
  • Reasoning about timing and scheduling

!27

def run(fn : int -> int) : int fn(42) end def deadlock(a : Actor) : unit var fut = a ! msg() ~~> fun(v) => … var value = get fut end

active

slide-36
SLIDE 36

Open Questions

  • Sharing attached closures
  • Deadlocking on attached closures
  • Reasoning about timing and scheduling

!28

def run(fn : int -> int) : int fn(42) end def deadlock(a : Actor) : unit var fut = a ! msg() ~~> fun(v) => … var value = get fut end

active

def nondeterministic(a : Actor) : unit val oldCount = this.count var fut = a ! msg() fut ~~> fun (v : Data) => this.count += 1 if oldCount == this.count then … end end

slide-37
SLIDE 37

Conclusion

  • Closures capturing state can be made to play nicely with actors
  • Attached closures must be run by their creating actor
  • Detached closures can be run by anyone
  • Some closures must be run asynchronously
  • Encore’s existing type system can express both kinds of closures
  • More work needed to reason about runtime behaviour

!29

slide-38
SLIDE 38

Attached and Detached Closures in Actors Thank you!

slide-39
SLIDE 39

Attached and Detached Closures in Actors

!31

Tetheredness Execution Sharability Comment Attached Synchronous Sharable Explicitly pass back closure to owner Attached Synchronous Unsharable Current Encore implementation Attached Asynchronous Sharable Encore, when chaining Attached Asynchronous Unsharable Delaying operations Detached Synchronous Sharable Safe ”normal” closures in Encore Detached Synchronous Unsharable Not useful? Detached Asynchronous Sharable Task paralellism Detached Asynchronous Unsharable Not useful?

slide-40
SLIDE 40

Capturing Linear Capabilities

!32

var x = new LinearThing() var f = fun () => x var x1 = f() var x2 = f() var x = new LinearThing() var f = fun () => x.foo() async f() async f() var x = new LinearThing() var a = new Actor() var f = fun () => a ! send(x) f() f()

slide-41
SLIDE 41

Bestowed References (Far References)

!33

slide-42
SLIDE 42

Bestowed References (Far References)

!34

slide-43
SLIDE 43

Bestowed References (Far References)

!34

x ! foo()

slide-44
SLIDE 44

Bestowed References (Far References)

!34

λ _ . x.foo() x ! foo()

slide-45
SLIDE 45

Await and Continuations

!35

def foo(a : Actor) : unit var fut = a ! compute() var result = await fut this.print(result) end def foo(a : Actor) : unit var fut = a ! compute() fut ~~> fun (result : Data) => this.print(result) end