Created
April 12, 2021 07:44
-
-
Save KaQuMiQ/ca507f881d85ed5b69c71dd62f56e074 to your computer and use it in GitHub Desktop.
Lock-free Future experiment
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Atomics | |
public final class Future<Value> { | |
private let fulfillmentStatus: ManagedAtomic<Bool> | |
private let valueStatus: ManagedAtomic<Bool> | |
private let handlingStatus: ManagedAtomic<Bool> | |
private let handlerStatus: ManagedAtomic<Bool> | |
private let completionStatus: ManagedAtomic<Bool> | |
private var value: Value? | |
private var handler: ((Value) -> Void)? | |
fileprivate init( | |
value: Value? = nil, | |
handler: ((Value) -> Void)? = nil | |
) { | |
self.fulfillmentStatus = .init(value != nil) | |
self.valueStatus = .init(value != nil) | |
self.handlingStatus = .init(handler != nil) | |
self.handlerStatus = .init(handler != nil) | |
if let value = value, let handler = handler { | |
self.completionStatus = .init(true) | |
handler(value) | |
self.value = nil | |
self.handler = nil | |
} else { | |
self.completionStatus = .init(false) | |
self.value = value | |
self.handler = handler | |
} | |
} | |
fileprivate func setHandler(_ handler: @escaping (Value) -> Void) { | |
if valueStatus.load(ordering: .sequentiallyConsistent) { | |
guard !completionStatus.exchange(true, ordering: .sequentiallyConsistent) else { return } | |
guard let value = self.value else { return assertionFailure("Invalid state") } | |
handler(value) | |
} else { | |
guard !handlingStatus.exchange(true, ordering: .sequentiallyConsistent) | |
else { return assertionFailure("Cannot replace handlers") } | |
self.handler = handler | |
handlerStatus.store(true, ordering: .sequentiallyConsistent) | |
guard valueStatus.load(ordering: .sequentiallyConsistent), | |
!completionStatus.exchange(true, ordering: .sequentiallyConsistent) | |
else { return } | |
guard let value = self.value else { return assertionFailure("Invalid state") } | |
handler(value) | |
// cleanup | |
self.handler = nil | |
self.value = nil | |
} | |
} | |
fileprivate func fulfill(_ value: Value) { | |
// check if already completed (or completion in progress) and mark as completed | |
guard !fulfillmentStatus.exchange(true, ordering: .sequentiallyConsistent) | |
else { return assertionFailure("Cannot fulfill more than once") } | |
// assign value, possible only if not completed yet | |
self.value = value | |
// mark value as available | |
valueStatus.store(true, ordering: .sequentiallyConsistent) | |
// check if handler was already assigned | |
guard handlerStatus.load(ordering: .sequentiallyConsistent), | |
// and completion status, set if not yet | |
!completionStatus.exchange(true, ordering: .sequentiallyConsistent) | |
else { return } | |
guard let handler = self.handler else { return assertionFailure("Invalid state") } | |
handler(value) | |
// cleanup | |
self.handler = nil | |
self.value = nil | |
} | |
} | |
public extension Future { | |
static func promise(_ promise: @escaping (@escaping (Value) -> Void) -> Void) -> Self { | |
let mapped: Self = .init() | |
promise(mapped.fulfill) | |
return mapped | |
} | |
} | |
public extension Future { | |
func map<NewValue>( | |
_ transform: @escaping (Value) -> NewValue | |
) -> Future<NewValue> { | |
let mapped = Future<NewValue>() | |
self.setHandler { mapped.fulfill(transform($0)) } | |
return mapped | |
} | |
func handle(_ handler: @escaping (Value) -> Void) { | |
self.setHandler(handler) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Does not work in current form