Skip to content

Instantly share code, notes, and snippets.

@KaQuMiQ
Created April 12, 2021 07:44
Show Gist options
  • Save KaQuMiQ/ca507f881d85ed5b69c71dd62f56e074 to your computer and use it in GitHub Desktop.
Save KaQuMiQ/ca507f881d85ed5b69c71dd62f56e074 to your computer and use it in GitHub Desktop.
Lock-free Future experiment
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)
}
}
@KaQuMiQ
Copy link
Author

KaQuMiQ commented Apr 12, 2021

Does not work in current form

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment