• 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 
5 package kotlinx.coroutines.debug.internal
6 
7 import java.lang.ref.*
8 import kotlin.coroutines.*
9 import kotlin.coroutines.jvm.internal.*
10 
11 internal const val CREATED = "CREATED"
12 internal const val RUNNING = "RUNNING"
13 internal const val SUSPENDED = "SUSPENDED"
14 
15 /**
16  * Internal implementation class where debugger tracks details it knows about each coroutine.
17  */
18 internal class DebugCoroutineInfoImpl(
19     context: CoroutineContext?,
20     /**
21      * A reference to a stack-trace that is converted to a [StackTraceFrame] which implements [CoroutineStackFrame].
22      * The actual reference to the coroutine is not stored here, so we keep a strong reference.
23      */
24     public val creationStackBottom: StackTraceFrame?,
25     @JvmField internal val sequenceNumber: Long
26 ) {
27     /**
28      * We cannot keep a strong reference to the context, because with the [Job] in the context it will indirectly
29      * keep a reference to the last frame of an abandoned coroutine which the debugger should not be preventing
30      * garbage-collection of. The reference to context will not disappear as long as the coroutine itself is not lost.
31      */
32     private val _context = WeakReference(context)
33     public val context: CoroutineContext? // can be null when the coroutine was already garbage-collected
34         get() = _context.get()
35 
36     public val creationStackTrace: List<StackTraceElement> get() = creationStackTrace()
37 
38     /**
39      * Last observed state of the coroutine.
40      * Can be CREATED, RUNNING, SUSPENDED.
41      */
42     public val state: String get() = _state
43     private var _state: String = CREATED
44 
45     @JvmField
46     internal var lastObservedThread: Thread? = null
47 
48     /**
49      * We cannot keep a strong reference to the last observed frame of the coroutine, because this will
50      * prevent garbage-collection of a coroutine that was lost.
51      */
52     private var _lastObservedFrame: WeakReference<CoroutineStackFrame>? = null
53     internal var lastObservedFrame: CoroutineStackFrame?
54         get() = _lastObservedFrame?.get()
<lambda>null55         set(value) { _lastObservedFrame = value?.let { WeakReference(it) } }
56 
57     /**
58      * Last observed stacktrace of the coroutine captured on its suspension or resumption point.
59      * It means that for [running][State.RUNNING] coroutines resulting stacktrace is inaccurate and
60      * reflects stacktrace of the resumption point, not the actual current stacktrace.
61      */
lastObservedStackTracenull62     public fun lastObservedStackTrace(): List<StackTraceElement> {
63         var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList()
64         val result = ArrayList<StackTraceElement>()
65         while (frame != null) {
66             frame.getStackTraceElement()?.let { result.add(it) }
67             frame = frame.callerFrame
68         }
69         return result
70     }
71 
creationStackTracenull72     private fun creationStackTrace(): List<StackTraceElement> {
73         val bottom = creationStackBottom ?: return emptyList()
74         // Skip "Coroutine creation stacktrace" frame
75         return sequence<StackTraceElement> { yieldFrames(bottom.callerFrame) }.toList()
76     }
77 
yieldFramesnull78     private tailrec suspend fun SequenceScope<StackTraceElement>.yieldFrames(frame: CoroutineStackFrame?) {
79         if (frame == null) return
80         frame.getStackTraceElement()?.let { yield(it) }
81         val caller = frame.callerFrame
82         if (caller != null) {
83             yieldFrames(caller)
84         }
85     }
86 
updateStatenull87     internal fun updateState(state: String, frame: Continuation<*>) {
88         // Propagate only duplicating transitions to running for KT-29997
89         if (_state == state && state == SUSPENDED && lastObservedFrame != null) return
90         _state = state
91         lastObservedFrame = frame as? CoroutineStackFrame
92         lastObservedThread = if (state == RUNNING) {
93             Thread.currentThread()
94         } else {
95             null
96         }
97     }
98 
toStringnull99     override fun toString(): String = "DebugCoroutineInfo(state=$state,context=$context)"
100 }
101