blob: ae346376e62c689cd474a41e2323bf999663576e [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
21import kotlin.coroutines.experimental.CoroutineContext
Roman Elizarov44ba4b12017-01-25 11:37:54 +030022
23private const val DEBUG_PROPERTY_NAME = "kotlinx.coroutines.debug"
24
25private val DEBUG = run {
26 val value = System.getProperty(DEBUG_PROPERTY_NAME)
27 when (value) {
28 "auto", null -> CoroutineId::class.java.desiredAssertionStatus()
29 "on", "" -> true
30 "off" -> false
31 else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'")
32 }
33}
34
35private val COROUTINE_ID = AtomicLong()
36
37/**
Roman Elizarov2f6d7c92017-02-03 15:16:07 +030038 * A coroutine dispatcher that is not confined to any specific thread.
39 * It executes initial continuation of the coroutine _right here_ in the current call-frame
Roman Elizarov44ba4b12017-01-25 11:37:54 +030040 * and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without
41 * mandating any specific threading policy.
42 */
Roman Elizarov2f6d7c92017-02-03 15:16:07 +030043public object Unconfined : CoroutineDispatcher() {
Roman Elizarov44ba4b12017-01-25 11:37:54 +030044 override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
45 override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
46}
47
Roman Elizarov32d95322017-02-09 15:57:31 +030048/**
Roman Elizarovfc7a9a22017-02-13 11:54:01 +030049 * @suppress **Deprecated**: `Here` was renamed to `Unconfined`.
Roman Elizarov32d95322017-02-09 15:57:31 +030050 */
Roman Elizarov2f6d7c92017-02-03 15:16:07 +030051@Deprecated(message = "`Here` was renamed to `Unconfined`",
52 replaceWith = ReplaceWith(expression = "Unconfined"))
53public typealias Here = Unconfined
54
Roman Elizarov44ba4b12017-01-25 11:37:54 +030055/**
56 * Creates context for the new coroutine with optional support for debugging facilities (when turned on).
57 *
58 * **Debugging facilities:** In debug mode every coroutine is assigned a unique consecutive identifier.
59 * Every thread that executes a coroutine has its name modified to include the name and identifier of the
60 * currently currently running coroutine.
61 * When one coroutine is suspended and resumes another coroutine that is dispatched in the same thread,
62 * then the thread name displays
63 * the whole stack of coroutine descriptions that are being executed on this thread.
64 *
65 * Enable debugging facilities with "`kotlinx.coroutines.debug`" system property, use the following values:
66 * * "`auto`" (default mode) -- enabled when assertions are enabled with "`-ea`" JVM option.
67 * * "`on`" or empty string -- enabled.
68 * * "`off`" -- disabled.
69 *
70 * Coroutine name can be explicitly assigned using [CoroutineName] context element.
71 * The string "coroutine" is used as a default name.
72 */
73public fun newCoroutineContext(context: CoroutineContext): CoroutineContext =
74 if (DEBUG) context + CoroutineId(COROUTINE_ID.incrementAndGet()) else context
75
76/**
77 * Executes a block using a given coroutine context.
78 */
79internal inline fun <T> withCoroutineContext(context: CoroutineContext, block: () -> T): T {
80 val oldName = updateContext(context)
81 try {
82 return block()
83 } finally {
84 restoreContext(oldName)
85 }
86}
87
88@PublishedApi
89internal fun updateContext(context: CoroutineContext): String? {
90 if (!DEBUG) return null
91 val newId = context[CoroutineId] ?: return null
92 val currentThread = Thread.currentThread()
93 val oldName = currentThread.name
94 val coroutineName = context[CoroutineName]?.name ?: "coroutine"
95 currentThread.name = buildString(oldName.length + coroutineName.length + 10) {
96 append(oldName)
97 append(" @")
98 append(coroutineName)
99 append('#')
100 append(newId.id)
101 }
102 return oldName
103}
104
105@PublishedApi
106internal fun restoreContext(oldName: String?) {
107 if (oldName != null) Thread.currentThread().name = oldName
108}
109
110private class CoroutineId(val id: Long) : AbstractCoroutineContextElement(CoroutineId) {
111 companion object Key : CoroutineContext.Key<CoroutineId>
112 override fun toString(): String = "CoroutineId($id)"
113}