blob: 5d8c3d5c6d3d1081350e1eeeaae881e1f3fcba64 [file] [log] [blame]
Vsevolod Tolstopyatove5b25232018-08-24 15:24:40 +03001/*
2 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +03004
Roman Elizarov0950dfa2018-07-13 10:33:25 +03005package kotlinx.coroutines
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +03006
7import org.junit.*
8import org.junit.Test
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +03009import java.lang.IllegalStateException
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030010import kotlin.test.*
11
12@Suppress("RedundantAsync")
13class ThreadLocalTest : TestBase() {
14 private val stringThreadLocal = ThreadLocal<String?>()
15 private val intThreadLocal = ThreadLocal<Int?>()
16 private val executor = newFixedThreadPoolContext(1, "threadLocalTest")
17
18 @After
19 fun tearDown() {
20 executor.close()
21 }
22
23 @Test
24 fun testThreadLocal() = runTest {
25 assertNull(stringThreadLocal.get())
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +030026 assertFalse(stringThreadLocal.isPresent())
Roman Elizarovf189cec2018-09-11 19:16:49 +030027 val deferred = async(Dispatchers.Default + stringThreadLocal.asContextElement("value")) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030028 assertEquals("value", stringThreadLocal.get())
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +030029 assertTrue(stringThreadLocal.isPresent())
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030030 withContext(executor) {
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +030031 assertTrue(stringThreadLocal.isPresent())
32 assertFailsWith<IllegalStateException> { intThreadLocal.ensurePresent() }
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030033 assertEquals("value", stringThreadLocal.get())
34 }
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +030035 assertTrue(stringThreadLocal.isPresent())
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030036 assertEquals("value", stringThreadLocal.get())
37 }
38
39 assertNull(stringThreadLocal.get())
40 deferred.await()
41 assertNull(stringThreadLocal.get())
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +030042 assertFalse(stringThreadLocal.isPresent())
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030043 }
44
45 @Test
46 fun testThreadLocalInitialValue() = runTest {
47 intThreadLocal.set(42)
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +030048 assertFalse(intThreadLocal.isPresent())
Roman Elizarovf189cec2018-09-11 19:16:49 +030049 val deferred = async(Dispatchers.Default + intThreadLocal.asContextElement(239)) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030050 assertEquals(239, intThreadLocal.get())
51 withContext(executor) {
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +030052 intThreadLocal.ensurePresent()
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030053 assertEquals(239, intThreadLocal.get())
54 }
55 assertEquals(239, intThreadLocal.get())
56 }
57
58 deferred.await()
59 assertEquals(42, intThreadLocal.get())
60 }
61
62 @Test
63 fun testMultipleThreadLocals() = runTest {
64 stringThreadLocal.set("test")
65 intThreadLocal.set(314)
66
Roman Elizarovf189cec2018-09-11 19:16:49 +030067 val deferred = async(Dispatchers.Default
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030068 + intThreadLocal.asContextElement(value = 239) + stringThreadLocal.asContextElement(value = "pew")) {
69 assertEquals(239, intThreadLocal.get())
70 assertEquals("pew", stringThreadLocal.get())
71
72 withContext(executor) {
73 assertEquals(239, intThreadLocal.get())
74 assertEquals("pew", stringThreadLocal.get())
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +030075 intThreadLocal.ensurePresent()
76 stringThreadLocal.ensurePresent()
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030077 }
78
79 assertEquals(239, intThreadLocal.get())
80 assertEquals("pew", stringThreadLocal.get())
81 }
82
83 deferred.await()
84 assertEquals(314, intThreadLocal.get())
85 assertEquals("test", stringThreadLocal.get())
86 }
87
88 @Test
89 fun testConflictingThreadLocals() = runTest {
90 intThreadLocal.set(42)
91
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030092 val deferred = GlobalScope.async(intThreadLocal.asContextElement(1)) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +030093 assertEquals(1, intThreadLocal.get())
94
95 withContext(executor + intThreadLocal.asContextElement(42)) {
96 assertEquals(42, intThreadLocal.get())
97 }
98
99 assertEquals(1, intThreadLocal.get())
100
Roman Elizarov6e3ffb12018-09-14 13:46:58 +0300101 val deferred = async(intThreadLocal.asContextElement(53)) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300102 assertEquals(53, intThreadLocal.get())
103 }
104
105 deferred.await()
106 assertEquals(1, intThreadLocal.get())
107
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300108 val deferred2 = GlobalScope.async(executor) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300109 assertNull(intThreadLocal.get())
110 }
111
112 deferred2.await()
113 assertEquals(1, intThreadLocal.get())
114 }
115
116 deferred.await()
117 assertEquals(42, intThreadLocal.get())
118 }
119
120 @Test
121 fun testThreadLocalModification() = runTest {
122 stringThreadLocal.set("main")
123
Roman Elizarovf189cec2018-09-11 19:16:49 +0300124 val deferred = async(Dispatchers.Default
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300125 + stringThreadLocal.asContextElement("initial")) {
126 assertEquals("initial", stringThreadLocal.get())
127
128 stringThreadLocal.set("overridden") // <- this value is not reflected in the context, so it's not restored
129
130 withContext(executor + stringThreadLocal.asContextElement("ctx")) {
131 assertEquals("ctx", stringThreadLocal.get())
132 }
133
Roman Elizarov6e3ffb12018-09-14 13:46:58 +0300134 val deferred = async(stringThreadLocal.asContextElement("async")) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300135 assertEquals("async", stringThreadLocal.get())
136 }
137
138 deferred.await()
139 assertEquals("initial", stringThreadLocal.get()) // <- not restored
140 }
141
142 deferred.await()
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +0300143 assertFalse(stringThreadLocal.isPresent())
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300144 assertEquals("main", stringThreadLocal.get())
145 }
146
147
148
149 private data class Counter(var cnt: Int)
150 private val myCounterLocal = ThreadLocal<Counter>()
151
152 @Test
153 fun testThreadLocalModificationMutableBox() = runTest {
154 myCounterLocal.set(Counter(42))
155
Roman Elizarovf189cec2018-09-11 19:16:49 +0300156 val deferred = async(Dispatchers.Default
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300157 + myCounterLocal.asContextElement(Counter(0))) {
158 assertEquals(0, myCounterLocal.get().cnt)
159
160 // Mutate
161 myCounterLocal.get().cnt = 71
162
163 withContext(executor + myCounterLocal.asContextElement(Counter(-1))) {
164 assertEquals(-1, myCounterLocal.get().cnt)
165 ++myCounterLocal.get().cnt
166 }
167
Roman Elizarov6e3ffb12018-09-14 13:46:58 +0300168 val deferred = async(myCounterLocal.asContextElement(Counter(31))) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300169 assertEquals(31, myCounterLocal.get().cnt)
170 ++myCounterLocal.get().cnt
171 }
172
173 deferred.await()
174 assertEquals(71, myCounterLocal.get().cnt)
175 }
176
177 deferred.await()
178 assertEquals(42, myCounterLocal.get().cnt)
179 }
180
181 @Test
182 fun testWithContext() = runTest {
183 expect(1)
184 newSingleThreadContext("withContext").use {
185 val data = 42
Roman Elizarovf189cec2018-09-11 19:16:49 +0300186 GlobalScope.async(Dispatchers.Default + intThreadLocal.asContextElement(42)) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300187
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300188 assertEquals(data, intThreadLocal.get())
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300189 expect(2)
190
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300191 GlobalScope.async(it + intThreadLocal.asContextElement(31)) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300192 assertEquals(31, intThreadLocal.get())
193 expect(3)
194 }.await()
195
196 withContext(it + intThreadLocal.asContextElement(2)) {
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300197 assertEquals(2, intThreadLocal.get())
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300198 expect(4)
199 }
200
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300201 GlobalScope.async(it) {
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300202 assertNull(intThreadLocal.get())
203 expect(5)
204 }.await()
205
206 expect(6)
207 }.await()
208 }
209
210 finish(7)
211 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300212
213 @Test
214 fun testScope() = runTest {
215 intThreadLocal.set(42)
216 val mainThread = Thread.currentThread()
217 GlobalScope.async {
218 assertNull(intThreadLocal.get())
219 assertNotSame(mainThread, Thread.currentThread())
220 }.await()
221
222 GlobalScope.async(intThreadLocal.asContextElement()) {
223 assertEquals(42, intThreadLocal.get())
224 assertNotSame(mainThread, Thread.currentThread())
225 }.await()
226 }
Vsevolod Tolstopyatov0355b2c2019-03-22 16:01:59 +0300227
228 @Test
229 fun testMissingThreadLocal() = runTest {
230 assertFailsWith<IllegalStateException> { stringThreadLocal.ensurePresent() }
231 assertFailsWith<IllegalStateException> { intThreadLocal.ensurePresent() }
232 }
Vsevolod Tolstopyatove3425972018-08-22 19:41:57 +0300233}