1 /* <lambda>null2 * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.coroutines.debug 6 7 import kotlinx.coroutines.* 8 import org.junit.Test 9 import kotlin.coroutines.* 10 import kotlin.test.* 11 12 class CoroutinesDumpTest : DebugTestBase() { 13 private val monitor = Any() 14 private var coroutineThread: Thread? = null // guarded by monitor 15 16 @Test 17 fun testSuspendedCoroutine() = runBlocking { 18 val deferred = async(Dispatchers.Default) { 19 sleepingOuterMethod() 20 } 21 22 awaitCoroutine() 23 val found = DebugProbes.dumpCoroutinesInfo().single { it.job === deferred } 24 verifyDump( 25 "Coroutine \"coroutine#1\":DeferredCoroutine{Active}@1e4a7dd4, state: SUSPENDED\n" + 26 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.sleepingNestedMethod(CoroutinesDumpTest.kt)\n" + 27 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.sleepingOuterMethod(CoroutinesDumpTest.kt)\n" + 28 "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + 29 "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n" + 30 "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)\n" + 31 "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable\$default(Cancellable.kt)\n" + 32 "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)\n", 33 ignoredCoroutine = "BlockingCoroutine" 34 ) { 35 deferred.cancel() 36 coroutineThread!!.interrupt() 37 } 38 assertSame(deferred, found.job) 39 } 40 41 @Test 42 fun testRunningCoroutine() = runBlocking { 43 val deferred = async(Dispatchers.IO) { 44 activeMethod(shouldSuspend = false) 45 assertTrue(true) 46 } 47 48 awaitCoroutine() 49 verifyDump( 50 "Coroutine \"coroutine#1\":DeferredCoroutine{Active}@227d9994, state: RUNNING\n" + 51 "\tat java.lang.Thread.sleep(Native Method)\n" + 52 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.nestedActiveMethod(CoroutinesDumpTest.kt)\n" + 53 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.activeMethod(CoroutinesDumpTest.kt)\n" + 54 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.access\$activeMethod(CoroutinesDumpTest.kt)\n" + 55 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest\$testRunningCoroutine\$1\$deferred\$1.invokeSuspend(CoroutinesDumpTest.kt)\n" + 56 "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + 57 "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n" + 58 "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)\n" + 59 "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable\$default(Cancellable.kt)\n" + 60 "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)\n" + 61 "\tat kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt)\n" + 62 "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt)\n" + 63 "\tat kotlinx.coroutines.BuildersKt.async(Unknown Source)\n" + 64 "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt)\n" + 65 "\tat kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" + 66 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest\$testRunningCoroutine\$1.invokeSuspend(CoroutinesDumpTest.kt)", 67 ignoredCoroutine = "BlockingCoroutine" 68 ) { 69 deferred.cancel() 70 coroutineThread?.interrupt() 71 } 72 } 73 74 @Test 75 fun testRunningCoroutineWithSuspensionPoint() = runBlocking { 76 val deferred = async(Dispatchers.IO) { 77 activeMethod(shouldSuspend = true) 78 yield() // tail-call 79 } 80 81 awaitCoroutine() 82 verifyDump( 83 "Coroutine \"coroutine#1\":DeferredCoroutine{Active}@1e4a7dd4, state: RUNNING\n" + 84 "\tat java.lang.Thread.sleep(Native Method)\n" + 85 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.nestedActiveMethod(CoroutinesDumpTest.kt)\n" + 86 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.activeMethod(CoroutinesDumpTest.kt)\n" + 87 "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + 88 "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n" + 89 "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)\n" + 90 "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable\$default(Cancellable.kt)\n" + 91 "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)\n" + 92 "\tat kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt)\n" + 93 "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt)\n" + 94 "\tat kotlinx.coroutines.BuildersKt.async(Unknown Source)\n" + 95 "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt)\n" + 96 "\tat kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" + 97 "\tat kotlinx.coroutines.debug.CoroutinesDumpTest\$testRunningCoroutineWithSuspensionPoint\$1.invokeSuspend(CoroutinesDumpTest.kt)", 98 ignoredCoroutine = "BlockingCoroutine" 99 ) { 100 deferred.cancel() 101 coroutineThread!!.interrupt() 102 } 103 } 104 105 @Test 106 fun testCreationStackTrace() = runBlocking { 107 val deferred = async(Dispatchers.IO) { 108 activeMethod(shouldSuspend = true) 109 } 110 111 awaitCoroutine() 112 val coroutine = DebugProbes.dumpCoroutinesInfo().first { it.job is Deferred<*> } 113 val result = coroutine.creationStackTrace.fold(StringBuilder()) { acc, element -> 114 acc.append(element.toString()) 115 acc.append('\n') 116 }.toString().trimStackTrace() 117 118 deferred.cancel() 119 coroutineThread!!.interrupt() 120 121 val expected = 122 "kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n" + 123 "kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)\n" + 124 "kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable\$default(Cancellable.kt)\n" + 125 "kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)\n" + 126 "kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt)\n" + 127 "kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt)\n" + 128 "kotlinx.coroutines.BuildersKt.async(Unknown Source)\n" + 129 "kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt)\n" + 130 "kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" + 131 "kotlinx.coroutines.debug.CoroutinesDumpTest\$testCreationStackTrace\$1.invokeSuspend(CoroutinesDumpTest.kt)" 132 if (!result.startsWith(expected)) { 133 error(""" 134 |Does not start with expected lines 135 |=== Actual result: 136 |$result 137 """.trimMargin() 138 ) 139 } 140 141 } 142 143 @Test 144 fun testFinishedCoroutineRemoved() = runBlocking { 145 val deferred = async(Dispatchers.IO) { 146 activeMethod(shouldSuspend = true) 147 } 148 149 awaitCoroutine() 150 deferred.cancel() 151 coroutineThread!!.interrupt() 152 deferred.join() 153 verifyDump(ignoredCoroutine = "BlockingCoroutine") 154 } 155 156 private suspend fun activeMethod(shouldSuspend: Boolean) { 157 nestedActiveMethod(shouldSuspend) 158 assertTrue(true) // tail-call 159 } 160 161 private suspend fun nestedActiveMethod(shouldSuspend: Boolean) { 162 if (shouldSuspend) yield() 163 notifyCoroutineStarted() 164 while (coroutineContext[Job]!!.isActive) { 165 try { 166 Thread.sleep(60_000) 167 } catch (_ : InterruptedException) { 168 } 169 } 170 } 171 172 private suspend fun sleepingOuterMethod() { 173 sleepingNestedMethod() 174 yield() // TCE 175 } 176 177 private suspend fun sleepingNestedMethod() { 178 yield() // Suspension point 179 notifyCoroutineStarted() 180 delay(Long.MAX_VALUE) 181 } 182 183 private fun awaitCoroutine() = synchronized(monitor) { 184 while (coroutineThread == null) (monitor as Object).wait() 185 while (coroutineThread!!.state != Thread.State.TIMED_WAITING) { 186 // Wait until thread sleeps to have a consistent stacktrace 187 } 188 } 189 190 private fun notifyCoroutineStarted() { 191 synchronized(monitor) { 192 coroutineThread = Thread.currentThread() 193 (monitor as Object).notifyAll() 194 } 195 } 196 } 197