Skip to content

Commit 52cd803

Browse files
fvascoFrancesco Vasco
authored and
Francesco Vasco
committed
Check toMillis overflow in operators with Duration
Fix #868
1 parent 4451d72 commit 52cd803

File tree

2 files changed

+85
-5
lines changed

2 files changed

+85
-5
lines changed

integration/kotlinx-coroutines-jdk8/src/time/Time.kt

+22-5
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,45 @@ package kotlinx.coroutines.time
66
import kotlinx.coroutines.CoroutineScope
77
import kotlinx.coroutines.selects.SelectBuilder
88
import java.time.Duration
9-
import java.util.concurrent.TimeUnit
9+
import java.time.temporal.ChronoUnit
1010

1111
/**
1212
* "java.time" adapter method for [kotlinx.coroutines.delay]
1313
*/
1414
public suspend fun delay(duration: Duration) =
15-
kotlinx.coroutines.delay(duration.toMillis())
15+
kotlinx.coroutines.delay(duration.toMillisDelay())
1616

1717
/**
1818
* "java.time" adapter method for [SelectBuilder.onTimeout]
1919
*/
2020
public fun <R> SelectBuilder<R>.onTimeout(duration: Duration, block: suspend () -> R) =
21-
onTimeout(duration.toMillis(), block)
21+
onTimeout(duration.toMillisDelay(), block)
2222

2323
/**
2424
* "java.time" adapter method for [kotlinx.coroutines.withTimeout]
2525
*/
2626
public suspend fun <T> withTimeout(duration: Duration, block: suspend CoroutineScope.() -> T): T =
27-
kotlinx.coroutines.withTimeout(duration.toMillis(), block)
27+
kotlinx.coroutines.withTimeout(duration.toMillisDelay(), block)
2828

2929
/**
3030
* "java.time" adapter method for [kotlinx.coroutines.withTimeoutOrNull]
3131
*/
3232
public suspend fun <T> withTimeoutOrNull(duration: Duration, block: suspend CoroutineScope.() -> T): T? =
33-
kotlinx.coroutines.withTimeoutOrNull(duration.toMillis(), block)
33+
kotlinx.coroutines.withTimeoutOrNull(duration.toMillisDelay(), block)
34+
35+
/**
36+
* Convert the [Duration] to millisecond delay.
37+
*
38+
* @return strictly positive duration is coerced to 1..[Long.MAX_VALUE] ms, `0` otherwise.
39+
*/
40+
private fun Duration.toMillisDelay(): Long =
41+
if (this <= ChronoUnit.MILLIS.duration) {
42+
if (this <= Duration.ZERO) 0
43+
else 1
44+
} else {
45+
// values of Duration.ofMillis(Long.MAX_VALUE)
46+
val maxSeconds = 9223372036854775
47+
val maxNanos = 807000000
48+
if (seconds < maxSeconds || seconds == maxSeconds && nano < maxNanos) toMillis()
49+
else Long.MAX_VALUE
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.coroutines.time
6+
7+
import kotlinx.coroutines.*
8+
import org.hamcrest.core.*
9+
import org.junit.*
10+
import kotlinx.coroutines.CompletableDeferred
11+
import kotlinx.coroutines.Job
12+
import kotlinx.coroutines.selects.select
13+
import java.time.temporal.ChronoUnit
14+
15+
class TimeTest : TestBase() {
16+
17+
private val durations = ChronoUnit.values().map { it.duration }
18+
19+
@Test
20+
fun testDelay() {
21+
runTest {
22+
for (duration in durations) {
23+
delay(duration.negated())
24+
25+
launch(start = CoroutineStart.UNDISPATCHED) {
26+
delay(duration)
27+
}
28+
.cancel()
29+
}
30+
}
31+
}
32+
33+
@Test
34+
fun testOnTimeout() {
35+
runTest {
36+
for (duration in durations) {
37+
select<Unit> {
38+
onTimeout(duration) {}
39+
onTimeout(duration.negated()) {}
40+
}
41+
}
42+
}
43+
}
44+
45+
@Test
46+
fun testWithTimeout() {
47+
runTest {
48+
for (duration in durations) {
49+
withTimeout(duration) {}
50+
}
51+
}
52+
}
53+
54+
@Test
55+
fun testWithTimeoutOrNull() {
56+
runTest {
57+
for (duration in durations) {
58+
withTimeoutOrNull(duration) {}
59+
}
60+
}
61+
}
62+
63+
}

0 commit comments

Comments
 (0)