@@ -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 */
68155public suspend fun <T > withTimeout (timeout : Duration , block : suspend CoroutineScope .() -> T ): T {
69156 contract {
0 commit comments