Skip to content

Commit 89014de

Browse files
fix: Atomic increment is now performed within one enqueued operation (#330)
* Version 1.5.10 * fix: Atomic increment is now performed within one enqueued operation --------- Co-authored-by: Brandon Sneed <[email protected]>
1 parent a1b519e commit 89014de

File tree

6 files changed

+52
-6
lines changed

6 files changed

+52
-6
lines changed

Examples/other_plugins/IDFACollection.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,15 @@ class IDFACollection: Plugin {
7777
extension IDFACollection: iOSLifecycle {
7878
func applicationDidBecomeActive(application: UIApplication?) {
7979
let status = ATTrackingManager.trackingAuthorizationStatus
80-
if status == .notDetermined && !alreadyAsked {
81-
// we don't know, so should ask the user.
82-
alreadyAsked = true
83-
askForPermission()
80+
81+
_alreadyAsked.withValue { alreadyAsked in
82+
if status == .notDetermined && !alreadyAsked {
83+
// we don't know, so should ask the user.
84+
alreadyAsked = true
85+
DispatchQueue.main.async {
86+
askForPermission()
87+
}
88+
}
8489
}
8590
}
8691
}

Sources/Segment/Plugins/Platforms/Vendors/AppleUtils.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ internal class iOSVendorSystem: VendorSystem {
7676
// BKS: It was discovered that on some platforms there can be a delay in retrieval.
7777
// It has to be fetched on the main thread, so we've spun it off
7878
// async and cache it when it comes back.
79+
// Note that due to how the `@Atomic` wrapper works, this boolean check may pass twice or more
80+
// times before the value is updated, fetching the user agent multiple times as the result.
81+
// This is not a big deal as the `userAgent` value is not expected to change often.
7982
if Self.asyncUserAgent == nil {
8083
DispatchQueue.main.async {
8184
Self.asyncUserAgent = WKWebView().value(forKey: "userAgent") as? String
@@ -248,6 +251,9 @@ internal class MacOSVendorSystem: VendorSystem {
248251
// BKS: It was discovered that on some platforms there can be a delay in retrieval.
249252
// It has to be fetched on the main thread, so we've spun it off
250253
// async and cache it when it comes back.
254+
// Note that due to how the `@Atomic` wrapper works, this boolean check may pass twice or more
255+
// times before the value is updated, fetching the user agent multiple times as the result.
256+
// This is not a big deal as the `userAgent` value is not expected to change often.
251257
if Self.asyncUserAgent == nil {
252258
DispatchQueue.main.async {
253259
Self.asyncUserAgent = WKWebView().value(forKey: "userAgent") as? String

Sources/Segment/Plugins/SegmentDestination.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion
116116
guard let storage = self.storage else { return }
117117
// Send Event to File System
118118
storage.write(.events, value: event)
119-
eventCount += 1
119+
self._eventCount.withValue { count in
120+
count += 1
121+
}
120122
}
121123

122124
public func flush() {

Sources/Segment/Utilities/Atomic.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,12 @@ public class Atomic<T> {
2929
get { return queue.sync { return value } }
3030
set { queue.sync { value = newValue } }
3131
}
32+
33+
@discardableResult
34+
public func withValue(_ operation: (inout T) -> Void) -> T {
35+
queue.sync {
36+
operation(&self.value)
37+
return self.value
38+
}
39+
}
3240
}

Sources/Segment/Utilities/Policies/CountBasedFlushPolicy.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ public class CountBasedFlushPolicy: FlushPolicy {
3737
}
3838

3939
public func updateState(event: RawEvent) {
40-
count += 1
40+
_count.withValue { value in
41+
value += 1
42+
}
4143
}
4244

4345
public func reset() {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import XCTest
2+
@testable import Segment
3+
4+
final class Atomic_Tests: XCTestCase {
5+
6+
func testAtomicIncrement() {
7+
8+
@Atomic var counter = 0
9+
10+
DispatchQueue.concurrentPerform(iterations: 1000) { _ in
11+
// counter += 1 would fail, because it is expanded to:
12+
// `let oldValue = queue.sync { counter }`
13+
// `queue.sync { counter = oldValue + 1 }`
14+
// And the threads are free to suspend in between the two calls to `queue.sync`.
15+
16+
_counter.withValue { value in
17+
value += 1
18+
}
19+
}
20+
21+
XCTAssertEqual(counter, 1000)
22+
}
23+
}

0 commit comments

Comments
 (0)