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
November 02, 2016
220

LaCasa: Lightweight Affinity and Object Capabilities in Scala

3b84657fdb075382e3781310ca8a9a70?s=128

Philipp Haller

November 02, 2016
Tweet

Transcript

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

    Haller1 and Alex Loiko2* OOPSLA 2016 Amsterdam, Netherlands November 2nd, 2016 1 KTH Royal Institute of Technology, Sweden 2 Google Stockholm, Sweden * Work done while at KTH
  2. Context: Concurrent Programming in Scala • Scala = “Scalable Language”

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

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

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

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

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

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

    Scala Joins (2008) • Scala futures (2012) • FlowPools (2012) • Scala Async (2013) • Contributions to Akka, Akka.js projects at Typesafe 3 Haller and Sommers Artima Press, 2012 Production use at Twitter, The Guardian and others Production use at Morgan Stanley, Gawker and others Production use at LinkedIn, The Huffington Post and others The programming-models-as-libraries approach has been successful in Scala!
  9. Problem • Problem: Scala cannot ensure concurrency safety for library-based

    concurrency abstractions • This also applies to Java, C++, and most other widely-used programming languages 4
  10. Goal 5

  11. Goal Static prevention of data races 5

  12. Goal Static prevention of data races • using a lightweight

    type system 5
  13. Goal Static prevention of data races • using a lightweight

    type system • that minimizes the effort to reuse existing code 5
  14. Goal Static prevention of data races • using a lightweight

    type system • that minimizes the effort to reuse existing code Focus: 5
  15. Goal Static prevention of data races • using a lightweight

    type system • that minimizes the effort to reuse existing code Focus: • Existing, full-featured languages like Scala 5
  16. Goal Static prevention of data races • using a lightweight

    type system • that minimizes the effort to reuse existing code Focus: • Existing, full-featured languages like Scala 5 In contrast to new language designs like Rust
  17. Isn’t it a Solved Problem? 6

  18. Isn’t it a Solved Problem? • A lot of progress

    in type systems for safe concurrency (linear and affine types, static capabilities, uniqueness types, ownership types, region inference, etc.) 6
  19. Isn’t it a Solved Problem? • A lot of progress

    in type systems for safe concurrency (linear and affine types, static capabilities, uniqueness types, ownership types, region inference, etc.) • Challenges: 6
  20. Isn’t it a Solved Problem? • A lot of progress

    in type systems for safe concurrency (linear and affine types, static capabilities, uniqueness types, ownership types, region inference, etc.) • Challenges: • Sound integration with advanced type system features 6
  21. Isn’t it a Solved Problem? • A lot of progress

    in type systems for safe concurrency (linear and affine types, static capabilities, uniqueness types, ownership types, region inference, etc.) • Challenges: • Sound integration with advanced type system features 6 Example: local type inference
  22. Isn’t it a Solved Problem? • A lot of progress

    in type systems for safe concurrency (linear and affine types, static capabilities, uniqueness types, ownership types, region inference, etc.) • Challenges: • Sound integration with advanced type system features • Adoption on large scale 6 Example: local type inference
  23. Isn’t it a Solved Problem? • A lot of progress

    in type systems for safe concurrency (linear and affine types, static capabilities, uniqueness types, ownership types, region inference, etc.) • Challenges: • Sound integration with advanced type system features • Adoption on large scale • Key: reuse of existing code 6 Example: local type inference
  24. Example Image data 7

  25. Example Image data apply filter 7

  26. Example Image data apply filter 7

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

    filter 2 7
  28. Example Image data apply filter Image processing pipeline: filter 1

    filter 2 7 Pipeline stages run concurrently
  29. Example: Implementation 8

  30. Example: Implementation • Assumptions: • Main memory expensive (image data

    large) 8
  31. Example: Implementation • Assumptions: • Main memory expensive (image data

    large) • Approach for high performance: • In-place update of image buffers • Pass mutable buffers by-reference 8
  32. 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 9
  33. Preventing Data Races 10

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

    Sending stage loses ownership • Type system prevents sender from accessing transferred objects 10
  35. Preventing Data Races • Approach: safe transfer of ownership •

    Sending stage loses ownership • Type system prevents sender from accessing transferred objects • Advantages: • No run-time overhead • Errors caught at compile time 10
  36. Ownership Transfer in Scala 11

  37. Ownership Transfer in Scala • Enter LaCasa: Affine references for

    Scala 11
  38. Ownership Transfer in Scala • Enter LaCasa: Affine references for

    Scala • “Transferable” references 11
  39. Ownership Transfer in Scala • Enter LaCasa: Affine references for

    Scala • “Transferable” references • At most one owner per transferable reference 11
  40. Ownership Transfer in Scala • Enter LaCasa: Affine references for

    Scala • “Transferable” references • At most one owner per transferable reference • LaCasa combines two concepts: 11
  41. Ownership Transfer in Scala • Enter LaCasa: Affine references for

    Scala • “Transferable” references • At most one owner per transferable reference • LaCasa combines two concepts: • Access permissions 11
  42. Ownership Transfer in Scala • Enter LaCasa: Affine references for

    Scala • “Transferable” references • At most one owner per transferable reference • LaCasa combines two concepts: • Access permissions • Encapsulated boxes 11
  43. Access Permissions 12

  44. Access Permissions • Permissions control access to transferable objects 12

  45. Access Permissions • Permissions control access to transferable objects CanAccess

    { type C } 12
  46. Access Permissions • Permissions control access to transferable objects •

    Type member C uniquely identifies box CanAccess { type C } 12
  47. Access Permissions • Permissions control access to transferable objects •

    Type member C uniquely identifies box CanAccess { type C } Box[T] { type C } 12
  48. Matching Boxes and Permissions • Type members match permissions and

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

    boxes • Via path-dependent types • Example: def method[T](box: Box[T]) (implicit p: CanAccess { type C = box.C }): Unit 13
  50. Creating Boxes and Permissions 14

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

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

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

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

    Message { var arr: Array[Int] = _ } 14
  55. 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 } } 14
  56. Creating Boxes and Permissions mkBox[Message] { packed => } class

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

    Message { var arr: Array[Int] = _ } implicit val access = packed.access val box = packed.box … 14
  58. Accessing Boxes • Boxes are encapsulated • Boxes must be

    opened for access 15
  59. Accessing Boxes • Boxes are encapsulated • Boxes must be

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

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

    opened for access mkBox[Message] { packed => implicit val access = packed.access val box = packed.box box open { msg => msg.arr = Array(1, 2, 3, 4) } } 15
  62. Consuming Permissions Example: transfering a box from one actor to

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

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

    another consumes its access permission mkBox[Message] { packed => implicit val access = packed.access val box = packed.box … someActor.send(box) // illegal to access `box` here! } 16 How to enforce this?
  65. Permissions and Continuations 17

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

    of permission-consuming call 17
  67. Permissions and Continuations • Make implicit permission unavailable in continuation

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

    packed.access val box = packed.box … someActor.send(box) { // make `access` unavailable … } } 18
  70. Restricting Continuations 19

  71. Restricting Continuations • Continuation disallows capturing variables of the type

    of access 19
  72. Restricting Continuations • Continuation disallows capturing variables of the type

    of access • Leverage spores [1] 19 [1] Miller, Haller, and Odersky. Spores: A type-based foundation for closures in the age of concurrency and distribution. ECOOP ’14
  73. Restricting Continuations • Continuation disallows capturing variables of the type

    of access • Leverage spores [1] def send(msg: Box[T]) (cont: NullarySpore[Unit] { type Excluded = msg.C }) (implicit p: CanAccess { type C = msg.C }): Nothing 19 [1] Miller, Haller, and Odersky. Spores: A type-based foundation for closures in the age of concurrency and distribution. ECOOP ’14
  74. Restricting Continuations • Continuation disallows capturing variables of the type

    of access • Leverage spores [1] def send(msg: Box[T]) (cont: NullarySpore[Unit] { type Excluded = msg.C }) (implicit p: CanAccess { type C = msg.C }): Nothing 19 [1] Miller, Haller, and Odersky. Spores: A type-based foundation for closures in the age of concurrency and distribution. ECOOP ’14
  75. Restricting Continuations • Continuation disallows capturing variables of the type

    of access • Leverage spores [1] def send(msg: Box[T]) (cont: NullarySpore[Unit] { type Excluded = msg.C }) (implicit p: CanAccess { type C = msg.C }): Nothing 19 “May not occur in captured types” [1] Miller, Haller, and Odersky. Spores: A type-based foundation for closures in the age of concurrency and distribution. ECOOP ’14
  76. Stack Locality 20

  77. Stack Locality • Permissions are restricted to be stack local

    
 (or “stack confined”) 20
  78. Stack Locality • Permissions are restricted to be stack local

    
 (or “stack confined”) • This prevents problematic “indirect capturing”: 20 def method[T](box: Box[T]) (implicit p: CanAccess { type C = box.C }) = { val fun = () => p someActor.send(box) { implicit val forbidden = fun() // could still access `box` … } }
  79. Stack Locality • Permissions are restricted to be stack local

    
 (or “stack confined”) • This prevents problematic “indirect capturing”: 20 def method[T](box: Box[T]) (implicit p: CanAccess { type C = box.C }) = { val fun = () => p someActor.send(box) { implicit val forbidden = fun() // could still access `box` … } } “error: p stack local”
  80. Recall Example mkBox[Message] { packed => implicit val access =

    packed.access val box = packed.box … someActor.send(box) { // `access` unavailable … } } 21
  81. Encapsulation Problem: not all types safe to transfer! 22

  82. Encapsulation Problem: not all types safe to transfer! 22 class

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

    code in Scala? • Empirical study of over 75,000 SLOC of open-source Scala code: 24 Project Version SLOC GitHub stats Scala stdlib 2.11.7 33,107 ✭5,795 257 Signal/Collect 8.0.6 10,159 ✭123 11 GeoTrellis 0.10.0-RC2 35,351 ✭400 38 -engine 3,868 -raster 22,291 -spark 9,192
  86. Object Capabilities in Scala Results of empirical study: 25 Project

    #classes/traits #ocap (%) #dir. insec. (%) Scala stdlib 1,505 644 (43%) 212/861 (25%) Signal/Collect 236 159 (67%) 60/77 (78%) GeoTrellis -engine 190 40 (21%) 124/150 (83%) -raster 670 233 (35%) 325/437 (74%) -spark 326 101 (31%) 167/225 (74%) Total 2,927 1,177 (40%) 888/1,750 (51%)
  87. Object Capabilities in Scala Results of empirical study: 25 Project

    #classes/traits #ocap (%) #dir. insec. (%) Scala stdlib 1,505 644 (43%) 212/861 (25%) Signal/Collect 236 159 (67%) 60/77 (78%) GeoTrellis -engine 190 40 (21%) 124/150 (83%) -raster 670 233 (35%) 325/437 (74%) -spark 326 101 (31%) 167/225 (74%) Total 2,927 1,177 (40%) 888/1,750 (51%)
  88. Object Capabilities in Scala Results of empirical study: 25 Project

    #classes/traits #ocap (%) #dir. insec. (%) Scala stdlib 1,505 644 (43%) 212/861 (25%) Signal/Collect 236 159 (67%) 60/77 (78%) GeoTrellis -engine 190 40 (21%) 124/150 (83%) -raster 670 233 (35%) 325/437 (74%) -spark 326 101 (31%) 167/225 (74%) Total 2,927 1,177 (40%) 888/1,750 (51%) Paper: immutability increases these percentages!
  89. In the Paper 26

  90. In the Paper • Implementation: • Compiler plugin for Scala

    2.11.x and integration with actors • Enforcement of continuation-passing style • Formalization: object-oriented core languages • CLC1: type-based notion of object capabilities • CLC2: uniqueness via flow-insensitive permissions • CLC3: concurrent extension • Soundness proof • Isolation theorem for processes with shared heap 26 Formal model in Coq
  91. Conclusion 27

  92. Conclusion • Type-based notion of the object capability discipline is

    possible and benefitial 27
  93. Conclusion • Type-based notion of the object capability discipline is

    possible and benefitial • Safe ownership transfer is possible for objects conforming to the object capability discipline 27
  94. Conclusion • Type-based notion of the object capability discipline is

    possible and benefitial • Safe ownership transfer is possible for objects conforming to the object capability discipline • Binary check whether a class is reusable unchanged 27
  95. Conclusion • Type-based notion of the object capability discipline is

    possible and benefitial • Safe ownership transfer is possible for objects conforming to the object capability discipline • Binary check whether a class is reusable unchanged • Sound integration with advanced type system features of Scala 27
  96. Conclusion • Type-based notion of the object capability discipline is

    possible and benefitial • Safe ownership transfer is possible for objects conforming to the object capability discipline • Binary check whether a class is reusable unchanged • Sound integration with advanced type system features of Scala • In medium to large open-source Scala projects, 21-67% of all classes conform to the object capability discipline 27
  97. Conclusion • Type-based notion of the object capability discipline is

    possible and benefitial • Safe ownership transfer is possible for objects conforming to the object capability discipline • Binary check whether a class is reusable unchanged • Sound integration with advanced type system features of Scala • In medium to large open-source Scala projects, 21-67% of all classes conform to the object capability discipline 27 Open-source implementation: https://github.com/phaller/lacasa
  98. Conclusion • Type-based notion of the object capability discipline is

    possible and benefitial • Safe ownership transfer is possible for objects conforming to the object capability discipline • Binary check whether a class is reusable unchanged • Sound integration with advanced type system features of Scala • In medium to large open-source Scala projects, 21-67% of all classes conform to the object capability discipline 27 Thank you! Open-source implementation: https://github.com/phaller/lacasa