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