commonMain.selects.SelectUnbiased.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlinx-coroutines-core Show documentation
Show all versions of kotlinx-coroutines-core Show documentation
Coroutines support libraries for Kotlin
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.selects
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
/**
* Waits for the result of multiple suspending functions simultaneously like [select], but in an _unbiased_
* way when multiple clauses are selectable at the same time.
*
* This unbiased implementation of `select` expression randomly shuffles the clauses before checking
* if they are selectable, thus ensuring that there is no statistical bias to the selection of the first
* clauses.
*
* See [select] function description for all the other details.
*/
public suspend inline fun selectUnbiased(crossinline builder: SelectBuilder.() -> Unit): R =
suspendCoroutineUninterceptedOrReturn { uCont ->
val scope = UnbiasedSelectBuilderImpl(uCont)
try {
builder(scope)
} catch (e: Throwable) {
scope.handleBuilderException(e)
}
scope.initSelectResult()
}
@PublishedApi
internal class UnbiasedSelectBuilderImpl(uCont: Continuation) :
SelectBuilder {
val instance = SelectBuilderImpl(uCont)
val clauses = arrayListOf<() -> Unit>()
@PublishedApi
internal fun handleBuilderException(e: Throwable) = instance.handleBuilderException(e)
@PublishedApi
internal fun initSelectResult(): Any? {
if (!instance.isSelected) {
try {
clauses.shuffle()
clauses.forEach { it.invoke() }
} catch (e: Throwable) {
instance.handleBuilderException(e)
}
}
return instance.getResult()
}
override fun SelectClause0.invoke(block: suspend () -> R) {
clauses += { registerSelectClause0(instance, block) }
}
override fun SelectClause1.invoke(block: suspend (Q) -> R) {
clauses += { registerSelectClause1(instance, block) }
}
override fun SelectClause2
.invoke(param: P, block: suspend (Q) -> R) {
clauses += { registerSelectClause2(instance, param, block) }
}
override fun onTimeout(timeMillis: Long, block: suspend () -> R) {
clauses += { instance.onTimeout(timeMillis, block) }
}
}