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 all versions of kotlinx-coroutines-core
Coroutines support libraries for Kotlin
@file:OptIn(ExperimentalContracts::class)
package kotlinx.coroutines.selects
import kotlin.contracts.*
import kotlin.coroutines.*
/**
* 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.
*/
@OptIn(ExperimentalContracts::class)
public suspend inline fun selectUnbiased(crossinline builder: SelectBuilder.() -> Unit): R {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}
return UnbiasedSelectImplementation(coroutineContext).run {
builder(this)
doSelect()
}
}
/**
* The unbiased `select` inherits the [standard one][SelectImplementation],
* but does not register clauses immediately. Instead, it stores all of them
* in [clausesToRegister] lists, shuffles and registers them in the beginning of [doSelect]
* (see [shuffleAndRegisterClauses]), and then delegates the rest
* to the parent's [doSelect] implementation.
*/
@PublishedApi
internal open class UnbiasedSelectImplementation(context: CoroutineContext) : SelectImplementation(context) {
private val clausesToRegister: MutableList = arrayListOf()
override fun SelectClause0.invoke(block: suspend () -> R) {
clausesToRegister += ClauseData(clauseObject, regFunc, processResFunc, PARAM_CLAUSE_0, block, onCancellationConstructor)
}
override fun SelectClause1.invoke(block: suspend (Q) -> R) {
clausesToRegister += ClauseData(clauseObject, regFunc, processResFunc, null, block, onCancellationConstructor)
}
override fun SelectClause2
.invoke(param: P, block: suspend (Q) -> R) {
clausesToRegister += ClauseData(clauseObject, regFunc, processResFunc, param, block, onCancellationConstructor)
}
@PublishedApi
override suspend fun doSelect(): R {
shuffleAndRegisterClauses()
return super.doSelect()
}
private fun shuffleAndRegisterClauses() = try {
clausesToRegister.shuffle()
clausesToRegister.forEach { it.register() }
} finally {
clausesToRegister.clear()
}
}