Repository: Kotlin/kotlinx.coroutines
File: kotlinx-coroutines-core/common/src/Delay.kt (Kotlin)

Code before:
/**
 * Delays coroutine for at least the given [duration] without blocking a thread and resumes it after the specified time.
 * If the given [duration] is non-positive, this function returns immediately.
 *
 * 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].
 * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
 * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
 *
 * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
 *
 * 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.
 */
public suspend fun delay(duration: Duration): Unit = delay(duration.toDelayMillis())

/** Returns [Delay] implementation of the given context */
internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay

/**
 * Convert this duration to its millisecond value. Durations which have a nanosecond component less than
 * a single millisecond will be rounded up to the next largest millisecond.
 */
internal fun Duration.toDelayMillis(): Long {
    val millis = inWholeMilliseconds
    val nanosecondsInMillisecond = 1_000_000
    return when {
        this <= Duration.ZERO -> 0L
        millis * nanosecondsInMillisecond < inWholeNanoseconds -> millis + 1
        else -> millis
    }
}


Review comment (bug):
it will fail on attempt to do `Duration.INFINITE.toDelayMillis()` as the `Duration.INFINITE.inWholeMilliseconds` returns Long.MAX_VALUE. Adding 1 to it will overflow and return Long.MIN_VALUE. Another place when it may overflow is `millis * nanosecondsInMillisecond` (for any millis duration above Long.MAX_VALUE / 1000_000). 

Probably the easiest way to round it up is to defer all the math to kotlin.time
```
(this + 999999.nanoseconds).inWholeMilliseconds
```



Code after:

/**
 * Delays coroutine for at least the given [duration] without blocking a thread and resumes it after the specified time.
 * If the given [duration] is non-positive, this function returns immediately.
 *
 * 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].
 * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
 * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
 *
 * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
 *
 * 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.
 */
public suspend fun delay(duration: Duration): Unit = delay(duration.toDelayMillis())

/** Returns [Delay] implementation of the given context */
internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay

/**
 * Convert this duration to its millisecond value. Durations which have a nanosecond component less than
 * a single millisecond will be rounded up to the next largest millisecond.
 */
internal fun Duration.toDelayMillis(): Long = when (isPositive()) {
    true -> plus(999_999L.nanoseconds).inWholeMilliseconds
    false -> 0L
}