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