xref: /aosp_15_r20/external/kotlinx.coroutines/kotlinx-coroutines-core/jdk8/src/time/Time.kt (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)
1 @file:OptIn(ExperimentalContracts::class)
2 
3 package kotlinx.coroutines.time
4 
5 import kotlinx.coroutines.*
6 import kotlinx.coroutines.flow.*
7 import kotlinx.coroutines.selects.*
8 import java.time.*
9 import java.time.temporal.*
10 import kotlin.contracts.*
11 
12 /**
13  * "java.time" adapter method for [kotlinx.coroutines.delay].
14  */
delaynull15 public suspend fun delay(duration: Duration): Unit = delay(duration.coerceToMillis())
16 
17 /**
18  * "java.time" adapter method for [kotlinx.coroutines.flow.debounce].
19  */
20 @FlowPreview
21 public fun <T> Flow<T>.debounce(timeout: Duration): Flow<T> = debounce(timeout.coerceToMillis())
22 
23 /**
24  * "java.time" adapter method for [kotlinx.coroutines.flow.sample].
25  */
26 @FlowPreview
27 public fun <T> Flow<T>.sample(period: Duration): Flow<T> = sample(period.coerceToMillis())
28 
29 /**
30  * "java.time" adapter method for [SelectBuilder.onTimeout].
31  */
32 public fun <R> SelectBuilder<R>.onTimeout(duration: Duration, block: suspend () -> R): Unit =
33         onTimeout(duration.coerceToMillis(), block)
34 
35 /**
36  * "java.time" adapter method for [kotlinx.coroutines.withTimeout].
37  */
38 public suspend fun <T> withTimeout(duration: Duration, block: suspend CoroutineScope.() -> T): T {
39     contract {
40         callsInPlace(block, InvocationKind.EXACTLY_ONCE)
41     }
42     return kotlinx.coroutines.withTimeout(duration.coerceToMillis(), block)
43 }
44 
45 /**
46  * "java.time" adapter method for [kotlinx.coroutines.withTimeoutOrNull].
47  */
withTimeoutOrNullnull48 public suspend fun <T> withTimeoutOrNull(duration: Duration, block: suspend CoroutineScope.() -> T): T? =
49         kotlinx.coroutines.withTimeoutOrNull(duration.coerceToMillis(), block)
50 
51 /**
52  * Coerces the given [Duration] to a millisecond delay.
53  * Negative values are coerced to zero, values that cannot
54  * be represented in milliseconds as long ("infinite" duration) are coerced to [Long.MAX_VALUE]
55  * and durations lesser than a millisecond are coerced to 1 millisecond.
56  *
57  * The rationale of coercion:
58  * 1) Too large durations typically indicate infinity and Long.MAX_VALUE is the
59  *    best approximation of infinity we can provide.
60  * 2) Coercing too small durations to 1 instead of 0 is crucial for two patterns:
61  *    - Programming with deadlines and delays
62  *    - Non-suspending fast-paths (e.g. `withTimeout(1 nanosecond) { 42 }` should not throw)
63  */
64 private fun Duration.coerceToMillis(): Long {
65     if (this <= Duration.ZERO) return 0
66     if (this <= ChronoUnit.MILLIS.duration) return 1
67 
68     // Maximum scalar values of Duration.ofMillis(Long.MAX_VALUE)
69     val maxSeconds = 9223372036854775
70     val maxNanos = 807000000
71     return if (seconds < maxSeconds || seconds == maxSeconds && nano < maxNanos) toMillis()
72     else Long.MAX_VALUE
73 }
74