Slide 1

Slide 1 text

Can we make concurrency in Scala safer? 1 Philipp Haller KTH Royal Institute of Technology Scala World 2016 Rheged Centre, Lake District, UK

Slide 2

Slide 2 text

Concurrency is Difficult 2

Slide 3

Slide 3 text

Concurrency is Difficult Hazards: • Race conditions • Deadlocks • Livelocks • etc. 2

Slide 4

Slide 4 text

Concurrency is Difficult Hazards: • Race conditions • Deadlocks • Livelocks • etc. 2 Remedies?

Slide 5

Slide 5 text

Concurrency is Difficult Hazards: • Race conditions • Deadlocks • Livelocks • etc. 2 Monitors Futures, promises Async/await STM Actors Join-calculus Agents CSP Reactive streams … Remedies?

Slide 6

Slide 6 text

Concurrency is Difficult Hazards: • Race conditions • Deadlocks • Livelocks • etc. 2 Monitors Futures, promises Async/await STM Actors Join-calculus Agents CSP Reactive streams … Remedies? Programming models as libraries typically can’t prevent these hazards statically!

Slide 7

Slide 7 text

Example Image data 3

Slide 8

Slide 8 text

Example Image data apply filter 3

Slide 9

Slide 9 text

Example Image data apply filter 3

Slide 10

Slide 10 text

Example Image data apply filter Image processing pipeline: filter 1 filter 2 3

Slide 11

Slide 11 text

Example Image data apply filter Image processing pipeline: filter 1 filter 2 Pipeline stages run concurrently 3

Slide 12

Slide 12 text

Example: Implementation 4

Slide 13

Slide 13 text

Example: Implementation • Assumptions: • Image data large • Main memory expensive 4

Slide 14

Slide 14 text

Example: Implementation • Assumptions: • Image data large • Main memory expensive • Approach for high performance: • In-place update of image buffers • Pass mutable buffers by-reference 4

Slide 15

Slide 15 text

Example: Problem Easy to produce data races: 1. Stage 1 sends a reference to a buffer to stage 2 2. Following the send, both stages have a reference to the same buffer 3. Stages can concurrently access the buffer 5

Slide 16

Slide 16 text

Preventing Data Races 6

Slide 17

Slide 17 text

Preventing Data Races • Approach: safe transfer of ownership 6

Slide 18

Slide 18 text

Preventing Data Races • Approach: safe transfer of ownership • Sending stage loses ownership 6

Slide 19

Slide 19 text

Preventing Data Races • Approach: safe transfer of ownership • Sending stage loses ownership • Compiler prevents sender from accessing objects that have been transferred 6

Slide 20

Slide 20 text

Preventing Data Races • Approach: safe transfer of ownership • Sending stage loses ownership • Compiler prevents sender from accessing objects that have been transferred • Advantages: 6

Slide 21

Slide 21 text

Preventing Data Races • Approach: safe transfer of ownership • Sending stage loses ownership • Compiler prevents sender from accessing objects that have been transferred • Advantages: • No run-time overhead 6

Slide 22

Slide 22 text

Preventing Data Races • Approach: safe transfer of ownership • Sending stage loses ownership • Compiler prevents sender from accessing objects that have been transferred • Advantages: • No run-time overhead • Safety does not compromise performance 6

Slide 23

Slide 23 text

Preventing Data Races • Approach: safe transfer of ownership • Sending stage loses ownership • Compiler prevents sender from accessing objects that have been transferred • Advantages: • No run-time overhead • Safety does not compromise performance • Errors caught at compile time 6

Slide 24

Slide 24 text

Ownership Transfer in Scala 7

Slide 25

Slide 25 text

Ownership Transfer in Scala • Enter LaCasa 7

Slide 26

Slide 26 text

Ownership Transfer in Scala • Enter LaCasa • LaCasa: Scala extension for affine references 7

Slide 27

Slide 27 text

Ownership Transfer in Scala • Enter LaCasa • LaCasa: Scala extension for affine references • “Transferable” references 7

Slide 28

Slide 28 text

Ownership Transfer in Scala • Enter LaCasa • LaCasa: Scala extension for affine references • “Transferable” references • At most one owner per transferable reference 7

Slide 29

Slide 29 text

Affine References in Scala 8

Slide 30

Slide 30 text

Affine References in Scala • LaCasa provides affine references by combining two concepts: 8

