Skip to content

Commit 769d482

Browse files
committed
Bug fixed on waitTimeoutOrNull (was flaky testOuterTimeoutTest)
1 parent 2a93646 commit 769d482

File tree

3 files changed

+31
-17
lines changed

3 files changed

+31
-17
lines changed

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/EventLoop.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ internal class EventLoopImpl(
154154
private val block: Runnable
155155
) : QueuedTask() {
156156
override fun invoke() { block.run() }
157+
override fun toString(): String = block.toString()
157158
}
158159

159160
private abstract inner class DelayedTask(
@@ -176,6 +177,8 @@ internal class EventLoopImpl(
176177
}
177178

178179
open fun cancel() {}
180+
181+
override fun toString(): String = "Delayed[nanos=$nanoTime,seq=$sequence]"
179182
}
180183

181184
private inner class DelayedResumeTask(
@@ -197,5 +200,6 @@ internal class EventLoopImpl(
197200
private val block: Runnable
198201
) : DelayedTask(time, timeUnit) {
199202
override fun invoke() { block.run() }
203+
override fun toString(): String = super.toString() + block.toString()
200204
}
201205
}

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ public suspend fun <T> withTimeoutOrNull(time: Long, unit: TimeUnit = TimeUnit.M
148148
try {
149149
block.startCoroutineUninterceptedOrReturn(completion)
150150
} catch (e: TimeoutException) {
151-
null // replace inner timeout exception with null result
151+
// replace inner timeout exception on our coroutine with null result
152+
if (e.coroutine == completion) null else throw e
152153
}
153154
}
154155
}

kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullTest.kt

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,26 +122,35 @@ class WithTimeoutOrNullTest : TestBase() {
122122
}
123123

124124
@Test
125-
fun testOuterTimeoutTest() {
126-
// stress test this particular case 1000 times
127-
val nTimes = if (isStressTest) 1000 else 1
128-
repeat(nTimes) {
129-
runBlocking {
130-
var counter = 0
131-
val result = withTimeoutOrNull(250) {
125+
fun testOuterTimeoutTest() = runBlocking {
126+
var counter = 0
127+
val result = withTimeoutOrNull(250) {
128+
while (true) {
129+
val inner = withTimeoutOrNull(100) {
132130
while (true) {
133-
val inner = withTimeoutOrNull(100) {
134-
while (true) {
135-
yield()
136-
}
137-
}
138-
assertThat(inner, IsNull())
139-
counter++
131+
yield()
140132
}
141133
}
142-
assertThat(result, IsNull())
143-
assertThat(counter, IsEqual(2))
134+
assertThat(inner, IsNull())
135+
counter++
144136
}
145137
}
138+
assertThat(result, IsNull())
139+
assertThat(counter, IsEqual(2))
140+
}
141+
142+
@Test
143+
fun testOuterTimeoutFiredBeforeInner() = runBlocking<Unit> {
144+
val result = withTimeoutOrNull(100) {
145+
Thread.sleep(200) // wait enough for outer timeout to fire
146+
run(NonCancellable) { yield() } // give an event loop a chance to run and process that cancellation
147+
withTimeoutOrNull(100) {
148+
yield() // will cancel because of outer timeout
149+
expectUnreached()
150+
}
151+
expectUnreached() // should not be reached, because it is outer timeout
152+
}
153+
// outer timeout results in null
154+
assertThat(result, IsNull())
146155
}
147156
}

0 commit comments

Comments
 (0)