1 /* 2 * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 package kotlinx.coroutines.debug 5 6 import kotlinx.coroutines.* 7 import kotlinx.coroutines.debug.internal.* 8 import org.junit.Test 9 import java.util.concurrent.* 10 import kotlin.test.* 11 12 class RunningThreadStackMergeTest : DebugTestBase() { 13 14 private val testMainBlocker = CountDownLatch(1) // Test body blocks on it 15 private val coroutineBlocker = CyclicBarrier(2) // Launched coroutine blocks on it 16 17 @Test <lambda>null18 fun testStackMergeWithContext() = runTest { 19 launchCoroutine() 20 awaitCoroutineStarted() 21 verifyDump( 22 "Coroutine \"coroutine#2\":StandaloneCoroutine{Active}@50284dc4, state: RUNNING\n" + 23 "\tat jdk.internal.misc.Unsafe.park(Native Method)\n" + 24 "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 25 "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer\$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)\n" + 26 "\tat java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)\n" + 27 "\tat java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)\n" + 28 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.nonSuspendingFun(RunningThreadStackMergeTest.kt:86)\n" + 29 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.access\$nonSuspendingFun(RunningThreadStackMergeTest.kt:12)\n" + 30 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$suspendingFunction\$2.invokeSuspend(RunningThreadStackMergeTest.kt:77)\n" + 31 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.suspendingFunction(RunningThreadStackMergeTest.kt:75)\n" + 32 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$launchCoroutine\$1.invokeSuspend(RunningThreadStackMergeTest.kt:68)\n" + 33 "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + 34 "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", 35 ignoredCoroutine = "BlockingCoroutine" 36 ) { 37 coroutineBlocker.await() 38 } 39 } 40 awaitCoroutineStartednull41 private fun awaitCoroutineStarted() { 42 testMainBlocker.await() 43 while (coroutineBlocker.numberWaiting != 1) { 44 Thread.sleep(10) 45 } 46 } 47 CoroutineScopenull48 private fun CoroutineScope.launchCoroutine() { 49 launch(Dispatchers.Default) { 50 suspendingFunction() 51 assertTrue(true) 52 } 53 } 54 suspendingFunctionnull55 private suspend fun suspendingFunction() { 56 // Typical use-case 57 withContext(Dispatchers.IO) { 58 yield() 59 nonSuspendingFun() 60 } 61 62 assertTrue(true) 63 } 64 nonSuspendingFunnull65 private fun nonSuspendingFun() { 66 testMainBlocker.countDown() 67 coroutineBlocker.await() 68 } 69 70 @Test <lambda>null71 fun testStackMergeEscapeSuspendMethod() = runTest { 72 launchEscapingCoroutine() 73 awaitCoroutineStarted() 74 Thread.sleep(10) 75 verifyDump( 76 "Coroutine \"coroutine#2\":StandaloneCoroutine{Active}@3aea3c67, state: RUNNING\n" + 77 "\tat jdk.internal.misc.Unsafe.park(Native Method)\n" + 78 "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 79 "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer\$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)\n" + 80 "\tat java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)\n" + 81 "\tat java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)\n" + 82 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.nonSuspendingFun(RunningThreadStackMergeTest.kt:83)\n" + 83 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.access\$nonSuspendingFun(RunningThreadStackMergeTest.kt:12)\n" + 84 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$suspendingFunctionWithContext\$2.invokeSuspend(RunningThreadStackMergeTest.kt:124)\n" + 85 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.suspendingFunctionWithContext(RunningThreadStackMergeTest.kt:122)\n" + 86 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$launchEscapingCoroutine\$1.invokeSuspend(RunningThreadStackMergeTest.kt:116)\n" + 87 "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + 88 "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", 89 ignoredCoroutine = "BlockingCoroutine" 90 ) { 91 coroutineBlocker.await() 92 } 93 } 94 CoroutineScopenull95 private fun CoroutineScope.launchEscapingCoroutine() { 96 launch(Dispatchers.Default) { 97 suspendingFunctionWithContext() 98 assertTrue(true) 99 } 100 } 101 suspendingFunctionWithContextnull102 private suspend fun suspendingFunctionWithContext() { 103 withContext(Dispatchers.IO) { 104 actualSuspensionPoint() 105 nonSuspendingFun() 106 } 107 108 assertTrue(true) 109 } 110 111 @Test <lambda>null112 fun testMergeThroughInvokeSuspend() = runTest { 113 launchEscapingCoroutineWithoutContext() 114 awaitCoroutineStarted() 115 verifyDump( 116 "Coroutine \"coroutine#2\":StandaloneCoroutine{Active}@3aea3c67, state: RUNNING\n" + 117 "\tat jdk.internal.misc.Unsafe.park(Native Method)\n" + 118 "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 119 "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer\$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)\n" + 120 "\tat java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)\n" + 121 "\tat java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)\n" + 122 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.nonSuspendingFun(RunningThreadStackMergeTest.kt:83)\n" + 123 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.suspendingFunctionWithoutContext(RunningThreadStackMergeTest.kt:160)\n" + 124 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$launchEscapingCoroutineWithoutContext\$1.invokeSuspend(RunningThreadStackMergeTest.kt:153)\n" + 125 "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + 126 "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", 127 ignoredCoroutine = "BlockingCoroutine" 128 ) { 129 coroutineBlocker.await() 130 } 131 } 132 CoroutineScopenull133 private fun CoroutineScope.launchEscapingCoroutineWithoutContext() { 134 launch(Dispatchers.IO) { 135 suspendingFunctionWithoutContext() 136 assertTrue(true) 137 } 138 } 139 suspendingFunctionWithoutContextnull140 private suspend fun suspendingFunctionWithoutContext() { 141 actualSuspensionPoint() 142 nonSuspendingFun() 143 assertTrue(true) 144 } 145 146 @Test <lambda>null147 fun testRunBlocking() = runBlocking { 148 verifyDump("Coroutine \"coroutine#1\":BlockingCoroutine{Active}@4bcd176c, state: RUNNING\n" + 149 "\tat java.lang.Thread.getStackTrace(Thread.java)\n" + 150 "\tat kotlinx.coroutines.debug.internal.DebugProbesImpl.enhanceStackTraceWithThreadDumpImpl(DebugProbesImpl.kt)\n" + 151 "\tat kotlinx.coroutines.debug.internal.DebugProbesImpl.dumpCoroutinesSynchronized(DebugProbesImpl.kt)\n" + 152 "\tat kotlinx.coroutines.debug.internal.DebugProbesImpl.dumpCoroutines(DebugProbesImpl.kt)\n" + 153 "\tat kotlinx.coroutines.debug.DebugProbes.dumpCoroutines(DebugProbes.kt)\n" + 154 "\tat kotlinx.coroutines.debug.StacktraceUtilsKt.verifyDump(StacktraceUtils.kt)\n" + 155 "\tat kotlinx.coroutines.debug.StacktraceUtilsKt.verifyDump\$default(StacktraceUtils.kt)\n" + 156 "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$testRunBlocking\$1.invokeSuspend(RunningThreadStackMergeTest.kt)\n" + 157 "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + 158 "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n") 159 } 160 161 actualSuspensionPointnull162 private suspend fun actualSuspensionPoint() { 163 nestedSuspensionPoint() 164 assertTrue(true) 165 } 166 nestedSuspensionPointnull167 private suspend fun nestedSuspensionPoint() { 168 yield() 169 assertTrue(true) 170 } 171 172 @Test // IDEA-specific debugger API test 173 @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") <lambda>null174 fun testActiveThread() = runBlocking<Unit> { 175 launchCoroutine() 176 awaitCoroutineStarted() 177 val info = DebugProbesImpl.dumpDebuggerInfo().find { it.state == "RUNNING" } 178 assertNotNull(info) 179 assertNotNull(info.lastObservedThreadName) 180 coroutineBlocker.await() 181 } 182 } 183