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 kotlin.coroutines.* 8 import kotlin.test.* 9 10 class ThreadContextMutableCopiesTest : TestBase() { 11 companion object { <lambda>null12 val threadLocalData: ThreadLocal<MutableList<String>> = ThreadLocal.withInitial { ArrayList() } 13 } 14 15 class MyMutableElement( 16 val mutableData: MutableList<String> 17 ) : CopyableThreadContextElement<MutableList<String>> { 18 19 companion object Key : CoroutineContext.Key<MyMutableElement> 20 21 override val key: CoroutineContext.Key<*> 22 get() = Key 23 updateThreadContextnull24 override fun updateThreadContext(context: CoroutineContext): MutableList<String> { 25 val st = threadLocalData.get() 26 threadLocalData.set(mutableData) 27 return st 28 } 29 restoreThreadContextnull30 override fun restoreThreadContext(context: CoroutineContext, oldState: MutableList<String>) { 31 threadLocalData.set(oldState) 32 } 33 copyForChildnull34 override fun copyForChild(): MyMutableElement { 35 return MyMutableElement(ArrayList(mutableData)) 36 } 37 mergeForChildnull38 override fun mergeForChild(overwritingElement: CoroutineContext.Element): MyMutableElement { 39 overwritingElement as MyMutableElement // <- app-specific, may be another subtype 40 return MyMutableElement((mutableData.toSet() + overwritingElement.mutableData).toMutableList()) 41 } 42 } 43 44 @Test <lambda>null45 fun testDataIsCopied() = runTest { 46 val root = MyMutableElement(ArrayList()) 47 runBlocking(root) { 48 val data = threadLocalData.get() 49 expect(1) 50 launch(root) { 51 assertNotSame(data, threadLocalData.get()) 52 assertEquals(data, threadLocalData.get()) 53 finish(2) 54 } 55 } 56 } 57 58 @Test testDataIsNotOverwrittennull59 fun testDataIsNotOverwritten() = runTest { 60 val root = MyMutableElement(ArrayList()) 61 runBlocking(root) { 62 expect(1) 63 val originalData = threadLocalData.get() 64 threadLocalData.get().add("X") 65 launch { 66 threadLocalData.get().add("Y") 67 // Note here, +root overwrites the data 68 launch(Dispatchers.Default + root) { 69 assertEquals(listOf("X", "Y"), threadLocalData.get()) 70 assertNotSame(originalData, threadLocalData.get()) 71 finish(2) 72 } 73 } 74 } 75 } 76 77 @Test <lambda>null78 fun testDataIsMerged() = runTest { 79 val root = MyMutableElement(ArrayList()) 80 runBlocking(root) { 81 expect(1) 82 val originalData = threadLocalData.get() 83 threadLocalData.get().add("X") 84 launch { 85 threadLocalData.get().add("Y") 86 // Note here, +root overwrites the data 87 launch(Dispatchers.Default + MyMutableElement(mutableListOf("Z"))) { 88 assertEquals(listOf("X", "Y", "Z"), threadLocalData.get()) 89 assertNotSame(originalData, threadLocalData.get()) 90 finish(2) 91 } 92 } 93 } 94 } 95 96 @Test <lambda>null97 fun testDataIsNotOverwrittenWithContext() = runTest { 98 val root = MyMutableElement(ArrayList()) 99 runBlocking(root) { 100 val originalData = threadLocalData.get() 101 threadLocalData.get().add("X") 102 expect(1) 103 launch { 104 threadLocalData.get().add("Y") 105 // Note here, +root overwrites the data 106 withContext(Dispatchers.Default + root) { 107 assertEquals(listOf("X", "Y"), threadLocalData.get()) 108 assertNotSame(originalData, threadLocalData.get()) 109 finish(2) 110 } 111 } 112 } 113 } 114 115 @Test <lambda>null116 fun testDataIsCopiedForRunBlocking() = runTest { 117 val root = MyMutableElement(ArrayList()) 118 val originalData = root.mutableData 119 runBlocking(root) { 120 assertNotSame(originalData, threadLocalData.get()) 121 } 122 } 123 124 @Test <lambda>null125 fun testDataIsCopiedForCoroutine() = runTest { 126 val root = MyMutableElement(ArrayList()) 127 val originalData = root.mutableData 128 expect(1) 129 launch(root) { 130 assertNotSame(originalData, threadLocalData.get()) 131 finish(2) 132 } 133 } 134 } 135