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 */ delaynull18public 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 */ withTimeoutOrNullnull51public 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