blob: 2d8a0a0b3f25d0248d6dda1a33014e790e5a67d4 [file] [log] [blame]
Roman Elizarov44ba4b12017-01-25 11:37:54 +03001package kotlinx.coroutines.experimental
2
3import java.util.concurrent.atomic.AtomicLong
4import kotlin.coroutines.AbstractCoroutineContextElement
5import kotlin.coroutines.CoroutineContext
6
7private const val DEBUG_PROPERTY_NAME = "kotlinx.coroutines.debug"
8
9private val DEBUG = run {
10 val value = System.getProperty(DEBUG_PROPERTY_NAME)
11 when (value) {
12 "auto", null -> CoroutineId::class.java.desiredAssertionStatus()
13 "on", "" -> true
14 "off" -> false
15 else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'")
16 }
17}
18
19private val COROUTINE_ID = AtomicLong()
20
21/**
22 * A coroutine dispatcher that executes initial continuation of the coroutine _right here_ in the current call-frame
23 * and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without
24 * mandating any specific threading policy.
25 */
26public object Here : CoroutineDispatcher() {
27 override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
28 override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
29}
30
31/**
32 * Creates context for the new coroutine with optional support for debugging facilities (when turned on).
33 *
34 * **Debugging facilities:** In debug mode every coroutine is assigned a unique consecutive identifier.
35 * Every thread that executes a coroutine has its name modified to include the name and identifier of the
36 * currently currently running coroutine.
37 * When one coroutine is suspended and resumes another coroutine that is dispatched in the same thread,
38 * then the thread name displays
39 * the whole stack of coroutine descriptions that are being executed on this thread.
40 *
41 * Enable debugging facilities with "`kotlinx.coroutines.debug`" system property, use the following values:
42 * * "`auto`" (default mode) -- enabled when assertions are enabled with "`-ea`" JVM option.
43 * * "`on`" or empty string -- enabled.
44 * * "`off`" -- disabled.
45 *
46 * Coroutine name can be explicitly assigned using [CoroutineName] context element.
47 * The string "coroutine" is used as a default name.
48 */
49public fun newCoroutineContext(context: CoroutineContext): CoroutineContext =
50 if (DEBUG) context + CoroutineId(COROUTINE_ID.incrementAndGet()) else context
51
52/**
53 * Executes a block using a given coroutine context.
54 */
55internal inline fun <T> withCoroutineContext(context: CoroutineContext, block: () -> T): T {
56 val oldName = updateContext(context)
57 try {
58 return block()
59 } finally {
60 restoreContext(oldName)
61 }
62}
63
64@PublishedApi
65internal fun updateContext(context: CoroutineContext): String? {
66 if (!DEBUG) return null
67 val newId = context[CoroutineId] ?: return null
68 val currentThread = Thread.currentThread()
69 val oldName = currentThread.name
70 val coroutineName = context[CoroutineName]?.name ?: "coroutine"
71 currentThread.name = buildString(oldName.length + coroutineName.length + 10) {
72 append(oldName)
73 append(" @")
74 append(coroutineName)
75 append('#')
76 append(newId.id)
77 }
78 return oldName
79}
80
81@PublishedApi
82internal fun restoreContext(oldName: String?) {
83 if (oldName != null) Thread.currentThread().name = oldName
84}
85
86private class CoroutineId(val id: Long) : AbstractCoroutineContextElement(CoroutineId) {
87 companion object Key : CoroutineContext.Key<CoroutineId>
88 override fun toString(): String = "CoroutineId($id)"
89}