1 package kotlinx.coroutines.selects
2
3 import kotlinx.coroutines.*
4 import kotlin.time.*
5
6 /**
7 * Clause that selects the given [block] after a specified timeout passes.
8 * If timeout is negative or zero, [block] is selected immediately.
9 *
10 * **Note: This is an experimental api.** It may be replaced with light-weight timer/timeout channels in the future.
11 *
12 * @param timeMillis timeout time in milliseconds.
13 */
14 @ExperimentalCoroutinesApi
15 @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
onTimeoutnull16 public fun <R> SelectBuilder<R>.onTimeout(timeMillis: Long, block: suspend () -> R): Unit =
17 OnTimeout(timeMillis).selectClause.invoke(block)
18
19 /**
20 * Clause that selects the given [block] after the specified [timeout] passes.
21 * If timeout is negative or zero, [block] is selected immediately.
22 *
23 * **Note: This is an experimental api.** It may be replaced with light-weight timer/timeout channels in the future.
24 */
25 @ExperimentalCoroutinesApi
26 public fun <R> SelectBuilder<R>.onTimeout(timeout: Duration, block: suspend () -> R): Unit =
27 onTimeout(timeout.toDelayMillis(), block)
28
29 /**
30 * We implement [SelectBuilder.onTimeout] as a clause, so each invocation creates
31 * an instance of [OnTimeout] that specifies the registration part according to
32 * the [timeout][timeMillis] parameter.
33 */
34 private class OnTimeout(
35 private val timeMillis: Long
36 ) {
37 @Suppress("UNCHECKED_CAST")
38 val selectClause: SelectClause0
39 get() = SelectClause0Impl(
40 clauseObject = this@OnTimeout,
41 regFunc = OnTimeout::register as RegistrationFunction
42 )
43
44 @Suppress("UNUSED_PARAMETER")
45 private fun register(select: SelectInstance<*>, ignoredParam: Any?) {
46 // Should this clause complete immediately?
47 if (timeMillis <= 0) {
48 select.selectInRegistrationPhase(Unit)
49 return
50 }
51 // Invoke `trySelect` after the timeout is reached.
52 val action = Runnable {
53 select.trySelect(this@OnTimeout, Unit)
54 }
55 select as SelectImplementation<*>
56 val context = select.context
57 val disposableHandle = context.delay.invokeOnTimeout(timeMillis, action, context)
58 // Do not forget to clean-up when this `select` is completed or cancelled.
59 select.disposeOnCompletion(disposableHandle)
60 }
61 }
62