From ff9d472852df745d51d7860d0c7fbc9bac665276 Mon Sep 17 00:00:00 2001 From: Francesco Vasco Date: Sun, 16 Feb 2020 18:21:43 +0100 Subject: [PATCH] kotlin.time.Duration support Fixes #1402 --- kotlinx-coroutines-core/common/src/Delay.kt | 15 ++++++++ kotlinx-coroutines-core/common/src/Timeout.kt | 34 +++++++++++++++++++ .../common/src/selects/Select.kt | 14 ++++++++ 3 files changed, 63 insertions(+) diff --git a/kotlinx-coroutines-core/common/src/Delay.kt b/kotlinx-coroutines-core/common/src/Delay.kt index acb924020a..092c665066 100644 --- a/kotlinx-coroutines-core/common/src/Delay.kt +++ b/kotlinx-coroutines-core/common/src/Delay.kt @@ -6,6 +6,8 @@ package kotlinx.coroutines import kotlinx.coroutines.selects.* import kotlin.coroutines.* +import kotlin.time.Duration +import kotlin.time.ExperimentalTime /** * This dispatcher _feature_ is implemented by [CoroutineDispatcher] implementations that natively support @@ -75,5 +77,18 @@ public suspend fun delay(timeMillis: Long) { } } +/** + * Delays coroutine for a given [duration] without blocking a thread and resumes it after the specified time. + * This suspending function is cancellable. + * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * immediately resumes with [CancellationException]. + * + * Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause. + * + * Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context. + */ +@ExperimentalTime +public suspend inline fun delay(duration: Duration) = delay(duration.toLongMilliseconds()) + /** Returns [Delay] implementation of the given context */ internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay diff --git a/kotlinx-coroutines-core/common/src/Timeout.kt b/kotlinx-coroutines-core/common/src/Timeout.kt index 7e6f0d0e2d..697ac11dc4 100644 --- a/kotlinx-coroutines-core/common/src/Timeout.kt +++ b/kotlinx-coroutines-core/common/src/Timeout.kt @@ -10,6 +10,8 @@ import kotlinx.coroutines.selects.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* import kotlin.jvm.* +import kotlin.time.Duration +import kotlin.time.ExperimentalTime /** * Runs a given suspending [block] of code inside a coroutine with a specified [timeout][timeMillis] and throws @@ -32,6 +34,22 @@ public suspend fun withTimeout(timeMillis: Long, block: suspend CoroutineSco } } +/** + * Runs a given suspending [block] of code inside a coroutine with the specified [timeout] and throws + * a [TimeoutCancellationException] if the timeout was exceeded. + * + * The code that is executing inside the [block] is cancelled on timeout and the active or next invocation of + * the cancellable suspending function inside the block throws a [TimeoutCancellationException]. + * + * The sibling function that does not throw an exception on timeout is [withTimeoutOrNull]. + * Note that the timeout action can be specified for a [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause. + * + * Implementation note: how the time is tracked exactly is an implementation detail of the context's [CoroutineDispatcher]. + */ +@ExperimentalTime +public suspend inline fun withTimeout(timeout: Duration, noinline block: suspend CoroutineScope.() -> T): T = + withTimeout(timeout.toLongMilliseconds(), block) + /** * Runs a given suspending block of code inside a coroutine with a specified [timeout][timeMillis] and returns * `null` if this timeout was exceeded. @@ -65,6 +83,22 @@ public suspend fun withTimeoutOrNull(timeMillis: Long, block: suspend Corout } } +/** + * Runs a given suspending block of code inside a coroutine with the specified [timeout] and returns + * `null` if this timeout was exceeded. + * + * The code that is executing inside the [block] is cancelled on timeout and the active or next invocation of + * cancellable suspending function inside the block throws a [TimeoutCancellationException]. + * + * The sibling function that throws an exception on timeout is [withTimeout]. + * Note that the timeout action can be specified for a [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause. + * + * Implementation note: how the time is tracked exactly is an implementation detail of the context's [CoroutineDispatcher]. + */ +@ExperimentalTime +public suspend inline fun withTimeoutOrNull(timeout: Duration, noinline block: suspend CoroutineScope.() -> T): T? = + withTimeoutOrNull(timeout.toLongMilliseconds(), block) + private fun setupTimeout( coroutine: TimeoutCoroutine, block: suspend CoroutineScope.() -> T diff --git a/kotlinx-coroutines-core/common/src/selects/Select.kt b/kotlinx-coroutines-core/common/src/selects/Select.kt index b741a1a40f..75693aecd0 100644 --- a/kotlinx-coroutines-core/common/src/selects/Select.kt +++ b/kotlinx-coroutines-core/common/src/selects/Select.kt @@ -14,6 +14,8 @@ import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* import kotlin.jvm.* import kotlin.native.concurrent.* +import kotlin.time.Duration +import kotlin.time.ExperimentalTime /** * Scope for [select] invocation. @@ -52,6 +54,18 @@ public interface SelectBuilder { public fun onTimeout(timeMillis: Long, block: suspend () -> R) } + +/** + * Clause that selects the given [block] after the specified [timeout] passes. + * If timeout is negative or zero, [block] is selected immediately. + * + * **Note: This is an experimental api.** It may be replaced with light-weight timer/timeout channels in the future. + */ +@ExperimentalCoroutinesApi +@ExperimentalTime +public inline fun SelectBuilder.onTimeout(timeout: Duration, noinline block: suspend () -> R) = + onTimeout(timeout.toLongMilliseconds(), block) + /** * Clause for [select] expression without additional parameters that does not select any value. */