Upgrade to Pro — share decks privately, control downloads, hide ads and more …

LaCasa: Lightweight Affinity and Object Capabilities in Scala

3b84657fdb075382e3781310ca8a9a70?s=47 Philipp Haller
September 29, 2016
130

LaCasa: Lightweight Affinity and Object Capabilities in Scala

3b84657fdb075382e3781310ca8a9a70?s=128

Philipp Haller

September 29, 2016
Tweet

Transcript

  1. LaCasa: Lightweight Affinity and Object Capabilities in Scala 1 Philipp

    Haller Northeastern University, USA September 29, 2016 KTH Royal Institute of Technology, Sweden
  2. Concurrency is Difficult 2

  3. Concurrency is Difficult Hazards: • Race conditions • Deadlocks •

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

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

    Livelocks • etc. 2 Monitors Futures, promises Actors CSP Join-calculus STM Async/await Reactive streams … Remedies?
  6. Concurrency in Scala • Scala = “Scalable Language” • cf.

    Guy Steele. Growing a Language. OOPSLA ’98 keynote • Concurrency models as libraries • Reuse of compilers, debuggers, and IDEs • Flexible extension and adaptation 3
  7. Concurrency in Scala • Scala = “Scalable Language” • cf.

    Guy Steele. Growing a Language. OOPSLA ’98 keynote • Concurrency models as libraries • Reuse of compilers, debuggers, and IDEs • Flexible extension and adaptation 3 Do not have to guess the answer to the question: Which concurrency model is going to “win”?
  8. Background • Authored or co-authored: • Scala Actors (2006) •

    Scala Joins (2008) • Scala futures/promises (2012) • FlowPools (2012) • Scala Async (2013) • Contributions to Akka, Akka.js projects at Typesafe 4 Haller and Sommers Artima Press, 2012
  9. Background • Authored or co-authored: • Scala Actors (2006) •

    Scala Joins (2008) • Scala futures/promises (2012) • FlowPools (2012) • Scala Async (2013) • Contributions to Akka, Akka.js projects at Typesafe 4 Haller and Sommers Artima Press, 2012 Production use at Twitter, The Guardian and others
  10. Background • Authored or co-authored: • Scala Actors (2006) •

    Scala Joins (2008) • Scala futures/promises (2012) • FlowPools (2012) • Scala Async (2013) • Contributions to Akka, Akka.js projects at Typesafe 4 Haller and Sommers Artima Press, 2012 Production use at Twitter, The Guardian and others Production use at Morgan Stanley, Gawker and others
  11. Background • Authored or co-authored: • Scala Actors (2006) •

    Scala Joins (2008) • Scala futures/promises (2012) • FlowPools (2012) • Scala Async (2013) • Contributions to Akka, Akka.js projects at Typesafe 4 Haller and Sommers Artima Press, 2012 Production use at Twitter, The Guardian and others Production use at Morgan Stanley, Gawker and others The programming-models-as-libraries approach has been successful in Scala!
  12. Problem: Programming-models-as-libraries typically cannot prevent common concurrency hazards statically! 5

  13. Example Image data 6

  14. Example Image data apply filter 6

  15. Example Image data apply filter 6

  16. Example Image data apply filter Image processing pipeline: filter 1

    filter 2 6
  17. Example Image data apply filter Image processing pipeline: filter 1

    filter 2 6 Pipeline stages run concurrently
  18. Example: Implementation 7

  19. Example: Implementation • Assumptions: • Image data large • Main

    memory expensive 7
  20. Example: Implementation • Assumptions: • Image data large • Main

    memory expensive • Approach for high performance: • In-place update of image buffers • Pass mutable buffers by-reference 7
  21. 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 8
  22. Preventing Data Races 9

  23. Preventing Data Races • Approach: safe transfer of ownership 9

  24. Preventing Data Races • Approach: safe transfer of ownership •

    Sending stage loses ownership 9
  25. Preventing Data Races • Approach: safe transfer of ownership •

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

    Sending stage loses ownership • Compiler prevents sender from accessing objects that have been transferred • Advantages: 9
  27. 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 9
  28. 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 9
  29. 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 9
  30. Ownership Transfer in Scala 10

  31. Ownership Transfer in Scala • Enter LaCasa 10

  32. Ownership Transfer in Scala • Enter LaCasa • LaCasa: Scala

    extension for affine references 10
  33. Ownership Transfer in Scala • Enter LaCasa • LaCasa: Scala

    extension for affine references • “Transferable” references 10
  34. Ownership Transfer in Scala • Enter LaCasa • LaCasa: Scala

    extension for affine references • “Transferable” references • At most one owner per transferable reference 10
  35. Motivation 11

  36. Motivation • Interesting applications of linearity/affinity/ ownership transfer 11

  37. Motivation • Interesting applications of linearity/affinity/ ownership transfer • Data-race

    safety 11
  38. Motivation • Interesting applications of linearity/affinity/ ownership transfer • Data-race

    safety • Safe memory management without GC 11
  39. Motivation • Interesting applications of linearity/affinity/ ownership transfer • Data-race

    safety • Safe memory management without GC • Session types (with extensions) 11
  40. Motivation • Interesting applications of linearity/affinity/ ownership transfer • Data-race

    safety • Safe memory management without GC • Session types (with extensions) • Scala for embedded real-time systems? 11
  41. Motivation • Interesting applications of linearity/affinity/ ownership transfer • Data-race

    safety • Safe memory management without GC • Session types (with extensions) • Scala for embedded real-time systems? • No GC/run-time system 11
  42. Isn’t it a Solved Problem? 12

  43. Isn’t it a Solved Problem? • Adoption surprisingly challenging 12

  44. Isn’t it a Solved Problem? • Adoption surprisingly challenging •

    Ownership not available in mainstream languages 12
  45. Isn’t it a Solved Problem? • Adoption surprisingly challenging •

    Ownership not available in mainstream languages • Contender: Rust 12
  46. Isn’t it a Solved Problem? • Adoption surprisingly challenging •

    Ownership not available in mainstream languages • Contender: Rust 12 What about existing languages?
  47. Isn’t it a Solved Problem? • Adoption surprisingly challenging •

    Ownership not available in mainstream languages • Contender: Rust • Own system [1] influential, but: 12 [1] Haller and Odersky. Capabilities for uniqueness and borrowing. ECOOP ’10 What about existing languages?
  48. Isn’t it a Solved Problem? • Adoption surprisingly challenging •

    Ownership not available in mainstream languages • Contender: Rust • Own system [1] influential, but: 12 [2] Gordon et al. Uniqueness and reference immutability for safe parallelism. OOPSLA ’12 [1] Haller and Odersky. Capabilities for uniqueness and borrowing. ECOOP ’10 What about existing languages? Influenced M# language [2] of Microsoft’s Midori project
  49. Isn’t it a Solved Problem? • Adoption surprisingly challenging •

    Ownership not available in mainstream languages • Contender: Rust • Own system [1] influential, but: • Interaction with type inference unclear 12 [2] Gordon et al. Uniqueness and reference immutability for safe parallelism. OOPSLA ’12 [1] Haller and Odersky. Capabilities for uniqueness and borrowing. ECOOP ’10 What about existing languages? Influenced M# language [2] of Microsoft’s Midori project
  50. Isn’t it a Solved Problem? • Adoption surprisingly challenging •

    Ownership not available in mainstream languages • Contender: Rust • Own system [1] influential, but: • Interaction with type inference unclear • Midori project not continued 12 [2] Gordon et al. Uniqueness and reference immutability for safe parallelism. OOPSLA ’12 [1] Haller and Odersky. Capabilities for uniqueness and borrowing. ECOOP ’10 What about existing languages? Influenced M# language [2] of Microsoft’s Midori project
  51. Affine References in Scala 13

  52. Affine References in Scala • LaCasa provides affine references by

    combining two concepts: 13
  53. Affine References in Scala • LaCasa provides affine references by

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

    combining two concepts: • Access permissions • Encapsulated boxes 13
  55. Access Permissions 14

  56. Access Permissions • Access to transferable objects controlled by implicit

    permissions 14
  57. Access Permissions • Access to transferable objects controlled by implicit

    permissions CanAccess { type C } 14
  58. Access Permissions • Access to transferable objects controlled by implicit

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

    permissions • Type member C uniquely identifies box CanAccess { type C } Box[T] { type C } 14
  60. 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 15
  61. 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 15
  62. Creating Boxes and Permissions 16

  63. Creating Boxes and Permissions class Message { var arr: Array[Int]

    = _ } 16
  64. Creating Boxes and Permissions mkBox[Message] { packed => } class

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

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

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

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

    Message { var arr: Array[Int] = _ } sealed trait Packed[+T] { val box: Box[T] val access: CanAccess { type C = box.C } } implicit val access = packed.access val theBox = packed.box … 16
  69. Accessing Boxes • Boxes are encapsulated • Boxes must be

    opened for access 17
  70. 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) } } 17
  71. 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 17
  72. Consuming Permissions Example: transfering a box from one actor to

    another consumes its access permission 18
  73. 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! } 18
  74. 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! } 18 How to enforce this?
  75. Permissions and Continuations 19

  76. Permissions and Continuations • Make implicit permission unavailable in continuation

    of permission-consuming call 19
  77. Permissions and Continuations • Make implicit permission unavailable in continuation

    of permission-consuming call • Scala’s type system is flow-insensitive => use continuation passing 19
  78. 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 19
  79. Continuation-Passing Style mkBox[Message] { packed => implicit val access =

    packed.access val theBox = packed.box … someActor.send(theBox) { // make `access` unavailable … } } 20
  80. Restricting Continuations • Continuation disallows capturing variables of the type

    of access • Leverage spores 21
  81. 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 21
  82. 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 21
  83. Intermezzo: Spores Spore [3] = closure • with explicit environment

    • with function type refined by captured types val msg = "hello" val num = 5 spore { val s = msg val x = num (p: Int) => s”Captured vars and param: $s, $x, $p” } 22 [3] Miller, Haller and Odersky. Spores: a type-based foundation for closures in the age of concurrency and distribution. ECOOP ’14
  84. Intermezzo: Spores Spore [3] = closure • with explicit environment

    • with function type refined by captured types val msg = "hello" val num = 5 spore { val s = msg val x = num (p: Int) => s”Captured vars and param: $s, $x, $p” } Spore[Int, String] { type Captured = (String, Int) } 22 [3] Miller, Haller and Odersky. Spores: a type-based foundation for closures in the age of concurrency and distribution. ECOOP ’14
  85. Spore Trait trait Spore[-T, +R] extends Function1[T, R] { type

    Captured type Excluded } 23
  86. Implicit Conversions • Goal:
 Implicitly convert function literals to spores

    • Enables more lightweight syntax • Enables checking excluded types • Example: 24 type SafeSpore[A, B] = Spore[A, B] { type Excluded = SomeExcludedType } val s: SafeSpore[A, B] = (x: A) => { … }
  87. Recall Example mkBox[Message] { packed => implicit val access =

    packed.access val theBox = packed.box … someActor.send(theBox) { // `access` unavailable … } } 25
  88. Encapsulation Problem: not all types safe to transfer! 26

  89. Encapsulation Problem: not all types safe to transfer! 26 class

    Message { var arr: Array[Int] = _ def leak(): Unit = { SomeObject.fld = arr } } object SomeObject { var fld: Array[Int] = _ }
  90. 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” 27 * simplified
  91. 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” 27 “Safe” = conforms to object capability model [3] * simplified
  92. 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” 27 “Safe” = conforms to object capability model [3] * simplified [3] Mark S. Miller. Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control. PhD dissertation, 2006
  93. Object Capabilities in Scala • How common are object-capability safe

    classes in Scala? • Results from empirical study: 28
  94. LaCasa: Summary • Encapsulated boxes • Alias protection via object

    capability model • Reusing a class requires only a single bit of information • Access permissions via implicits + path-dependent types • Can statically prevent data races • Sound integration with Scala’s local type inference • Not shown: unique fields 29
  95. More Contributions 30

  96. More Contributions • Formalization: object-oriented core languages 30

  97. More Contributions • Formalization: object-oriented core languages • CLC1: type-based

    notion of object capabilities 30
  98. More Contributions • Formalization: object-oriented core languages • CLC1: type-based

    notion of object capabilities • CLC2: uniqueness via flow-insensitive permissions 30
  99. More Contributions • Formalization: object-oriented core languages • CLC1: type-based

    notion of object capabilities • CLC2: uniqueness via flow-insensitive permissions • CLC3: concurrent extension 30
  100. More Contributions • Formalization: object-oriented core languages • CLC1: type-based

    notion of object capabilities • CLC2: uniqueness via flow-insensitive permissions • CLC3: concurrent extension • Soundness proof, establishing heap separation and uniqueness invariants 30
  101. More Contributions • Formalization: object-oriented core languages • CLC1: type-based

    notion of object capabilities • CLC2: uniqueness via flow-insensitive permissions • CLC3: concurrent extension • Soundness proof, establishing heap separation and uniqueness invariants • Isolation theorem for processes with shared heap 30
  102. Details • Paper to appear at OOPSLA ’16 • Technical

    report: [arXiv:1607.05609] • Mechanization of formal model in Coq 31
  103. Status and Plans 32

  104. Status and Plans • Prototype implementation:1 • Compiler plug-in for

    Scala 2.11 • Integration with actors (straw man) 32 1 https://github.com/phaller/lacasa
  105. Status and Plans • Prototype implementation:1 • Compiler plug-in for

    Scala 2.11 • Integration with actors (straw man) • Ongoing and future work: • Adapters for Akka and other libraries • Empirical study on the effort to adapt existing code • Port to Dotty Scala Compiler 32 1 https://github.com/phaller/lacasa
  106. Status and Plans • Prototype implementation:1 • Compiler plug-in for

    Scala 2.11 • Integration with actors (straw man) • Ongoing and future work: • Adapters for Akka and other libraries • Empirical study on the effort to adapt existing code • Port to Dotty Scala Compiler 32 Thank you! 1 https://github.com/phaller/lacasa