blob: d8eeedfb268a429fdb405b082765e92b1f0d8e7f [file] [log] [blame]
Roman Elizarovf16fd272017-02-07 11:26:00 +03001/*
2 * Copyright 2016-2017 JetBrains s.r.o.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Roman Elizarov44ba4b12017-01-25 11:37:54 +030017package kotlinx.coroutines.experimental
18
19import java.util.concurrent.atomic.AtomicLong
Roman Elizarovea4a51b2017-01-31 12:01:25 +030020import kotlin.coroutines.experimental.AbstractCoroutineContextElement
Roman Elizarovc0d559b2017-09-28 14:27:05 +030021import kotlin.coroutines.experimental.ContinuationInterceptor
Roman Elizarovea4a51b2017-01-31 12:01:25 +030022import kotlin.coroutines.experimental.CoroutineContext
Roman Elizarov44ba4b12017-01-25 11:37:54 +030023
Roman Elizarov590c8882018-04-11 22:21:23 +030024/**
25 * Name of the property that control coroutine debugging. See [newCoroutineContext].
26 */
27public const val DEBUG_PROPERTY_NAME = "kotlinx.coroutines.debug"
28
29/**
30 * Automatic debug configuration value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext].
31 */
32public const val DEBUG_PROPERTY_VALUE_AUTO = "auto"
33
34/**
35 * Debug turned on value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext].
36 */
37public const val DEBUG_PROPERTY_VALUE_ON = "on"
38
39/**
40 * Debug turned on value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext].
41 */
42public const val DEBUG_PROPERTY_VALUE_OFF = "off"
Roman Elizarov44ba4b12017-01-25 11:37:54 +030043
Vsevolod Tolstopyatovf5e63ca2018-04-12 19:59:56 +030044internal val DEBUG = run {
Roman Elizarove824eaa2017-07-10 10:15:40 +030045 val value = try { System.getProperty(DEBUG_PROPERTY_NAME) }
46 catch (e: SecurityException) { null }
Roman Elizarov44ba4b12017-01-25 11:37:54 +030047 when (value) {
Dmytro Danylyk0bb18fc2018-04-10 21:10:41 +100048 DEBUG_PROPERTY_VALUE_AUTO, null -> CoroutineId::class.java.desiredAssertionStatus()
49 DEBUG_PROPERTY_VALUE_ON, "" -> true
50 DEBUG_PROPERTY_VALUE_OFF -> false
Roman Elizarov44ba4b12017-01-25 11:37:54 +030051 else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'")
52 }
53}
54
55private val COROUTINE_ID = AtomicLong()
56
Roman Elizarov731f0ad2017-02-22 20:48:45 +030057// for tests only
58internal fun resetCoroutineId() {
59 COROUTINE_ID.set(0)
60}
61
Roman Elizarov44ba4b12017-01-25 11:37:54 +030062/**
Roman Elizarovc0d559b2017-09-28 14:27:05 +030063 * This is the default [CoroutineDispatcher] that is used by all standard builders like
64 * [launch], [async], etc if no dispatcher nor any other [ContinuationInterceptor] is specified in their context.
65 *
66 * It is currently equal to [CommonPool], but the value is subject to change in the future.
67 */
Roman Elizarove1c0b652017-12-01 14:02:57 +030068@Suppress("PropertyName")
69public actual val DefaultDispatcher: CoroutineDispatcher = CommonPool
Roman Elizarovc0d559b2017-09-28 14:27:05 +030070
71/**
72 * Creates context for the new coroutine. It installs [DefaultDispatcher] when no other dispatcher nor
73 * [ContinuationInterceptor] is specified, and adds optional support for debugging facilities (when turned on).
Roman Elizarov44ba4b12017-01-25 11:37:54 +030074 *
75 * **Debugging facilities:** In debug mode every coroutine is assigned a unique consecutive identifier.
76 * Every thread that executes a coroutine has its name modified to include the name and identifier of the
77 * currently currently running coroutine.
78 * When one coroutine is suspended and resumes another coroutine that is dispatched in the same thread,
79 * then the thread name displays
80 * the whole stack of coroutine descriptions that are being executed on this thread.
81 *
Roman Elizarov590c8882018-04-11 22:21:23 +030082 * Enable debugging facilities with "`kotlinx.coroutines.debug`" ([DEBUG_PROPERTY_NAME]) system property
83 * , use the following values:
84 * * "`auto`" (default mode, [DEBUG_PROPERTY_VALUE_AUTO]) -- enabled when assertions are enabled with "`-ea`" JVM option.
85 * * "`on`" ([DEBUG_PROPERTY_VALUE_ON]) or empty string -- enabled.
86 * * "`off`" ([DEBUG_PROPERTY_VALUE_OFF]) -- disabled.
Roman Elizarov44ba4b12017-01-25 11:37:54 +030087 *
88 * Coroutine name can be explicitly assigned using [CoroutineName] context element.
89 * The string "coroutine" is used as a default name.
90 */
Roman Elizarove8f694e2017-11-28 10:12:00 +030091@JvmOverloads // for binary compatibility with newCoroutineContext(context: CoroutineContext) version
Ilya Gorbunovf0cd1802018-04-17 19:59:31 +030092@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
Roman Elizarovaa461cf2018-04-11 13:20:29 +030093public actual fun newCoroutineContext(context: CoroutineContext, parent: Job? = null): CoroutineContext {
Roman Elizarovc0d559b2017-09-28 14:27:05 +030094 val debug = if (DEBUG) context + CoroutineId(COROUTINE_ID.incrementAndGet()) else context
Roman Elizarove8f694e2017-11-28 10:12:00 +030095 val wp = if (parent == null) debug else debug + parent
Roman Elizarovc0d559b2017-09-28 14:27:05 +030096 return if (context !== DefaultDispatcher && context[ContinuationInterceptor] == null)
Roman Elizarove8f694e2017-11-28 10:12:00 +030097 wp + DefaultDispatcher else wp
Roman Elizarovc0d559b2017-09-28 14:27:05 +030098}
Roman Elizarov44ba4b12017-01-25 11:37:54 +030099
100/**
101 * Executes a block using a given coroutine context.
102 */
Roman Elizarovf29203c2018-01-11 12:39:36 +0300103internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, block: () -> T): T {
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300104 val oldName = context.updateThreadContext()
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300105 try {
106 return block()
107 } finally {
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300108 restoreThreadContext(oldName)
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300109 }
110}
111
112@PublishedApi
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300113internal fun CoroutineContext.updateThreadContext(): String? {
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300114 if (!DEBUG) return null
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300115 val coroutineId = this[CoroutineId] ?: return null
116 val coroutineName = this[CoroutineName]?.name ?: "coroutine"
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300117 val currentThread = Thread.currentThread()
118 val oldName = currentThread.name
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300119 currentThread.name = buildString(oldName.length + coroutineName.length + 10) {
120 append(oldName)
121 append(" @")
122 append(coroutineName)
123 append('#')
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300124 append(coroutineId.id)
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300125 }
126 return oldName
127}
128
Roman Elizarovc12123e2018-01-24 22:07:12 +0300129internal actual val CoroutineContext.coroutineName: String? get() {
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300130 if (!DEBUG) return null
131 val coroutineId = this[CoroutineId] ?: return null
132 val coroutineName = this[CoroutineName]?.name ?: "coroutine"
133 return "$coroutineName#${coroutineId.id}"
134}
135
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300136@PublishedApi
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300137internal fun restoreThreadContext(oldName: String?) {
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300138 if (oldName != null) Thread.currentThread().name = oldName
139}
140
141private class CoroutineId(val id: Long) : AbstractCoroutineContextElement(CoroutineId) {
142 companion object Key : CoroutineContext.Key<CoroutineId>
143 override fun toString(): String = "CoroutineId($id)"
144}