ExecutorProtocol

public protocol ExecutorProtocol : AnyObject

A protocol that defines objects that execute futures.

Executors provide the context in which futures execute. This context may be the current or a background thread, a Dispatch queue, a run loop, etc. Executors typically enqueue submitted futures in a ready to run queue and immediately return to the caller. This queue is later on consumed by the executor in its execution context, driving futures to completion by polling them. The polling algorithm is roughly:

var context = self.makeContext()
while var future = self.readyToRunQueue.pop() {
    if future.poll(&context).isReady {
        self.drop(future)
    } else {
        self.pendingFutures.push(future)
    }
}

Futures that are ready to run are polled by the executor. If the future returns that it completed (see Poll), it is let go by the executor, which causes the future and associated resources to be released. Otherwise, it is added into a pending futures registry. Before returning, the future registers the context’s waker (see Context and WakerProtocol) to receive a notification when the external event the future is waiting on fires. As a result of the notification, the future is added back into the ready to run queue and the process repeats until the future signifies completion.

Futures are submitted for execution via the trySubmit(_:) method of an executor. Executors guarantee that submitted futures will be executed asynchronously. In other words, trySubmit(_:) merely schedules the future to be executed at some point in the future and returns to the caller immediately. This is an important invariant that custom implementations must also maintain, since it removes all concerns about reentrancy.

Executors may deny to receive the future, in which case trySubmit(_:) returns an appropriate error of type Failure. This can be the case when the executor is shutting down or is at capacity and needs to provide backpressure. Executors that never fail, declare their Failure type as Never.

This protocol makes no guarantees on the safety of concurrent calls to trySubmit(_:). Each executor implementation is free to declare support for concurrent submissions, documenting the fact accordingly. This ambiguity is not a problem in practice since you typically use concrete executor types so you always know the specifics. The built-in QueueExecutor is an executor that supports concurrent submissions.

  • The type of error the executor may fail with when submitting futures.

    Declaration

    Swift

    associatedtype Failure : Error
  • The maximum number of futures the executor can track concurrently.

    If the executor is unbounded, this property returns Int.max; see isUnbounded.

    Declaration

    Swift

    var capacity: Int { get }
  • trySubmit(_:) Default implementation

    Submits a future to be executed by this executor.

    The executor may deny to receive the future, in which case the returned result will contain an appropriate error. Typical cases where submission may fail is when the executor is shutting down or is at capacity and needs to provide backpressure. The latter case is expected to be a transient state which the executor will recover from and submission may be retried.

    Default Implementation

    Submits a stream to be executed by this executor.

    The executor may deny to receive the stream, in which case the returned result will contain an appropriate error. Typical cases where submission may fail is when the executor is shutting down or is at capacity and needs to provide backpressure. The latter case is expected to be a transient state which the executor will recover from and submission may be retried.

    Use this method with care as it may trap at runtime. This method is convenience for:

    executor.trySubmit(stream.ignoreOutput())
    

    Declaration

    Swift

    func trySubmit<F>(_ future: F) -> Result<Void, Failure> where F : FutureProtocol, F.Output == Void
  • isUnbounded Extension method

    Returns a boolean denoting whether the executor can accept an unlimited number of futures.

    Declaration

    Swift

    @inlinable
    public var isUnbounded: Bool { get }
  • submit(_:) Extension method

    Submits a future to be executed by this executor.

    Use this method with care as it may trap at runtime. This method is convenience for:

    try! executor.trySubmit(future).get()
    

    Declaration

    Swift

    @inlinable
    public func submit<F>(_ future: F) where F : FutureProtocol, F.Output == Void
  • submit(_:) Extension method

    Submits a stream to be executed by this executor.

    Use this method with care as it may trap at runtime. This method is convenience for:

    try! executor.trySubmit(stream.ignoreOutput()).get()
    

    Declaration

    Swift

    @inlinable
    public func submit<S>(_ stream: S) where S : StreamProtocol, S.Output == Void
  • trySpawn(_:) Extension method

    Submits a future into the executor and returns a handle that can be used to extract its result or cancel its execution.

    The handle is a cancellable future itself and can be safely sent and waited on any thread or submitted into another executor; see Task.

    Declaration

    Swift

    @inlinable
    public func trySpawn<F>(_ future: F) -> Result<Task<F.Output>, Failure> where F : FutureProtocol
  • spawn(_:) Extension method

    Submits a future into the executor and returns a handle that can be used to extract its result or cancel its execution.

    The handle is a cancellable future itself and can be safely sent and waited on any thread or submitted into another executor; see Task.

    Use this method with care as it may trap at runtime. This method is convenience for:

    try! executor.trySpawn(future).get()
    

    Declaration

    Swift

    @inlinable
    public func spawn<F>(_ future: F) -> Task<F.Output> where F : FutureProtocol