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