Skip to content

Commit 944a486

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

File tree

2 files changed

+72
-5
lines changed

2 files changed

+72
-5
lines changed

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

+19-5
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,42 @@ 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
109

1110
/**
1211
* "java.time" adapter method for [kotlinx.coroutines.delay]
1312
*/
1413
public suspend fun delay(duration: Duration) =
15-
kotlinx.coroutines.delay(duration.toMillis())
14+
kotlinx.coroutines.delay(duration.toMillisOrMax())
1615

1716
/**
1817
* "java.time" adapter method for [SelectBuilder.onTimeout]
1918
*/
2019
public fun <R> SelectBuilder<R>.onTimeout(duration: Duration, block: suspend () -> R) =
21-
onTimeout(duration.toMillis(), block)
20+
onTimeout(duration.toMillisOrMax(), block)
2221

2322
/**
2423
* "java.time" adapter method for [kotlinx.coroutines.withTimeout]
2524
*/
2625
public suspend fun <T> withTimeout(duration: Duration, block: suspend CoroutineScope.() -> T): T =
27-
kotlinx.coroutines.withTimeout(duration.toMillis(), block)
26+
kotlinx.coroutines.withTimeout(duration.toMillisOrMax(), block)
2827

2928
/**
3029
* "java.time" adapter method for [kotlinx.coroutines.withTimeoutOrNull]
3130
*/
3231
public suspend fun <T> withTimeoutOrNull(duration: Duration, block: suspend CoroutineScope.() -> T): T? =
33-
kotlinx.coroutines.withTimeoutOrNull(duration.toMillis(), block)
32+
kotlinx.coroutines.withTimeoutOrNull(duration.toMillisOrMax(), block)
33+
34+
/**
35+
* Try to convert a [Duration] to milliseconds, negative durations are translated to `0`,
36+
* too large durations are capped to `Duration.ofMillis(Long.MAX_VALUE)`.
37+
*/
38+
private fun Duration.toMillisOrMax(): Long {
39+
// values of Duration.ofMillis(Long.MAX_VALUE)
40+
val maxSeconds = 9223372036854775
41+
val maxNanos = 807000000
42+
return when {
43+
isNegative -> 0
44+
seconds < maxSeconds || seconds == maxSeconds && nano < maxNanos -> toMillis()
45+
else -> Long.MAX_VALUE
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
@Test
18+
fun testDelay() {
19+
runTest {
20+
launch(start = CoroutineStart.UNDISPATCHED) {
21+
delay(ChronoUnit.FOREVER.duration)
22+
}
23+
.cancel()
24+
}
25+
}
26+
27+
@Test
28+
fun testOnTimeout() {
29+
runTest {
30+
val completedJob: Job = CompletableDeferred(Unit)
31+
select<Unit> {
32+
onTimeout(ChronoUnit.FOREVER.duration) {}
33+
onTimeout(ChronoUnit.FOREVER.duration.negated()) {}
34+
completedJob.onJoin {}
35+
}
36+
}
37+
}
38+
39+
@Test
40+
fun testWithTimeout() {
41+
runTest {
42+
withTimeout(ChronoUnit.FOREVER.duration) {}
43+
}
44+
}
45+
46+
@Test
47+
fun testWithTimeoutOrNull() {
48+
runTest {
49+
withTimeoutOrNull(ChronoUnit.FOREVER.duration) {}
50+
}
51+
}
52+
53+
}

0 commit comments

Comments
 (0)