Skip to content

Commit f819989

Browse files
committed
Document the withTimeout function
1 parent 16a4eef commit f819989

File tree

1 file changed

+95
-8
lines changed

1 file changed

+95
-8
lines changed

kotlinx-coroutines-core/common/src/Timeout.kt

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,11 @@ public suspend fun <T> withTimeout(timeMillis: Long, block: suspend CoroutineSco
4646
}
4747

4848
/**
49-
* Runs a given suspending [block] of code inside a coroutine with the specified [timeout] and throws
50-
* a [TimeoutCancellationException] if the timeout was exceeded.
51-
* If the given [timeout] is non-positive, [TimeoutCancellationException] is thrown immediately.
52-
*
53-
* The code that is executing inside the [block] is cancelled on timeout and the active or next invocation of
54-
* the cancellable suspending function inside the block throws a [TimeoutCancellationException].
49+
* Calls the specified suspending [block] with the specified [timeout], suspends until it completes,
50+
* and returns the result.
5551
*
56-
* The sibling function that does not throw an exception on timeout is [withTimeoutOrNull].
57-
* Note that the timeout action can be specified for a [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
52+
* If the [block] execution times out, it is cancelled with a [TimeoutCancellationException].
53+
* If the [timeout] is non-positive, this happens immediately and the [block] is not executed.
5854
*
5955
* **The timeout event is asynchronous with respect to the code running in the block** and may happen at any time,
6056
* even right before the return from inside the timeout [block]. Keep this in mind if you open or acquire some
@@ -64,6 +60,97 @@ public suspend fun <T> withTimeout(timeMillis: Long, block: suspend CoroutineSco
6460
* section of the coroutines guide for details.
6561
*
6662
* > Implementation note: how the time is tracked exactly is an implementation detail of the context's [CoroutineDispatcher].
63+
*
64+
* ## Structured Concurrency
65+
*
66+
* [withTimeout] behaves like [coroutineScope], as it, too, creates a new *scoped child coroutine*.
67+
* Refer to the documentation of [coroutineScope] for details.
68+
*
69+
* ## Pitfalls
70+
*
71+
* ### Cancellation is cooperative
72+
*
73+
* [withTimeout] will not automatically stop all code inside it from being executed once the timeout gets triggered.
74+
* It only cancels the running [block], but it's up to the [block] to notice that it was cancelled, for example,
75+
* using [ensureActive], checking [isActive], or using [suspendCancellableCoroutine].
76+
*
77+
* For example, this JVM code will run to completion, taking 10 seconds to do so:
78+
*
79+
* ```
80+
* withTimeout(1.seconds) {
81+
* Thread.sleep(10_000)
82+
* }
83+
* ```
84+
*
85+
* On the JVM, use the `runInterruptible` function to propagate cancellations
86+
* to blocking JVM code as thread interruptions.
87+
*
88+
* See the [Cancellation is cooperative](https://kotlinlang.org/docs/cancellation-and-timeouts.html#cancellation-is-cooperative).
89+
* section of the coroutines guide for details.
90+
*
91+
* ### [TimeoutCancellationException] is not considered an error
92+
*
93+
* Consider this code:
94+
*
95+
* ```
96+
* coroutineScope {
97+
* launch {
98+
* withTimeout(10.milliseconds) {
99+
* // Some operation that is going to time out
100+
* awaitCancellation()
101+
* }
102+
* }
103+
* }
104+
* ```
105+
*
106+
* Here, the timeout will be triggered, and [withTimeout] will finish with a [TimeoutCancellationException].
107+
* However, [coroutineScope] will finish normally.
108+
* The reason is that when coroutines finish with a [CancellationException],
109+
* the error does not get propagated to the parent, just like it doesn't when a child actually gets cancelled.
110+
*
111+
* For ensuring that timeouts are treated as true errors that should cause the parent to fail,
112+
* use [withTimeoutOrNull] and check the return value:
113+
*
114+
* ```
115+
* coroutineScope {
116+
* launch {
117+
* withTimeoutOrNull(10.milliseconds) {
118+
* // Some operation that is going to time out
119+
* awaitCancellation()
120+
* } ?: error("Timed out!")
121+
* }
122+
* }
123+
* ```
124+
*
125+
* If [withTimeout] has to return a nullable value and [withTimeoutOrNull] can not be used,
126+
* this pattern can help instead:
127+
*
128+
* ```
129+
* coroutineScope {
130+
* launch {
131+
* try {
132+
* withTimeoutOrNull(10.milliseconds) {
133+
* // Some operation that is going to time out
134+
* awaitCancellation()
135+
* }
136+
* } catch (e: TimeoutCancellationException) {
137+
* error("Timed out!")
138+
* }
139+
* }
140+
* }
141+
* ```
142+
*
143+
* Another option is to specify the timeout action in a [select] invocation
144+
* with [onTimeout][SelectBuilder.onTimeout] clause.
145+
*
146+
* ### Returning closeable resources
147+
*
148+
* Values returned from [withTimeout] will typically be lost if the caller is cancelled.
149+
*
150+
* See the corresponding section in the [coroutineScope] documentation for details.
151+
*
152+
* @see withTimeoutOrNull
153+
* @see SelectBuilder.onTimeout
67154
*/
68155
public suspend fun <T> withTimeout(timeout: Duration, block: suspend CoroutineScope.() -> T): T {
69156
contract {

0 commit comments

Comments
 (0)