From 680ea122fc7cceed683817d01871a0bc170c7f54 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Thu, 3 Mar 2022 13:15:01 -0800 Subject: [PATCH] Add a lock around Future.result And redefine Future.isSealed in terms of Future.result, rather than storing it separately. --- SignalCoreKit/src/Promises/Future.swift | 39 +++++++++++++++---------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/SignalCoreKit/src/Promises/Future.swift b/SignalCoreKit/src/Promises/Future.swift index 27f1f69..6a4d504 100644 --- a/SignalCoreKit/src/Promises/Future.swift +++ b/SignalCoreKit/src/Promises/Future.swift @@ -1,30 +1,30 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation public final class Future { public typealias ResultType = Swift.Result - public private(set) var isSealed = false - public private(set) var result: ResultType? + + private let lock = UnfairLock() + private var resultUnsynchronized: ResultType? + private var observersUnsynchronized = [(ResultType) -> Void]() public init() {} public convenience init(value: Value) { self.init() - sealResult(.success(value)) + self.resultUnsynchronized = .success(value) } public convenience init(error: Error) { self.init() - sealResult(.failure(error)) + self.resultUnsynchronized = .failure(error) } - private var observers = [(ResultType) -> Void]() - private let observerLock = UnfairLock() public func observe(on queue: DispatchQueue? = nil, block: @escaping (ResultType) -> Void) { - observerLock.withLock { + lock.withLock { func execute(_ result: ResultType) { // If a queue is not specified, try and run on the main // queue. Eventually we'll want to switch this default, @@ -35,20 +35,19 @@ public final class Future { } } - if let result = result { + if let result = resultUnsynchronized { execute(result) return } - observers.append(execute) + observersUnsynchronized.append(execute) } } private func sealResult(_ result: ResultType) { - observerLock.withLock { - guard !isSealed else { return } - self.result = result - self.isSealed = true - observers.forEach { $0(result) } - observers.removeAll() + lock.withLock { + guard self.resultUnsynchronized == nil else { return } + self.resultUnsynchronized = result + observersUnsynchronized.forEach { $0(result) } + observersUnsynchronized.removeAll() } } @@ -70,6 +69,14 @@ public final class Future { 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 {