swift-nio/Sources/NIO/EventLoopFuture.swift

992 lines
46 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import NIOConcurrencyHelpers
/// Internal list of callbacks.
///
/// Most of these are closures that pull a value from one future, call a user callback, push the
/// result into another, then return a list of callbacks from the target future that are now ready to be invoked.
///
/// In particular, note that _run() here continues to obtain and execute lists of callbacks until it completes.
/// This eliminates recursion when processing `flatMap()` chains.
private struct CallbackList: ExpressibleByArrayLiteral {
typealias Element = () -> CallbackList
var firstCallback: Element?
var furtherCallbacks: [Element]?
init() {
firstCallback = nil
furtherCallbacks = nil
}
init(arrayLiteral: Element...) {
self.init()
if !arrayLiteral.isEmpty {
firstCallback = arrayLiteral[0]
if arrayLiteral.count > 1 {
furtherCallbacks = Array(arrayLiteral.dropFirst())
}
}
}
mutating func append(_ callback: @escaping () -> CallbackList) {
if self.firstCallback == nil {
self.firstCallback = callback
} else {
if self.furtherCallbacks != nil {
self.furtherCallbacks!.append(callback)
} else {
self.furtherCallbacks = [callback]
}
}
}
private func allCallbacks() -> [Element] {
switch (self.firstCallback, self.furtherCallbacks) {
case (.none, _):
return []
case (.some(let onlyCallback), .none):
return [onlyCallback]
case (.some(let first), .some(let others)):
return [first] + others
}
}
func _run() {
switch (self.firstCallback, self.furtherCallbacks) {
case (.none, _):
return
case (.some(let onlyCallback), .none):
var onlyCallback = onlyCallback
loop: while true {
let cbl = onlyCallback()
switch (cbl.firstCallback, cbl.furtherCallbacks) {
case (.none, _):
break loop
case (.some(let ocb), .none):
onlyCallback = ocb
continue loop
case (.some(_), .some(_)):
var pending = cbl.allCallbacks()
while pending.count > 0 {
let list = pending
pending = []
for f in list {
let next = f()
pending.append(contentsOf: next.allCallbacks())
}
}
break loop
}
}
case (.some(let first), .some(let others)):
var pending = [first]+others
while pending.count > 0 {
let list = pending
pending = []
for f in list {
let next = f()
pending.append(contentsOf: next.allCallbacks())
}
}
}
}
}
/// A promise to provide a result later.
///
/// This is the provider API for `EventLoopFuture<Value>`. If you want to return an
/// unfulfilled `EventLoopFuture<Value>` -- presumably because you are interfacing to
/// some asynchronous service that will return a real result later, follow this
/// pattern:
///
/// ```
/// func someAsyncOperation(args) -> EventLoopFuture<ResultType> {
/// let promise = eventLoop.makePromise(of: ResultType.self)
/// someAsyncOperationWithACallback(args) { result -> Void in
/// // when finished...
/// promise.succeed(result)
/// // if error...
/// promise.fail(error)
/// }
/// return promise.futureResult
/// }
/// ```
///
/// Note that the future result is returned before the async process has provided a value.
///
/// It's actually not very common to use this directly. Usually, you really want one
/// of the following:
///
/// * If you have an `EventLoopFuture` and want to do something else after it completes,
/// use `.flatMap()`
/// * If you just want to get a value back after running something on another thread,
/// use `EventLoopFuture<ResultType>.async()`
/// * If you already have a value and need an `EventLoopFuture<>` object to plug into
/// some other API, create an already-resolved object with `eventLoop.newSucceededFuture(result)`
/// or `eventLoop.newFailedFuture(error:)`.
///
/// - note: `EventLoopPromise` has reference semantics.
public struct EventLoopPromise<Value> {
/// The `EventLoopFuture` which is used by the `EventLoopPromise`. You can use it to add callbacks which are notified once the
/// `EventLoopPromise` is completed.
public let futureResult: EventLoopFuture<Value>
/// General initializer
///
/// - parameters:
/// - eventLoop: The event loop this promise is tied to.
/// - file: The file this promise was allocated in, for debugging purposes.
/// - line: The line this promise was allocated on, for debugging purposes.
init(eventLoop: EventLoop, file: StaticString, line: UInt) {
futureResult = EventLoopFuture<Value>(eventLoop: eventLoop, file: file, line: line)
}
/// Deliver a successful result to the associated `EventLoopFuture<Value>` object.
///
/// - parameters:
/// - result: The successful result of the operation.
public func succeed(_ result: Value) {
_resolve(value: .success(result))
}
/// Deliver an error to the associated `EventLoopFuture<Value>` object.
///
/// - parameters:
/// - error: The error from the operation.
public func fail(_ error: Error) {
_resolve(value: .failure(error))
}
/// Fire the associated `EventLoopFuture` on the appropriate event loop.
///
/// This method provides the primary difference between the `EventLoopPromise` and most
/// other `Promise` implementations: specifically, all callbacks fire on the `EventLoop`
/// that was used to create the promise.
///
/// - parameters:
/// - value: The value to fire the future with.
private func _resolve(value: Result<Value, Error>) {
if futureResult.eventLoop.inEventLoop {
_setValue(value: value)._run()
} else {
futureResult.eventLoop.execute {
self._setValue(value: value)._run()
}
}
}
/// Set the future result and get the associated callbacks.
///
/// - parameters:
/// - value: The result of the promise.
/// - returns: The callback list to run.
fileprivate func _setValue(value: Result<Value, Error>) -> CallbackList {
return futureResult._setValue(value: value)
}
}
/// Holder for a result that will be provided later.
///
/// Functions that promise to do work asynchronously can return an `EventLoopFuture<Value>`.
/// The recipient of such an object can then observe it to be notified when the operation completes.
///
/// The provider of a `EventLoopFuture<Value>` can create and return a placeholder object
/// before the actual result is available. For example:
///
/// ```
/// func getNetworkData(args) -> EventLoopFuture<NetworkResponse> {
/// let promise = eventLoop.makePromise(of: NetworkResponse.self)
/// queue.async {
/// . . . do some work . . .
/// promise.succeed(response)
/// . . . if it fails, instead . . .
/// promise.fail(error)
/// }
/// return promise.futureResult
/// }
/// ```
///
/// Note that this function returns immediately; the promise object will be given a value
/// later on. This behaviour is common to Future/Promise implementations in many programming
/// languages. If you are unfamiliar with this kind of object, the following resources may be
/// helpful:
///
/// - [Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises)
/// - [Scala](http://docs.scala-lang.org/overviews/core/futures.html)
/// - [Python](https://docs.google.com/document/d/10WOZgLQaYNpOrag-eTbUm-JUCCfdyfravZ4qSOQPg1M/edit)
///
/// If you receive a `EventLoopFuture<Value>` from another function, you have a number of options:
/// The most common operation is to use `flatMap()` or `map()` to add a function that will be called
/// with the eventual result. Both methods returns a new `EventLoopFuture<Value>` immediately
/// that will receive the return value from your function, but they behave differently. If you have
/// a function that can return synchronously, the `map` function will transform the result of type
/// `Value` to a the new result of type `NewValue` and return an `EventLoopFuture<NewValue>`.
///
/// ```
/// let networkData = getNetworkData(args)
///
/// // When network data is received, convert it.
/// let processedResult: EventLoopFuture<Processed> = networkData.map { (n: NetworkResponse) -> Processed in
/// ... parse network data ....
/// return processedResult
/// }
/// ```
///
/// If however you need to do more asynchronous processing, you can call `flatMap()`. The return value of the
/// function passed to `flatMap` must be a new `EventLoopFuture<NewValue>` object: the return value of `flatMap()` is
/// a new `EventLoopFuture<NewValue>` that will contain the eventual result of both the original operation and
/// the subsequent one.
///
/// ```
/// // When converted network data is available, begin the database operation.
/// let databaseResult: EventLoopFuture<DBResult> = processedResult.flatMap { (p: Processed) -> EventLoopFuture<DBResult> in
/// return someDatabaseOperation(p)
/// }
/// ```
///
/// In essence, future chains created via `flatMap()` provide a form of data-driven asynchronous programming
/// that allows you to dynamically declare data dependencies for your various operations.
///
/// `EventLoopFuture` chains created via `flatMap()` are sufficient for most purposes. All of the registered
/// functions will eventually run in order. If one of those functions throws an error, that error will
/// bypass the remaining functions. You can use `flatMapError()` to handle and optionally recover from
/// errors in the middle of a chain.
///
/// At the end of an `EventLoopFuture` chain, you can use `whenSuccess()` or `whenFailure()` to add an
/// observer callback that will be invoked with the result or error at that point. (Note: If you ever
/// find yourself invoking `promise.succeed()` from inside a `whenSuccess()` callback, you probably should
/// use `flatMap()` or `cascade(promise:)` instead.)
///
/// `EventLoopFuture` objects are typically obtained by:
/// * Using `EventLoopFuture<Value>.async` or a similar wrapper function.
/// * Using `.flatMap()` on an existing future to create a new future for the next step in a series of operations.
/// * Initializing an `EventLoopFuture` that already has a value or an error
///
/// ### Threading and Futures
///
/// One of the major performance advantages of NIO over something like Node.js or Pythons asyncio is that NIO will
/// by default run multiple event loops at once, on different threads. As most network protocols do not require
/// blocking operation, at least in their low level implementations, this provides enormous speedups on machines
/// with many cores such as most modern servers.
///
/// However, it can present a challenge at higher levels of abstraction when coordination between those threads
/// becomes necessary. This is usually the case whenever the events on one connection (that is, one `Channel`) depend
/// on events on another one. As these `Channel`s may be scheduled on different event loops (and so different threads)
/// care needs to be taken to ensure that communication between the two loops is done in a thread-safe manner that
/// avoids concurrent mutation of shared state from multiple loops at once.
///
/// The main primitives NIO provides for this use are the `EventLoopPromise` and `EventLoopFuture`. As their names
/// suggest, these two objects are aware of event loops, and so can help manage the safety and correctness of your
/// programs. However, understanding the exact semantics of these objects is critical to ensuring the safety of your code.
///
/// #### Callbacks
///
/// The most important principle of the `EventLoopPromise` and `EventLoopFuture` is this: all callbacks registered on
/// an `EventLoopFuture` will execute on the thread corresponding to the event loop that created the `Future`,
/// *regardless* of what thread succeeds or fails the corresponding `EventLoopPromise`.
///
/// This means that if *your code* created the `EventLoopPromise`, you can be extremely confident of what thread the
/// callback will execute on: after all, you held the event loop in hand when you created the `EventLoopPromise`.
/// However, if your code is handed an `EventLoopFuture` or `EventLoopPromise`, and you want to register callbacks
/// on those objects, you cannot be confident that those callbacks will execute on the same `EventLoop` that your
/// code does.
///
/// This presents a problem: how do you ensure thread-safety when registering callbacks on an arbitrary
/// `EventLoopFuture`? The short answer is that when you are holding an `EventLoopFuture`, you can always obtain a
/// new `EventLoopFuture` whose callbacks will execute on your event loop. You do this by calling
/// `EventLoopFuture.hopTo(eventLoop:)`. This function returns a new `EventLoopFuture` whose callbacks are guaranteed
/// to fire on the provided event loop. As an added bonus, `hopTo` will check whether the provided `EventLoopFuture`
/// was already scheduled to dispatch on the event loop in question, and avoid doing any work if that was the case.
///
/// This means that for any `EventLoopFuture` that your code did not create itself (via
/// `EventLoopPromise.futureResult`), use of `hopTo` is **strongly encouraged** to help guarantee thread-safety. It
/// should only be elided when thread-safety is provably not needed.
///
/// The "thread affinity" of `EventLoopFuture`s is critical to writing safe, performant concurrent code without
/// boilerplate. It allows you to avoid needing to write or use locks in your own code, instead using the natural
/// synchronization of the `EventLoop` to manage your thread-safety. In general, if any of your `ChannelHandler`s
/// or `EventLoopFuture` callbacks need to invoke a lock (either directly or in the form of `DispatchQueue`) this
/// should be considered a code smell worth investigating: the `EventLoop`-based synchronization guarantees of
/// `EventLoopFuture` should be sufficient to guarantee thread-safety.
public final class EventLoopFuture<Value> {
// TODO: Provide a tracing facility. It would be nice to be able to set '.debugTrace = true' on any EventLoopFuture or EventLoopPromise and have every subsequent chained EventLoopFuture report the success result or failure error. That would simplify some debugging scenarios.
fileprivate var value: Result<Value, Error>? {
didSet {
_isFulfilled.store(true)
}
}
private let _isFulfilled: UnsafeEmbeddedAtomic<Bool>
/// The `EventLoop` which is tied to the `EventLoopFuture` and is used to notify all registered callbacks.
public let eventLoop: EventLoop
/// Whether this `EventLoopFuture` has been fulfilled. This is a thread-safe
/// computed-property.
internal var isFulfilled: Bool {
return _isFulfilled.load()
}
/// Callbacks that should be run when this `EventLoopFuture<Value>` gets a value.
/// These callbacks may give values to other `EventLoopFuture`s; if that happens,
/// they return any callbacks from those `EventLoopFuture`s so that we can run
/// the entire chain from the top without recursing.
fileprivate var callbacks: CallbackList = CallbackList()
private init(eventLoop: EventLoop, value: Result<Value, Error>?, file: StaticString, line: UInt) {
self.eventLoop = eventLoop
self.value = value
self._isFulfilled = UnsafeEmbeddedAtomic(value: value != nil)
debugOnly {
if let me = eventLoop as? SelectableEventLoop {
me.promiseCreationStoreAdd(future: self, file: file, line: line)
}
}
}
fileprivate convenience init(eventLoop: EventLoop, file: StaticString, line: UInt) {
self.init(eventLoop: eventLoop, value: nil, file: file, line: line)
}
/// A EventLoopFuture<Value> that has already succeeded
convenience init(eventLoop: EventLoop, result: Value, file: StaticString, line: UInt) {
self.init(eventLoop: eventLoop, value: .success(result), file: file, line: line)
}
/// A EventLoopFuture<Value> that has already failed
convenience init(eventLoop: EventLoop, error: Error, file: StaticString, line: UInt) {
self.init(eventLoop: eventLoop, value: .failure(error), file: file, line: line)
}
deinit {
debugOnly {
if let eventLoop = self.eventLoop as? SelectableEventLoop {
let creation = eventLoop.promiseCreationStoreRemove(future: self)
if !isFulfilled {
fatalError("leaking promise created at \(creation)", file: creation.file, line: creation.line)
}
} else {
precondition(isFulfilled, "leaking an unfulfilled Promise")
}
}
self._isFulfilled.destroy()
}
}
extension EventLoopFuture: Equatable {
public static func ==(lhs: EventLoopFuture, rhs: EventLoopFuture) -> Bool {
return lhs === rhs
}
}
// 'flatMap' and 'map' implementations. This is really the key of the entire system.
extension EventLoopFuture {
/// When the current `EventLoopFuture<Value>` is fulfilled, run the provided callback,
/// which will provide a new `EventLoopFuture`.
///
/// This allows you to dynamically dispatch new asynchronous tasks as phases in a
/// longer series of processing steps. Note that you can use the results of the
/// current `EventLoopFuture<Value>` when determining how to dispatch the next operation.
///
/// This works well when you have APIs that already know how to return `EventLoopFuture`s.
/// You can do something with the result of one and just return the next future:
///
/// ```
/// let d1 = networkRequest(args).future()
/// let d2 = d1.flatMap { t -> EventLoopFuture<NewValue> in
/// . . . something with t . . .
/// return netWorkRequest(args)
/// }
/// d2.whenSuccess { u in
/// NSLog("Result of second request: \(u)")
/// }
/// ```
///
/// Note: In a sense, the `EventLoopFuture<NewValue>` is returned before it's created.
///
/// - parameters:
/// - callback: Function that will receive the value of this `EventLoopFuture` and return
/// a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value.
public func flatMap<NewValue>(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Value) -> EventLoopFuture<NewValue>) -> EventLoopFuture<NewValue> {
let next = EventLoopPromise<NewValue>(eventLoop: eventLoop, file: file, line: line)
_whenComplete {
switch self.value! {
case .success(let t):
let futureU = callback(t)
if futureU.eventLoop.inEventLoop {
return futureU._addCallback {
next._setValue(value: futureU.value!)
}
} else {
futureU.cascade(promise: next)
return CallbackList()
}
case .failure(let error):
return next._setValue(value: .failure(error))
}
}
return next.futureResult
}
/// When the current `EventLoopFuture<Value>` is fulfilled, run the provided callback, which
/// performs a synchronous computation and returns a new value of type `NewValue`. The provided
/// callback may optionally `throw`.
///
/// Operations performed in `flatMapThrowing` should not block, or they will block the entire
/// event loop. `flatMapThrowing` is intended for use when you have a data-driven function that
/// performs a simple data transformation that can potentially error.
///
/// If your callback function throws, the returned `EventLoopFuture` will error.
///
/// - parameters:
/// - callback: Function that will receive the value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value.
public func flatMapThrowing<NewValue>(file: StaticString = #file,
line: UInt = #line,
_ callback: @escaping (Value) throws -> NewValue) -> EventLoopFuture<NewValue> {
return self.flatMap(file: file, line: line) { (value: Value) -> EventLoopFuture<NewValue> in
do {
return EventLoopFuture<NewValue>(eventLoop: self.eventLoop, result: try callback(value), file: file, line: line)
} catch {
return EventLoopFuture<NewValue>(eventLoop: self.eventLoop, error: error, file: file, line: line)
}
}
}
/// When the current `EventLoopFuture<Value>` is in an error state, run the provided callback, which
/// may recover from the error and returns a new value of type `Value`. The provided callback may optionally `throw`,
/// in which case the `EventLoopFuture` will be in a failed state with the new thrown error.
///
/// Operations performed in `flatMapErrorThrowing` should not block, or they will block the entire
/// event loop. `flatMapErrorThrowing` is intended for use when you have the ability to synchronously
/// recover from errors.
///
/// If your callback function throws, the returned `EventLoopFuture` will error.
///
/// - parameters:
/// - callback: Function that will receive the error value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value or a rethrown error.
public func flatMapErrorThrowing(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Error) throws -> Value) -> EventLoopFuture<Value> {
return self.flatMapError(file: file, line: line) { value in
do {
return EventLoopFuture(eventLoop: self.eventLoop, result: try callback(value), file: file, line: line)
} catch {
return EventLoopFuture(eventLoop: self.eventLoop, error: error, file: file, line: line)
}
}
}
/// When the current `EventLoopFuture<Value>` is fulfilled, run the provided callback, which
/// performs a synchronous computation and returns a new value of type `NewValue`.
///
/// Operations performed in `map` should not block, or they will block the entire event
/// loop. `map` is intended for use when you have a data-driven function that performs
/// a simple data transformation that can potentially error.
///
/// ```
/// let future1 = eventually()
/// let future2 = future1.map { T -> U in
/// ... stuff ...
/// return u
/// }
/// let future3 = future2.map { U -> V in
/// ... stuff ...
/// return v
/// }
/// ```
///
/// - parameters:
/// - callback: Function that will receive the value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value.
public func map<NewValue>(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Value) -> (NewValue)) -> EventLoopFuture<NewValue> {
if NewValue.self == Value.self && NewValue.self == Void.self {
whenSuccess(callback as! (Value) -> Void)
return self as! EventLoopFuture<NewValue>
} else {
return flatMap { return EventLoopFuture<NewValue>(eventLoop: self.eventLoop, result: callback($0), file: file, line: line) }
}
}
/// When the current `EventLoopFuture<Value>` is in an error state, run the provided callback, which
/// may recover from the error by returning an `EventLoopFuture<NewValue>`. The callback is intended to potentially
/// recover from the error by returning a new `EventLoopFuture` that will eventually contain the recovered
/// result.
///
/// If the callback cannot recover it should return a failed `EventLoopFuture`.
///
/// - parameters:
/// - callback: Function that will receive the error value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the recovered value.
public func flatMapError(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Error) -> EventLoopFuture<Value>) -> EventLoopFuture<Value> {
let next = EventLoopPromise<Value>(eventLoop: eventLoop, file: file, line: line)
_whenComplete {
switch self.value! {
case .success(let t):
return next._setValue(value: .success(t))
case .failure(let e):
let t = callback(e)
if t.eventLoop.inEventLoop {
return t._addCallback {
next._setValue(value: t.value!)
}
} else {
t.cascade(promise: next)
return CallbackList()
}
}
}
return next.futureResult
}
/// When the current `EventLoopFuture<Value>` is in an error state, run the provided callback, which
/// can recover from the error and return a new value of type `Value`. The provided callback may not `throw`,
/// so this function should be used when the error is always recoverable.
///
/// Operations performed in `mapIfError` should not block, or they will block the entire
/// event loop. `mapIfError` is intended for use when you have the ability to synchronously
/// recover from errors.
///
/// - parameters:
/// - callback: Function that will receive the error value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the recovered value.
public func mapIfError(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Error) -> Value) -> EventLoopFuture<Value> {
return flatMapError(file: file, line: line) {
return EventLoopFuture<Value>(eventLoop: self.eventLoop, result: callback($0), file: file, line: line)
}
}
/// Add a callback. If there's already a value, invoke it and return the resulting list of new callback functions.
fileprivate func _addCallback(_ callback: @escaping () -> CallbackList) -> CallbackList {
self.eventLoop.assertInEventLoop()
if value == nil {
callbacks.append(callback)
return CallbackList()
}
return callback()
}
/// Add a callback. If there's already a value, run as much of the chain as we can.
fileprivate func _whenComplete(_ callback: @escaping () -> CallbackList) {
if eventLoop.inEventLoop {
_addCallback(callback)._run()
} else {
eventLoop.execute {
self._addCallback(callback)._run()
}
}
}
fileprivate func _whenCompleteWithValue(_ callback: @escaping (Result<Value, Error>) -> Void) {
_whenComplete {
callback(self.value!)
return CallbackList()
}
}
/// Adds an observer callback to this `EventLoopFuture` that is called when the
/// `EventLoopFuture` has a success result.
///
/// An observer callback cannot return a value, meaning that this function cannot be chained
/// from. If you are attempting to create a computation pipeline, consider `map` or `flatMap`.
/// If you find yourself passing the results from this `EventLoopFuture` to a new `EventLoopPromise`
/// in the body of this function, consider using `cascade` instead.
///
/// - parameters:
/// - callback: The callback that is called with the successful result of the `EventLoopFuture`.
public func whenSuccess(_ callback: @escaping (Value) -> Void) {
_whenComplete {
if case .success(let t) = self.value! {
callback(t)
}
return CallbackList()
}
}
/// Adds an observer callback to this `EventLoopFuture` that is called when the
/// `EventLoopFuture` has a failure result.
///
/// An observer callback cannot return a value, meaning that this function cannot be chained
/// from. If you are attempting to create a computation pipeline, consider `mapIfError` or `flatMapError`.
/// If you find yourself passing the results from this `EventLoopFuture` to a new `EventLoopPromise`
/// in the body of this function, consider using `cascade` instead.
///
/// - parameters:
/// - callback: The callback that is called with the failed result of the `EventLoopFuture`.
public func whenFailure(_ callback: @escaping (Error) -> Void) {
_whenComplete {
if case .failure(let e) = self.value! {
callback(e)
}
return CallbackList()
}
}
/// Adds an observer callback to this `EventLoopFuture` that is called when the
/// `EventLoopFuture` has any result.
///
/// Unlike its friends `whenSuccess` and `whenFailure`, `whenComplete` does not receive the result
/// of the `EventLoopFuture`. This is because its primary purpose is to do the appropriate cleanup
/// of any resources that needed to be kept open until the `EventLoopFuture` had resolved.
///
/// - parameters:
/// - callback: The callback that is called when the `EventLoopFuture` is fulfilled.
public func whenComplete(_ callback: @escaping (Result<Value, Error>) -> Void) {
_whenComplete {
callback(self.value!)
return CallbackList()
}
}
/// Internal: Set the value and return a list of callbacks that should be invoked as a result.
fileprivate func _setValue(value: Result<Value, Error>) -> CallbackList {
self.eventLoop.assertInEventLoop()
if self.value == nil {
self.value = value
let callbacks = self.callbacks
self.callbacks = CallbackList()
return callbacks
}
return CallbackList()
}
}
extension EventLoopFuture {
/// Return a new `EventLoopFuture` that succeeds when this "and" another
/// provided `EventLoopFuture` both succeed. It then provides the pair
/// of results. If either one fails, the combined `EventLoopFuture` will fail with
/// the first error encountered.
public func and<OtherValue>(_ other: EventLoopFuture<OtherValue>,
file: StaticString = #file,
line: UInt = #line) -> EventLoopFuture<(Value, OtherValue)> {
let promise = EventLoopPromise<(Value, OtherValue)>(eventLoop: eventLoop, file: file, line: line)
var tvalue: Value?
var uvalue: OtherValue?
assert(self.eventLoop === promise.futureResult.eventLoop)
_whenComplete { () -> CallbackList in
switch self.value! {
case .failure(let error):
return promise._setValue(value: .failure(error))
case .success(let t):
if let u = uvalue {
return promise._setValue(value: .success((t, u)))
} else {
tvalue = t
}
}
return CallbackList()
}
let hopOver = other.hopTo(eventLoop: self.eventLoop)
hopOver._whenComplete { () -> CallbackList in
self.eventLoop.assertInEventLoop()
switch other.value! {
case .failure(let error):
return promise._setValue(value: .failure(error))
case .success(let u):
if let t = tvalue {
return promise._setValue(value: .success((t, u)))
} else {
uvalue = u
}
}
return CallbackList()
}
return promise.futureResult
}
/// Return a new EventLoopFuture that contains this "and" another value.
/// This is just syntactic sugar for `future.and(loop.newSucceedFuture<NewValue>(result: result))`.
public func and<OtherValue>(value: OtherValue,
file: StaticString = #file,
line: UInt = #line) -> EventLoopFuture<(Value, OtherValue)> {
return and(EventLoopFuture<OtherValue>(eventLoop: self.eventLoop, result: value, file: file, line: line))
}
}
extension EventLoopFuture {
/// Fulfill the given `EventLoopPromise` with the results from this `EventLoopFuture`.
///
/// This is useful when allowing users to provide promises for you to fulfill, but
/// when you are calling functions that return their own promises. They allow you to
/// tidy up your computational pipelines. For example:
///
/// ```
/// doWork().flatMap {
/// doMoreWork($0)
/// }.flatMap {
/// doYetMoreWork($0)
/// }.flatMapError {
/// maybeRecoverFromError($0)
/// }.map {
/// transformData($0)
/// }.cascade(promise: userPromise)
/// ```
///
/// - parameters:
/// - promise: The `EventLoopPromise` to fulfill with the results of this future.
public func cascade(promise: EventLoopPromise<Value>?) {
guard let promise = promise else { return }
_whenCompleteWithValue { v in
switch v {
case .failure(let err):
promise.fail(err)
case .success(let value):
promise.succeed(value)
}
}
}
/// Fulfill the given `EventLoopPromise` with the error result from this `EventLoopFuture`,
/// if one exists.
///
/// This is an alternative variant of `cascade` that allows you to potentially return early failures in
/// error cases, while passing the user `EventLoopPromise` onwards. In general, however, `cascade` is
/// more broadly useful.
///
/// - parameters:
/// - promise: The `EventLoopPromise` to fulfill with the results of this future.
public func cascadeFailure<NewValue>(promise: EventLoopPromise<NewValue>?) {
guard let promise = promise else { return }
self.whenFailure { err in
promise.fail(err)
}
}
}
extension EventLoopFuture {
/// Wait for the resolution of this `EventLoopFuture` by blocking the current thread until it
/// resolves.
///
/// If the `EventLoopFuture` resolves with a value, that value is returned from `wait()`. If
/// the `EventLoopFuture` resolves with an error, that error will be thrown instead.
/// `wait()` will block whatever thread it is called on, so it must not be called on event loop
/// threads: it is primarily useful for testing, or for building interfaces between blocking
/// and non-blocking code.
///
/// - returns: The value of the `EventLoopFuture` when it completes.
/// - throws: The error value of the `EventLoopFuture` if it errors.
public func wait(file: StaticString = #file, line: UInt = #line) throws -> Value {
if !(self.eventLoop is EmbeddedEventLoop) {
let explainer: () -> String = { """
BUG DETECTED: wait() must not be called when on an EventLoop.
Calling wait() on any EventLoop can lead to
- deadlocks
- stalling processing of other connections (Channels) that are handled on the EventLoop that wait was called on
Further information:
- current eventLoop: \(MultiThreadedEventLoopGroup.currentEventLoop.debugDescription)
- event loop associated to future: \(self.eventLoop)
"""
}
precondition(!eventLoop.inEventLoop, explainer(), file: file, line: line)
precondition(MultiThreadedEventLoopGroup.currentEventLoop == nil, explainer(), file: file, line: line)
}
var v: Result<Value, Error>? = nil
let lock = ConditionLock(value: 0)
_whenComplete { () -> CallbackList in
lock.lock()
v = self.value
lock.unlock(withValue: 1)
return CallbackList()
}
lock.lock(whenValue: 1)
lock.unlock()
switch(v!) {
case .success(let result):
return result
case .failure(let error):
throw error
}
}
}
extension EventLoopFuture {
/// Returns a new `EventLoopFuture` that fires only when this `EventLoopFuture` and
/// all the provided `futures` complete. It then provides the result of folding the value of this
/// `EventLoopFuture` with the values of all the provided `futures`.
///
/// This function is suited when you have APIs that already know how to return `EventLoopFuture`s.
///
/// The returned `EventLoopFuture` will fail as soon as the a failure is encountered in any of the
/// `futures` (or in this one). However, the failure will not occur until all preceding
/// `EventLoopFutures` have completed. At the point the failure is encountered, all subsequent
/// `EventLoopFuture` objects will no longer be waited for. This function therefore fails fast: once
/// a failure is encountered, it will immediately fail the overall EventLoopFuture.
///
/// - parameters:
/// - futures: An array of `EventLoopFuture<NewValue>` to wait for.
/// - with: A function that will be used to fold the values of two `EventLoopFuture`s and return a new value wrapped in an `EventLoopFuture`.
/// - returns: A new `EventLoopFuture` with the folded value whose callbacks run on `self.eventLoop`.
public func fold<OtherValue>(_ futures: [EventLoopFuture<OtherValue>],
with combiningFunction: @escaping (Value, OtherValue) -> EventLoopFuture<Value>) -> EventLoopFuture<Value> {
let body = futures.reduce(self) { (f1: EventLoopFuture<Value>, f2: EventLoopFuture<OtherValue>) -> EventLoopFuture<Value> in
let newFuture = f1.and(f2).flatMap { (args: (Value, OtherValue)) -> EventLoopFuture<Value> in
let (f1Value, f2Value) = args
self.eventLoop.assertInEventLoop()
return combiningFunction(f1Value, f2Value)
}
assert(newFuture.eventLoop === self.eventLoop)
return newFuture
}
return body
}
}
extension EventLoopFuture {
/// Returns a new `EventLoopFuture` that fires only when all the provided futures complete.
///
/// This extension is only available when you have a collection of `EventLoopFuture`s that do not provide
/// result data: that is, they are completion notifiers. In this case, you can wait for all of them. The
/// returned `EventLoopFuture` will fail as soon as any of the futures fails: otherwise, it will succeed
/// only when all of them do.
///
/// - parameters:
/// - futures: An array of `EventLoopFuture<Void>` to wait for.
/// - eventLoop: The `EventLoop` on which the new `EventLoopFuture` callbacks will fire.
/// - returns: A new `EventLoopFuture`.
public static func andAll(_ futures: [EventLoopFuture<Void>], eventLoop: EventLoop) -> EventLoopFuture<Void> {
let body = EventLoopFuture<Void>.reduce((), futures, eventLoop: eventLoop) { (_: (), _: ()) in }
return body
}
/// Returns a new `EventLoopFuture` that fires only when all the provided futures complete.
/// The new `EventLoopFuture` contains the result of reducing the `initialResult` with the
/// values of the `[EventLoopFuture<NewValue>]`.
///
/// This function makes copies of the result for each EventLoopFuture, for a version which avoids
/// making copies, check out `reduce<NewValue>(into:)`.
///
/// The returned `EventLoopFuture` will fail as soon as a failure is encountered in any of the
/// `futures`. However, the failure will not occur until all preceding
/// `EventLoopFutures` have completed. At the point the failure is encountered, all subsequent
/// `EventLoopFuture` objects will no longer be waited for. This function therefore fails fast: once
/// a failure is encountered, it will immediately fail the overall `EventLoopFuture`.
///
/// - parameters:
/// - initialResult: An initial result to begin the reduction.
/// - futures: An array of `EventLoopFuture` to wait for.
/// - eventLoop: The `EventLoop` on which the new `EventLoopFuture` callbacks will fire.
/// - nextPartialResult: The bifunction used to produce partial results.
/// - returns: A new `EventLoopFuture` with the reduced value.
public static func reduce<InputValue>(_ initialResult: Value,
_ futures: [EventLoopFuture<InputValue>],
eventLoop: EventLoop,
_ nextPartialResult: @escaping (Value, InputValue) -> Value) -> EventLoopFuture<Value> {
let f0 = eventLoop.makeSucceededFuture(initialResult)
let body = f0.fold(futures) { (t: Value, u: InputValue) -> EventLoopFuture<Value> in
eventLoop.makeSucceededFuture(nextPartialResult(t, u))
}
return body
}
/// Returns a new `EventLoopFuture` that fires only when all the provided futures complete.
/// The new `EventLoopFuture` contains the result of combining the `initialResult` with the
/// values of the `[EventLoopFuture<NewValue>]`. This function is analogous to the standard library's
/// `reduce(into:)`, which does not make copies of the result type for each `EventLoopFuture`.
///
/// The returned `EventLoopFuture` will fail as soon as a failure is encountered in any of the
/// `futures`. However, the failure will not occur until all preceding
/// `EventLoopFutures` have completed. At the point the failure is encountered, all subsequent
/// `EventLoopFuture` objects will no longer be waited for. This function therefore fails fast: once
/// a failure is encountered, it will immediately fail the overall `EventLoopFuture`.
///
/// - parameters:
/// - initialResult: An initial result to begin the reduction.
/// - futures: An array of `EventLoopFuture` to wait for.
/// - eventLoop: The `EventLoop` on which the new `EventLoopFuture` callbacks will fire.
/// - updateAccumulatingResult: The bifunction used to combine partialResults with new elements.
/// - returns: A new `EventLoopFuture` with the combined value.
public static func reduce<InputValue>(into initialResult: Value,
_ futures: [EventLoopFuture<InputValue>],
eventLoop: EventLoop,
_ updateAccumulatingResult: @escaping (inout Value, InputValue) -> Void) -> EventLoopFuture<Value> {
let p0 = eventLoop.makePromise(of: Value.self)
var result: Value = initialResult
let f0 = eventLoop.makeSucceededFuture(())
let future = f0.fold(futures) { (_: (), value: InputValue) -> EventLoopFuture<Void> in
eventLoop.assertInEventLoop()
updateAccumulatingResult(&result, value)
return eventLoop.makeSucceededFuture(())
}
future.whenSuccess {
eventLoop.assertInEventLoop()
p0.succeed(result)
}
future.whenFailure { (error) in
eventLoop.assertInEventLoop()
p0.fail(error)
}
return p0.futureResult
}
}
public extension EventLoopFuture {
/// Returns an `EventLoopFuture` that fires when this future completes, but executes its callbacks on the
/// target event loop instead of the original one.
///
/// It is common to want to "hop" event loops when you arrange some work: for example, you're closing one channel
/// from another, and want to hop back when the close completes. This method lets you spell that requirement
/// succinctly. It also contains an optimisation for the case when the loop you're hopping *from* is the same as
/// the one you're hopping *to*, allowing you to avoid doing allocations in that case.
///
/// - parameters:
/// - target: The `EventLoop` that the returned `EventLoopFuture` will run on.
/// - returns: An `EventLoopFuture` whose callbacks run on `target` instead of the original loop.
func hopTo(eventLoop target: EventLoop) -> EventLoopFuture<Value> {
if target === self.eventLoop {
// We're already on that event loop, nothing to do here. Save an allocation.
return self
}
let hoppingPromise = target.makePromise(of: Value.self)
self.cascade(promise: hoppingPromise)
return hoppingPromise.futureResult
}
}
/// Execute the given function and synchronously complete the given `EventLoopPromise` (if not `nil`).
func executeAndComplete<Value>(_ promise: EventLoopPromise<Value>?, _ body: () throws -> Value) {
do {
let result = try body()
promise?.succeed(result)
} catch let e {
promise?.fail(e)
}
}