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