Skip to content

Commit 4431985

Browse files
authored
Merge pull request #2645 from DataDog/nogorodnikov/rum-9940/return-datadog-context-on-context-thread
RUM-9940: Make `getDatadogContext` read on the context thread
2 parents a3206f3 + ae8aedd commit 4431985

File tree

9 files changed

+98
-70
lines changed

9 files changed

+98
-70
lines changed

dd-sdk-android-core/api/apiSurface

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,6 @@ fun Collection<ByteArray>.join(ByteArray, ByteArray = ByteArray(0), ByteArray =
310310
fun java.util.concurrent.Executor.executeSafe(String, com.datadog.android.api.InternalLogger, Runnable)
311311
fun java.util.concurrent.ScheduledExecutorService.scheduleSafe(String, Long, java.util.concurrent.TimeUnit, com.datadog.android.api.InternalLogger, Runnable): java.util.concurrent.ScheduledFuture<*>?
312312
fun java.util.concurrent.ExecutorService.submitSafe(String, com.datadog.android.api.InternalLogger, Runnable): java.util.concurrent.Future<*>?
313-
fun <T> java.util.concurrent.ExecutorService.submitSafe(String, com.datadog.android.api.InternalLogger, java.util.concurrent.Callable<T>): java.util.concurrent.Future<T>?
314313
val NULL_MAP_VALUE: Object
315314
object com.datadog.android.core.internal.utils.JsonSerializer
316315
fun toJsonElement(Any?): com.google.gson.JsonElement

dd-sdk-android-core/api/dd-sdk-android-core.api

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,6 @@ public final class com/datadog/android/core/internal/utils/ConcurrencyExtKt {
813813
public static final fun executeSafe (Ljava/util/concurrent/Executor;Ljava/lang/String;Lcom/datadog/android/api/InternalLogger;Ljava/lang/Runnable;)V
814814
public static final fun scheduleSafe (Ljava/util/concurrent/ScheduledExecutorService;Ljava/lang/String;JLjava/util/concurrent/TimeUnit;Lcom/datadog/android/api/InternalLogger;Ljava/lang/Runnable;)Ljava/util/concurrent/ScheduledFuture;
815815
public static final fun submitSafe (Ljava/util/concurrent/ExecutorService;Ljava/lang/String;Lcom/datadog/android/api/InternalLogger;Ljava/lang/Runnable;)Ljava/util/concurrent/Future;
816-
public static final fun submitSafe (Ljava/util/concurrent/ExecutorService;Ljava/lang/String;Lcom/datadog/android/api/InternalLogger;Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
817816
}
818817

819818
public final class com/datadog/android/core/internal/utils/JsonSerializer {

dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver
3535
import com.datadog.android.core.internal.system.BuildSdkVersionProvider
3636
import com.datadog.android.core.internal.time.DefaultAppStartTimeProvider
3737
import com.datadog.android.core.internal.utils.executeSafe
38+
import com.datadog.android.core.internal.utils.getSafe
3839
import com.datadog.android.core.internal.utils.scheduleSafe
3940
import com.datadog.android.core.internal.utils.submitSafe
4041
import com.datadog.android.core.thread.FlushableExecutorService
@@ -47,9 +48,7 @@ import java.io.File
4748
import java.util.Locale
4849
import java.util.UUID
4950
import java.util.concurrent.Callable
50-
import java.util.concurrent.CancellationException
5151
import java.util.concurrent.ConcurrentHashMap
52-
import java.util.concurrent.ExecutionException
5352
import java.util.concurrent.ExecutorService
5453
import java.util.concurrent.ScheduledExecutorService
5554
import java.util.concurrent.TimeUnit
@@ -300,25 +299,13 @@ internal class DatadogCore(
300299

301300
override val trackingConsent: TrackingConsent
302301
get() {
303-
val future = coreFeature.contextExecutorService.submitSafe(
302+
return coreFeature.contextExecutorService.submitSafe(
304303
"getTrackingConsent",
305304
internalLogger,
306305
Callable<TrackingConsent> {
307306
coreFeature.trackingConsentProvider.getConsent()
308307
}
309-
)
310-
return try {
311-
future?.get() ?: TrackingConsent.NOT_GRANTED
312-
} catch (e: InterruptedException) {
313-
logTrackingConsentGetFailure(internalLogger, e)
314-
TrackingConsent.NOT_GRANTED
315-
} catch (e: CancellationException) {
316-
logTrackingConsentGetFailure(internalLogger, e)
317-
TrackingConsent.NOT_GRANTED
318-
} catch (e: ExecutionException) {
319-
logTrackingConsentGetFailure(internalLogger, e)
320-
TrackingConsent.NOT_GRANTED
321-
}
308+
).getSafe("getTrackingConsent", internalLogger) ?: TrackingConsent.NOT_GRANTED
322309
}
323310

324311
override val rootStorageDir: File
@@ -371,7 +358,9 @@ internal class DatadogCore(
371358
}
372359

373360
override fun getDatadogContext(): DatadogContext? {
374-
return contextProvider?.context
361+
return coreFeature.contextExecutorService
362+
.submitSafe("getDatadogContext", internalLogger, Callable { contextProvider?.context })
363+
.getSafe("getDatadogContext", internalLogger)
375364
}
376365

377366
// endregion
@@ -569,15 +558,6 @@ internal class DatadogCore(
569558
)
570559
}
571560

572-
private fun logTrackingConsentGetFailure(internalLogger: InternalLogger, throwable: Throwable) {
573-
internalLogger.log(
574-
level = InternalLogger.Level.ERROR,
575-
targets = listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
576-
messageBuilder = { UNABLE_TO_GET_TRACKING_CONSENT },
577-
throwable = throwable
578-
)
579-
}
580-
581561
/**
582562
* Stops all process for this instance of the Datadog SDK.
583563
*/
@@ -633,8 +613,6 @@ internal class DatadogCore(
633613
"No need to write last RUM view event: NDK" +
634614
" crash reports feature is not enabled and API is below 30."
635615

636-
internal const val UNABLE_TO_GET_TRACKING_CONSENT = "Unable to get tracking consent."
637-
638616
internal val CONFIGURATION_TELEMETRY_DELAY_MS = TimeUnit.SECONDS.toMillis(5)
639617

640618
// fallback for APIs below Android N, see [DefaultAppStartTimeProvider].

dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import com.datadog.android.core.internal.persistence.file.advanced.FeatureFileOr
5353
import com.datadog.android.core.internal.persistence.file.batch.BatchFileReaderWriter
5454
import com.datadog.android.core.internal.persistence.tlvformat.TLVBlockFileReader
5555
import com.datadog.android.core.internal.utils.executeSafe
56+
import com.datadog.android.core.internal.utils.getSafe
5657
import com.datadog.android.core.internal.utils.submitSafe
5758
import com.datadog.android.core.persistence.PersistenceStrategy
5859
import com.datadog.android.internal.profiler.BenchmarkSdkUploads
@@ -62,9 +63,7 @@ import com.datadog.android.security.Encryption
6263
import java.util.Collections
6364
import java.util.Locale
6465
import java.util.concurrent.Callable
65-
import java.util.concurrent.CancellationException
6666
import java.util.concurrent.ConcurrentHashMap
67-
import java.util.concurrent.ExecutionException
6867
import java.util.concurrent.atomic.AtomicBoolean
6968
import java.util.concurrent.atomic.AtomicReference
7069

@@ -187,9 +186,10 @@ internal class SdkFeature(
187186
}
188187

189188
override fun getWriteContextSync(): Pair<DatadogContext, EventWriteScope>? {
190-
val future = coreFeature.contextExecutorService
189+
val operationName = "getWriteContextSync-${wrappedFeature.name}"
190+
return coreFeature.contextExecutorService
191191
.submitSafe(
192-
"getWriteContextSync-${wrappedFeature.name}",
192+
operationName,
193193
internalLogger,
194194
Callable {
195195
val contextProvider = coreFeature.contextProvider
@@ -199,18 +199,7 @@ internal class SdkFeature(
199199
context to eventBatchWriteScope
200200
}
201201
)
202-
return try {
203-
future?.get()
204-
} catch (e: CancellationException) {
205-
logGetWriteContextSyncError(e)
206-
null
207-
} catch (e: ExecutionException) {
208-
logGetWriteContextSyncError(e)
209-
null
210-
} catch (e: InterruptedException) {
211-
logGetWriteContextSyncError(e)
212-
null
213-
}
202+
.getSafe(operationName, internalLogger)
214203
}
215204

216205
override fun sendEvent(event: Any) {
@@ -461,15 +450,6 @@ internal class SdkFeature(
461450
)
462451
}
463452

464-
private fun logGetWriteContextSyncError(e: Exception) {
465-
internalLogger.log(
466-
level = InternalLogger.Level.ERROR,
467-
target = InternalLogger.Target.USER,
468-
{ FAILED_TO_GET_WRITE_CONTEXT_SYNC },
469-
e
470-
)
471-
}
472-
473453
// endregion
474454

475455
// Used for nightly tests only
@@ -493,7 +473,6 @@ internal class SdkFeature(
493473
"Feature \"%s\" already has this listener registered."
494474
const val NO_EVENT_RECEIVER =
495475
"Feature \"%s\" has no event receiver registered, ignoring event."
496-
internal const val FAILED_TO_GET_WRITE_CONTEXT_SYNC = "Failed to get write context in a sync manner."
497476
internal const val TRACK_NAME = "track"
498477
internal const val METER_NAME = "dd-sdk-android"
499478
internal const val BATCH_COUNT_METRIC_NAME = "android.benchmark.batch_count"

dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/user/DatadogUserInfoProvider.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal class DatadogUserInfoProvider(
1313
internal val dataWriter: DataWriter<UserInfo>
1414
) : MutableUserInfoProvider {
1515

16+
@Volatile
1617
private var internalUserInfo = UserInfo()
1718
set(value) {
1819
field = value

dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/ConcurrencyExt.kt

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import com.datadog.android.api.InternalLogger
1111
import com.datadog.android.lint.InternalApi
1212
import java.util.Locale
1313
import java.util.concurrent.Callable
14+
import java.util.concurrent.CancellationException
15+
import java.util.concurrent.ExecutionException
1416
import java.util.concurrent.Executor
1517
import java.util.concurrent.ExecutorService
1618
import java.util.concurrent.Future
@@ -20,6 +22,7 @@ import java.util.concurrent.ScheduledFuture
2022
import java.util.concurrent.TimeUnit
2123

2224
internal const val ERROR_TASK_REJECTED = "Unable to schedule %s task on the executor"
25+
internal const val ERROR_FUTURE_GET_FAILED = "Unable to get result of the %s task"
2326

2427
/**
2528
* Executes [Runnable] without throwing [RejectedExecutionException] if it cannot be accepted
@@ -118,8 +121,7 @@ fun ExecutorService.submitSafe(
118121
* @param internalLogger Internal logger.
119122
* @param callable Task to run.
120123
*/
121-
@InternalApi
122-
fun <T> ExecutorService.submitSafe(
124+
internal fun <T> ExecutorService.submitSafe(
123125
operationName: String,
124126
internalLogger: InternalLogger,
125127
callable: Callable<T>
@@ -137,3 +139,36 @@ fun <T> ExecutorService.submitSafe(
137139
null
138140
}
139141
}
142+
143+
internal fun <T> Future<T>?.getSafe(
144+
operationName: String,
145+
internalLogger: InternalLogger
146+
): T? {
147+
return try {
148+
this?.get()
149+
} catch (e: InterruptedException) {
150+
internalLogger.log(
151+
InternalLogger.Level.ERROR,
152+
listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
153+
{ ERROR_FUTURE_GET_FAILED.format(Locale.US, operationName) },
154+
e
155+
)
156+
null
157+
} catch (e: CancellationException) {
158+
internalLogger.log(
159+
InternalLogger.Level.ERROR,
160+
listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
161+
{ ERROR_FUTURE_GET_FAILED.format(Locale.US, operationName) },
162+
e
163+
)
164+
null
165+
} catch (e: ExecutionException) {
166+
internalLogger.log(
167+
InternalLogger.Level.ERROR,
168+
listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
169+
{ ERROR_FUTURE_GET_FAILED.format(Locale.US, operationName) },
170+
e
171+
)
172+
null
173+
}
174+
}

dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -679,12 +679,6 @@ internal class DatadogCoreTest {
679679

680680
// When + Then
681681
assertThat(trackingConsent).isEqualTo(TrackingConsent.NOT_GRANTED)
682-
mockInternalLogger.verifyLog(
683-
InternalLogger.Level.ERROR,
684-
listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
685-
DatadogCore.UNABLE_TO_GET_TRACKING_CONSENT,
686-
fakeThrowable
687-
)
688682
}
689683

690684
@Test

dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/SdkFeatureTest.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -627,12 +627,6 @@ internal class SdkFeatureTest {
627627

628628
// Then
629629
assertThat(writeContext).isNull()
630-
mockInternalLogger.verifyLog(
631-
InternalLogger.Level.ERROR,
632-
InternalLogger.Target.USER,
633-
SdkFeature.FAILED_TO_GET_WRITE_CONTEXT_SYNC,
634-
throwable
635-
)
636630
}
637631

638632
@Test

dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/ConcurrencyExtTest.kt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ package com.datadog.android.core.internal.utils
99
import com.datadog.android.api.InternalLogger
1010
import com.datadog.android.utils.forge.Configurator
1111
import com.datadog.android.utils.verifyLog
12+
import com.datadog.tools.unit.forge.aThrowable
13+
import fr.xgouchet.elmyr.Forge
1214
import fr.xgouchet.elmyr.annotation.Forgery
1315
import fr.xgouchet.elmyr.annotation.LongForgery
1416
import fr.xgouchet.elmyr.annotation.StringForgery
@@ -26,9 +28,12 @@ import org.mockito.kotlin.doReturn
2628
import org.mockito.kotlin.doThrow
2729
import org.mockito.kotlin.mock
2830
import org.mockito.kotlin.verify
31+
import org.mockito.kotlin.verifyNoInteractions
2932
import org.mockito.kotlin.whenever
3033
import org.mockito.quality.Strictness
3134
import java.util.concurrent.Callable
35+
import java.util.concurrent.CancellationException
36+
import java.util.concurrent.ExecutionException
3237
import java.util.concurrent.ExecutorService
3338
import java.util.concurrent.Future
3439
import java.util.concurrent.RejectedExecutionException
@@ -219,4 +224,48 @@ internal class ConcurrencyExtTest {
219224
exception
220225
)
221226
}
227+
228+
@Test
229+
fun `M return result W getSafe()`(
230+
@StringForgery operationName: String
231+
) {
232+
// Given
233+
val future = mock<Future<Any>>()
234+
val fakeResult = Any()
235+
whenever(future.getSafe(operationName, mockInternalLogger)) doReturn fakeResult
236+
237+
// When
238+
val result = future.getSafe(operationName, mockInternalLogger)
239+
240+
// Then
241+
assertThat(result).isSameAs(fakeResult)
242+
verifyNoInteractions(mockInternalLogger)
243+
}
244+
245+
@Test
246+
fun `M log error W getSafe() { exception thrown }`(
247+
@StringForgery operationName: String,
248+
forge: Forge
249+
) {
250+
// Given
251+
val future = mock<Future<Any>>()
252+
val fakeException = forge.anElementFrom(
253+
ExecutionException(forge.aThrowable()),
254+
CancellationException(),
255+
InterruptedException()
256+
)
257+
whenever(future.getSafe(operationName, mockInternalLogger)) doThrow fakeException
258+
259+
// When
260+
val result = future.getSafe(operationName, mockInternalLogger)
261+
262+
// Then
263+
assertThat(result).isNull()
264+
mockInternalLogger.verifyLog(
265+
level = InternalLogger.Level.ERROR,
266+
targets = listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
267+
message = "Unable to get result of the $operationName task",
268+
throwable = fakeException
269+
)
270+
}
222271
}

0 commit comments

Comments
 (0)