-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathSelectUnbiased.kt
67 lines (58 loc) · 2.5 KB
/
SelectUnbiased.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/*
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@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 <R> selectUnbiased(crossinline builder: SelectBuilder<R>.() -> Unit): R {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}
return UnbiasedSelectImplementation<R>(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<R>(context: CoroutineContext) : SelectImplementation<R>(context) {
private val clausesToRegister: MutableList<ClauseData<R>> = arrayListOf()
override fun SelectClause0.invoke(block: suspend () -> R) {
clausesToRegister += ClauseData(clauseObject, regFunc, processResFunc, PARAM_CLAUSE_0, block, onCancellationConstructor)
}
override fun <Q> SelectClause1<Q>.invoke(block: suspend (Q) -> R) {
clausesToRegister += ClauseData(clauseObject, regFunc, processResFunc, null, block, onCancellationConstructor)
}
override fun <P, Q> SelectClause2<P, Q>.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()
}
}