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

LaCasa: Lightweight Affinity and Object Capabilities in Scala

Philipp Haller
September 29, 2016
260

LaCasa: Lightweight Affinity and Object Capabilities in Scala

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

    View Slide

  2. Concurrency is Difficult
    2

    View Slide

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

    View Slide

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

    View Slide

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

    Remedies?

    View Slide

  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

    View Slide

  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”?

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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!

    View Slide

  12. Problem:
    Programming-models-as-libraries typically cannot
    prevent common concurrency hazards statically!
    5

    View Slide

  13. Example
    Image
    data
    6

    View Slide

  14. Example
    Image
    data
    apply filter
    6

    View Slide

  15. Example
    Image
    data
    apply filter
    6

    View Slide

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

    View Slide

  17. Example
    Image
    data
    apply filter
    Image processing pipeline:
    filter 1 filter 2
    6
    Pipeline stages
    run concurrently

    View Slide

  18. Example: Implementation
    7

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  22. Preventing Data Races
    9

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  30. Ownership Transfer in Scala
    10

    View Slide

  31. Ownership Transfer in Scala
    • Enter LaCasa
    10

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  35. Motivation
    11

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  42. Isn’t it a Solved Problem?
    12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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?

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  51. Affine References in Scala
    13

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  55. Access Permissions
    14

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  62. Creating Boxes and
    Permissions
    16

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    16

    View Slide

  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

    View Slide

  69. Accessing Boxes
    • Boxes are encapsulated
    • Boxes must be opened for access
    17

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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?

    View Slide

  75. Permissions and
    Continuations
    19

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  79. Continuation-Passing Style
    mkBox[Message] { packed =>
    implicit val access = packed.access
    val theBox = packed.box

    someActor.send(theBox) {
    // make `access` unavailable

    }
    }
    20

    View Slide

  80. Restricting Continuations
    • Continuation disallows capturing variables of
    the type of access
    • Leverage spores
    21

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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) => { … }

    View Slide

  87. Recall Example
    mkBox[Message] { packed =>
    implicit val access = packed.access
    val theBox = packed.box

    someActor.send(theBox) {
    // `access` unavailable

    }
    }
    25

    View Slide

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

    View Slide

  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] = _
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  95. More Contributions
    30

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  102. Details
    • Paper to appear at OOPSLA ’16
    • Technical report: [arXiv:1607.05609]
    • Mechanization of formal model in Coq
    31

    View Slide

  103. Status and Plans
    32

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide