@@ -32,6 +32,16 @@ internal object DebugProbesImpl {
32
32
// To sort coroutines by creation order, used as unique id
33
33
private var sequenceNumber: Long = 0
34
34
35
+ /*
36
+ * This is an optimization in the face of KT-29997:
37
+ * Consider suspending call stack a()->b()->c() and c() completes its execution and every call is
38
+ * "almost" in tail position.
39
+ *
40
+ * Then at least three RUNNING -> RUNNING transitions will occur consecutively and complexity of each is O(depth).
41
+ * To avoid that quadratic complexity, we are caching owner of such chains in this map and update it incrementally.
42
+ */
43
+ private val ownersCache = WeakHashMap <CoroutineStackFrame , ArtificialStackFrame <* >>()
44
+
35
45
@Synchronized
36
46
public fun install () {
37
47
if (++ installations > 1 ) return
@@ -172,7 +182,7 @@ internal object DebugProbesImpl {
172
182
* 4) Prepend dumped stacktrace (trimmed by target frame) to continuation stacktrace
173
183
*
174
184
* Heuristic may fail on recursion and overloads, but it will be automatically improved
175
- * with KT-29997
185
+ * with KT-29997.
176
186
*/
177
187
val indexOfResumeWith = actualTrace.indexOfFirst {
178
188
it.className == " kotlin.coroutines.jvm.internal.BaseContinuationImpl" &&
@@ -248,14 +258,33 @@ internal object DebugProbesImpl {
248
258
249
259
private fun updateState (frame : Continuation <* >, state : State ) {
250
260
if (! isInstalled) return
261
+ if (state == State .RUNNING ) {
262
+ updateRunningState(frame, state)
263
+ return
264
+ }
265
+
251
266
// Find ArtificialStackFrame of the coroutine
252
267
val owner = frame.owner()
253
268
updateState(owner, frame, state)
254
269
}
255
270
271
+ @Synchronized // See comment to ownersCache
272
+ private fun updateRunningState (continuation : Continuation <* >, state : State ) {
273
+ val frame = continuation as ? CoroutineStackFrame ? : return
274
+ val owner = ownersCache.remove(frame) ? : frame.owner() ? : return
275
+ val completion = frame.callerFrame
276
+ if (completion != null ) {
277
+ ownersCache[completion] = owner
278
+ }
279
+
280
+ val coroutineState = capturedCoroutines[owner] ? : return
281
+ coroutineState.updateState(state, continuation)
282
+ }
283
+
256
284
@Synchronized
257
285
private fun updateState (owner : ArtificialStackFrame <* >? , frame : Continuation <* >, state : State ) {
258
286
val coroutineState = capturedCoroutines[owner] ? : return
287
+ if (coroutineState.state == State .RUNNING && frame is CoroutineStackFrame ) ownersCache.remove(frame)
259
288
coroutineState.updateState(state, frame)
260
289
}
261
290
0 commit comments