Slide 31

Slide 31 text

Affine References in Scala • LaCasa provides affine references by combining two concepts: • Access permissions 8

Slide 32

Slide 32 text

Affine References in Scala • LaCasa provides affine references by combining two concepts: • Access permissions • Encapsulated boxes 8

Slide 33

Slide 33 text

Access Permissions 9

Slide 34

Slide 34 text

Access Permissions • Access to transferable objects controlled by implicit permissions 9

Slide 35

Slide 35 text

Access Permissions • Access to transferable objects controlled by implicit permissions CanAccess { type C } 9

Slide 36

Slide 36 text

Access Permissions • Access to transferable objects controlled by implicit permissions • Type member C uniquely identifies box CanAccess { type C } 9

Slide 37

Slide 37 text

Access Permissions • Access to transferable objects controlled by implicit permissions • Type member C uniquely identifies box CanAccess { type C } Box[T] { type C } 9

Slide 38

Slide 38 text

Matching Boxes and Permissions • Type members match permissions and boxes • Via path-dependent types • Example: def method(box: Box[T]) (implicit p: CanAccess { type C = box.C }): Unit 10

Slide 39

Slide 39 text

Matching Boxes and Permissions • Type members match permissions and boxes • Via path-dependent types • Example: def method(box: Box[T]) (implicit p: CanAccess { type C = box.C }): Unit 10

Slide 40

Slide 40 text

Creating Boxes and Permissions 11

Slide 41

Slide 41 text

Creating Boxes and Permissions class Message { var arr: Array[Int] = _ } 11

Slide 42

Slide 42 text

Creating Boxes and Permissions mkBox[Message] { packed => } class Message { var arr: Array[Int] = _ } 11

Slide 43

Slide 43 text

Creating Boxes and Permissions mkBox[Message] { packed => } class Message { var arr: Array[Int] = _ } LaCasa library 11

Slide 44

Slide 44 text

Creating Boxes and Permissions mkBox[Message] { packed => } class Message { var arr: Array[Int] = _ } 11

Slide 45

Slide 45 text

Creating Boxes and Permissions mkBox[Message] { packed => } class Message { var arr: Array[Int] = _ } implicit val access = packed.access val theBox = packed.box … 11

Slide 46

Slide 46 text

Creating Boxes and Permissions mkBox[Message] { packed => } class Message { var arr: Array[Int] = _ } sealed trait Packed[+T] { val box: Box[T] implicit val access: CanAccess { type C = box.C } } implicit val access = packed.access val theBox = packed.box … 11

Slide 47

Slide 47 text

Accessing Boxes • Boxes are encapsulated • Boxes must be opened for access 12

Slide 48

Slide 48 text

Accessing Boxes • Boxes are encapsulated • Boxes must be opened for access mkBox[Message] { packed => implicit val access = packed.access val theBox = packed.box theBox open { msg => msg.arr = Array(1, 2, 3, 4) } } 12

Slide 49

Slide 49 text

Accessing Boxes • Boxes are encapsulated • Boxes must be opened for access mkBox[Message] { packed => implicit val access = packed.access val theBox = packed.box theBox open { msg => msg.arr = Array(1, 2, 3, 4) } } Requires implicit access permission 12

Slide 50

Slide 50 text

Consuming Permissions Example: transfering a box from one actor to another consumes its access permission 13

Slide 51

Slide 51 text

