1 /*
<lambda>null2  * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines
6 
7 import kotlinx.coroutines.selects.*
8 import kotlin.coroutines.*
9 import kotlin.time.*
10 
11 /**
12  * This dispatcher _feature_ is implemented by [CoroutineDispatcher] implementations that natively support
13  * scheduled execution of tasks.
14  *
15  * Implementation of this interface affects operation of
16  * [delay][kotlinx.coroutines.delay] and [withTimeout] functions.
17  *
18  * @suppress **This an internal API and should not be used from general code.**
19  */
20 @InternalCoroutinesApi
21 public interface Delay {
22 
23     /** @suppress **/
24     @Deprecated(
25         message = "Deprecated without replacement as an internal method never intended for public use",
26         level = DeprecationLevel.ERROR
27     ) // Error since 1.6.0
28     public suspend fun delay(time: Long) {
29         if (time <= 0) return // don't delay
30         return suspendCancellableCoroutine { scheduleResumeAfterDelay(time, it) }
31     }
32 
33     /**
34      * Schedules resume of a specified [continuation] after a specified delay [timeMillis].
35      *
36      * Continuation **must be scheduled** to resume even if it is already cancelled, because a cancellation is just
37      * an exception that the coroutine that used `delay` might wanted to catch and process. It might
38      * need to close some resources in its `finally` blocks, for example.
39      *
40      * This implementation is supposed to use dispatcher's native ability for scheduled execution in its thread(s).
41      * In order to avoid an extra delay of execution, the following code shall be used to resume this
42      * [continuation] when the code is already executing in the appropriate thread:
43      *
44      * ```kotlin
45      * with(continuation) { resumeUndispatchedWith(Unit) }
46      * ```
47      */
48     public fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>)
49 
50     /**
51      * Schedules invocation of a specified [block] after a specified delay [timeMillis].
52      * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] of this invocation
53      * request if it is not needed anymore.
54      */
55     public fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle =
56         DefaultDelay.invokeOnTimeout(timeMillis, block, context)
57 }
58 
59 /**
60  * Enhanced [Delay] interface that provides additional diagnostics for [withTimeout].
61  * Is going to be removed once there is proper JVM-default support.
62  * Then we'll be able put this function into [Delay] without breaking binary compatibility.
63  */
64 @InternalCoroutinesApi
65 internal interface DelayWithTimeoutDiagnostics : Delay {
66     /**
67      * Returns a string that explains that the timeout has occurred, and explains what can be done about it.
68      */
timeoutMessagenull69     fun timeoutMessage(timeout: Duration): String
70 }
71 
72 /**
73  * Suspends until cancellation, in which case it will throw a [CancellationException].
74  *
75  * This function returns [Nothing], so it can be used in any coroutine,
76  * regardless of the required return type.
77  *
78  * Usage example in callback adapting code:
79  *
80  * ```kotlin
81  * fun currentTemperature(): Flow<Temperature> = callbackFlow {
82  *     val callback = SensorCallback { degreesCelsius: Double ->
83  *         trySend(Temperature.celsius(degreesCelsius))
84  *     }
85  *     try {
86  *         registerSensorCallback(callback)
87  *         awaitCancellation() // Suspends to keep getting updates until cancellation.
88  *     } finally {
89  *         unregisterSensorCallback(callback)
90  *     }
91  * }
92  * ```
93  *
94  * Usage example in (non declarative) UI code:
95  *
96  * ```kotlin
97  * suspend fun showStuffUntilCancelled(content: Stuff): Nothing {
98  *     someSubView.text = content.title
99  *     anotherSubView.text = content.description
100  *     someView.visibleInScope {
101  *         awaitCancellation() // Suspends so the view stays visible.
102  *     }
103  * }
104  * ```
105  */
106 public suspend fun awaitCancellation(): Nothing = suspendCancellableCoroutine {}
107 
108 /**
109  * Delays coroutine for a given time without blocking a thread and resumes it after a specified time.
110  * If the given [timeMillis] is non-positive, this function returns immediately.
111  *
112  * This suspending function is cancellable.
113  * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
114  * immediately resumes with [CancellationException].
115  * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
116  * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
117  *
118  * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
119  *
120  * Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
121  *
122  * Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
123  * @param timeMillis time in milliseconds.
124  */
delaynull125 public suspend fun delay(timeMillis: Long) {
126     if (timeMillis <= 0) return // don't delay
127     return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
128         // if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
129         if (timeMillis < Long.MAX_VALUE) {
130             cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
131         }
132     }
133 }
134 
135 /**
136  * Delays coroutine for a given [duration] without blocking a thread and resumes it after the specified time.
137  * If the given [duration] is non-positive, this function returns immediately.
138  *
139  * This suspending function is cancellable.
140  * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
141  * immediately resumes with [CancellationException].
142  * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
143  * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
144  *
145  * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
146  *
147  * Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
148  *
149  * Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
150  */
delaynull151 public suspend fun delay(duration: Duration): Unit = delay(duration.toDelayMillis())
152 
153 /** Returns [Delay] implementation of the given context */
154 internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay
155 
156 /**
157  * Convert this duration to its millisecond value.
158  * Positive durations are coerced at least `1`.
159  */
160 internal fun Duration.toDelayMillis(): Long =
161     if (this > Duration.ZERO) inWholeMilliseconds.coerceAtLeast(1) else 0
162