Skip to content

Commit

Permalink
Cleanup lazy coroutines that have been cancelled but not yet garbage … (
Browse files Browse the repository at this point in the history
#2315)

Cleanup lazy coroutines that have been cancelled but not yet garbage collected

Fixes #2294
  • Loading branch information
qwwdfsad committed Oct 19, 2020
1 parent 20ad25f commit 993c192
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
Expand Up @@ -156,7 +156,11 @@ internal object DebugProbesImpl {
// Stable ordering of coroutines by their sequence number
.sortedBy { it.info.sequenceNumber }
// Leave in the dump only the coroutines that were not collected while we were dumping them
.mapNotNull { owner -> owner.info.context?.let { context -> create(owner, context) } }
.mapNotNull { owner ->
// Fuse map and filter into one operation to save an inline
if (owner.isFinished()) null
else owner.info.context?.let { context -> create(owner, context) }
}
}

/*
Expand All @@ -183,10 +187,27 @@ internal object DebugProbesImpl {
dumpCoroutinesSynchronized(out)
}

/*
* Filters out coroutines that do not call probeCoroutineCompleted,
* are completed, but not yet garbage collected.
*
* Typically, we intercept completion of the coroutine so it invokes "probeCoroutineCompleted",
* but it's not the case for lazy coroutines that get cancelled before start.
*/
private fun CoroutineOwner<*>.isFinished(): Boolean {
// Guarded by lock
val job = info.context?.get(Job) ?: return false
if (!job.isCompleted) return false
capturedCoroutinesMap.remove(this) // Clean it up by the way
return true
}

private fun dumpCoroutinesSynchronized(out: PrintStream): Unit = coroutineStateLock.write {
check(isInstalled) { "Debug probes are not installed" }
out.print("Coroutines dump ${dateFormat.format(System.currentTimeMillis())}")
capturedCoroutines
.asSequence()
.filter { !it.isFinished() }
.sortedBy { it.info.sequenceNumber }
.forEach { owner ->
val info = owner.info
Expand Down
23 changes: 23 additions & 0 deletions kotlinx-coroutines-debug/test/LazyCoroutineTest.kt
@@ -0,0 +1,23 @@
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.debug

import kotlinx.coroutines.*
import org.junit.Test
import kotlin.test.*

class LazyCoroutineTest : DebugTestBase() {

@Test
fun testLazyCompletedCoroutine() = runTest {
val job = launch(start = CoroutineStart.LAZY) {}
job.invokeOnCompletion { expect(2) }
expect(1)
job.cancelAndJoin()
expect(3)
assertEquals(1, DebugProbes.dumpCoroutinesInfo().size) // Outer runBlocking
verifyPartialDump(1, "BlockingCoroutine{Active}")
finish(4)
}
}

0 comments on commit 993c192

Please sign in to comment.