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