• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines
6 
7 import kotlinx.coroutines.flow.*
8 import kotlin.coroutines.*
9 import kotlin.test.*
10 
11 class ThreadContextMutableCopiesTest : TestBase() {
12     companion object {
<lambda>null13         val threadLocalData: ThreadLocal<MutableList<String>> = ThreadLocal.withInitial { ArrayList() }
14     }
15 
16     class MyMutableElement(
17         val mutableData: MutableList<String>
18     ) : CopyableThreadContextElement<MutableList<String>> {
19 
20         companion object Key : CoroutineContext.Key<MyMutableElement>
21 
22         override val key: CoroutineContext.Key<*>
23             get() = Key
24 
updateThreadContextnull25         override fun updateThreadContext(context: CoroutineContext): MutableList<String> {
26             val st = threadLocalData.get()
27             threadLocalData.set(mutableData)
28             return st
29         }
30 
restoreThreadContextnull31         override fun restoreThreadContext(context: CoroutineContext, oldState: MutableList<String>) {
32             threadLocalData.set(oldState)
33         }
34 
copyForChildnull35         override fun copyForChild(): MyMutableElement {
36             return MyMutableElement(ArrayList(mutableData))
37         }
38 
mergeForChildnull39         override fun mergeForChild(overwritingElement: CoroutineContext.Element): MyMutableElement {
40             overwritingElement as MyMutableElement // <- app-specific, may be another subtype
41             return MyMutableElement((mutableData.toSet() + overwritingElement.mutableData).toMutableList())
42         }
43     }
44 
45     @Test
<lambda>null46     fun testDataIsCopied() = runTest {
47         val root = MyMutableElement(ArrayList())
48         runBlocking(root) {
49             val data = threadLocalData.get()
50             expect(1)
51             launch(root) {
52                 assertNotSame(data, threadLocalData.get())
53                 assertEquals(data, threadLocalData.get())
54                 finish(2)
55             }
56         }
57     }
58 
59     @Test
testDataIsNotOverwrittennull60     fun testDataIsNotOverwritten() = runTest {
61         val root = MyMutableElement(ArrayList())
62         runBlocking(root) {
63             expect(1)
64             val originalData = threadLocalData.get()
65             threadLocalData.get().add("X")
66             launch {
67                 threadLocalData.get().add("Y")
68                 // Note here, +root overwrites the data
69                 launch(Dispatchers.Default + root) {
70                     assertEquals(listOf("X", "Y"), threadLocalData.get())
71                     assertNotSame(originalData, threadLocalData.get())
72                     finish(2)
73                 }
74             }
75         }
76     }
77 
78     @Test
<lambda>null79     fun testDataIsMerged() = runTest {
80         val root = MyMutableElement(ArrayList())
81         runBlocking(root) {
82             expect(1)
83             val originalData = threadLocalData.get()
84             threadLocalData.get().add("X")
85             launch {
86                 threadLocalData.get().add("Y")
87                 // Note here, +root overwrites the data
88                 launch(Dispatchers.Default + MyMutableElement(mutableListOf("Z"))) {
89                     assertEquals(listOf("X", "Y", "Z"), threadLocalData.get())
90                     assertNotSame(originalData, threadLocalData.get())
91                     finish(2)
92                 }
93             }
94         }
95     }
96 
97     @Test
<lambda>null98     fun testDataIsNotOverwrittenWithContext() = runTest {
99         val root = MyMutableElement(ArrayList())
100         runBlocking(root) {
101             val originalData = threadLocalData.get()
102             threadLocalData.get().add("X")
103             expect(1)
104             launch {
105                 threadLocalData.get().add("Y")
106                 // Note here, +root overwrites the data
107                 withContext(Dispatchers.Default + root) {
108                     assertEquals(listOf("X", "Y"), threadLocalData.get())
109                     assertNotSame(originalData, threadLocalData.get())
110                     finish(2)
111                 }
112             }
113         }
114     }
115 
116     @Test
<lambda>null117     fun testDataIsCopiedForRunBlocking() = runTest {
118         val root = MyMutableElement(ArrayList())
119         val originalData = root.mutableData
120         runBlocking(root) {
121             assertNotSame(originalData, threadLocalData.get())
122         }
123     }
124 
125     @Test
<lambda>null126     fun testDataIsCopiedForCoroutine() = runTest {
127         val root = MyMutableElement(ArrayList())
128         val originalData = root.mutableData
129         expect(1)
130         launch(root) {
131             assertNotSame(originalData, threadLocalData.get())
132             finish(2)
133         }
134     }
135 
136     @Test
<lambda>null137     fun testDataIsCopiedThroughFlowOnUndispatched() = runTest {
138         expect(1)
139         val root = MyMutableElement(ArrayList())
140         val originalData = root.mutableData
141         flow {
142             assertNotSame(originalData, threadLocalData.get())
143             emit(1)
144         }
145             .flowOn(root)
146             .single()
147         finish(2)
148     }
149 
150     @Test
<lambda>null151     fun testDataIsCopiedThroughFlowOnDispatched() = runTest {
152         expect(1)
153         val root = MyMutableElement(ArrayList())
154         val originalData = root.mutableData
155         flow {
156             assertNotSame(originalData, threadLocalData.get())
157             emit(1)
158         }
159             .flowOn(root + Dispatchers.Default)
160             .single()
161         finish(2)
162     }
163 }
164