Kotlin Actors - No Drama Concurrency

Kotlin Actors - No Drama Concurrency

5701f31a8433a22ae736282de8d08cd6?s=128

Sam Edwards

July 14, 2020
Tweet

Transcript

  1. Sam Edwards @HandstandSam Actors No Drama Concurrency

  2. Concurrency?

  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.
  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.
  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.
  6. None
  7. None
  8. None
  9. None
  10. Coroutines

  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)
 }
  12. “Essentially, coroutines are light-weight threads. They are launched in a

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

    context of some CoroutineScope.”
  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
  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") }
  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
  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
  18. None
  19. Actors

  20. Actors are: • A Single Coroutine • Processes incoming Messages

    • Backed by a Channel • Concurrent
  21. Actors are: A Single Coroutine Actor Coroutine Process Messages Messages

    via Channel Messages via Channel for() loop
  22. Actors Processes incoming Messages/Intentions sealed class Intention { class FindByLabel(

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

    val label: String, val deferred: CompletableDeferred<ItemWithQuantity?> ) : Intention() class Upsert(val itemWithQuantity: ItemWithQuantity) : Intention() class Remove(val itemWithQuantity: ItemWithQuantity) : Intention() object Empty : Intention() }
  24. Actors are: Processes incoming Messages/Intentions Return values with CompletableDeferred override

    suspend fun findByLabel(label: String): ItemWithQuantity? { val deferred = CompletableDeferred<ItemWithQuantity?>() actor.send( Intention.FindByLabel( label = label, deferred = deferred ) ) return deferred.await() }
  25. Actors are: Processes incoming Messages/Intentions private val actor = scope.actor<Intention>

    { val itemsInCart: MutableMap<String, ItemWithQuantity> = 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 } } }
  26. Actors are: Backed by a Channel public fun <E> CoroutineScope.actor(

    context: CoroutineContext = EmptyCoroutineContext, capacity: Int = 0, // todo: Maybe Channel.DEFAULT here? start: CoroutineStart = CoroutineStart.DEFAULT, onCompletion: CompletionHandler? = null, block: suspend ActorScope<E>.() -> Unit ): SendChannel<E> { val newContext = newCoroutineContext(context) val channel = Channel<E>(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 }
  27. Actors are: Concurrent • It’s a single coroutine, processing messages

    sequentially • Incoming messages are queued and processed
  28. OMG! @ObsoleteCoroutinesApi

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

  30. @ObsoleteCoroutinesApi


  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.
  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.
  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.
  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.
  35. kotlinx.coroutines - Writing complex actors https://github.com/Kotlin/kotlinx.coroutines/issues/87

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

  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.
  38. What About StateFlow? I thought you’d never ask…

  39. StateFlow?
 $

  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.
  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.
  42. Actor + StateFlow Example https://github.com/handstandsam/ShoppingApp/pull/29

  43. OMG! @ExperimentalCoroutinesApi

  44. @ExperimentalCoroutinesApi


  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.
  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.
  47. Actor and StateFlow

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

  49. Thank You! Sam Edwards - @HandstandSam