Skip to content

Commit 838b052

Browse files
qwwdfsadelizarov
authored andcommitted
Check cancellation when starting unconfined coroutine
Fixes #621
1 parent d9b4ab2 commit 838b052

File tree

2 files changed

+125
-5
lines changed

2 files changed

+125
-5
lines changed

common/kotlinx-coroutines-core-common/src/Dispatched.kt

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ internal class DispatchedContinuation<in T>(
4343
_state = CompletedExceptionally(exception)
4444
resumeMode = MODE_ATOMIC_DEFAULT
4545
dispatcher.dispatch(context, this)
46-
} else
46+
} else {
4747
resumeUndispatchedWithException(exception)
48+
}
4849
}
4950

5051
@Suppress("NOTHING_TO_INLINE") // we need it inline to save us an entry on the stack
@@ -54,8 +55,11 @@ internal class DispatchedContinuation<in T>(
5455
_state = value
5556
resumeMode = MODE_CANCELLABLE
5657
dispatcher.dispatch(context, this)
57-
} else
58-
resumeUndispatched(value)
58+
} else {
59+
if (!resumeCancelled()) {
60+
resumeUndispatched(value)
61+
}
62+
}
5963
}
6064

6165
@Suppress("NOTHING_TO_INLINE") // we need it inline to save us an entry on the stack
@@ -65,8 +69,22 @@ internal class DispatchedContinuation<in T>(
6569
_state = CompletedExceptionally(exception)
6670
resumeMode = MODE_CANCELLABLE
6771
dispatcher.dispatch(context, this)
68-
} else
69-
resumeUndispatchedWithException(exception)
72+
} else {
73+
if (!resumeCancelled()) {
74+
resumeUndispatchedWithException(exception)
75+
}
76+
}
77+
}
78+
79+
@Suppress("NOTHING_TO_INLINE")
80+
inline fun resumeCancelled(): Boolean {
81+
val job = context[Job]
82+
if (job != null && !job.isActive) {
83+
resumeWithException(job.getCancellationException())
84+
return true
85+
}
86+
87+
return false
7088
}
7189

7290
@Suppress("NOTHING_TO_INLINE") // we need it inline to save us an entry on the stack
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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.experimental
6+
7+
import kotlin.test.*
8+
9+
class ExperimentalDispatchModeTest : TestBase() {
10+
@Test
11+
fun testUnconfinedCancellation() = runTest {
12+
val parent = Job()
13+
launch(parent) {
14+
expect(1)
15+
parent.cancel()
16+
launch(Dispatchers.Unconfined) {
17+
expectUnreached()
18+
}
19+
20+
}.join()
21+
finish(2)
22+
}
23+
24+
@Test
25+
fun testUnconfinedCancellationState() = runTest {
26+
val parent = Job()
27+
launch(parent) {
28+
expect(1)
29+
parent.cancel()
30+
val job = launch(Dispatchers.Unconfined) {
31+
expectUnreached()
32+
}
33+
34+
assertTrue(job.isCancelled)
35+
assertTrue(job.isCompleted)
36+
assertFalse(job.isActive)
37+
}.join()
38+
finish(2)
39+
}
40+
41+
@Test
42+
fun testUnconfinedCancellationLazy() = runTest {
43+
val parent = Job()
44+
launch(parent) {
45+
expect(1)
46+
val job = launch(Dispatchers.Unconfined, start = CoroutineStart.LAZY) {
47+
expectUnreached()
48+
}
49+
job.invokeOnCompletion { expect(2) }
50+
assertFalse(job.isCompleted)
51+
52+
parent.cancel()
53+
job.join()
54+
}.join()
55+
finish(3)
56+
}
57+
58+
@Test
59+
fun testUndispatchedCancellation() = runTest {
60+
val parent = Job()
61+
launch(parent) {
62+
expect(1)
63+
parent.cancel()
64+
launch(start = CoroutineStart.UNDISPATCHED) {
65+
expect(2)
66+
yield()
67+
expectUnreached()
68+
}
69+
70+
}.join()
71+
finish(3)
72+
}
73+
74+
@Test
75+
fun testCancelledAtomicUnconfined() = runTest {
76+
val parent = Job()
77+
launch(parent) {
78+
expect(1)
79+
parent.cancel()
80+
launch(Dispatchers.Unconfined, start = CoroutineStart.ATOMIC) {
81+
expect(2)
82+
yield()
83+
expectUnreached()
84+
}
85+
}.join()
86+
finish(3)
87+
}
88+
89+
90+
@Test
91+
fun testCancelledWithContextUnconfined() = runTest {
92+
val parent = Job()
93+
launch(parent) {
94+
expect(1)
95+
parent.cancel()
96+
withContext(Dispatchers.Unconfined) {
97+
expectUnreached()
98+
}
99+
}.join()
100+
finish(2)
101+
}
102+
}

0 commit comments

Comments
 (0)