xref: /aosp_15_r20/external/kotlinx.coroutines/kotlinx-coroutines-core/common/src/selects/OnTimeout.kt (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)
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