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 */ delaynull15public 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 */ withTimeoutOrNullnull48public 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