1
+ /*
2
+ * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3
+ */
4
+
5
+ package kotlinx.coroutines.experimental
6
+
7
+ import kotlin.coroutines.experimental.*
8
+
9
+ /* *
10
+ * An extension point to define elements in [CoroutineContext] that are installed into thread local
11
+ * variables every time the coroutine from the specified context in resumed on a thread.
12
+ *
13
+ * Implementations on this interface are looked up via [java.util.ServiceLoader].
14
+ *
15
+ * Example usage looks like this:
16
+ *
17
+ * ```
18
+ * // declare custom coroutine context element
19
+ * class MyElement : AbstractCoroutineContextElement(Key) {
20
+ * companion object Key : CoroutineContext.Key<MyElement>
21
+ * // some state is kept here
22
+ * }
23
+ *
24
+ * // declare thread local variable
25
+ * private val myThreadLocal = ThreadLocal<MyElement?>()
26
+ *
27
+ * // declare extension point implementation
28
+ * class MyCoroutineContextThreadLocal : CoroutineContextThreadLocal<MyElement?> {
29
+ * // this is invoked before coroutine is resumed on current thread
30
+ * override fun updateThreadContext(context: CoroutineContext): MyElement? {
31
+ * val oldValue = myThreadLocal.get()
32
+ * myThreadLocal.set(context[MyElement])
33
+ * return oldValue
34
+ * }
35
+ *
36
+ * // this is invoked after coroutine has suspended on current thread
37
+ * override fun restoreThreadContext(context: CoroutineContext, oldValue: MyElement?) {
38
+ * myThreadLocal.set(oldValue)
39
+ * }
40
+ * }
41
+ * ```
42
+ *
43
+ * Now, `MyCoroutineContextThreadLocal` fully qualified class named shall be registered via
44
+ * `META-INF/services/kotlinx.coroutines.experimental.CoroutineContextThreadLocal` file.
45
+ */
46
+ public interface CoroutineContextThreadLocal <T > {
47
+ /* *
48
+ * Updates context of the current thread.
49
+ * This function is invoked before the coroutine in the specified [context] is resumed in the current thread.
50
+ * The result of this function is the old value that will be passed to [restoreThreadContext].
51
+ */
52
+ public fun updateThreadContext (context : CoroutineContext ): T
53
+
54
+ /* *
55
+ * Restores context of the current thread.
56
+ * This function is invoked after the coroutine in the specified [context] is suspended in the current thread.
57
+ * The value of [oldValue] is the result of the previous invocation of [updateThreadContext].
58
+ */
59
+ public fun restoreThreadContext (context : CoroutineContext , oldValue : T )
60
+ }
61
+
62
+ /* *
63
+ * This class is used when multiple [CoroutineContextThreadLocal] are installed.
64
+ */
65
+ internal class CoroutineContextThreadLocalList (
66
+ private val impls : Array <CoroutineContextThreadLocal <Any ?>>
67
+ ) : CoroutineContextThreadLocal<Any?> {
68
+ init {
69
+ require(impls.size > 1 )
70
+ }
71
+
72
+ private val threadLocalStack = object : ThreadLocal <ArrayList <Any ?>>() {
73
+ override fun initialValue (): ArrayList <Any ?> = ArrayList ()
74
+ }
75
+
76
+ override fun updateThreadContext (context : CoroutineContext ): Any? {
77
+ val stack = threadLocalStack.get()!!
78
+ val lastIndex = impls.lastIndex
79
+ for (i in 0 until lastIndex) {
80
+ stack.add(impls[i].updateThreadContext(context))
81
+ }
82
+ return impls[lastIndex].updateThreadContext(context)
83
+ }
84
+
85
+ override fun restoreThreadContext (context : CoroutineContext , oldValue : Any? ) {
86
+ val stack = threadLocalStack.get()!!
87
+ val lastIndex = impls.lastIndex
88
+ impls[lastIndex].restoreThreadContext(context, oldValue)
89
+ for (i in lastIndex - 1 downTo 0 ) {
90
+ impls[i].restoreThreadContext(context, stack.removeAt(stack.lastIndex))
91
+ }
92
+ }
93
+ }
0 commit comments