Skip to content

Commit 01b56bc

Browse files
elizarovqwwdfsad
authored andcommitted
Fix native-mt ensureNeverFrozen with coroutine launch
Fixes #2064
1 parent 383343a commit 01b56bc

File tree

6 files changed

+71
-4
lines changed

6 files changed

+71
-4
lines changed

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ public abstract class AbstractCoroutine<in T>(
137137
* * [LAZY] does nothing.
138138
*/
139139
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
140-
initParentJob()
141-
startCoroutine(start, receiver, this, block)
140+
startAbstractCoroutine(start, receiver, this, block)
142141
}
143142
}

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

+9-2
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,7 @@ public suspend fun <T> withContext(
156156
}
157157
// SLOW PATH -- use new dispatcher
158158
val coroutine = DispatchedCoroutine(newContext, uCont)
159-
coroutine.initParentJob()
160-
startCoroutine(CoroutineStart.DEFAULT, coroutine, coroutine, block)
159+
coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
161160
coroutine.getResult()
162161
}
163162

@@ -194,6 +193,14 @@ internal expect fun <T, R> startCoroutine(
194193
block: suspend R.() -> T
195194
)
196195

196+
// initParentJob + startCoroutine
197+
internal expect fun <T, R> startAbstractCoroutine(
198+
start: CoroutineStart,
199+
receiver: R,
200+
coroutine: AbstractCoroutine<T>,
201+
block: suspend R.() -> T
202+
)
203+
197204
/**
198205
* On JVM & JS lazy coroutines are eagerly started (to record creation trace), the started later.
199206
* On Native the block is saved so that it can be shared with another worker, the created and started later.

kotlinx-coroutines-core/js/src/Builders.kt

+11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ internal actual inline fun <T, R> startCoroutine(
1717
) =
1818
startCoroutineImpl(start, receiver, completion, block)
1919

20+
@Suppress("NOTHING_TO_INLINE") // Save an entry on call stack
21+
internal actual inline fun <T, R> startAbstractCoroutine(
22+
start: CoroutineStart,
23+
receiver: R,
24+
coroutine: AbstractCoroutine<T>,
25+
noinline block: suspend R.() -> T
26+
) {
27+
coroutine.initParentJob()
28+
startCoroutineImpl(start, receiver, coroutine, block)
29+
}
30+
2031
@Suppress("NOTHING_TO_INLINE") // Save an entry on call stack
2132
internal actual inline fun <T, R> saveLazyCoroutine(
2233
coroutine: AbstractCoroutine<T>,

kotlinx-coroutines-core/jvm/src/Builders.kt

+11
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,17 @@ internal actual inline fun <T, R> startCoroutine(
105105
) =
106106
startCoroutineImpl(start, receiver, completion, block)
107107

108+
@Suppress("NOTHING_TO_INLINE") // Save an entry on call stack
109+
internal actual inline fun <T, R> startAbstractCoroutine(
110+
start: CoroutineStart,
111+
receiver: R,
112+
coroutine: AbstractCoroutine<T>,
113+
noinline block: suspend R.() -> T
114+
) {
115+
coroutine.initParentJob()
116+
startCoroutineImpl(start, receiver, coroutine, block)
117+
}
118+
108119
@Suppress("NOTHING_TO_INLINE") // Save an entry on call stack
109120
internal actual inline fun <T, R> saveLazyCoroutine(
110121
coroutine: AbstractCoroutine<T>,

kotlinx-coroutines-core/native/src/Builders.kt

+27
Original file line numberDiff line numberDiff line change
@@ -88,25 +88,52 @@ internal fun runEventLoop(eventLoop: EventLoop?, isCompleted: () -> Boolean) {
8888

8989
// --------------- Kotlin/Native specialization hooks ---------------
9090

91+
// just start
9192
internal actual fun <T, R> startCoroutine(
9293
start: CoroutineStart,
9394
receiver: R,
9495
completion: Continuation<T>,
9596
block: suspend R.() -> T
97+
) =
98+
startCoroutine(start, receiver, completion, block) {}
99+
100+
// initParentJob + startCoroutine
101+
internal actual fun <T, R> startAbstractCoroutine(
102+
start: CoroutineStart,
103+
receiver: R,
104+
coroutine: AbstractCoroutine<T>,
105+
block: suspend R.() -> T
106+
) {
107+
// See https://github.com/Kotlin/kotlinx.coroutines/issues/2064
108+
// We shall do initParentJob only after freezing the block
109+
startCoroutine(start, receiver, coroutine, block) {
110+
coroutine.initParentJob()
111+
}
112+
}
113+
114+
private fun <T, R> startCoroutine(
115+
start: CoroutineStart,
116+
receiver: R,
117+
completion: Continuation<T>,
118+
block: suspend R.() -> T,
119+
initParentJobIfNeeded: () -> Unit
96120
) {
97121
val curThread = currentThread()
98122
val newThread = completion.context[ContinuationInterceptor].thread()
99123
if (newThread != curThread) {
100124
check(start != CoroutineStart.UNDISPATCHED) {
101125
"Cannot start an undispatched coroutine in another thread $newThread from current $curThread"
102126
}
127+
block.freeze() // freeze the block, safely get FreezingException if it cannot be frozen
128+
initParentJobIfNeeded() // only initParentJob here if needed
103129
if (start != CoroutineStart.LAZY) {
104130
newThread.execute {
105131
startCoroutineImpl(start, receiver, completion, block)
106132
}
107133
}
108134
return
109135
}
136+
initParentJobIfNeeded()
110137
startCoroutineImpl(start, receiver, completion, block)
111138
}
112139

kotlinx-coroutines-core/native/test/WorkerDispatcherTest.kt

+12
Original file line numberDiff line numberDiff line change
@@ -326,5 +326,17 @@ class WorkerDispatcherTest : TestBase() {
326326
finish(2)
327327
}
328328

329+
@Test
330+
fun testEnsureNeverFrozenWithContext() = runTest {
331+
expect(1)
332+
val x = Data("OK")
333+
x.ensureNeverFrozen()
334+
assertFailsWith<FreezingException> {
335+
val s = withContext(dispatcher) { x.s }
336+
println(s) // does not actually execute
337+
}
338+
finish(2)
339+
}
340+
329341
private data class Data(val s: String)
330342
}

0 commit comments

Comments
 (0)