Files
UUVPN/iOS-SwiftUI-Code/GRDB.swift-6.29.3/GRDB/Utils/OnDemandFuture.swift
T
2025-01-28 12:28:03 +08:00

117 lines
4.0 KiB
Swift

#if canImport(Combine)
import Combine
import Foundation
/// A publisher that eventually produces one value and then finishes or fails.
///
/// Like a Combine.Future wrapped in Combine.Deferred, OnDemandFuture starts
/// producing its value on demand.
///
/// Unlike Combine.Future wrapped in Combine.Deferred, OnDemandFuture guarantees
/// that it starts producing its value on demand, **synchronously**, and that
/// it produces its value on promise completion, **synchronously**.
///
/// Both two extra scheduling guarantees are used by GRDB in order to be
/// able to spawn concurrent database reads right from the database writer
/// queue, and fulfill GRDB preconditions.
@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *)
struct OnDemandFuture<Output, Failure: Error>: Publisher {
typealias Promise = (Result<Output, Failure>) -> Void
typealias Output = Output
typealias Failure = Failure
fileprivate let attemptToFulfill: (@escaping Promise) -> Void
init(_ attemptToFulfill: @escaping (@escaping Promise) -> Void) {
self.attemptToFulfill = attemptToFulfill
}
func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input {
let subscription = OnDemandFutureSubscription(
attemptToFulfill: attemptToFulfill,
downstream: subscriber)
subscriber.receive(subscription: subscription)
}
}
@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *)
private class OnDemandFutureSubscription<Downstream: Subscriber>: Subscription {
typealias Promise = (Result<Downstream.Input, Downstream.Failure>) -> Void
private enum State {
case waitingForDemand(downstream: Downstream, attemptToFulfill: (@escaping Promise) -> Void)
case waitingForFulfillment(downstream: Downstream)
case finished
}
private var state: State
private let lock = NSRecursiveLock() // Allow re-entrancy
init(
attemptToFulfill: @escaping (@escaping Promise) -> Void,
downstream: Downstream)
{
self.state = .waitingForDemand(downstream: downstream, attemptToFulfill: attemptToFulfill)
}
func request(_ demand: Subscribers.Demand) {
lock.synchronized {
switch state {
case let .waitingForDemand(downstream: downstream, attemptToFulfill: attemptToFulfill):
guard demand > 0 else {
return
}
state = .waitingForFulfillment(downstream: downstream)
attemptToFulfill { result in
switch result {
case let .success(value):
self.receive(value)
case let .failure(error):
self.receive(completion: .failure(error))
}
}
case .waitingForFulfillment, .finished:
break
}
}
}
func cancel() {
lock.synchronized {
state = .finished
}
}
private func receive(_ value: Downstream.Input) {
lock.synchronized { sideEffect in
switch state {
case let .waitingForFulfillment(downstream: downstream):
state = .finished
sideEffect = {
_ = downstream.receive(value)
downstream.receive(completion: .finished)
}
case .waitingForDemand, .finished:
break
}
}
}
private func receive(completion: Subscribers.Completion<Downstream.Failure>) {
lock.synchronized { sideEffect in
switch state {
case let .waitingForFulfillment(downstream: downstream):
state = .finished
sideEffect = {
downstream.receive(completion: completion)
}
case .waitingForDemand, .finished:
break
}
}
}
}
#endif