Skip to content

Commit c45c081

Browse files
committed
OpRepo: Use private dispatch queue instead of lock
* In a previous commit, an unfair lock was used to synchronize access to the delta queue and synchronize flushing behavior * A dispatch queue seems more appropriate for the Operation Repo to use considering it already polls and flushes on a global queue. * Without the lock or dispatch queue, I reproduced crashes by creating multiple async threads that either added tags or called to flush the operation repo. * With the dispatch queue, those crashes do no happen and behavior seems as expected.
1 parent cb88aa6 commit c45c081

File tree

3 files changed

+40
-35
lines changed

3 files changed

+40
-35
lines changed

iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ import OneSignalCore
3535
public class OSOperationRepo: NSObject {
3636
public static let sharedInstance = OSOperationRepo()
3737
private var hasCalledStart = false
38-
private let deltaQueueLock = UnfairLock()
38+
39+
// The Operation Repo dispatch queue, serial. This synchronizes access to `deltaQueue` and flushing behavior.
40+
private let dispatchQueue = DispatchQueue(label: "OneSignal.OSOperationRepo", target: .global())
3941

4042
// Maps delta names to the interfaces for the operation executors
4143
var deltasToExecutorMap: [String: OSOperationExecutor] = [:]
@@ -63,7 +65,7 @@ public class OSOperationRepo: NSObject {
6365
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo calling start()")
6466
// register as user observer
6567
NotificationCenter.default.addObserver(self,
66-
selector: #selector(self.flushDeltaQueue),
68+
selector: #selector(self.addFlushDeltaQueueToDispatchQueue),
6769
name: Notification.Name(OS_ON_USER_WILL_CHANGE),
6870
object: nil)
6971
// Read the Deltas from cache, if any...
@@ -77,7 +79,7 @@ public class OSOperationRepo: NSObject {
7779
}
7880

7981
private func pollFlushQueue() {
80-
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(pollIntervalMilliseconds)) { [weak self] in
82+
self.dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(pollIntervalMilliseconds)) { [weak self] in
8183
self?.flushDeltaQueue()
8284
self?.pollFlushQueue()
8385
}
@@ -102,16 +104,21 @@ public class OSOperationRepo: NSObject {
102104
return
103105
}
104106
start()
105-
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo enqueueDelta: \(delta)")
106-
107-
deltaQueueLock.locked {
108-
deltaQueue.append(delta)
107+
self.dispatchQueue.async {
108+
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo enqueueDelta: \(delta)")
109+
self.deltaQueue.append(delta)
109110
// Persist the deltas (including new delta) to storage
110111
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_OPERATION_REPO_DELTA_QUEUE_KEY, withValue: self.deltaQueue)
111112
}
112113
}
113114

114-
@objc public func flushDeltaQueue(inBackground: Bool = false) {
115+
@objc public func addFlushDeltaQueueToDispatchQueue(inBackground: Bool = false) {
116+
self.dispatchQueue.async {
117+
self.flushDeltaQueue(inBackground: inBackground)
118+
}
119+
}
120+
121+
private func flushDeltaQueue(inBackground: Bool = false) {
115122
guard !paused else {
116123
OneSignalLog.onesignalLog(.LL_DEBUG, message: "OSOperationRepo not flushing queue due to being paused")
117124
return
@@ -125,38 +132,36 @@ public class OSOperationRepo: NSObject {
125132
OSBackgroundTaskManager.beginBackgroundTask(OPERATION_REPO_BACKGROUND_TASK)
126133
}
127134

128-
start()
135+
self.start()
129136

130-
deltaQueueLock.locked {
131-
if !deltaQueue.isEmpty {
132-
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo flushDeltaQueue in background: \(inBackground) with queue: \(deltaQueue)")
133-
}
137+
if !self.deltaQueue.isEmpty {
138+
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo flushDeltaQueue in background: \(inBackground) with queue: \(self.deltaQueue)")
139+
}
134140

135-
var index = 0
136-
for delta in deltaQueue {
137-
if let executor = deltasToExecutorMap[delta.name] {
138-
executor.enqueueDelta(delta)
139-
deltaQueue.remove(at: index)
140-
} else {
141-
// keep in queue if no executor matches, we may not have the executor available yet
142-
index += 1
143-
}
141+
var index = 0
142+
for delta in self.deltaQueue {
143+
if let executor = self.deltasToExecutorMap[delta.name] {
144+
executor.enqueueDelta(delta)
145+
self.deltaQueue.remove(at: index)
146+
} else {
147+
// keep in queue if no executor matches, we may not have the executor available yet
148+
index += 1
144149
}
150+
}
145151

146-
// Persist the deltas (including removed deltas) to storage after they are divvy'd up to executors.
147-
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_OPERATION_REPO_DELTA_QUEUE_KEY, withValue: self.deltaQueue)
152+
// Persist the deltas (including removed deltas) to storage after they are divvy'd up to executors.
153+
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_OPERATION_REPO_DELTA_QUEUE_KEY, withValue: self.deltaQueue)
148154

149-
for executor in executors {
150-
executor.cacheDeltaQueue()
151-
}
155+
for executor in self.executors {
156+
executor.cacheDeltaQueue()
157+
}
152158

153-
for executor in executors {
154-
executor.processDeltaQueue(inBackground: inBackground)
155-
}
159+
for executor in self.executors {
160+
executor.processDeltaQueue(inBackground: inBackground)
161+
}
156162

157-
if inBackground {
158-
OSBackgroundTaskManager.endBackgroundTask(OPERATION_REPO_BACKGROUND_TASK)
159-
}
163+
if inBackground {
164+
OSBackgroundTaskManager.endBackgroundTask(OPERATION_REPO_BACKGROUND_TASK)
160165
}
161166
}
162167
}

iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ extension OneSignalUserManagerImpl {
594594
*/
595595
@objc
596596
public func runBackgroundTasks() {
597-
OSOperationRepo.sharedInstance.flushDeltaQueue(inBackground: true)
597+
OSOperationRepo.sharedInstance.addFlushDeltaQueueToDispatchQueue(inBackground: true)
598598
}
599599
}
600600

iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ final class OneSignalUserTests: XCTestCase {
8484
for _ in 1...4 {
8585
DispatchQueue.global().async {
8686
print("🧪 flushDeltaQueue on thread \(Thread.current)")
87-
OSOperationRepo.sharedInstance.flushDeltaQueue()
87+
OSOperationRepo.sharedInstance.addFlushDeltaQueueToDispatchQueue()
8888
}
8989
}
9090

0 commit comments

Comments
 (0)