Skip to content

Commit 4578a34

Browse files
author
Ignacio Bonafonte
authored
Merge pull request #379 from open-telemetry/fix-status-thread-race-condition
Fix a thread race condition that happens when span is ended from various threads
2 parents 6270e74 + d14133f commit 4578a34

File tree

1 file changed

+47
-24
lines changed

1 file changed

+47
-24
lines changed

Sources/OpenTelemetrySdk/Trace/RecordEventsReadableSpan.swift

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,23 @@ import OpenTelemetryApi
99
/// Implementation for the Span class that records trace events.
1010
public class RecordEventsReadableSpan: ReadableSpan {
1111
public var isRecording = true
12+
13+
fileprivate let internalStatusQueue = DispatchQueue(label: "org.opentelemetry.RecordEventsReadableSpan.internalStatusQueue", attributes: .concurrent)
1214
/// The displayed name of the span.
15+
fileprivate var internalName: String
1316
public var name: String {
14-
didSet {
15-
if hasEnded {
16-
name = oldValue
17+
get {
18+
var value: String = ""
19+
internalStatusQueue.sync {
20+
value = internalName
21+
}
22+
return value
23+
}
24+
set {
25+
internalStatusQueue.sync(flags: .barrier) {
26+
if !internalEnd {
27+
internalName = newValue
28+
}
1729
}
1830
}
1931
}
@@ -55,11 +67,22 @@ public class RecordEventsReadableSpan: ReadableSpan {
5567
public private(set) var totalAttributeCount: Int = 0
5668
/// Number of events recorded.
5769
public private(set) var totalRecordedEvents = 0
70+
5871
/// The status of the span.
59-
public var status = Status.unset {
60-
didSet {
61-
if hasEnded {
62-
status = oldValue
72+
fileprivate var internalStatus: Status = .unset
73+
public var status: Status {
74+
get {
75+
var value: Status = .unset
76+
internalStatusQueue.sync {
77+
value = internalStatus
78+
}
79+
return value
80+
}
81+
set {
82+
internalStatusQueue.sync(flags: .barrier) {
83+
if !internalEnd {
84+
internalStatus = newValue
85+
}
6386
}
6487
}
6588
}
@@ -72,12 +95,13 @@ public class RecordEventsReadableSpan: ReadableSpan {
7295
/// The end time of the span.
7396
public private(set) var endTime: Date?
7497
/// True if the span is ended.
75-
fileprivate var endAtomic = false
76-
private let endSyncLock = Lock()
98+
fileprivate var internalEnd = false
7799
public var hasEnded: Bool {
78-
endSyncLock.withLock {
79-
return endAtomic
100+
var value = false
101+
internalStatusQueue.sync {
102+
value = internalEnd
80103
}
104+
return value
81105
}
82106

83107
private let eventsSyncLock = Lock()
@@ -99,7 +123,7 @@ public class RecordEventsReadableSpan: ReadableSpan {
99123
startTime: Date?)
100124
{
101125
self.context = context
102-
self.name = name
126+
self.internalName = name
103127
self.instrumentationScopeInfo = instrumentationScopeInfo
104128
self.parentContext = parentContext
105129
self.hasRemoteParent = hasRemoteParent
@@ -216,6 +240,7 @@ public class RecordEventsReadableSpan: ReadableSpan {
216240
if !isRecording {
217241
return
218242
}
243+
219244
if value == nil {
220245
attributes.removeValueForKey(key: key)
221246
}
@@ -262,23 +287,22 @@ public class RecordEventsReadableSpan: ReadableSpan {
262287
}
263288

264289
public func end(time: Date) {
265-
var wasAlreadyEnded = false
266-
endSyncLock.withLockVoid {
267-
wasAlreadyEnded = endAtomic
268-
endAtomic = true
269-
}
270-
if wasAlreadyEnded {
271-
return
272-
}
290+
internalStatusQueue.sync(flags: .barrier) {
291+
if internalEnd {
292+
return
273293

294+
} else {
295+
internalEnd = true
296+
}
297+
}
274298
eventsSyncLock.withLockVoid {
275299
attributesSyncLock.withLockVoid {
276300
isRecording = false
277301
}
278302
}
279303
endTime = time
280-
spanProcessor.onEnd(span: self)
281304
OpenTelemetry.instance.contextProvider.removeContextForSpan(self)
305+
spanProcessor.onEnd(span: self)
282306
}
283307

284308
public var description: String {
@@ -287,13 +311,12 @@ public class RecordEventsReadableSpan: ReadableSpan {
287311

288312
internal func getTotalRecordedEvents() -> Int {
289313
eventsSyncLock.withLock {
290-
return totalRecordedEvents
314+
totalRecordedEvents
291315
}
292316
}
293-
317+
294318
/// For testing purposes
295319
internal func getDroppedLinksCount() -> Int {
296320
return totalRecordedLinks - links.count
297321
}
298-
299322
}

0 commit comments

Comments
 (0)