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