@@ -28,32 +28,23 @@ internal object DebugProbesImpl {
28
28
private val capturedCoroutinesMap = ConcurrentWeakMap <CoroutineOwner <* >, Boolean > ()
29
29
private val capturedCoroutines: Set <CoroutineOwner <* >> get() = capturedCoroutinesMap.keys
30
30
31
- @Volatile
32
- private var installations = 0
31
+ private val installations = atomic(0 )
33
32
34
33
/* *
35
34
* This internal method is used by IDEA debugger under the JVM name of
36
35
* "isInstalled$kotlinx_coroutines_debug".
37
36
*/
38
- internal val isInstalled: Boolean get() = installations > 0
37
+ internal val isInstalled: Boolean get() = installations.value > 0
39
38
40
39
// To sort coroutines by creation order, used as unique id
41
40
private val sequenceNumber = atomic(0L )
42
- /*
43
- * RW-lock that guards all debug probes state changes.
44
- * All individual coroutine state transitions are guarded by read-lock
45
- * and do not interfere with each other.
46
- * All state reads are guarded by the write lock to guarantee a strongly-consistent
47
- * snapshot of the system.
48
- */
49
- private val coroutineStateLock = ReentrantReadWriteLock ()
50
41
51
42
public var sanitizeStackTraces: Boolean = true
52
43
public var enableCreationStackTraces: Boolean = true
53
44
54
45
/*
55
46
* Substitute for service loader, DI between core and debug modules.
56
- * If the agent was installed via command line -javaagent parameter, do not use byte-byddy to avoud
47
+ * If the agent was installed via command line -javaagent parameter, do not use byte-buddy to avoid dynamic attach.
57
48
*/
58
49
private val dynamicAttach = getDynamicAttach()
59
50
@@ -77,16 +68,16 @@ internal object DebugProbesImpl {
77
68
*/
78
69
private val callerInfoCache = ConcurrentWeakMap <CoroutineStackFrame , DebugCoroutineInfoImpl >(weakRefQueue = true )
79
70
80
- public fun install (): Unit = coroutineStateLock.write {
81
- if (++ installations > 1 ) return
71
+ fun install () {
72
+ if (installations.incrementAndGet() > 1 ) return
82
73
startWeakRefCleanerThread()
83
74
if (AgentInstallationType .isInstalledStatically) return
84
75
dynamicAttach?.invoke(true ) // attach
85
76
}
86
77
87
- public fun uninstall (): Unit = coroutineStateLock.write {
78
+ fun uninstall () {
88
79
check(isInstalled) { " Agent was not installed" }
89
- if (-- installations != 0 ) return
80
+ if (installations.decrementAndGet() != 0 ) return
90
81
stopWeakRefCleanerThread()
91
82
capturedCoroutinesMap.clear()
92
83
callerInfoCache.clear()
@@ -107,7 +98,7 @@ internal object DebugProbesImpl {
107
98
thread.join()
108
99
}
109
100
110
- public fun hierarchyToString (job : Job ): String = coroutineStateLock.write {
101
+ fun hierarchyToString (job : Job ): String {
111
102
check(isInstalled) { " Debug probes are not installed" }
112
103
val jobToStack = capturedCoroutines
113
104
.filter { it.delegate.context[Job ] != null }
@@ -149,20 +140,19 @@ internal object DebugProbesImpl {
149
140
* Private method that dumps coroutines so that different public-facing method can use
150
141
* to produce different result types.
151
142
*/
152
- private inline fun <R : Any > dumpCoroutinesInfoImpl (crossinline create : (CoroutineOwner <* >, CoroutineContext ) -> R ): List <R > =
153
- coroutineStateLock.write {
154
- check(isInstalled) { " Debug probes are not installed" }
155
- capturedCoroutines
156
- .asSequence()
157
- // Stable ordering of coroutines by their sequence number
158
- .sortedBy { it.info.sequenceNumber }
159
- // Leave in the dump only the coroutines that were not collected while we were dumping them
160
- .mapNotNull { owner ->
161
- // Fuse map and filter into one operation to save an inline
162
- if (owner.isFinished()) null
163
- else owner.info.context?.let { context -> create(owner, context) }
164
- }.toList()
165
- }
143
+ private inline fun <R : Any > dumpCoroutinesInfoImpl (crossinline create : (CoroutineOwner <* >, CoroutineContext ) -> R ): List <R > {
144
+ check(isInstalled) { " Debug probes are not installed" }
145
+ return capturedCoroutines
146
+ .asSequence()
147
+ // Stable ordering of coroutines by their sequence number
148
+ .sortedBy { it.info.sequenceNumber }
149
+ // Leave in the dump only the coroutines that were not collected while we were dumping them
150
+ .mapNotNull { owner ->
151
+ // Fuse map and filter into one operation to save an inline
152
+ if (owner.isFinished()) null
153
+ else owner.info.context?.let { context -> create(owner, context) }
154
+ }.toList()
155
+ }
166
156
167
157
/*
168
158
* This method optimises the number of packages sent by the IDEA debugger
@@ -280,7 +270,7 @@ internal object DebugProbesImpl {
280
270
return true
281
271
}
282
272
283
- private fun dumpCoroutinesSynchronized (out : PrintStream ): Unit = coroutineStateLock.write {
273
+ private fun dumpCoroutinesSynchronized (out : PrintStream ) {
284
274
check(isInstalled) { " Debug probes are not installed" }
285
275
out .print (" Coroutines dump ${dateFormat.format(System .currentTimeMillis())} " )
286
276
capturedCoroutines
@@ -441,7 +431,7 @@ internal object DebugProbesImpl {
441
431
}
442
432
443
433
// See comment to callerInfoCache
444
- private fun updateRunningState (frame : CoroutineStackFrame , state : String ): Unit = coroutineStateLock.read {
434
+ private fun updateRunningState (frame : CoroutineStackFrame , state : String ) {
445
435
if (! isInstalled) return
446
436
// Lookup coroutine info in cache or by traversing stack frame
447
437
val info: DebugCoroutineInfoImpl
@@ -466,7 +456,7 @@ internal object DebugProbesImpl {
466
456
return if (caller.getStackTraceElement() != null ) caller else caller.realCaller()
467
457
}
468
458
469
- private fun updateState (owner : CoroutineOwner <* >, frame : Continuation <* >, state : String ) = coroutineStateLock.read {
459
+ private fun updateState (owner : CoroutineOwner <* >, frame : Continuation <* >, state : String ) {
470
460
if (! isInstalled) return
471
461
owner.info.updateState(state, frame)
472
462
}
0 commit comments