Skip to content

Commit 43b7ceb

Browse files
committed
Fix thread safety issue in withLatestFrom operator
Fixes #163 and #171 by adding proper synchronization around latestValue access using os_unfair_lock. The latestValue property is now protected when accessed from multiple threads (the 'other' publisher thread and the upstream publisher thread).
1 parent df8c2c3 commit 43b7ceb

1 file changed

Lines changed: 19 additions & 4 deletions

File tree

Sources/Operators/WithLatestFrom.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ private extension Publishers.WithLatestFrom {
183183

184184
// Secondary (other) publisher
185185
private var latestValue: Other.Output?
186+
private let lock = Lock()
186187
private var otherSubscription: Cancellable?
187188
private var preInitialDemand = Subscribers.Demand.none
188189

@@ -205,7 +206,11 @@ private extension Publishers.WithLatestFrom {
205206
}
206207

207208
func request(_ demand: Subscribers.Demand) {
208-
guard latestValue != nil else {
209+
lock.lock()
210+
let hasLatestValue = latestValue != nil
211+
lock.unlock()
212+
213+
guard hasLatestValue else {
209214
preInitialDemand += demand
210215
return
211216
}
@@ -225,7 +230,9 @@ private extension Publishers.WithLatestFrom {
225230
},
226231
receiveValue: { [weak self] value in
227232
guard let self else { return .none }
233+
lock.lock()
228234
latestValue = value
235+
lock.unlock()
229236

230237
if !gotInitialValue {
231238
// When getting initial value, start pulling values
@@ -234,8 +241,13 @@ private extension Publishers.WithLatestFrom {
234241
upstream: upstream,
235242
downstream: downstream,
236243
transformOutput: { [weak self] value in
237-
guard let self,
238-
let other = latestValue else { return nil }
244+
guard let self else { return nil }
245+
246+
lock.lock()
247+
let other = latestValue
248+
lock.unlock()
249+
250+
guard let other else { return nil }
239251

240252
return resultSelector(value, other)
241253
},
@@ -265,7 +277,10 @@ private extension Publishers.WithLatestFrom {
265277
otherSubscription?.cancel()
266278
}
267279

268-
deinit { cancel() }
280+
deinit {
281+
cancel()
282+
lock.cleanupLock()
283+
}
269284
}
270285
}
271286
#endif

0 commit comments

Comments
 (0)