diff --git a/Sources/Segment/Utilities/Atomic.swift b/Sources/Segment/Utilities/Atomic.swift index ab4e8c87..50984aba 100644 --- a/Sources/Segment/Utilities/Atomic.swift +++ b/Sources/Segment/Utilities/Atomic.swift @@ -7,29 +7,26 @@ import Foundation +// NOTE: Revised from previous implementation which used a struct and NSLock's. +// Thread Sanitizer was *correctly* capturing this issue, which was a little obscure +// given the property wrapper PLUS the semantics of a struct. Moving to `class` +// removes the semantics problem and lets TSan approve of what's happening. +// +// Additionally, moving to a lock free version is just desirable, so moved to a queue. +// +// Also see thread here: https://github.com/apple/swift-evolution/pull/1387 + @propertyWrapper -public struct Atomic { - var value: T - private let lock = NSLock() +public class Atomic { + private var value: T + private let queue = DispatchQueue(label: "com.segment.atomic.\(UUID().uuidString)") public init(wrappedValue value: T) { self.value = value } public var wrappedValue: T { - get { return load() } - set { store(newValue: newValue) } - } - - func load() -> T { - lock.lock() - defer { lock.unlock() } - return value - } - - mutating func store(newValue: T) { - lock.lock() - defer { lock.unlock() } - value = newValue + get { return queue.sync { return value } } + set { queue.sync { value = newValue } } } } diff --git a/Sources/Segment/Utilities/Policies/CountBasedFlushPolicy.swift b/Sources/Segment/Utilities/Policies/CountBasedFlushPolicy.swift index a68abf07..2cadfa45 100644 --- a/Sources/Segment/Utilities/Policies/CountBasedFlushPolicy.swift +++ b/Sources/Segment/Utilities/Policies/CountBasedFlushPolicy.swift @@ -10,7 +10,7 @@ import Foundation public class CountBasedFlushPolicy: FlushPolicy { public weak var analytics: Analytics? internal var desiredCount: Int? - internal var count: Int = 0 + @Atomic internal var count: Int = 0 init() { }