From 57e652a8dec5f805360b093e3f4902c2b4900d15 Mon Sep 17 00:00:00 2001 From: Isaac Weisberg Date: Tue, 2 Jun 2026 18:46:19 +0200 Subject: [PATCH] ReplayBufferBase propagate outiside locks --- RxSwift/Subjects/ReplaySubject.swift | 115 ++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index 7da848d89..44821f7b7 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -103,7 +103,7 @@ private class ReplayBufferBase: rxAbstractMethod() } - func replayBuffer(_: Observer) where Observer.Element == Element { + func replayBuffer() -> ReplayBufferSnapshot { rxAbstractMethod() } @@ -140,24 +140,33 @@ private class ReplayBufferBase: } override func subscribe(_ observer: Observer) -> Disposable where Observer.Element == Element { - lock.performLocked { self.synchronized_subscribe(observer) } + let subscription = lock.performLocked { self.synchronized_subscribe(observer) } + + if let replayObserver = subscription.replayObserver { + replayObserver.replay(subscription.replayBuffer) + } else { + subscription.replayBuffer.replay(observer) + + if let stoppedEvent = subscription.stoppedEvent { + observer.on(stoppedEvent) + } + } + + return subscription.disposable } - func synchronized_subscribe(_ observer: Observer) -> Disposable where Observer.Element == Element { + func synchronized_subscribe(_ observer: Observer) -> (replayBuffer: ReplayBufferSnapshot, stoppedEvent: Event?, disposable: Disposable, replayObserver: ReplaySubjectSubscriptionObserver?) where Observer.Element == Element { if isDisposed { - observer.on(.error(RxError.disposed(object: self))) - return Disposables.create() + return (.empty, .error(RxError.disposed(object: self)), Disposables.create(), nil) } - let anyObserver = observer.asObserver() - - replayBuffer(anyObserver) + let replayBuffer = self.replayBuffer() if let stoppedEvent { - observer.on(stoppedEvent) - return Disposables.create() + return (replayBuffer, stoppedEvent, Disposables.create(), nil) } else { - let key = observers.insert(observer.on) - return SubscriptionDisposable(owner: self, key: key) + let replayObserver = ReplaySubjectSubscriptionObserver(observer: observer) + let key = observers.insert(replayObserver.on) + return (replayBuffer, nil, SubscriptionDisposable(owner: self, key: key), replayObserver) } } @@ -189,6 +198,76 @@ private class ReplayBufferBase: } } +private enum ReplayBufferSnapshot { + case empty + case one(Element) + case many(Queue) + + func replay(_ observer: Observer) where Observer.Element == Element { + switch self { + case .empty: + break + case let .one(value): + observer.on(.next(value)) + case let .many(queue): + for value in queue { + observer.on(.next(value)) + } + } + } +} + +private final class ReplaySubjectSubscriptionObserver: ObserverType { + typealias Element = Observer.Element + + private let lock = RecursiveLock() + private let observer: Observer + private var replaying = true + private var pendingEvents = Queue>(capacity: 1) + + init(observer: Observer) { + self.observer = observer + } + + func on(_ event: Event) { + let shouldForward = lock.performLocked { () -> Bool in + if replaying { + pendingEvents.enqueue(event) + return false + } + + return true + } + + if shouldForward { + observer.on(event) + } + } + + func replay(_ replayBuffer: ReplayBufferSnapshot) { + replayBuffer.replay(observer) + + while let event = lock.performLocked({ () -> Event? in + if let event = pendingEvents.dequeue() { + return event + } + + replaying = false + return nil + }) { + observer.on(event) + + if event.isStopEvent { + lock.performLocked { + self.replaying = false + self.pendingEvents = Queue(capacity: 0) + } + return + } + } + } +} + private final class ReplayOne: ReplayBufferBase { private var value: Element? @@ -202,10 +281,12 @@ private final class ReplayOne: ReplayBufferBase { self.value = value } - override func replayBuffer(_ observer: Observer) where Observer.Element == Element { + override func replayBuffer() -> ReplayBufferSnapshot { if let value { - observer.on(.next(value)) + return .one(value) } + + return .empty } override func synchronized_dispose() { @@ -225,10 +306,8 @@ private class ReplayManyBase: ReplayBufferBase { queue.enqueue(value) } - override func replayBuffer(_ observer: Observer) where Observer.Element == Element { - for item in queue { - observer.on(.next(item)) - } + override func replayBuffer() -> ReplayBufferSnapshot { + .many(queue) } override func synchronized_dispose() {