Skip to content

Commit 803f03c

Browse files
lblodergetsentry-botromtsn
authored
update coroutines to 1.6.1, use CopyableThreadContextElement (#2838)
Co-authored-by: Sentry Github Bot <[email protected]> Co-authored-by: Roman Zavarnitsyn <[email protected]>
1 parent 1c0c691 commit 803f03c

File tree

5 files changed

+104
-5
lines changed

5 files changed

+104
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Breaking changes:
1717

1818
Breaking changes:
1919
- Move enableNdk from SentryOptions to SentryAndroidOptions ([#2793](https://github.com/getsentry/sentry-java/pull/2793))
20+
- Fix Coroutine Context Propagation using CopyableThreadContextElement, requires `kotlinx-coroutines-core` version `1.6.1` or higher ([#2838](https://github.com/getsentry/sentry-java/pull/2838))
2021

2122
## Unreleased
2223

buildSrc/src/main/java/Config.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ object Config {
104104
val retrofit2 = "$retrofit2Group:retrofit:$retrofit2Version"
105105
val retrofit2Gson = "$retrofit2Group:converter-gson:$retrofit2Version"
106106

107-
val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
107+
val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"
108108

109109
val fragment = "androidx.fragment:fragment-ktx:1.3.5"
110110

sentry-kotlin-extensions/api/sentry-kotlin-extensions.api

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
public final class io/sentry/kotlin/SentryContext : kotlin/coroutines/AbstractCoroutineContextElement, kotlinx/coroutines/ThreadContextElement {
1+
public final class io/sentry/kotlin/SentryContext : kotlin/coroutines/AbstractCoroutineContextElement, kotlinx/coroutines/CopyableThreadContextElement {
22
public fun <init> ()V
3+
public fun <init> (Lio/sentry/IHub;)V
4+
public synthetic fun <init> (Lio/sentry/IHub;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
5+
public fun copyForChild ()Lkotlinx/coroutines/CopyableThreadContextElement;
36
public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
47
public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
8+
public fun mergeForChild (Lkotlin/coroutines/CoroutineContext$Element;)Lkotlin/coroutines/CoroutineContext;
59
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
610
public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
711
public fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Lio/sentry/IHub;)V

sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,25 @@ package io.sentry.kotlin
22

33
import io.sentry.IHub
44
import io.sentry.Sentry
5-
import kotlinx.coroutines.ThreadContextElement
5+
import kotlinx.coroutines.CopyableThreadContextElement
66
import kotlin.coroutines.AbstractCoroutineContextElement
77
import kotlin.coroutines.CoroutineContext
88

99
/**
1010
* Sentry context element for [CoroutineContext].
1111
*/
12-
public class SentryContext : ThreadContextElement<IHub>, AbstractCoroutineContextElement(Key) {
12+
public class SentryContext(private val hub: IHub = Sentry.getCurrentHub().clone()) :
13+
CopyableThreadContextElement<IHub>, AbstractCoroutineContextElement(Key) {
1314

1415
private companion object Key : CoroutineContext.Key<SentryContext>
1516

16-
private val hub: IHub = Sentry.getCurrentHub().clone()
17+
override fun copyForChild(): CopyableThreadContextElement<IHub> {
18+
return SentryContext(hub.clone())
19+
}
20+
21+
override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext {
22+
return overwritingElement[Key] ?: SentryContext(hub.clone())
23+
}
1724

1825
override fun updateThreadContext(context: CoroutineContext): IHub {
1926
val oldState = Sentry.getCurrentHub()

sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package io.sentry.kotlin
22

33
import io.sentry.Sentry
4+
import kotlinx.coroutines.CoroutineName
45
import kotlinx.coroutines.joinAll
56
import kotlinx.coroutines.launch
67
import kotlinx.coroutines.runBlocking
78
import kotlin.test.AfterTest
89
import kotlin.test.BeforeTest
910
import kotlin.test.Test
1011
import kotlin.test.assertEquals
12+
import kotlin.test.assertNotEquals
13+
import kotlin.test.assertNotNull
1114
import kotlin.test.assertNull
1215

1316
class SentryContextTest {
@@ -80,6 +83,90 @@ class SentryContextTest {
8083
}
8184
}
8285

86+
@Test
87+
fun testContextIsClonedWhenPassedToChild() = runBlocking {
88+
Sentry.setTag("parent", "parentValue")
89+
launch(SentryContext()) {
90+
Sentry.setTag("c1", "c1value")
91+
assertEquals("c1value", getTag("c1"))
92+
assertEquals("parentValue", getTag("parent"))
93+
assertNull(getTag("c2"))
94+
95+
val c2 = launch() {
96+
Sentry.setTag("c2", "c2value")
97+
assertEquals("c2value", getTag("c2"))
98+
assertEquals("parentValue", getTag("parent"))
99+
assertNotNull(getTag("c1"))
100+
}
101+
102+
c2.join()
103+
104+
assertNotNull(getTag("c1"))
105+
assertNull(getTag("c2"))
106+
}
107+
assertNull(getTag("c1"))
108+
assertNull(getTag("c2"))
109+
}
110+
111+
@Test
112+
fun testExplicitlyPassedContextOverridesPropagatedContext() = runBlocking {
113+
Sentry.setTag("parent", "parentValue")
114+
launch(SentryContext()) {
115+
Sentry.setTag("c1", "c1value")
116+
assertEquals("c1value", getTag("c1"))
117+
assertEquals("parentValue", getTag("parent"))
118+
assertNull(getTag("c2"))
119+
120+
val c2 = launch(
121+
SentryContext(
122+
Sentry.getCurrentHub().clone().also {
123+
it.setTag("cloned", "clonedValue")
124+
}
125+
)
126+
) {
127+
Sentry.setTag("c2", "c2value")
128+
assertEquals("c2value", getTag("c2"))
129+
assertEquals("parentValue", getTag("parent"))
130+
assertNotNull(getTag("c1"))
131+
assertNotNull(getTag("cloned"))
132+
}
133+
134+
c2.join()
135+
136+
assertNotNull(getTag("c1"))
137+
assertNull(getTag("c2"))
138+
assertNull(getTag("cloned"))
139+
}
140+
assertNull(getTag("c1"))
141+
assertNull(getTag("c2"))
142+
assertNull(getTag("cloned"))
143+
}
144+
145+
@Test
146+
fun `mergeForChild returns copy of initial context if Key not present`() {
147+
val initialContextElement = SentryContext(
148+
Sentry.getCurrentHub().clone().also {
149+
it.setTag("cloned", "clonedValue")
150+
}
151+
)
152+
val mergedContextElement = initialContextElement.mergeForChild(CoroutineName("test"))
153+
154+
assertNotEquals(initialContextElement, mergedContextElement)
155+
assertNotNull((mergedContextElement)[initialContextElement.key])
156+
}
157+
158+
@Test
159+
fun `mergeForChild returns passed context`() {
160+
val initialContextElement = SentryContext(
161+
Sentry.getCurrentHub().clone().also {
162+
it.setTag("cloned", "clonedValue")
163+
}
164+
)
165+
val mergedContextElement = SentryContext().mergeForChild(initialContextElement)
166+
167+
assertEquals(initialContextElement, mergedContextElement)
168+
}
169+
83170
private fun getTag(tag: String): String? {
84171
var value: String? = null
85172
Sentry.configureScope {

0 commit comments

Comments
 (0)