1 /*
<lambda>null2  * Copyright 2016-2020 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      * Delays coroutine for a given time without blocking a thread and resumes it after a specified time.
24      *
25      * This suspending function is cancellable.
26      * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
27      * immediately resumes with [CancellationException].
28      * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
29      * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
30      */
31     public suspend fun delay(time: Long) {
32         if (time <= 0) return // don't delay
33         return suspendCancellableCoroutine { scheduleResumeAfterDelay(time, it) }
34     }
35 
36     /**
37      * Schedules resume of a specified [continuation] after a specified delay [timeMillis].
38      *
39      * Continuation **must be scheduled** to resume even if it is already cancelled, because a cancellation is just
40      * an exception that the coroutine that used `delay` might wanted to catch and process. It might
41      * need to close some resources in its `finally` blocks, for example.
42      *
43      * This implementation is supposed to use dispatcher's native ability for scheduled execution in its thread(s).
44      * In order to avoid an extra delay of execution, the following code shall be used to resume this
45      * [continuation] when the code is already executing in the appropriate thread:
46      *
47      * ```kotlin
48      * with(continuation) { resumeUndispatchedWith(Unit) }
49      * ```
50      */
51     public fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>)
52 
53     /**
54      * Schedules invocation of a specified [block] after a specified delay [timeMillis].
55      * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] of this invocation
56      * request if it is not needed anymore.
57      *
58      * This implementation uses a built-in single-threaded scheduled executor service.
59      */
60     public fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle =
61         DefaultDelay.invokeOnTimeout(timeMillis, block, context)
62 }
63 
64 /**
65  * Suspends until cancellation, in which case it will throw a [CancellationException].
66  *
67  * This function returns [Nothing], so it can be used in any coroutine,
68  * regardless of the required return type.
69  *
70  * Usage example in callback adapting code:
71  *
72  * ```kotlin
73  * fun currentTemperature(): Flow<Temperature> = callbackFlow {
74  *     val callback = SensorCallback { degreesCelsius: Double ->
75  *         trySend(Temperature.celsius(degreesCelsius))
76  *     }
77  *     try {
78  *         registerSensorCallback(callback)
79  *         awaitCancellation() // Suspends to keep getting updates until cancellation.
80  *     } finally {
81  *         unregisterSensorCallback(callback)
82  *     }
83  * }
84  * ```
85  *
86  * Usage example in (non declarative) UI code:
87  *
88  * ```kotlin
89  * suspend fun showStuffUntilCancelled(content: Stuff): Nothing {
90  *     someSubView.text = content.title
91  *     anotherSubView.text = content.description
92  *     someView.visibleInScope {
93  *         awaitCancellation() // Suspends so the view stays visible.
94  *     }
95  * }
96  * ```
97  */
<lambda>null98 public suspend fun awaitCancellation(): Nothing = suspendCancellableCoroutine {}
99 
100 /**
101  * Delays coroutine for a given time without blocking a thread and resumes it after a specified time.
102  *
103  * This suspending function is cancellable.
104  * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
105  * immediately resumes with [CancellationException].
106  * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
107  * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
108  *
109  * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
110  *
111  * Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
112  *
113  * Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
114  * @param timeMillis time in milliseconds.
115  */
delaynull116 public suspend fun delay(timeMillis: Long) {
117     if (timeMillis <= 0) return // don't delay
118     return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
119         // if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
120         if (timeMillis < Long.MAX_VALUE) {
121             cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
122         }
123     }
124 }
125 
126 /**
127  * Delays coroutine for a given [duration] without blocking a thread and resumes it after the specified time.
128  *
129  * This suspending function is cancellable.
130  * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
131  * immediately resumes with [CancellationException].
132  * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
133  * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
134  *
135  * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
136  *
137  * Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
138  *
139  * Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
140  */
141 @ExperimentalTime
delaynull142 public suspend fun delay(duration: Duration): Unit = delay(duration.toDelayMillis())
143 
144 /** Returns [Delay] implementation of the given context */
145 internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay
146 
147 /**
148  * Convert this duration to its millisecond value.
149  * Positive durations are coerced at least `1`.
150  */
151 @ExperimentalTime
152 internal fun Duration.toDelayMillis(): Long =
153     if (this > Duration.ZERO) toLongMilliseconds().coerceAtLeast(1) else 0
154