Skip to content

Commit 2133b50

Browse files
author
Ignacio Bonafonte
authored
Merge pull request #306 from seemk/fix-tracerprovider-tracerget-race
Fix race condition in TracerProviderSdk.get when creating a new tracer. When multiple threads attempt to get a tracer from tracer provider and the tracer has not yet been constructed, there's a race condition, which usually leads to EXC_BAD_ACCESS.
2 parents 7c0ed78 + 815f2c7 commit 2133b50

File tree

1 file changed

+21
-11
lines changed

1 file changed

+21
-11
lines changed

Sources/OpenTelemetrySdk/Trace/TracerProviderSdk.swift

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Foundation
77
import OpenTelemetryApi
88

99
public class TracerProviderSdk: TracerProvider {
10+
private var tracerLock = pthread_rwlock_t()
1011
private var tracerProvider = [InstrumentationLibraryInfo: TracerSdk]()
1112
internal var sharedState: TracerSharedState
1213
internal static let emptyName = "unknown"
@@ -19,6 +20,7 @@ public class TracerProviderSdk: TracerProvider {
1920
sampler: Sampler = Samplers.parentBased(root: Samplers.alwaysOn),
2021
spanProcessors: [SpanProcessor] = [])
2122
{
23+
pthread_rwlock_init(&tracerLock, nil)
2224
sharedState = TracerSharedState(clock: clock,
2325
idGenerator: idGenerator,
2426
resource: resource,
@@ -27,6 +29,10 @@ public class TracerProviderSdk: TracerProvider {
2729
spanProcessors: spanProcessors)
2830
}
2931

32+
deinit {
33+
pthread_rwlock_destroy(&tracerLock)
34+
}
35+
3036
public func get(instrumentationName: String, instrumentationVersion: String? = nil) -> Tracer {
3137
if sharedState.hasBeenShutdown {
3238
return DefaultTracer.instance
@@ -39,20 +45,24 @@ public class TracerProviderSdk: TracerProvider {
3945
instrumentationName = TracerProviderSdk.emptyName
4046
}
4147
let instrumentationLibraryInfo = InstrumentationLibraryInfo(name: instrumentationName, version: instrumentationVersion ?? "")
42-
if let tracer = tracerProvider[instrumentationLibraryInfo] {
43-
return tracer
44-
} else {
45-
// Re-check if the value was added since the previous check, this can happen if multiple
46-
// threads try to access the same named tracer during the same time. This way we ensure that
47-
// we create only one TracerSdk per name.
48-
if let tracer = tracerProvider[instrumentationLibraryInfo] {
49-
// A different thread already added the named Tracer, just reuse.
50-
return tracer
48+
49+
if pthread_rwlock_rdlock(&tracerLock) == 0 {
50+
let existingTracer = tracerProvider[instrumentationLibraryInfo]
51+
pthread_rwlock_unlock(&tracerLock)
52+
53+
if existingTracer != nil {
54+
return existingTracer!
5155
}
52-
let tracer = TracerSdk(sharedState: sharedState, instrumentationLibraryInfo: instrumentationLibraryInfo)
56+
}
57+
58+
let tracer = TracerSdk(sharedState: sharedState, instrumentationLibraryInfo: instrumentationLibraryInfo)
59+
60+
if pthread_rwlock_wrlock(&tracerLock) == 0 {
5361
tracerProvider[instrumentationLibraryInfo] = tracer
54-
return tracer
62+
pthread_rwlock_unlock(&tracerLock)
5563
}
64+
65+
return tracer
5666
}
5767

5868
/// Returns the active Clock.

0 commit comments

Comments
 (0)