• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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