Roman Elizarov | f16fd27 | 2017-02-07 11:26:00 +0300 | [diff] [blame] | 1 | /* |
| 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 Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 17 | package kotlinx.coroutines.experimental |
| 18 | |
| 19 | import java.util.concurrent.atomic.AtomicLong |
Roman Elizarov | ea4a51b | 2017-01-31 12:01:25 +0300 | [diff] [blame] | 20 | import kotlin.coroutines.experimental.AbstractCoroutineContextElement |
Roman Elizarov | c0d559b | 2017-09-28 14:27:05 +0300 | [diff] [blame] | 21 | import kotlin.coroutines.experimental.ContinuationInterceptor |
Roman Elizarov | ea4a51b | 2017-01-31 12:01:25 +0300 | [diff] [blame] | 22 | import kotlin.coroutines.experimental.CoroutineContext |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 23 | |
Roman Elizarov | 590c888 | 2018-04-11 22:21:23 +0300 | [diff] [blame] | 24 | /** |
| 25 | * Name of the property that control coroutine debugging. See [newCoroutineContext]. |
| 26 | */ |
| 27 | public const val DEBUG_PROPERTY_NAME = "kotlinx.coroutines.debug" |
| 28 | |
| 29 | /** |
| 30 | * Automatic debug configuration value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext]. |
| 31 | */ |
| 32 | public const val DEBUG_PROPERTY_VALUE_AUTO = "auto" |
| 33 | |
| 34 | /** |
| 35 | * Debug turned on value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext]. |
| 36 | */ |
| 37 | public const val DEBUG_PROPERTY_VALUE_ON = "on" |
| 38 | |
| 39 | /** |
| 40 | * Debug turned on value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext]. |
| 41 | */ |
| 42 | public const val DEBUG_PROPERTY_VALUE_OFF = "off" |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 43 | |
Vsevolod Tolstopyatov | f5e63ca | 2018-04-12 19:59:56 +0300 | [diff] [blame] | 44 | internal val DEBUG = run { |
Roman Elizarov | e824eaa | 2017-07-10 10:15:40 +0300 | [diff] [blame] | 45 | val value = try { System.getProperty(DEBUG_PROPERTY_NAME) } |
| 46 | catch (e: SecurityException) { null } |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 47 | when (value) { |
Dmytro Danylyk | 0bb18fc | 2018-04-10 21:10:41 +1000 | [diff] [blame] | 48 | DEBUG_PROPERTY_VALUE_AUTO, null -> CoroutineId::class.java.desiredAssertionStatus() |
| 49 | DEBUG_PROPERTY_VALUE_ON, "" -> true |
| 50 | DEBUG_PROPERTY_VALUE_OFF -> false |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 51 | else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'") |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | private val COROUTINE_ID = AtomicLong() |
| 56 | |
Roman Elizarov | 731f0ad | 2017-02-22 20:48:45 +0300 | [diff] [blame] | 57 | // for tests only |
| 58 | internal fun resetCoroutineId() { |
| 59 | COROUTINE_ID.set(0) |
| 60 | } |
| 61 | |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 62 | /** |
Roman Elizarov | c0d559b | 2017-09-28 14:27:05 +0300 | [diff] [blame] | 63 | * 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 Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 68 | @Suppress("PropertyName") |
| 69 | public actual val DefaultDispatcher: CoroutineDispatcher = CommonPool |
Roman Elizarov | c0d559b | 2017-09-28 14:27:05 +0300 | [diff] [blame] | 70 | |
| 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 Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 74 | * |
| 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 Elizarov | 590c888 | 2018-04-11 22:21:23 +0300 | [diff] [blame] | 82 | * 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 Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 87 | * |
| 88 | * Coroutine name can be explicitly assigned using [CoroutineName] context element. |
| 89 | * The string "coroutine" is used as a default name. |
| 90 | */ |
Roman Elizarov | e8f694e | 2017-11-28 10:12:00 +0300 | [diff] [blame] | 91 | @JvmOverloads // for binary compatibility with newCoroutineContext(context: CoroutineContext) version |
Ilya Gorbunov | f0cd180 | 2018-04-17 19:59:31 +0300 | [diff] [blame] | 92 | @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 93 | public actual fun newCoroutineContext(context: CoroutineContext, parent: Job? = null): CoroutineContext { |
Roman Elizarov | c0d559b | 2017-09-28 14:27:05 +0300 | [diff] [blame] | 94 | val debug = if (DEBUG) context + CoroutineId(COROUTINE_ID.incrementAndGet()) else context |
Roman Elizarov | e8f694e | 2017-11-28 10:12:00 +0300 | [diff] [blame] | 95 | val wp = if (parent == null) debug else debug + parent |
Roman Elizarov | c0d559b | 2017-09-28 14:27:05 +0300 | [diff] [blame] | 96 | return if (context !== DefaultDispatcher && context[ContinuationInterceptor] == null) |
Roman Elizarov | e8f694e | 2017-11-28 10:12:00 +0300 | [diff] [blame] | 97 | wp + DefaultDispatcher else wp |
Roman Elizarov | c0d559b | 2017-09-28 14:27:05 +0300 | [diff] [blame] | 98 | } |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 99 | |
| 100 | /** |
| 101 | * Executes a block using a given coroutine context. |
| 102 | */ |
Roman Elizarov | f29203c | 2018-01-11 12:39:36 +0300 | [diff] [blame] | 103 | internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, block: () -> T): T { |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 104 | val oldName = context.updateThreadContext() |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 105 | try { |
| 106 | return block() |
| 107 | } finally { |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 108 | restoreThreadContext(oldName) |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 109 | } |
| 110 | } |
| 111 | |
| 112 | @PublishedApi |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 113 | internal fun CoroutineContext.updateThreadContext(): String? { |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 114 | if (!DEBUG) return null |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 115 | val coroutineId = this[CoroutineId] ?: return null |
| 116 | val coroutineName = this[CoroutineName]?.name ?: "coroutine" |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 117 | val currentThread = Thread.currentThread() |
| 118 | val oldName = currentThread.name |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 119 | currentThread.name = buildString(oldName.length + coroutineName.length + 10) { |
| 120 | append(oldName) |
| 121 | append(" @") |
| 122 | append(coroutineName) |
| 123 | append('#') |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 124 | append(coroutineId.id) |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 125 | } |
| 126 | return oldName |
| 127 | } |
| 128 | |
Roman Elizarov | c12123e | 2018-01-24 22:07:12 +0300 | [diff] [blame] | 129 | internal actual val CoroutineContext.coroutineName: String? get() { |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 130 | 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 Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 136 | @PublishedApi |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 137 | internal fun restoreThreadContext(oldName: String?) { |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 138 | if (oldName != null) Thread.currentThread().name = oldName |
| 139 | } |
| 140 | |
| 141 | private class CoroutineId(val id: Long) : AbstractCoroutineContextElement(CoroutineId) { |
| 142 | companion object Key : CoroutineContext.Key<CoroutineId> |
| 143 | override fun toString(): String = "CoroutineId($id)" |
| 144 | } |