// // Copyright 2024 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation public final class Future { public typealias ResultType = Swift.Result private let lock = UnfairLock() private var resultUnsynchronized: ResultType? private var observersUnsynchronized = [(ResultType) -> Void]() public init() {} public convenience init(value: Value) { self.init() self.resultUnsynchronized = .success(value) } public convenience init(error: Error) { self.init() self.resultUnsynchronized = .failure(error) } public func observe(on scheduler: Scheduler? = nil, block: @escaping (ResultType) -> Void) { lock.withLock { func execute(_ result: ResultType) { // If a scheduler is not specified, try and run on the main // queue. Eventually we'll want to switch this default, // but for now it matches the behavior we expect from // PromiseKit. (scheduler ?? DispatchQueue.main).asyncIfNecessary { block(result) } } if let result = resultUnsynchronized { execute(result) return } observersUnsynchronized.append(execute) } } private func sealResult(_ result: ResultType) { let observers: [(ResultType) -> Void] = lock.withLock { guard self.resultUnsynchronized == nil else { return [] } self.resultUnsynchronized = result let observers = observersUnsynchronized observersUnsynchronized.removeAll() return observers } observers.forEach { $0(result) } } public func resolve(_ value: Value) { sealResult(.success(value)) } public func resolve( on scheduler: Scheduler? = nil, with thenable: T ) where T.Value == Value { thenable.done(on: scheduler) { value in self.sealResult(.success(value)) }.catch(on: scheduler) { error in self.sealResult(.failure(error)) } } public func reject(_ error: Error) { sealResult(.failure(error)) } public var result: ResultType? { return lock.withLock { self.resultUnsynchronized } } public var isSealed: Bool { return lock.withLock { self.resultUnsynchronized != nil } } } public extension Future where Value == Void { func resolve() { resolve(()) } }