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

Kotlin Actors - No Drama Concurrency

Kotlin Actors - No Drama Concurrency

Sam Edwards

July 14, 2020
Tweet

More Decks by Sam Edwards

Other Decks in Programming

Transcript

  1. Sam Edwards @HandstandSam
    Actors
    No Drama Concurrency

    View Slide

  2. Concurrency?

    View Slide

  3. Concurrency is the ability of different parts or units of a program,
    algorithm, or problem to be executed out-of-order or in partial order,
    without affecting the final outcome. This allows for parallel execution
    of the concurrent units, which can significantly improve overall
    speed of the execution in multi-processor and multi-core systems.

    View Slide

  4. Concurrency is:
    • the ability of different parts or units of a program,
    algorithm, or problem to be executed out-of-order or
    in partial order, without affecting the final outcome.
    This allows for:
    • parallel execution of the concurrent units, which can
    significantly improve overall speed of the execution in
    multi-processor and multi-core systems.

    View Slide

  5. Concurrency is:
    • the ability of different parts or units of a program,
    algorithm, or problem to be executed out-of-order or
    in partial order, without affecting the final outcome.
    This allows for:
    • parallel execution of the concurrent units, which can
    significantly improve overall speed of the execution in
    multi-processor and multi-core systems.

    View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. Coroutines

    View Slide

  11. Kotlin Coroutines
    Explained in one slide
    • A function with a suspend keyword signifies it must run in a Coroutine


    suspend fun doNetworkRequest() : Result {

    // Network Request Occurs

    }

    • suspend functions must be executed within a CoroutineContext and Once
    inside a coroutine, code executes sequentially


    CoroutineContext(Dispatchers.Default).launch{

    val result = doNetworkRequest()

    processResult(result)

    }

    View Slide

  12. “Essentially, coroutines are light-weight
    threads. They are launched in a context
    of some CoroutineScope.”

    View Slide

  13. “Essentially, coroutines are light-weight
    threads. They are launched in a context
    of some CoroutineScope.”

    View Slide

  14. Concurrent Code with Kotlin Coroutines?
    https://kotlinlang.org/docs/reference/coroutines/shared-mutable-state-and-concurrency.html
    • Mutex

    • Thread Safe Data Structures - AtomicInteger(), etc (Blocks thread)

    • Thread Confinement - Main Thread!!!

    • Actors - Executes in it’s own Coroutine

    View Slide

  15. Mutex
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/
    val mutex = Mutex()
    var counter = 0
    fun main() = runBlocking {
    withContext(Dispatchers.Default) {
    massiveRun {
    // protect each increment with lock
    mutex.withLock {
    counter++
    }
    }
    }
    println("Counter = $counter")
    }

    View Slide

  16. Thread-Safe Data Structures
    https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concurrent/-atomic-int/
    • val atomicInt = AtomicInt(0)

    • atomicInt.increment()

    • atomicInt.decrement()

    • atomicInt.value

    View Slide

  17. Thread Confinement
    https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concurrent/-atomic-int/
    • CoroutineContext(Dispatchers.Main).launch {}
    • Executes suspended code on the Main thread

    • runBlocking {}
    • Executes suspended code on the current thread

    View Slide

  18. View Slide

  19. Actors

    View Slide

  20. Actors are:
    • A Single Coroutine
    • Processes incoming Messages
    • Backed by a Channel
    • Concurrent

    View Slide

  21. Actors are:
    A Single Coroutine
    Actor Coroutine
    Process Messages
    Messages via Channel
    Messages via Channel
    for() loop

    View Slide

  22. Actors
    Processes incoming Messages/Intentions
    sealed class Intention {
    class FindByLabel(
    val label: String,
    val deferred: CompletableDeferred
    ) : Intention()
    class Upsert(val itemWithQuantity: ItemWithQuantity) : Intention()
    class Remove(val itemWithQuantity: ItemWithQuantity) : Intention()
    object Empty : Intention()
    }

    View Slide

  23. Actors
    Processes incoming Messages/Intentions
    sealed class Intention {
    class FindByLabel(
    val label: String,
    val deferred: CompletableDeferred
    ) : Intention()
    class Upsert(val itemWithQuantity: ItemWithQuantity) : Intention()
    class Remove(val itemWithQuantity: ItemWithQuantity) : Intention()
    object Empty : Intention()
    }

    View Slide

  24. Actors are:
    Processes incoming Messages/Intentions
    Return values with CompletableDeferred
    override suspend fun findByLabel(label: String): ItemWithQuantity? {
    val deferred = CompletableDeferred()
    actor.send(
    Intention.FindByLabel(
    label = label,
    deferred = deferred
    )
    )
    return deferred.await()
    }

    View Slide

  25. Actors are:
    Processes incoming Messages/Intentions
    private val actor = scope.actor {
    val itemsInCart: MutableMap = mutableMapOf()
    for (intention in channel) {
    when (intention) {
    is Intention.FindByLabel -> {
    intention.deferred.complete(itemsInCart[intention.label])
    }
    is Intention.Upsert -> {
    itemsInCart[intention.itemWithQuantity.item.label] = intention.itemWithQuantity
    }
    is Intention.Remove -> itemsInCart.remove(intention.itemWithQuantity.item.label)
    is Intention.Empty -> itemsInCart.clear()
    }
    val sortedItems = itemsInCart.values.toList()
    .sortedBy { it.item.label }
    if (itemsChannel.value != sortedItems) {
    itemsChannel.send(sortedItems)
    } else {
    // No change detected, don't emit
    }
    }
    }

    View Slide

  26. Actors are:
    Backed by a Channel
    public fun CoroutineScope.actor(
    context: CoroutineContext = EmptyCoroutineContext,
    capacity: Int = 0, // todo: Maybe Channel.DEFAULT here?
    start: CoroutineStart = CoroutineStart.DEFAULT,
    onCompletion: CompletionHandler? = null,
    block: suspend ActorScope.() -> Unit
    ): SendChannel {
    val newContext = newCoroutineContext(context)
    val channel = Channel(capacity)
    val coroutine = if (start.isLazy)
    LazyActorCoroutine(newContext, channel, block) else
    ActorCoroutine(newContext, channel, active = true)
    if (onCompletion != null) coroutine.invokeOnCompletion(handler = onCompletion)
    coroutine.start(start, coroutine, block)
    return coroutine
    }

    View Slide

  27. Actors are:
    Concurrent
    • It’s a single coroutine, processing messages sequentially
    • Incoming messages are queued and processed

    View Slide

  28. OMG!
    @ObsoleteCoroutinesApi

    View Slide

  29. kotlinx.coroutines - Actors
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html

    View Slide

  30. @ObsoleteCoroutinesApi


    View Slide

  31. kotlinx.coroutines - ObsoleteCoroutinesApi
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-obsolete-coroutines-api/
    annotation class ObsoleteCoroutinesApi (source)
    Marks declarations that are obsolete in coroutines API, which means that the design of
    the corresponding declarations has serious known flaws and they will be redesigned in
    the future. Roughly speaking, these declarations will be deprecated in the future but
    there is no replacement for them yet, so they cannot be deprecated right away.

    View Slide

  32. kotlinx.coroutines - ObsoleteCoroutinesApi
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-obsolete-coroutines-api/
    annotation class ObsoleteCoroutinesApi (source)
    Marks declarations that are obsolete in coroutines API, which means that the design of
    the corresponding declarations has serious known flaws and they will be redesigned in
    the future. Roughly speaking, these declarations will be deprecated in the future but
    there is no replacement for them yet, so they cannot be deprecated right away.

    View Slide

  33. kotlinx.coroutines - ObsoleteCoroutinesApi
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-obsolete-coroutines-api/
    annotation class ObsoleteCoroutinesApi (source)
    Marks declarations that are obsolete in coroutines API, which means that the design of
    the corresponding declarations has serious known flaws and they will be redesigned in
    the future. Roughly speaking, these declarations will be deprecated in the future but
    there is no replacement for them yet, so they cannot be deprecated right away.

    View Slide

  34. kotlinx.coroutines - ObsoleteCoroutinesApi
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-obsolete-coroutines-api/
    annotation class ObsoleteCoroutinesApi (source)
    Marks declarations that are obsolete in coroutines API, which means that the design of
    the corresponding declarations has serious known flaws and they will be redesigned in
    the future. Roughly speaking, these declarations will be deprecated in the future but
    there is no replacement for them yet, so they cannot be deprecated right away.

    View Slide

  35. kotlinx.coroutines - Writing complex actors
    https://github.com/Kotlin/kotlinx.coroutines/issues/87

    View Slide

  36. kotlinx.coroutines - Writing complex actors
    https://github.com/Kotlin/kotlinx.coroutines/issues/87

    View Slide

  37. Are Actors Going Away?
    Kotlin GDE AMA with Roman Elizarov
    • Actors are not deprecated, and they will remain.

    • Original Actors were designed pre-structured concurrency. They won’t
    drop simple actors.

    • The replacements will support the simple use case just as well.

    View Slide

  38. What About
    StateFlow?
    I thought you’d never ask…

    View Slide

  39. StateFlow?

    $

    View Slide

  40. StateFlow
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/index.html
    • A Flow that represents a read-only state with a single updatable
    data value that emits updates to the value to its collectors. The current value
    can be retrieved via value property. The flow of future updates to the
    value can be observed by collecting values from this flow.

    View Slide

  41. MutableStateFlow
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-mutable-state-flow/index.html
    A mutable StateFlow that provides a setter for value.

    View Slide

  42. Actor + StateFlow
    Example
    https://github.com/handstandsam/ShoppingApp/pull/29

    View Slide

  43. OMG!
    @ExperimentalCoroutinesApi

    View Slide

  44. @ExperimentalCoroutinesApi


    View Slide

  45. kotlinx.coroutines - ExperimentalCoroutinesApi
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-experimental-coroutines-api/
    annotation class ExperimentalCoroutinesApi (source)
    Marks declarations that are still experimental in coroutines API, which means that the
    design of the corresponding declarations has open issues which may (or may not) lead
    to their changes in the future. Roughly speaking, there is a chance that those
    declarations will be deprecated in the near future or the semantics of their behavior
    may change in some way that may break some code.

    View Slide

  46. kotlinx.coroutines - ExperimentalCoroutinesApi
    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-experimental-coroutines-api/
    annotation class ExperimentalCoroutinesApi (source)
    Marks declarations that are still experimental in coroutines API, which means that the
    design of the corresponding declarations has open issues which may (or may not) lead
    to their changes in the future. Roughly speaking, there is a chance that those
    declarations will be deprecated in the near future or the semantics of their behavior
    may change in some way that may break some code.

    View Slide

  47. Actor
    and
    StateFlow

    View Slide

  48. Actors and StateFlow
    •Complimentary
    •Actor (Thread Safety)
    •StateFlow (Data Container)

    View Slide

  49. Thank You!
    Sam Edwards - @HandstandSam

    View Slide