Consuming Permissions Example: transfering a box from one actor to another consumes its access permission mkBox[Message] { packed => implicit val access = packed.access val theBox = packed.box … someActor.send(theBox) // illegal to access `theBox` here! } 13

Slide 52

Slide 52 text

Consuming Permissions Example: transfering a box from one actor to another consumes its access permission mkBox[Message] { packed => implicit val access = packed.access val theBox = packed.box … someActor.send(theBox) // illegal to access `theBox` here! } How to enforce this? 13

Slide 53

Slide 53 text

Permissions and Continuations 14

Slide 54

Slide 54 text

Permissions and Continuations • Make implicit permission unavailable in continuation of permission-consuming call 14

Slide 55

Slide 55 text

Permissions and Continuations • Make implicit permission unavailable in continuation of permission-consuming call • Scala’s type system is flow-insensitive => use continuation passing 14

Slide 56

Slide 56 text

Permissions and Continuations • Make implicit permission unavailable in continuation of permission-consuming call • Scala’s type system is flow-insensitive => use continuation passing • Restrict continuation to exclude consumed permission 14

Slide 57

Slide 57 text

Continuation-Passing Style mkBox[Message] { packed => implicit val access = packed.access val theBox = packed.box … someActor.send(theBox) { // make `access` unavailable … } } 15

Slide 58

Slide 58 text

Restricting Continuations • Continuation disallows capturing variables of the type of access • Leverage spores 16

Slide 59

Slide 59 text

Restricting Continuations • Continuation disallows capturing variables of the type of access • Leverage spores def send(msg: Box[T]) (cont: NullarySpore[Unit] { type Excluded = msg.C }) (implicit p: CanAccess { type C = msg.C }): Nothing 16

Slide 60

Slide 60 text

Restricting Continuations • Continuation disallows capturing variables of the type of access • Leverage spores def send(msg: Box[T]) (cont: NullarySpore[Unit] { type Excluded = msg.C }) (implicit p: CanAccess { type C = msg.C }): Nothing 16

Slide 61

Slide 61 text

Intermezzo: Spores Spore = closure • with explicit environment • with function type refined by captured types val msg = "hello" spore { val s = msg (x: Int) => s”Message and param: ($s, $x)” } 17

Slide 62

Slide 62 text

Intermezzo: Spores Spore = closure • with explicit environment • with function type refined by captured types val msg = "hello" spore { val s = msg (x: Int) => s”Message and param: ($s, $x)” } Spore[Int, String] { type Captured = String } 17

Slide 63

Slide 63 text

Spore Trait trait Spore[-T, +R] extends Function1[T, R] { type Captured type Excluded } 18

Slide 64

Slide 64 text

Implicit Conversions • Goal:
 Implicitly convert function literals to spores • Enables more lightweight syntax • Enables checking excluded types • Example: 19 type SafeSpore[A, B] = Spore[A, B] { type Excluded = SomeExcludedType } val s: SafeSpore[A, B] = (x: A) => { … }

Slide 65

Slide 65 text

Recall Example mkBox[Message] { packed => implicit val access = packed.access val theBox = packed.box … someActor.send(theBox) { // `access` unavailable … } } 20

Slide 66

Slide 66 text

Encapsulation Problem: not all types safe to transfer! 21

Slide 67

Slide 67 text

Encapsulation Problem: not all types safe to transfer! 21 class Message { var arr: Array[Int] = _ def leak(): Unit = { SomeObject.fld = arr } } object SomeObject { var fld: Array[Int] = _ }

Slide 68

Slide 68 text

Encapsulation • Ensuring absence of data races (“concurrency safety”) requires restricting types put into boxes • Requirements for “safe” classes:* • Methods only access parameters and this • Methods only instantiate “safe” classes • Types of fields are “safe” 22 * simplified

Slide 69

Slide 69 text

Encapsulation • Ensuring absence of data races (“concurrency safety”) requires restricting types put into boxes • Requirements for “safe” classes:* • Methods only access parameters and this • Methods only instantiate “safe” classes • Types of fields are “safe” 22 “Safe” = conforms to object capability model * simplified

Slide 70

Slide 70 text

Object Capabilities in Scala • How common are object-capability safe classes in Scala? • Results from empirical study: 23

Slide 71

Slide 71 text

LaCasa: Summary • Encapsulated boxes • Insight: object capabilities for alias protection • Access permissions via implicits + path- dependent types • Can statically prevent data races • Not shown: storing boxes in unique fields 24

Slide 72

Slide 72 text

More Details • Paper to appear at OOPSLA ’16 • Technical report: arxiv.org/abs/1607.05609 • Formal model mechanized in Coq 25

Slide 73

Slide 73 text

Status and Plans • Prototype implementation: compiler plug-in for Scala 2.11 and actor integration (straw man) • https://github.com/phaller/lacasa • Plans: • Adapters for Akka, Reactors, etc. • Port to Dotty Scala Compiler • Implicit functions for permissions 26

Slide 74

Slide 74 text

Status and Plans • Prototype implementation: compiler plug-in for Scala 2.11 and actor integration (straw man) • https://github.com/phaller/lacasa • Plans: • Adapters for Akka, Reactors, etc. • Port to Dotty Scala Compiler • Implicit functions for permissions 26 Thank you!