Skip to content

Commit 3e0ca9b

Browse files
qwwdfsadelizarov
authored andcommitted
Change contract of supervisorScope to align it with coroutineScope
1 parent 21f171c commit 3e0ca9b

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ public fun SupervisorJob(parent: Job? = null) : Job = SupervisorJobImpl(parent)
3232
*
3333
* A failure of a child does not cause this scope to fail and does not affect its other children,
3434
* so a custom policy for handling failures of its children can be implemented. See [SupervisorJob] for details.
35+
* A failure of the scope itself (exception thrown in the [block] or cancellation) fails the scope with all its children,
36+
* but does not cancel parent job.
3537
*/
36-
public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R {
38+
public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R {
3739
// todo: optimize implementation to a single allocated object
3840
// todo: fix copy-and-paste with coroutineScope
3941
val owner = SupervisorCoroutine<R>(coroutineContext)
@@ -62,6 +64,6 @@ private class SupervisorJobImpl(parent: Job?) : JobSupport(true) {
6264
private class SupervisorCoroutine<R>(
6365
parentContext: CoroutineContext
6466
) : AbstractCoroutine<R>(parentContext, true) {
65-
override val cancelsParent: Boolean get() = true
67+
override val cancelsParent: Boolean get() = false
6668
override fun childCancelled(cause: Throwable): Boolean = false
6769
}

common/kotlinx-coroutines-core-common/test/SupervisorTest.kt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class SupervisorTest : TestBase() {
3232
finish(5)
3333
assertTrue(job1.isCancelled)
3434
assertTrue(job2.isCancelled)
35+
assertFalse(supervisor.isCancelled)
36+
assertFalse(supervisor.isCompleted)
3537
}
3638

3739
@Test
@@ -53,6 +55,57 @@ class SupervisorTest : TestBase() {
5355
assertEquals("OK", result)
5456
}
5557

58+
@Test
59+
fun testSupervisorScopeIsolation() = runTest(
60+
unhandled = listOf(
61+
{ it -> it is TestException2 })
62+
) {
63+
val result = supervisorScope {
64+
expect(1)
65+
val job = launch {
66+
expect(2)
67+
delay(Long.MAX_VALUE)
68+
}
69+
70+
val failingJob = launch {
71+
expect(3)
72+
throw TestException2()
73+
}
74+
75+
failingJob.join()
76+
yield()
77+
expect(4)
78+
assertTrue(job.isActive)
79+
assertFalse(job.isCancelled)
80+
job.cancel()
81+
"OK"
82+
}
83+
assertEquals("OK", result)
84+
finish(5)
85+
}
86+
87+
@Test
88+
fun testThrowingSupervisorScope() = runTest {
89+
try {
90+
expect(1)
91+
supervisorScope {
92+
async {
93+
try {
94+
delay(Long.MAX_VALUE)
95+
} finally {
96+
expect(3)
97+
}
98+
}
99+
100+
expect(2)
101+
yield()
102+
throw TestException2()
103+
}
104+
} catch (e: Throwable) {
105+
finish(4)
106+
}
107+
}
108+
56109
@Test
57110
fun testSupervisorWithParentCancelNormally() {
58111
val parent = Job()

0 commit comments

Comments
 (0)