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

LaCasa: Lightweight Affinity and Object Capabilities in Scala

Philipp Haller
November 02, 2016
370

LaCasa: Lightweight Affinity and Object Capabilities in Scala

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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!

    View Slide

  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

    View Slide

  10. Goal
    5

    View Slide

  11. Goal
    Static prevention of data races
    5

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  17. Isn’t it a Solved Problem?
    6

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  24. Example
    Image
    data
    7

    View Slide

  25. Example
    Image
    data
    apply filter
    7

    View Slide

  26. Example
    Image
    data
    apply filter
    7

    View Slide

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

    View Slide

  28. Example
    Image
    data
    apply filter
    Image processing pipeline:
    filter 1 filter 2
    7
    Pipeline stages
    run concurrently

    View Slide

  29. Example: Implementation
    8

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  33. Preventing Data Races
    10

    View Slide

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

    View Slide

  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

    View Slide

  36. Ownership Transfer in Scala
    11

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  43. Access Permissions
    12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  50. Creating Boxes and
    Permissions
    14

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    14

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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?

    View Slide

  65. Permissions and
    Continuations
    17

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

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

    }
    }
    18

    View Slide

  70. Restricting Continuations
    19

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  76. Stack Locality
    20

    View Slide

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

    (or “stack confined”)
    20

    View Slide

  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`

    }
    }

    View Slide

  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”

    View Slide

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

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

    }
    }
    21

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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%)

    View Slide

  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%)

    View Slide

  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!

    View Slide

  89. In the Paper
    26

    View Slide

  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

    View Slide

  91. Conclusion
    27

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide