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 full-size slide

  2. Concurrency?

    View full-size 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 full-size 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 full-size 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 full-size slide

  6. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  9. 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 full-size slide

  10. 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 full-size slide

  11. 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 full-size slide

  12. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  15. 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 full-size slide

  16. 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 full-size slide

  17. 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 full-size slide

  18. 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 full-size slide

  19. 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 full-size slide

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

    View full-size slide

  21. OMG!
    @ObsoleteCoroutinesApi

    View full-size slide

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

    View full-size slide

  23. @ObsoleteCoroutinesApi


    View full-size slide

  24. 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 full-size slide

  25. 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 full-size slide

  26. 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 full-size slide

  27. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  30. 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 full-size slide

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

    View full-size slide

  32. StateFlow?

    $

    View full-size slide

  33. 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 full-size slide

  34. 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 full-size slide

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

    View full-size slide

  36. OMG!
    @ExperimentalCoroutinesApi

    View full-size slide

  37. @ExperimentalCoroutinesApi


    View full-size slide

  38. 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 full-size slide

  39. 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 full-size slide

  40. Actor
    and
    StateFlow

    View full-size slide

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

    View full-size slide

  42. Thank You!
    Sam Edwards - @HandstandSam

    View full-size slide