@@ -156,7 +156,11 @@ internal object DebugProbesImpl {
156
156
// Stable ordering of coroutines by their sequence number
157
157
.sortedBy { it.info.sequenceNumber }
158
158
// Leave in the dump only the coroutines that were not collected while we were dumping them
159
- .mapNotNull { owner -> owner.info.context?.let { context -> create(owner, context) } }
159
+ .mapNotNull { owner ->
160
+ // Fuse map and filter into one operation to save an inline
161
+ if (owner.isFinished()) null
162
+ else owner.info.context?.let { context -> create(owner, context) }
163
+ }
160
164
}
161
165
162
166
/*
@@ -183,10 +187,27 @@ internal object DebugProbesImpl {
183
187
dumpCoroutinesSynchronized(out )
184
188
}
185
189
190
+ /*
191
+ * Filters out coroutines that do not call probeCoroutineCompleted,
192
+ * are completed, but not yet garbage collected.
193
+ *
194
+ * Typically, we intercept completion of the coroutine so it invokes "probeCoroutineCompleted",
195
+ * but it's not the case for lazy coroutines that get cancelled before start.
196
+ */
197
+ private fun CoroutineOwner <* >.isFinished (): Boolean {
198
+ // Guarded by lock
199
+ val job = info.context?.get(Job ) ? : return false
200
+ if (! job.isCompleted) return false
201
+ capturedCoroutinesMap.remove(this ) // Clean it up by the way
202
+ return true
203
+ }
204
+
186
205
private fun dumpCoroutinesSynchronized (out : PrintStream ): Unit = coroutineStateLock.write {
187
206
check(isInstalled) { " Debug probes are not installed" }
188
207
out .print (" Coroutines dump ${dateFormat.format(System .currentTimeMillis())} " )
189
208
capturedCoroutines
209
+ .asSequence()
210
+ .filter { ! it.isFinished() }
190
211
.sortedBy { it.info.sequenceNumber }
191
212
.forEach { owner ->
192
213
val info = owner.info
0 commit comments