• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * Suspends until cancellation, in which case it will throw a [CancellationException].
61  *
62  * This function returns [Nothing], so it can be used in any coroutine,
63  * regardless of the required return type.
64  *
65  * Usage example in callback adapting code:
66  *
67  * ```kotlin
68  * fun currentTemperature(): Flow<Temperature> = callbackFlow {
69  *     val callback = SensorCallback { degreesCelsius: Double ->
70  *         trySend(Temperature.celsius(degreesCelsius))
71  *     }
72  *     try {
73  *         registerSensorCallback(callback)
74  *         awaitCancellation() // Suspends to keep getting updates until cancellation.
75  *     } finally {
76  *         unregisterSensorCallback(callback)
77  *     }
78  * }
79  * ```
80  *
81  * Usage example in (non declarative) UI code:
82  *
83  * ```kotlin
84  * suspend fun showStuffUntilCancelled(content: Stuff): Nothing {
85  *     someSubView.text = content.title
86  *     anotherSubView.text = content.description
87  *     someView.visibleInScope {
88  *         awaitCancellation() // Suspends so the view stays visible.
89  *     }
90  * }
91  * ```
92  */
<lambda>null93 public suspend fun awaitCancellation(): Nothing = suspendCancellableCoroutine {}
94 
95 /**
96  * Delays coroutine for a given time without blocking a thread and resumes it after a specified time.
97  *
98  * This suspending function is cancellable.
99  * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
100  * immediately resumes with [CancellationException].
101  * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
102  * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
103  *
104  * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
105  *
106  * Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
107  *
108  * Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
109  * @param timeMillis time in milliseconds.
110  */
delaynull111 public suspend fun delay(timeMillis: Long) {
112     if (timeMillis <= 0) return // don't delay
113     return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
114         // if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
115         if (timeMillis < Long.MAX_VALUE) {
116             cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
117         }
118     }
119 }
120 
121 /**
122  * Delays coroutine for a given [duration] without blocking a thread and resumes it after the specified time.
123  *
124  * This suspending function is cancellable.
125  * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
126  * immediately resumes with [CancellationException].
127  * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
128  * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
129  *
130  * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
131  *
132  * Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
133  *
134  * Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
135  */
delaynull136 public suspend fun delay(duration: Duration): Unit = delay(duration.toDelayMillis())
137 
138 /** Returns [Delay] implementation of the given context */
139 internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay
140 
141 /**
142  * Convert this duration to its millisecond value.
143  * Positive durations are coerced at least `1`.
144  */
145 internal fun Duration.toDelayMillis(): Long =
146     if (this > Duration.ZERO) inWholeMilliseconds.coerceAtLeast(1) else 0
147