2694 lines
88 KiB
C++
2694 lines
88 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <folly/Optional.h>
|
|
#include <folly/Portability.h>
|
|
#include <folly/ScopeGuard.h>
|
|
#include <folly/Try.h>
|
|
#include <folly/Unit.h>
|
|
#include <folly/Utility.h>
|
|
#include <folly/executors/DrivableExecutor.h>
|
|
#include <folly/executors/TimedDrivableExecutor.h>
|
|
#include <folly/functional/Invoke.h>
|
|
#include <folly/futures/Portability.h>
|
|
#include <folly/futures/Promise.h>
|
|
#include <folly/futures/detail/Types.h>
|
|
#include <folly/lang/Exception.h>
|
|
|
|
#if FOLLY_HAS_COROUTINES
|
|
#include <folly/experimental/coro/Traits.h>
|
|
#include <experimental/coroutine>
|
|
#endif
|
|
|
|
// boring predeclarations and details
|
|
#include <folly/futures/Future-pre.h>
|
|
|
|
namespace folly {
|
|
|
|
class FOLLY_EXPORT FutureException : public std::logic_error {
|
|
public:
|
|
using std::logic_error::logic_error;
|
|
};
|
|
|
|
class FOLLY_EXPORT FutureInvalid : public FutureException {
|
|
public:
|
|
FutureInvalid() : FutureException("Future invalid") {}
|
|
};
|
|
|
|
/// At most one continuation may be attached to any given Future.
|
|
///
|
|
/// If a continuation is attached to a future to which another continuation has
|
|
/// already been attached, then an instance of FutureAlreadyContinued will be
|
|
/// thrown instead.
|
|
class FOLLY_EXPORT FutureAlreadyContinued : public FutureException {
|
|
public:
|
|
FutureAlreadyContinued() : FutureException("Future already continued") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT FutureNotReady : public FutureException {
|
|
public:
|
|
FutureNotReady() : FutureException("Future not ready") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT FutureCancellation : public FutureException {
|
|
public:
|
|
FutureCancellation() : FutureException("Future was cancelled") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT FutureTimeout : public FutureException {
|
|
public:
|
|
FutureTimeout() : FutureException("Timed out") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT FuturePredicateDoesNotObtain : public FutureException {
|
|
public:
|
|
FuturePredicateDoesNotObtain()
|
|
: FutureException("Predicate does not obtain") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT FutureNoTimekeeper : public FutureException {
|
|
public:
|
|
FutureNoTimekeeper() : FutureException("No timekeeper available") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT FutureNoExecutor : public FutureException {
|
|
public:
|
|
FutureNoExecutor() : FutureException("No executor provided to via") {}
|
|
};
|
|
|
|
template <class T>
|
|
class Future;
|
|
|
|
template <class T>
|
|
class SemiFuture;
|
|
|
|
template <class T>
|
|
class FutureSplitter;
|
|
|
|
#if FOLLY_FUTURE_USING_FIBER
|
|
|
|
namespace fibers {
|
|
class Baton;
|
|
}
|
|
|
|
#endif
|
|
|
|
namespace futures {
|
|
namespace detail {
|
|
template <class T>
|
|
class FutureBase {
|
|
protected:
|
|
using Core = futures::detail::Core<T>;
|
|
using CoreCallback = typename Core::Callback;
|
|
|
|
public:
|
|
typedef T value_type;
|
|
|
|
/// Construct from a value (perfect forwarding)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `hasValue() == true`
|
|
template <
|
|
class T2 = T,
|
|
typename = typename std::enable_if<
|
|
!isFuture<typename std::decay<T2>::type>::value &&
|
|
!isSemiFuture<typename std::decay<T2>::type>::value &&
|
|
std::is_constructible<Try<T>, T2>::value>::type>
|
|
/* implicit */ FutureBase(T2&& val);
|
|
|
|
/// Construct a (logical) FutureBase-of-void.
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `hasValue() == true`
|
|
template <class T2 = T>
|
|
/* implicit */ FutureBase(
|
|
typename std::enable_if<std::is_same<Unit, T2>::value>::type*);
|
|
|
|
template <
|
|
class... Args,
|
|
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
|
|
type = 0>
|
|
explicit FutureBase(in_place_t, Args&&... args)
|
|
: core_(Core::make(in_place, std::forward<Args>(args)...)) {}
|
|
|
|
FutureBase(FutureBase<T> const&) = delete;
|
|
FutureBase(SemiFuture<T>&&) noexcept;
|
|
FutureBase(Future<T>&&) noexcept;
|
|
|
|
// not copyable
|
|
FutureBase(Future<T> const&) = delete;
|
|
FutureBase(SemiFuture<T> const&) = delete;
|
|
|
|
~FutureBase();
|
|
|
|
/// true if this has a shared state;
|
|
/// false if this has been either moved-out or created without a shared state.
|
|
bool valid() const noexcept {
|
|
return core_ != nullptr;
|
|
}
|
|
|
|
/// Returns a reference to the result value if it is ready, with a reference
|
|
/// category and const-qualification like those of the future.
|
|
///
|
|
/// Does not `wait()`; see `get()` for that.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
/// - `isReady() == true` (else throws FutureNotReady)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - If an exception has been captured (i.e., if `hasException() == true`),
|
|
/// throws that exception.
|
|
/// - This call does not mutate the future's value.
|
|
/// - However calling code may mutate that value (including moving it out by
|
|
/// move-constructing or move-assigning another value from it), for
|
|
/// example, via the `&` or the `&&` overloads or via casts.
|
|
T& value() &;
|
|
T const& value() const&;
|
|
T&& value() &&;
|
|
T const&& value() const&&;
|
|
|
|
/// Returns a reference to the result's Try if it is ready, with a reference
|
|
/// category and const-qualification like those of the future.
|
|
///
|
|
/// Does not `wait()`; see `get()` for that.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
/// - `isReady() == true` (else throws FutureNotReady)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - This call does not mutate the future's result.
|
|
/// - However calling code may mutate that result (including moving it out by
|
|
/// move-constructing or move-assigning another result from it), for
|
|
/// example, via the `&` or the `&&` overloads or via casts.
|
|
Try<T>& result() &;
|
|
Try<T> const& result() const&;
|
|
Try<T>&& result() &&;
|
|
Try<T> const&& result() const&&;
|
|
|
|
/// True when the result (or exception) is ready; see value(), result(), etc.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
bool isReady() const;
|
|
|
|
/// True if the result is a value (not an exception) on a future for which
|
|
/// isReady returns true.
|
|
///
|
|
/// Equivalent to result().hasValue()
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
/// - `isReady() == true` (else throws FutureNotReady)
|
|
bool hasValue() const;
|
|
|
|
/// True if the result is an exception (not a value) on a future for which
|
|
/// isReady returns true.
|
|
///
|
|
/// Equivalent to result().hasException()
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
/// - `isReady() == true` (else throws FutureNotReady)
|
|
bool hasException() const;
|
|
|
|
/// Returns either an Optional holding the result or an empty Optional
|
|
/// depending on whether or not (respectively) the promise has been
|
|
/// fulfilled (i.e., `isReady() == true`).
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (note however that this moves-out the result when
|
|
/// it returns a populated `Try<T>`, which effects any subsequent use of
|
|
/// that result, e.g., `poll()`, `result()`, `value()`, `get()`, etc.)
|
|
Optional<Try<T>> poll();
|
|
|
|
/// This is not the method you're looking for.
|
|
///
|
|
/// This needs to be public because it's used by make* and when*, and it's
|
|
/// not worth listing all those and their fancy template signatures as
|
|
/// friends. But it's not for public consumption.
|
|
void setCallback_(
|
|
CoreCallback&& func,
|
|
InlineContinuation = InlineContinuation::forbid);
|
|
|
|
/// Provides a threadsafe back-channel so the consumer's thread can send an
|
|
/// interrupt-object to the producer's thread.
|
|
///
|
|
/// If the promise-holder registers an interrupt-handler and consumer thread
|
|
/// raises an interrupt early enough (details below), the promise-holder
|
|
/// will typically halt its work, fulfilling the future with an exception
|
|
/// or some special non-exception value.
|
|
///
|
|
/// However this interrupt request is voluntary, asynchronous, & advisory:
|
|
///
|
|
/// - Voluntary: the producer will see the interrupt only if the producer uses
|
|
/// a `Promise` object and registers an interrupt-handler;
|
|
/// see `Promise::setInterruptHandler()`.
|
|
/// - Asynchronous: the producer will see the interrupt only if `raise()` is
|
|
/// called before (or possibly shortly after) the producer is done producing
|
|
/// its result, which is asynchronous with respect to the call to `raise()`.
|
|
/// - Advisory: the producer's interrupt-handler can do whatever it wants,
|
|
/// including ignore the interrupt or perform some action other than halting
|
|
/// its producer-work.
|
|
///
|
|
/// Guidelines:
|
|
///
|
|
/// - It is ideal if the promise-holder can both halt its work and fulfill the
|
|
/// promise early, typically with the same exception that was delivered to
|
|
/// the promise-holder in the form of an interrupt.
|
|
/// - If the promise-holder does not do this, and if it holds the promise
|
|
/// alive for a long time, then the whole continuation chain will not be
|
|
/// invoked and the whole future chain will be kept alive for that long time
|
|
/// as well.
|
|
/// - It is also ideal if the promise-holder can invalidate the promise.
|
|
/// - The promise-holder must also track whether it has set a result in the
|
|
/// interrupt handler so that it does not attempt to do so outside the
|
|
/// interrupt handler, and must track whether it has set a result in its
|
|
/// normal flow so that it does not attempt to do so in the interrupt
|
|
/// handler, since setting a result twice is an error. Because the interrupt
|
|
/// handler can be invoked in some other thread, this tracking may have to
|
|
/// be done with some form of concurrency control.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - has no visible effect if `raise()` was previously called on `this` or
|
|
/// any other Future/SemiFuture that uses the same shared state as `this`.
|
|
/// - has no visible effect if the producer never (either in the past or in
|
|
/// the future) registers an interrupt-handler.
|
|
/// - has no visible effect if the producer fulfills its promise (sets the
|
|
/// result) before (or possibly also shortly after) receiving the interrupt.
|
|
/// - otherwise the promise-holder's interrupt-handler is called, passing the
|
|
/// exception (within an `exception_wrapper`).
|
|
///
|
|
/// The specific thread used to invoke the producer's interrupt-handler (if
|
|
/// it is called at all) depends on timing:
|
|
///
|
|
/// - if the interrupt-handler is registered prior to `raise()` (or possibly
|
|
/// concurrently within the call to `raise()`), the interrupt-handler will
|
|
/// be executed using this current thread within the call to `raise()`.
|
|
/// - if the interrupt-handler is registered after `raise()` (and possibly
|
|
/// concurrently within the call to `raise()`), the interrupt-handler will
|
|
/// be executed using the producer's thread within the call to
|
|
/// `Promise::setInterruptHandler()`.
|
|
///
|
|
/// Synchronizes between `raise()` (in the consumer's thread)
|
|
/// and `Promise::setInterruptHandler()` (in the producer's thread).
|
|
void raise(exception_wrapper interrupt);
|
|
|
|
/// Raises the specified exception-interrupt.
|
|
/// See `raise(exception_wrapper)` for details.
|
|
template <class E>
|
|
void raise(E&& exception) {
|
|
raise(make_exception_wrapper<typename std::remove_reference<E>::type>(
|
|
std::forward<E>(exception)));
|
|
}
|
|
|
|
/// Raises a FutureCancellation interrupt.
|
|
/// See `raise(exception_wrapper)` for details.
|
|
void cancel() {
|
|
raise(FutureCancellation());
|
|
}
|
|
|
|
protected:
|
|
friend class Promise<T>;
|
|
template <class>
|
|
friend class SemiFuture;
|
|
template <class>
|
|
friend class Future;
|
|
|
|
// Throws FutureInvalid if there is no shared state object; else returns it
|
|
// by ref.
|
|
//
|
|
// Implementation methods should usually use this instead of `this->core_`.
|
|
// The latter should be used only when you need the possibly-null pointer.
|
|
Core& getCore() {
|
|
return getCoreImpl(*this);
|
|
}
|
|
Core const& getCore() const {
|
|
return getCoreImpl(*this);
|
|
}
|
|
|
|
template <typename Self>
|
|
static decltype(auto) getCoreImpl(Self& self) {
|
|
if (!self.core_) {
|
|
throw_exception<FutureInvalid>();
|
|
}
|
|
return *self.core_;
|
|
}
|
|
|
|
Try<T>& getCoreTryChecked() {
|
|
return getCoreTryChecked(*this);
|
|
}
|
|
Try<T> const& getCoreTryChecked() const {
|
|
return getCoreTryChecked(*this);
|
|
}
|
|
|
|
template <typename Self>
|
|
static decltype(auto) getCoreTryChecked(Self& self) {
|
|
auto& core = self.getCore();
|
|
if (!core.hasResult()) {
|
|
throw_exception<FutureNotReady>();
|
|
}
|
|
return core.getTry();
|
|
}
|
|
|
|
// shared core state object
|
|
// usually you should use `getCore()` instead of directly accessing `core_`.
|
|
Core* core_;
|
|
|
|
explicit FutureBase(Core* obj) : core_(obj) {}
|
|
|
|
explicit FutureBase(futures::detail::EmptyConstruct) noexcept;
|
|
|
|
void detach();
|
|
|
|
void throwIfInvalid() const;
|
|
void throwIfContinued() const;
|
|
|
|
void assign(FutureBase<T>&& other) noexcept;
|
|
|
|
Executor* getExecutor() const {
|
|
return getCore().getExecutor();
|
|
}
|
|
|
|
DeferredExecutor* getDeferredExecutor() const {
|
|
return getCore().getDeferredExecutor();
|
|
}
|
|
|
|
// Sets the Executor within the Core state object of `this`.
|
|
// Must be called either before attaching a callback or after the callback
|
|
// has already been invoked, but not concurrently with anything which might
|
|
// trigger invocation of the callback.
|
|
void setExecutor(futures::detail::KeepAliveOrDeferred x) {
|
|
getCore().setExecutor(std::move(x));
|
|
}
|
|
|
|
// Variant: returns a value
|
|
// e.g. f.thenTry([](Try<T> t){ return t.value(); });
|
|
template <typename F, typename R>
|
|
typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
|
|
thenImplementation(F&& func, R, InlineContinuation);
|
|
|
|
// Variant: returns a Future
|
|
// e.g. f.thenTry([](Try<T> t){ return makeFuture<T>(t); });
|
|
template <typename F, typename R>
|
|
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
|
|
thenImplementation(F&& func, R, InlineContinuation);
|
|
};
|
|
template <class T>
|
|
Future<T> convertFuture(SemiFuture<T>&& sf, const Future<T>& f);
|
|
|
|
class DeferredExecutor;
|
|
|
|
template <typename T>
|
|
DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future);
|
|
|
|
template <typename T>
|
|
futures::detail::DeferredWrapper stealDeferredExecutor(SemiFuture<T>& future);
|
|
} // namespace detail
|
|
|
|
template <class T>
|
|
void detachOn(folly::Executor::KeepAlive<> exec, folly::SemiFuture<T>&& fut);
|
|
|
|
template <class T>
|
|
void detachOnGlobalCPUExecutor(folly::SemiFuture<T>&& fut);
|
|
} // namespace futures
|
|
|
|
/// The interface (along with Future) for the consumer-side of a
|
|
/// producer/consumer pair.
|
|
///
|
|
/// Future vs. SemiFuture:
|
|
///
|
|
/// - The consumer-side should generally start with a SemiFuture, not a Future.
|
|
/// - Example, when a library creates and returns a future, it should usually
|
|
/// return a `SemiFuture`, not a Future.
|
|
/// - Reason: so the thread policy for continuations (`.thenValue`, etc.) can be
|
|
/// specified by the library's caller (using `.via()`).
|
|
/// - A SemiFuture is converted to a Future using `.via()`.
|
|
/// - Use `makePromiseContract()` when creating both a Promise and an associated
|
|
/// SemiFuture/Future.
|
|
///
|
|
/// When practical, prefer SemiFuture/Future's nonblocking style/pattern:
|
|
///
|
|
/// - the nonblocking style uses continuations, e.g., `.thenValue`, etc.; the
|
|
/// continuations are deferred until the result is available.
|
|
/// - the blocking style blocks until complete, e.g., `.wait()`, `.get()`, etc.
|
|
/// - the two styles cannot be mixed within the same future; use one or the
|
|
/// other.
|
|
///
|
|
/// SemiFuture/Future also provide a back-channel so an interrupt can
|
|
/// be sent from consumer to producer; see SemiFuture/Future's `raise()`
|
|
/// and Promise's `setInterruptHandler()`.
|
|
///
|
|
/// The consumer-side SemiFuture/Future objects should generally be accessed
|
|
/// via a single thread. That thread is referred to as the 'consumer thread.'
|
|
template <class T>
|
|
class SemiFuture : private futures::detail::FutureBase<T> {
|
|
private:
|
|
using Base = futures::detail::FutureBase<T>;
|
|
using DeferredExecutor = futures::detail::DeferredExecutor;
|
|
using TimePoint = std::chrono::system_clock::time_point;
|
|
|
|
public:
|
|
~SemiFuture();
|
|
|
|
/// Creates/returns an invalid SemiFuture, that is, one with no shared state.
|
|
///
|
|
/// Postcondition:
|
|
///
|
|
/// - `RESULT.valid() == false`
|
|
static SemiFuture<T> makeEmpty();
|
|
|
|
/// Type of the value that the producer, when successful, produces.
|
|
using typename Base::value_type;
|
|
|
|
/// Construct a SemiFuture from a value (perfect forwarding)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `hasValue() == true`
|
|
/// - `hasException() == false`
|
|
/// - `value()`, `get()`, `result()` will return the forwarded `T`
|
|
template <
|
|
class T2 = T,
|
|
typename = typename std::enable_if<
|
|
!isFuture<typename std::decay<T2>::type>::value &&
|
|
!isSemiFuture<typename std::decay<T2>::type>::value &&
|
|
std::is_constructible<Try<T>, T2>::value>::type>
|
|
/* implicit */ SemiFuture(T2&& val) : Base(std::forward<T2>(val)) {}
|
|
|
|
/// Construct a (logical) SemiFuture-of-void.
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `hasValue() == true`
|
|
template <class T2 = T>
|
|
/* implicit */ SemiFuture(
|
|
typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr)
|
|
: Base(p) {}
|
|
|
|
/// Construct a SemiFuture from a `T` constructed from `args`
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `hasValue() == true`
|
|
/// - `hasException() == false`
|
|
/// - `value()`, `get()`, `result()` will return the newly constructed `T`
|
|
template <
|
|
class... Args,
|
|
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
|
|
type = 0>
|
|
explicit SemiFuture(in_place_t, Args&&... args)
|
|
: Base(in_place, std::forward<Args>(args)...) {}
|
|
|
|
SemiFuture(SemiFuture<T> const&) = delete;
|
|
// movable
|
|
SemiFuture(SemiFuture<T>&&) noexcept;
|
|
// safe move-constructabilty from Future
|
|
/* implicit */ SemiFuture(Future<T>&&) noexcept;
|
|
|
|
using Base::cancel;
|
|
using Base::hasException;
|
|
using Base::hasValue;
|
|
using Base::isReady;
|
|
using Base::poll;
|
|
using Base::raise;
|
|
using Base::result;
|
|
using Base::setCallback_;
|
|
using Base::valid;
|
|
using Base::value;
|
|
|
|
SemiFuture& operator=(SemiFuture const&) = delete;
|
|
SemiFuture& operator=(SemiFuture&&) noexcept;
|
|
SemiFuture& operator=(Future<T>&&) noexcept;
|
|
|
|
/// Blocks until the promise is fulfilled, either by value (which is returned)
|
|
/// or exception (which is thrown).
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
/// - must not have a continuation, e.g., via `.thenValue()` or similar
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
T get() &&;
|
|
|
|
/// Blocks until the semifuture is fulfilled, or until `dur` elapses. Returns
|
|
/// the value (moved-out), or throws the exception (which might be a
|
|
/// FutureTimeout exception).
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
T get(HighResDuration dur) &&;
|
|
|
|
/// Blocks until the future is fulfilled. Returns the Try of the result
|
|
/// (moved-out).
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
Try<T> getTry() &&;
|
|
|
|
/// Blocks until the future is fulfilled, or until `dur` elapses.
|
|
/// Returns the Try of the result (moved-out), or throws FutureTimeout
|
|
/// exception.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
Try<T> getTry(HighResDuration dur) &&;
|
|
|
|
/// Blocks the caller's thread until this Future `isReady()`, i.e., until the
|
|
/// asynchronous producer has stored a result or exception.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `&RESULT == this`
|
|
SemiFuture<T>& wait() &;
|
|
|
|
/// Blocks the caller's thread until this Future `isReady()`, i.e., until the
|
|
/// asynchronous producer has stored a result or exception.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (but the calling code can trivially move-out `*this`
|
|
/// by assigning or constructing the result into a distinct object).
|
|
/// - `&RESULT == this`
|
|
/// - `isReady() == true`
|
|
SemiFuture<T>&& wait() &&;
|
|
|
|
/// Blocks until the future is fulfilled, or `dur` elapses.
|
|
/// Returns true if the future was fulfilled.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
bool wait(HighResDuration dur) &&;
|
|
|
|
/// Returns a Future which will call back on the other side of executor.
|
|
Future<T> via(Executor::KeepAlive<> executor) &&;
|
|
Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &&;
|
|
|
|
/// Defer work to run on the consumer of the future.
|
|
/// Function must take a Try as a parameter.
|
|
/// This work will be run either on an executor that the caller sets on the
|
|
/// SemiFuture, or inline with the call to .get().
|
|
///
|
|
/// NB: This is a custom method because boost-blocking executors is a
|
|
/// special-case for work deferral in folly. With more general boost-blocking
|
|
/// support all executors would boost block and we would simply use some form
|
|
/// of driveable executor here.
|
|
///
|
|
/// All forms of defer will run the continuation inline with the execution of
|
|
/// the previous callback in the chain if the callback attached to the
|
|
/// previous future that triggers execution of func runs on the same executor
|
|
/// that func would be executed on.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
template <typename F>
|
|
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
|
|
defer(F&& func) &&;
|
|
|
|
/// Defer work to run on the consumer of the future.
|
|
/// Function must take a const Executor::KeepAlive<>& and a Try as parameters.
|
|
///
|
|
/// As for defer(F&& func) except as the first parameter to func a KeepAlive
|
|
/// representing the executor running the work will be provided.
|
|
template <typename F>
|
|
SemiFuture<
|
|
typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
|
|
deferExTry(F&& func) &&;
|
|
|
|
/// Defer work to run on the consumer of the future.
|
|
/// Function must take a Try as a parameter.
|
|
///
|
|
/// As for defer(F&& func) but supporting function references.
|
|
template <typename R, typename... Args>
|
|
auto defer(R (&func)(Args...)) && {
|
|
return std::move(*this).defer(&func);
|
|
}
|
|
|
|
/// Defer for functions taking a T rather than a Try<T>.
|
|
///
|
|
/// All forms of defer will run the continuation inline with the execution of
|
|
/// the previous callback in the chain if the callback attached to the
|
|
/// previous future that triggers execution of func runs on the same executor
|
|
/// that func would be executed on.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
template <typename F>
|
|
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
|
|
deferValue(F&& func) &&;
|
|
|
|
/// Defer for functions taking a T rather than a Try<T>.
|
|
/// Function must take a const Executor::KeepAlive<>& and a T as parameters.
|
|
///
|
|
/// As for deferValue(F&& func) except as the first parameter to func a
|
|
/// KeepAlive representing the executor running the work will be provided.
|
|
template <typename F>
|
|
SemiFuture<
|
|
typename futures::detail::valueExecutorCallableResult<T, F>::value_type>
|
|
deferExValue(F&& func) &&;
|
|
|
|
/// Defer work to run on the consumer of the future.
|
|
/// Function must take a T as a parameter.
|
|
///
|
|
/// As for deferValue(F&& func) but supporting function references.
|
|
template <typename R, typename... Args>
|
|
auto deferValue(R (&func)(Args...)) && {
|
|
return std::move(*this).deferValue(&func);
|
|
}
|
|
|
|
/// Set an error continuation for this SemiFuture where the continuation can
|
|
/// be called with a known exception type and returns a `T`, `Future<T>`, or
|
|
/// `SemiFuture<T>`.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// ```
|
|
/// makeSemiFuture()
|
|
/// .defer([] {
|
|
/// throw std::runtime_error("oh no!");
|
|
/// return 42;
|
|
/// })
|
|
/// .deferError(folly::tag_t<std::runtime_error>{}, [] (auto const& e) {
|
|
/// LOG(INFO) << "std::runtime_error: " << e.what();
|
|
/// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
template <class ExceptionType, class F>
|
|
SemiFuture<T> deferError(tag_t<ExceptionType>, F&& func) &&;
|
|
|
|
/// As for deferError(tag_t<ExceptionType>, F&& func) but supporting function
|
|
/// references.
|
|
template <class ExceptionType, class R, class... Args>
|
|
SemiFuture<T> deferError(tag_t<ExceptionType> tag, R (&func)(Args...)) && {
|
|
return std::move(*this).deferError(tag, &func);
|
|
}
|
|
|
|
/// As for deferError(tag_t<ExceptionType>, F&& func) but makes the exception
|
|
/// explicit as a template argument rather than using a tag type.
|
|
template <class ExceptionType, class F>
|
|
SemiFuture<T> deferError(F&& func) && {
|
|
return std::move(*this).deferError(
|
|
tag_t<ExceptionType>{}, std::forward<F>(func));
|
|
}
|
|
|
|
/// Set an error continuation for this SemiFuture where the continuation can
|
|
/// be called with `exception_wrapper&&` and returns a `T`, `Future<T>`, or
|
|
/// `SemiFuture<T>`.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// makeSemiFuture()
|
|
/// .defer([] {
|
|
/// throw std::runtime_error("oh no!");
|
|
/// return 42;
|
|
/// })
|
|
/// .deferError([] (exception_wrapper&& e) {
|
|
/// LOG(INFO) << e.what();
|
|
/// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)
|
|
/// });
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
SemiFuture<T> deferError(F&& func) &&;
|
|
|
|
/// As for deferError(tag_t<ExceptionType>, F&& func) but supporting function
|
|
/// references.
|
|
template <class R, class... Args>
|
|
SemiFuture<T> deferError(R (&func)(Args...)) && {
|
|
return std::move(*this).deferError(&func);
|
|
}
|
|
|
|
/// Convenience method for ignoring the value and creating a Future<Unit>.
|
|
/// Exceptions still propagate.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
SemiFuture<Unit> unit() &&;
|
|
|
|
/// If this SemiFuture completes within duration dur from now, propagate its
|
|
/// value. Otherwise satisfy the returned SemiFuture with a FutureTimeout
|
|
/// exception.
|
|
///
|
|
/// The optional Timekeeper is as with futures::sleep().
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
SemiFuture<T> within(HighResDuration dur, Timekeeper* tk = nullptr) && {
|
|
return std::move(*this).within(dur, FutureTimeout(), tk);
|
|
}
|
|
|
|
/// If this SemiFuture completes within duration dur from now, propagate its
|
|
/// value. Otherwise satisfy the returned SemiFuture with exception e.
|
|
///
|
|
/// The optional Timekeeper is as with futures::sleep().
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class E>
|
|
SemiFuture<T> within(HighResDuration dur, E e, Timekeeper* tk = nullptr) &&;
|
|
|
|
/// Delay the completion of this SemiFuture for at least this duration from
|
|
/// now. The optional Timekeeper is as with futures::sleep().
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
SemiFuture<T> delayed(HighResDuration dur, Timekeeper* tk = nullptr) &&;
|
|
|
|
/// Returns a future that completes inline, as if the future had no executor.
|
|
/// Intended for porting legacy code without behavioral change, and for rare
|
|
/// cases where this is really the intended behavior.
|
|
/// Future is unsafe in the sense that the executor it completes on is
|
|
/// non-deterministic in the standard case.
|
|
/// For new code, or to update code that temporarily uses this, please
|
|
/// use via and pass a meaningful executor.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
Future<T> toUnsafeFuture() &&;
|
|
|
|
#if FOLLY_HAS_COROUTINES
|
|
|
|
// Customise the co_viaIfAsync() operator so that SemiFuture<T> can be
|
|
// directly awaited within a folly::coro::Task coroutine.
|
|
friend Future<T> co_viaIfAsync(
|
|
folly::Executor::KeepAlive<> executor,
|
|
SemiFuture<T>&& future) noexcept {
|
|
return std::move(future).via(std::move(executor));
|
|
}
|
|
|
|
#endif
|
|
|
|
private:
|
|
friend class Promise<T>;
|
|
template <class>
|
|
friend class futures::detail::FutureBase;
|
|
template <class>
|
|
friend class SemiFuture;
|
|
template <class>
|
|
friend class Future;
|
|
friend futures::detail::DeferredWrapper
|
|
futures::detail::stealDeferredExecutor<T>(SemiFuture<T>&);
|
|
friend DeferredExecutor* futures::detail::getDeferredExecutor<T>(
|
|
SemiFuture<T>&);
|
|
|
|
using Base::setExecutor;
|
|
using Base::throwIfInvalid;
|
|
using typename Base::Core;
|
|
|
|
template <class T2>
|
|
friend SemiFuture<T2> makeSemiFuture(Try<T2>);
|
|
|
|
explicit SemiFuture(Core* obj) : Base(obj) {}
|
|
|
|
explicit SemiFuture(futures::detail::EmptyConstruct) noexcept
|
|
: Base(futures::detail::EmptyConstruct{}) {}
|
|
|
|
// Throws FutureInvalid if !this->core_
|
|
futures::detail::DeferredWrapper stealDeferredExecutor();
|
|
|
|
/// Blocks until the future is fulfilled, or `dur` elapses.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `&RESULT == this`
|
|
/// - `isReady()` will be indeterminate - may or may not be true
|
|
SemiFuture<T>& wait(HighResDuration dur) &;
|
|
|
|
static void releaseDeferredExecutor(Core* core);
|
|
};
|
|
|
|
template <class T>
|
|
std::pair<Promise<T>, SemiFuture<T>> makePromiseContract() {
|
|
auto p = Promise<T>();
|
|
auto f = p.getSemiFuture();
|
|
return std::make_pair(std::move(p), std::move(f));
|
|
}
|
|
|
|
/// The interface (along with SemiFuture) for the consumer-side of a
|
|
/// producer/consumer pair.
|
|
///
|
|
/// Future vs. SemiFuture:
|
|
///
|
|
/// - The consumer-side should generally start with a SemiFuture, not a Future.
|
|
/// - Example, when a library creates and returns a future, it should usually
|
|
/// return a `SemiFuture`, not a Future.
|
|
/// - Reason: so the thread policy for continuations (`.thenValue`, etc.) can be
|
|
/// specified by the library's caller (using `.via()`).
|
|
/// - A SemiFuture is converted to a Future using `.via()`.
|
|
/// - Use `makePromiseContract()` when creating both a Promise and an associated
|
|
/// SemiFuture/Future.
|
|
///
|
|
/// When practical, prefer SemiFuture/Future's nonblocking style/pattern:
|
|
///
|
|
/// - the nonblocking style uses continuations, e.g., `.thenValue`, etc.; the
|
|
/// continuations are deferred until the result is available.
|
|
/// - the blocking style blocks until complete, e.g., `.wait()`, `.get()`, etc.
|
|
/// - the two styles cannot be mixed within the same future; use one or the
|
|
/// other.
|
|
///
|
|
/// SemiFuture/Future also provide a back-channel so an interrupt can
|
|
/// be sent from consumer to producer; see SemiFuture/Future's `raise()`
|
|
/// and Promise's `setInterruptHandler()`.
|
|
///
|
|
/// The consumer-side SemiFuture/Future objects should generally be accessed
|
|
/// via a single thread. That thread is referred to as the 'consumer thread.'
|
|
template <class T>
|
|
class Future : private futures::detail::FutureBase<T> {
|
|
private:
|
|
using Base = futures::detail::FutureBase<T>;
|
|
|
|
public:
|
|
/// Type of the value that the producer, when successful, produces.
|
|
using typename Base::value_type;
|
|
|
|
/// Construct a Future from a value (perfect forwarding)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `hasValue() == true`
|
|
/// - `value()`, `get()`, `result()` will return the forwarded `T`
|
|
template <
|
|
class T2 = T,
|
|
typename = typename std::enable_if<
|
|
!isFuture<typename std::decay<T2>::type>::value &&
|
|
!isSemiFuture<typename std::decay<T2>::type>::value &&
|
|
std::is_constructible<Try<T>, T2>::value>::type>
|
|
/* implicit */ Future(T2&& val) : Base(std::forward<T2>(val)) {}
|
|
|
|
/// Construct a (logical) Future-of-void.
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `hasValue() == true`
|
|
template <class T2 = T>
|
|
/* implicit */ Future(
|
|
typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr)
|
|
: Base(p) {}
|
|
|
|
/// Construct a Future from a `T` constructed from `args`
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `isReady() == true`
|
|
/// - `hasValue() == true`
|
|
/// - `hasException() == false`
|
|
/// - `value()`, `get()`, `result()` will return the newly constructed `T`
|
|
template <
|
|
class... Args,
|
|
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
|
|
type = 0>
|
|
explicit Future(in_place_t, Args&&... args)
|
|
: Base(in_place, std::forward<Args>(args)...) {}
|
|
|
|
Future(Future<T> const&) = delete;
|
|
// movable
|
|
Future(Future<T>&&) noexcept;
|
|
|
|
// converting move
|
|
template <
|
|
class T2,
|
|
typename std::enable_if<
|
|
!std::is_same<T, typename std::decay<T2>::type>::value &&
|
|
std::is_constructible<T, T2&&>::value &&
|
|
std::is_convertible<T2&&, T>::value,
|
|
int>::type = 0>
|
|
/* implicit */ Future(Future<T2>&& other)
|
|
: Future(std::move(other).thenValue(
|
|
[](T2&& v) { return T(std::move(v)); })) {}
|
|
|
|
template <
|
|
class T2,
|
|
typename std::enable_if<
|
|
!std::is_same<T, typename std::decay<T2>::type>::value &&
|
|
std::is_constructible<T, T2&&>::value &&
|
|
!std::is_convertible<T2&&, T>::value,
|
|
int>::type = 0>
|
|
explicit Future(Future<T2>&& other)
|
|
: Future(std::move(other).thenValue(
|
|
[](T2&& v) { return T(std::move(v)); })) {}
|
|
|
|
template <
|
|
class T2,
|
|
typename std::enable_if<
|
|
!std::is_same<T, typename std::decay<T2>::type>::value &&
|
|
std::is_constructible<T, T2&&>::value,
|
|
int>::type = 0>
|
|
Future& operator=(Future<T2>&& other) {
|
|
return operator=(
|
|
std::move(other).thenValue([](T2&& v) { return T(std::move(v)); }));
|
|
}
|
|
|
|
using Base::cancel;
|
|
using Base::hasException;
|
|
using Base::hasValue;
|
|
using Base::isReady;
|
|
using Base::poll;
|
|
using Base::raise;
|
|
using Base::result;
|
|
using Base::setCallback_;
|
|
using Base::valid;
|
|
using Base::value;
|
|
|
|
/// Creates/returns an invalid Future, that is, one with no shared state.
|
|
///
|
|
/// Postcondition:
|
|
///
|
|
/// - `RESULT.valid() == false`
|
|
static Future<T> makeEmpty();
|
|
|
|
// not copyable
|
|
Future& operator=(Future const&) = delete;
|
|
|
|
// movable
|
|
Future& operator=(Future&&) noexcept;
|
|
|
|
/// Call e->drive() repeatedly until the future is fulfilled.
|
|
///
|
|
/// Examples of DrivableExecutor include EventBase and ManualExecutor.
|
|
///
|
|
/// Returns the fulfilled value (moved-out) or throws the fulfilled exception.
|
|
T getVia(DrivableExecutor* e);
|
|
|
|
/// Call e->drive() repeatedly until the future is fulfilled, or `dur`
|
|
/// elapses.
|
|
///
|
|
/// Returns the fulfilled value (moved-out), throws the fulfilled exception,
|
|
/// or on timeout throws FutureTimeout.
|
|
T getVia(TimedDrivableExecutor* e, HighResDuration dur);
|
|
|
|
/// Call e->drive() repeatedly until the future is fulfilled. Examples
|
|
/// of DrivableExecutor include EventBase and ManualExecutor. Returns a
|
|
/// reference to the Try of the value.
|
|
Try<T>& getTryVia(DrivableExecutor* e);
|
|
|
|
/// getTryVia but will wait only until `dur` elapses. Returns the
|
|
/// Try of the value (moved-out) or may throw a FutureTimeout exception.
|
|
Try<T>& getTryVia(TimedDrivableExecutor* e, HighResDuration dur);
|
|
|
|
/// Unwraps the case of a Future<Future<T>> instance, and returns a simple
|
|
/// Future<T> instance.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class F = T>
|
|
typename std::
|
|
enable_if<isFuture<F>::value, Future<typename isFuture<T>::Inner>>::type
|
|
unwrap() &&;
|
|
|
|
/// Returns a Future which will call back on the other side of executor.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
Future<T> via(Executor::KeepAlive<> executor) &&;
|
|
Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &&;
|
|
|
|
/// Returns a Future which will call back on the other side of executor.
|
|
///
|
|
/// When practical, use the rvalue-qualified overload instead - it's faster.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `RESULT.valid() == true`
|
|
/// - when `this` gets fulfilled, it automatically fulfills RESULT
|
|
Future<T> via(Executor::KeepAlive<> executor) &;
|
|
Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &;
|
|
|
|
/// When this Future has completed, execute func which is a function that
|
|
/// can be called with either `T&&` or `Try<T>&&`.
|
|
///
|
|
/// Func shall return either another Future or a value.
|
|
///
|
|
/// thenInline will run the continuation inline with the execution of the
|
|
/// previous callback in the chain if the callback attached to the previous
|
|
/// future that triggers execution of func runs on the same executor that func
|
|
/// would be executed on.
|
|
///
|
|
/// A Future for the return type of func is returned.
|
|
///
|
|
/// Versions of these functions with Inline in the name will run the
|
|
/// continuation inline if the executor the previous task completes on matches
|
|
/// the executor the next is to be enqueued on to.
|
|
///
|
|
/// Future<string> f2 = f1.thenTry([](Try<T>&&) { return string("foo"); });
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <typename F>
|
|
Future<typename futures::detail::tryCallableResult<T, F>::value_type> then(
|
|
F&& func) && {
|
|
return std::move(*this).thenTry(std::forward<F>(func));
|
|
}
|
|
template <typename F>
|
|
Future<typename futures::detail::tryCallableResult<T, F>::value_type>
|
|
thenInline(F&& func) && {
|
|
return std::move(*this).thenTryInline(std::forward<F>(func));
|
|
}
|
|
|
|
/// Variant where func is an member function
|
|
///
|
|
/// struct Worker { R doWork(Try<T>); }
|
|
///
|
|
/// Worker *w;
|
|
/// Future<R> f2 = f1.thenTry(&Worker::doWork, w);
|
|
///
|
|
/// This is just sugar for
|
|
///
|
|
/// f1.thenTry(std::bind(&Worker::doWork, w));
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <typename R, typename Caller, typename... Args>
|
|
Future<typename isFuture<R>::Inner> then(
|
|
R (Caller::*func)(Args...),
|
|
Caller* instance) &&;
|
|
|
|
/// Execute the callback via the given Executor. The executor doesn't stick.
|
|
///
|
|
/// Contrast
|
|
///
|
|
/// f.via(x).then(b).then(c)
|
|
///
|
|
/// with
|
|
///
|
|
/// f.then(x, b).then(c)
|
|
///
|
|
/// In the former both b and c execute via x. In the latter, only b executes
|
|
/// via x, and c executes via the same executor (if any) that f had.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class Arg>
|
|
auto then(Executor::KeepAlive<> x, Arg&& arg) && = delete;
|
|
|
|
/// When this Future has completed, execute func which is a function that
|
|
/// can be called with `Try<T>&&` (often a lambda with parameter type
|
|
/// `auto&&` or `auto`).
|
|
///
|
|
/// Func shall return either another Future or a value.
|
|
///
|
|
/// Versions of these functions with Inline in the name will run the
|
|
/// continuation inline with the execution of the previous callback in the
|
|
/// chain if the callback attached to the previous future that triggers
|
|
/// execution of func runs on the same executor that func would be executed
|
|
/// on.
|
|
///
|
|
/// A Future for the return type of func is returned.
|
|
///
|
|
/// Future<string> f2 = std::move(f1).thenTry([](auto&& t) {
|
|
/// ...
|
|
/// return string("foo");
|
|
/// });
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
template <typename F>
|
|
Future<typename futures::detail::tryCallableResult<T, F>::value_type> thenTry(
|
|
F&& func) &&;
|
|
|
|
template <typename F>
|
|
Future<typename futures::detail::tryCallableResult<T, F>::value_type>
|
|
thenTryInline(F&& func) &&;
|
|
|
|
template <typename F>
|
|
Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
|
|
thenExTry(F&& func) &&;
|
|
|
|
template <typename F>
|
|
Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
|
|
thenExTryInline(F&& func) &&;
|
|
|
|
template <typename R, typename... Args>
|
|
auto thenTry(R (&func)(Args...)) && {
|
|
return std::move(*this).thenTry(&func);
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
auto thenTryInline(R (&func)(Args...)) && {
|
|
return std::move(*this).thenTryInline(&func);
|
|
}
|
|
|
|
/// When this Future has completed, execute func which is a function that
|
|
/// can be called with `T&&` (often a lambda with parameter type
|
|
/// `auto&&` or `auto`).
|
|
///
|
|
/// Func shall return either another Future or a value.
|
|
///
|
|
/// Versions of these functions with Inline in the name will run the
|
|
/// continuation inline with the execution of the previous callback in the
|
|
/// chain if the callback attached to the previous future that triggers
|
|
/// execution of func runs on the same executor that func would be executed
|
|
/// on.
|
|
///
|
|
/// A Future for the return type of func is returned.
|
|
///
|
|
/// Future<string> f2 = f1.thenValue([](auto&& v) {
|
|
/// ...
|
|
/// return string("foo");
|
|
/// });
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
template <typename F>
|
|
Future<typename futures::detail::valueCallableResult<T, F>::value_type>
|
|
thenValue(F&& func) &&;
|
|
|
|
template <typename F>
|
|
Future<typename futures::detail::valueCallableResult<T, F>::value_type>
|
|
thenValueInline(F&& func) &&;
|
|
|
|
template <typename F>
|
|
Future<
|
|
typename futures::detail::valueExecutorCallableResult<T, F>::value_type>
|
|
thenExValue(F&& func) &&;
|
|
|
|
template <typename F>
|
|
Future<
|
|
typename futures::detail::valueExecutorCallableResult<T, F>::value_type>
|
|
thenExValueInline(F&& func) &&;
|
|
|
|
template <typename R, typename... Args>
|
|
auto thenValue(R (&func)(Args...)) && {
|
|
return std::move(*this).thenValue(&func);
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
auto thenValueInline(R (&func)(Args...)) && {
|
|
return std::move(*this).thenValueInline(&func);
|
|
}
|
|
|
|
/// Set an error continuation for this Future where the continuation can
|
|
/// be called with a known exception type and returns a `T`, `Future<T>`, or
|
|
/// `SemiFuture<T>`.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// makeFuture()
|
|
/// .thenTry([] {
|
|
/// throw std::runtime_error("oh no!");
|
|
/// return 42;
|
|
/// })
|
|
/// .thenError(folly::tag_t<std::runtime_error>{}, [] (auto const& e) {
|
|
/// LOG(INFO) << "std::runtime_error: " << e.what();
|
|
/// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)
|
|
/// });
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
template <class ExceptionType, class F>
|
|
typename std::enable_if<
|
|
isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
|
|
Future<T>>::type
|
|
thenError(tag_t<ExceptionType>, F&& func) &&;
|
|
|
|
template <class ExceptionType, class F>
|
|
typename std::enable_if<
|
|
!isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
|
|
Future<T>>::type
|
|
thenError(tag_t<ExceptionType>, F&& func) &&;
|
|
|
|
template <class ExceptionType, class R, class... Args>
|
|
Future<T> thenError(tag_t<ExceptionType> tag, R (&func)(Args...)) && {
|
|
return std::move(*this).thenError(tag, &func);
|
|
}
|
|
|
|
template <class ExceptionType, class F>
|
|
Future<T> thenError(F&& func) && {
|
|
return std::move(*this).thenError(
|
|
tag_t<ExceptionType>{}, std::forward<F>(func));
|
|
}
|
|
|
|
/// Set an error continuation for this Future where the continuation can
|
|
/// be called with `exception_wrapper&&` and returns a `T`, `Future<T>`, or
|
|
/// `SemiFuture<T>`.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// makeFuture()
|
|
/// .thenTry([] {
|
|
/// throw std::runtime_error("oh no!");
|
|
/// return 42;
|
|
/// })
|
|
/// .thenError([] (exception_wrapper&& e) {
|
|
/// LOG(INFO) << e.what();
|
|
/// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)
|
|
/// });
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
typename std::enable_if<
|
|
isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,
|
|
Future<T>>::type
|
|
thenError(F&& func) &&;
|
|
|
|
template <class F>
|
|
typename std::enable_if<
|
|
!isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,
|
|
Future<T>>::type
|
|
thenError(F&& func) &&;
|
|
|
|
template <class R, class... Args>
|
|
Future<T> thenError(R (&func)(Args...)) && {
|
|
return std::move(*this).thenError(&func);
|
|
}
|
|
|
|
/// Convenience method for ignoring the value and creating a Future<Unit>.
|
|
/// Exceptions still propagate.
|
|
/// This function is identical to .unit().
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
Future<Unit> then() &&;
|
|
|
|
/// Convenience method for ignoring the value and creating a Future<Unit>.
|
|
/// Exceptions still propagate.
|
|
/// This function is identical to parameterless .then().
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
Future<Unit> unit() && {
|
|
return std::move(*this).then();
|
|
}
|
|
|
|
/// Set an error continuation for this Future. The continuation should take an
|
|
/// argument of the type that you want to catch, and should return a value of
|
|
/// the same type as this Future, or a Future of that type (see overload
|
|
/// below).
|
|
///
|
|
/// Example:
|
|
///
|
|
/// makeFuture()
|
|
/// .thenValue([] {
|
|
/// throw std::runtime_error("oh no!");
|
|
/// return 42;
|
|
/// })
|
|
/// .thenError<std::runtime_error>([] (std::runtime_error& e) {
|
|
/// LOG(INFO) << "std::runtime_error: " << e.what();
|
|
/// return -1; // or makeFuture<int>(-1)
|
|
/// });
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
[[deprecated(
|
|
"onError loses the attached executor and is weakly typed. Please move to thenError instead.")]]
|
|
typename std::enable_if<
|
|
!is_invocable_v<F, exception_wrapper> &&
|
|
!futures::detail::Extract<F>::ReturnsFuture::value,
|
|
Future<T>>::type
|
|
onError(F&& func) && = delete;
|
|
|
|
/// Overload of onError where the error continuation returns a Future<T>
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
[[deprecated(
|
|
"onError loses the attached executor and is weakly typed. Please move to thenError instead.")]]
|
|
typename std::enable_if<
|
|
!is_invocable_v<F, exception_wrapper> &&
|
|
futures::detail::Extract<F>::ReturnsFuture::value,
|
|
Future<T>>::type
|
|
onError(F&& func) && = delete;
|
|
|
|
/// Overload of onError that takes exception_wrapper and returns Future<T>
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
[[deprecated(
|
|
"onError loses the attached executor and is weakly typed. Please move to thenError instead.")]]
|
|
typename std::enable_if<
|
|
is_invocable_v<F, exception_wrapper> &&
|
|
futures::detail::Extract<F>::ReturnsFuture::value,
|
|
Future<T>>::type
|
|
onError(F&& func) && = delete;
|
|
|
|
/// Overload of onError that takes exception_wrapper and returns T
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
[[deprecated(
|
|
"onError loses the attached executor and is weakly typed. Please move to thenError instead.")]]
|
|
typename std::enable_if<
|
|
is_invocable_v<F, exception_wrapper> &&
|
|
!futures::detail::Extract<F>::ReturnsFuture::value,
|
|
Future<T>>::type
|
|
onError(F&& func) && = delete;
|
|
|
|
template <class R, class... Args>
|
|
Future<T> onError(R (&func)(Args...)) && = delete;
|
|
|
|
// clang-format off
|
|
template <class F>
|
|
[[deprecated(
|
|
"onError loses the attached executor and is weakly typed. Please move to thenError instead.")]]
|
|
Future<T> onError(F&& func) & = delete;
|
|
|
|
/// func is like std::function<void()> and is executed unconditionally, and
|
|
/// the value/exception is passed through to the resulting Future.
|
|
/// func shouldn't throw, but if it does it will be captured and propagated,
|
|
/// and discard any value/exception that this Future has obtained.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
Future<T> ensure(F&& func) &&;
|
|
// clang-format on
|
|
|
|
/// Like thenError, but for timeouts. example:
|
|
///
|
|
/// Future<int> f = makeFuture<int>(42)
|
|
/// .delayed(long_time)
|
|
/// .onTimeout(short_time,
|
|
/// [] { return -1; });
|
|
///
|
|
/// or perhaps
|
|
///
|
|
/// Future<int> f = makeFuture<int>(42)
|
|
/// .delayed(long_time)
|
|
/// .onTimeout(short_time,
|
|
/// [] { return makeFuture<int>(some_exception); });
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
Future<T> onTimeout(HighResDuration, F&& func, Timekeeper* = nullptr) &&;
|
|
|
|
/// If this Future completes within duration dur from now, propagate its
|
|
/// value. Otherwise satisfy the returned SemiFuture with a FutureTimeout
|
|
/// exception.
|
|
///
|
|
/// The optional Timekeeper is as with futures::sleep().
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
Future<T> within(HighResDuration dur, Timekeeper* tk = nullptr) &&;
|
|
|
|
/// If this SemiFuture completes within duration dur from now, propagate its
|
|
/// value. Otherwise satisfy the returned SemiFuture with exception e.
|
|
///
|
|
/// The optional Timekeeper is as with futures::sleep().
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class E>
|
|
Future<T>
|
|
within(HighResDuration dur, E exception, Timekeeper* tk = nullptr) &&;
|
|
|
|
/// Delay the completion of this Future for at least this duration from
|
|
/// now. The optional Timekeeper is as with futures::sleep().
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
/// - `RESULT.valid() == true`
|
|
Future<T> delayed(HighResDuration, Timekeeper* = nullptr) &&;
|
|
|
|
/// Blocks until the future is fulfilled. Returns the value (moved-out), or
|
|
/// throws the exception. The future must not already have a continuation.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
T get() &&;
|
|
|
|
/// Blocks until the future is fulfilled, or until `dur` elapses. Returns the
|
|
/// value (moved-out), or throws the exception (which might be a FutureTimeout
|
|
/// exception).
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == false`
|
|
T get(HighResDuration dur) &&;
|
|
|
|
/// A reference to the Try of the value
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
/// - `isReady() == true` (else throws FutureNotReady)
|
|
Try<T>& getTry();
|
|
|
|
/// Blocks until this Future is complete.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true`
|
|
/// - `&RESULT == this`
|
|
/// - `isReady() == true`
|
|
Future<T>& wait() &;
|
|
|
|
/// Blocks until this Future is complete.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (but the calling code can trivially move-out `*this`
|
|
/// by assigning or constructing the result into a distinct object).
|
|
/// - `&RESULT == this`
|
|
/// - `isReady() == true`
|
|
Future<T>&& wait() &&;
|
|
|
|
/// Blocks until this Future is complete, or `dur` elapses.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (so you may call `wait(...)` repeatedly)
|
|
/// - `&RESULT == this`
|
|
/// - `isReady()` will be indeterminate - may or may not be true
|
|
Future<T>& wait(HighResDuration dur) &;
|
|
|
|
/// Blocks until this Future is complete or until `dur` passes.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (but the calling code can trivially move-out `*this`
|
|
/// by assigning or constructing the result into a distinct object).
|
|
/// - `&RESULT == this`
|
|
/// - `isReady()` will be indeterminate - may or may not be true
|
|
Future<T>&& wait(HighResDuration dur) &&;
|
|
|
|
/// Call e->drive() repeatedly until the future is fulfilled. Examples
|
|
/// of DrivableExecutor include EventBase and ManualExecutor. Returns a
|
|
/// reference to this Future so that you can chain calls if desired.
|
|
/// value (moved-out), or throws the exception.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (does not move-out `*this`)
|
|
/// - `&RESULT == this`
|
|
Future<T>& waitVia(DrivableExecutor* e) &;
|
|
|
|
/// Overload of waitVia() for rvalue Futures
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (but the calling code can trivially move-out `*this`
|
|
/// by assigning or constructing the result into a distinct object).
|
|
/// - `&RESULT == this`
|
|
Future<T>&& waitVia(DrivableExecutor* e) &&;
|
|
|
|
/// As waitVia but may return early after dur passes.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (does not move-out `*this`)
|
|
/// - `&RESULT == this`
|
|
Future<T>& waitVia(TimedDrivableExecutor* e, HighResDuration dur) &;
|
|
|
|
/// Overload of waitVia() for rvalue Futures
|
|
/// As waitVia but may return early after dur passes.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (but the calling code can trivially move-out `*this`
|
|
/// by assigning or constructing the result into a distinct object).
|
|
/// - `&RESULT == this`
|
|
Future<T>&& waitVia(TimedDrivableExecutor* e, HighResDuration dur) &&;
|
|
|
|
/// If the value in this Future is equal to the given Future, when they have
|
|
/// both completed, the value of the resulting Future<bool> will be true. It
|
|
/// will be false otherwise (including when one or both Futures have an
|
|
/// exception)
|
|
Future<bool> willEqual(Future<T>&);
|
|
|
|
/// predicate behaves like std::function<bool(T const&)>
|
|
/// If the predicate does not obtain with the value, the result
|
|
/// is a folly::FuturePredicateDoesNotObtain exception
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class F>
|
|
Future<T> filter(F&& predicate) &&;
|
|
|
|
/// Like reduce, but works on a Future<std::vector<T / Try<T>>>, for example
|
|
/// the result of collect or collectAll
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws FutureInvalid)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - Calling code should act as if `valid() == false`,
|
|
/// i.e., as if `*this` was moved into RESULT.
|
|
/// - `RESULT.valid() == true`
|
|
template <class I, class F>
|
|
Future<I> reduce(I&& initial, F&& func) &&;
|
|
|
|
/// Moves-out `*this`, creating/returning a corresponding SemiFuture.
|
|
/// Result will behave like `*this` except result won't have an Executor.
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `RESULT.valid() ==` the original value of `this->valid()`
|
|
/// - RESULT will not have an Executor regardless of whether `*this` had one
|
|
SemiFuture<T> semi() && {
|
|
return SemiFuture<T>{std::move(*this)};
|
|
}
|
|
|
|
#if FOLLY_HAS_COROUTINES
|
|
|
|
// Overload needed to customise behaviour of awaiting a Future<T>
|
|
// inside a folly::coro::Task coroutine.
|
|
friend Future<T> co_viaIfAsync(
|
|
folly::Executor::KeepAlive<> executor,
|
|
Future<T>&& future) noexcept {
|
|
return std::move(future).via(std::move(executor));
|
|
}
|
|
|
|
#endif
|
|
|
|
protected:
|
|
friend class Promise<T>;
|
|
template <class>
|
|
friend class futures::detail::FutureBase;
|
|
template <class>
|
|
friend class Future;
|
|
template <class>
|
|
friend class SemiFuture;
|
|
template <class>
|
|
friend class FutureSplitter;
|
|
|
|
using Base::setExecutor;
|
|
using Base::throwIfContinued;
|
|
using Base::throwIfInvalid;
|
|
using typename Base::Core;
|
|
|
|
explicit Future(Core* obj) : Base(obj) {}
|
|
|
|
explicit Future(futures::detail::EmptyConstruct) noexcept
|
|
: Base(futures::detail::EmptyConstruct{}) {}
|
|
|
|
template <class T2>
|
|
friend Future<T2> makeFuture(Try<T2>);
|
|
|
|
template <class FT>
|
|
friend Future<FT> futures::detail::convertFuture(
|
|
SemiFuture<FT>&& sf,
|
|
const Future<FT>& f);
|
|
|
|
using Base::detach;
|
|
template <class T2>
|
|
friend void futures::detachOn(
|
|
folly::Executor::KeepAlive<> exec,
|
|
folly::SemiFuture<T2>&& fut);
|
|
};
|
|
|
|
/// A Timekeeper handles the details of keeping time and fulfilling delay
|
|
/// promises. The returned Future<Unit> will either complete after the
|
|
/// elapsed time, or in the event of some kind of exceptional error may hold
|
|
/// an exception. These Futures respond to cancellation. If you use a lot of
|
|
/// Delays and many of them ultimately are unneeded (as would be the case for
|
|
/// Delays that are used to trigger timeouts of async operations), then you
|
|
/// can and should cancel them to reclaim resources.
|
|
///
|
|
/// Users will typically get one of these via Future::sleep(HighResDuration dur)
|
|
/// or use them implicitly behind the scenes by passing a timeout to some Future
|
|
/// operation.
|
|
///
|
|
/// Although we don't formally alias Delay = Future<Unit>,
|
|
/// that's an appropriate term for it. People will probably also call these
|
|
/// Timeouts, and that's ok I guess, but that term is so overloaded I thought
|
|
/// it made sense to introduce a cleaner term.
|
|
///
|
|
/// Remember that HighResDuration is a std::chrono duration (millisecond
|
|
/// resolution at the time of writing). When writing code that uses specific
|
|
/// durations, prefer using the explicit std::chrono type, e.g.
|
|
/// std::chrono::milliseconds over HighResDuration. This makes the code more
|
|
/// legible and means you won't be unpleasantly surprised if we redefine
|
|
/// HighResDuration to microseconds, or something.
|
|
///
|
|
/// timekeeper.after(std::chrono::duration_cast<HighResDuration>(someNanoseconds))
|
|
class Timekeeper {
|
|
public:
|
|
virtual ~Timekeeper() = default;
|
|
|
|
/// Returns a future that will complete after the given duration with the
|
|
/// elapsed time. Exceptional errors can happen but they must be
|
|
/// exceptional. Use the steady (monotonic) clock.
|
|
///
|
|
/// The consumer thread may cancel this Future to reclaim resources.
|
|
virtual SemiFuture<Unit> after(HighResDuration dur) = 0;
|
|
|
|
/// Unsafe version of after that returns an inline Future.
|
|
/// Any work added to this future will run inline on the Timekeeper's thread.
|
|
/// This can potentially cause problems with timing.
|
|
///
|
|
/// Please migrate to use after + a call to via with a valid, non-inline
|
|
/// executor.
|
|
Future<Unit> afterUnsafe(HighResDuration dur) {
|
|
return after(dur).toUnsafeFuture();
|
|
}
|
|
|
|
/// Returns a future that will complete at the requested time.
|
|
///
|
|
/// You may cancel this SemiFuture to reclaim resources.
|
|
///
|
|
/// NB This is sugar for `after(when - now)`, so while you are welcome to
|
|
/// use a std::chrono::system_clock::time_point it will not track changes to
|
|
/// the system clock but rather execute that many milliseconds in the future
|
|
/// according to the steady clock.
|
|
template <class Clock>
|
|
SemiFuture<Unit> at(std::chrono::time_point<Clock> when);
|
|
|
|
/// Unsafe version of at that returns an inline Future.
|
|
/// Any work added to this future will run inline on the Timekeeper's thread.
|
|
/// This can potentially cause problems with timing.
|
|
///
|
|
/// Please migrate to use at + a call to via with a valid, non-inline
|
|
/// executor.
|
|
template <class Clock>
|
|
Future<Unit> atUnsafe(std::chrono::time_point<Clock> when) {
|
|
return at(when).toUnsafeFuture();
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
std::pair<Promise<T>, Future<T>> makePromiseContract(Executor::KeepAlive<> e) {
|
|
auto p = Promise<T>();
|
|
auto f = p.getSemiFuture().via(std::move(e));
|
|
return std::make_pair(std::move(p), std::move(f));
|
|
}
|
|
|
|
template <class F>
|
|
auto makeAsyncTask(folly::Executor::KeepAlive<> ka, F&& func) {
|
|
return
|
|
[func = std::forward<F>(func), ka = std::move(ka)](auto&& param) mutable {
|
|
return via(
|
|
ka,
|
|
[func = std::move(func),
|
|
param = std::forward<decltype(param)>(param)]() mutable {
|
|
return func(std::forward<decltype(param)>(param));
|
|
});
|
|
};
|
|
}
|
|
|
|
/// This namespace is for utility functions that would usually be static
|
|
/// members of Future, except they don't make sense there because they don't
|
|
/// depend on the template type (rather, on the type of their arguments in
|
|
/// some cases). This is the least-bad naming scheme we could think of. Some
|
|
/// of the functions herein have really-likely-to-collide names, like "map"
|
|
/// and "sleep".
|
|
namespace futures {
|
|
/// Returns a Future that will complete after the specified duration. The
|
|
/// HighResDuration typedef of a `std::chrono` duration type indicates the
|
|
/// resolution you can expect to be meaningful (milliseconds at the time of
|
|
/// writing). Normally you wouldn't need to specify a Timekeeper, we will
|
|
/// use the global futures timekeeper (we run a thread whose job it is to
|
|
/// keep time for futures timeouts) but we provide the option for power
|
|
/// users.
|
|
///
|
|
/// The Timekeeper thread will be lazily created the first time it is
|
|
/// needed. If your program never uses any timeouts or other time-based
|
|
/// Futures you will pay no Timekeeper thread overhead.
|
|
SemiFuture<Unit> sleep(HighResDuration, Timekeeper* = nullptr);
|
|
[[deprecated(
|
|
"futures::sleep now returns a SemiFuture<Unit>. "
|
|
"sleepUnsafe is deprecated. "
|
|
"Please call futures::sleep and apply an executor with .via")]] Future<Unit>
|
|
sleepUnsafe(HighResDuration, Timekeeper* = nullptr);
|
|
|
|
/**
|
|
* Set func as the callback for each input Future and return a vector of
|
|
* Futures containing the results in the input order.
|
|
*/
|
|
template <
|
|
class It,
|
|
class F,
|
|
class ItT = typename std::iterator_traits<It>::value_type,
|
|
class Tag = std::enable_if_t<is_invocable_v<F, typename ItT::value_type&&>>,
|
|
class Result = typename decltype(
|
|
std::declval<ItT>().thenValue(std::declval<F>()))::value_type>
|
|
std::vector<Future<Result>> mapValue(It first, It last, F func);
|
|
|
|
/**
|
|
* Set func as the callback for each input Future and return a vector of
|
|
* Futures containing the results in the input order.
|
|
*/
|
|
template <
|
|
class It,
|
|
class F,
|
|
class ItT = typename std::iterator_traits<It>::value_type,
|
|
class Tag =
|
|
std::enable_if_t<!is_invocable_v<F, typename ItT::value_type&&>>,
|
|
class Result = typename decltype(
|
|
std::declval<ItT>().thenTry(std::declval<F>()))::value_type>
|
|
std::vector<Future<Result>> mapTry(It first, It last, F func, int = 0);
|
|
|
|
/**
|
|
* Set func as the callback for each input Future and return a vector of
|
|
* Futures containing the results in the input order and completing on
|
|
* exec.
|
|
*/
|
|
template <
|
|
class It,
|
|
class F,
|
|
class ItT = typename std::iterator_traits<It>::value_type,
|
|
class Tag = std::enable_if_t<is_invocable_v<F, typename ItT::value_type&&>>,
|
|
class Result =
|
|
typename decltype(std::move(std::declval<ItT>())
|
|
.via(std::declval<Executor*>())
|
|
.thenValue(std::declval<F>()))::value_type>
|
|
std::vector<Future<Result>> mapValue(Executor& exec, It first, It last, F func);
|
|
|
|
/**
|
|
* Set func as the callback for each input Future and return a vector of
|
|
* Futures containing the results in the input order and completing on
|
|
* exec.
|
|
*/
|
|
template <
|
|
class It,
|
|
class F,
|
|
class ItT = typename std::iterator_traits<It>::value_type,
|
|
class Tag =
|
|
std::enable_if_t<!is_invocable_v<F, typename ItT::value_type&&>>,
|
|
class Result =
|
|
typename decltype(std::move(std::declval<ItT>())
|
|
.via(std::declval<Executor*>())
|
|
.thenTry(std::declval<F>()))::value_type>
|
|
std::vector<Future<Result>>
|
|
mapTry(Executor& exec, It first, It last, F func, int = 0);
|
|
|
|
// Sugar for the most common case
|
|
template <class Collection, class F>
|
|
auto mapValue(Collection&& c, F&& func)
|
|
-> decltype(mapValue(c.begin(), c.end(), func)) {
|
|
return mapValue(c.begin(), c.end(), std::forward<F>(func));
|
|
}
|
|
|
|
template <class Collection, class F>
|
|
auto mapTry(Collection&& c, F&& func)
|
|
-> decltype(mapTry(c.begin(), c.end(), func)) {
|
|
return mapTry(c.begin(), c.end(), std::forward<F>(func));
|
|
}
|
|
|
|
// Sugar for the most common case
|
|
template <class Collection, class F>
|
|
auto mapValue(Executor& exec, Collection&& c, F&& func)
|
|
-> decltype(mapValue(exec, c.begin(), c.end(), func)) {
|
|
return mapValue(exec, c.begin(), c.end(), std::forward<F>(func));
|
|
}
|
|
|
|
template <class Collection, class F>
|
|
auto mapTry(Executor& exec, Collection&& c, F&& func)
|
|
-> decltype(mapTry(exec, c.begin(), c.end(), func)) {
|
|
return mapTry(exec, c.begin(), c.end(), std::forward<F>(func));
|
|
}
|
|
|
|
/// Carry out the computation contained in the given future if
|
|
/// the predicate holds.
|
|
///
|
|
/// thunk behaves like std::function<Future<T2>(void)> or
|
|
/// std::function<SemiFuture<T2>(void)>
|
|
template <class F>
|
|
auto when(bool p, F&& thunk)
|
|
-> decltype(std::declval<invoke_result_t<F>>().unit());
|
|
|
|
#if FOLLY_FUTURE_USING_FIBER
|
|
|
|
SemiFuture<Unit> wait(std::unique_ptr<fibers::Baton> baton);
|
|
SemiFuture<Unit> wait(std::shared_ptr<fibers::Baton> baton);
|
|
|
|
#endif
|
|
|
|
/**
|
|
* Returns a lazy SemiFuture constructed by f, which also ensures that ensure is
|
|
* called before completion.
|
|
* f doesn't get called until the SemiFuture is activated (e.g. through a .get()
|
|
* or .via() call). If f gets called, ensure is guaranteed to be called as well.
|
|
*/
|
|
template <typename F, class Ensure>
|
|
auto ensure(F&& f, Ensure&& ensure);
|
|
|
|
} // namespace futures
|
|
|
|
/**
|
|
Make a completed SemiFuture by moving in a value. e.g.
|
|
|
|
string foo = "foo";
|
|
auto f = makeSemiFuture(std::move(foo));
|
|
|
|
or
|
|
|
|
auto f = makeSemiFuture<string>("foo");
|
|
*/
|
|
template <class T>
|
|
SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t);
|
|
|
|
/** Make a completed void SemiFuture. */
|
|
SemiFuture<Unit> makeSemiFuture();
|
|
|
|
/**
|
|
Make a SemiFuture by executing a function.
|
|
|
|
If the function returns a value of type T, makeSemiFutureWith
|
|
returns a completed SemiFuture<T>, capturing the value returned
|
|
by the function.
|
|
|
|
If the function returns a SemiFuture<T> already, makeSemiFutureWith
|
|
returns just that.
|
|
|
|
Either way, if the function throws, a failed Future is
|
|
returned that captures the exception.
|
|
*/
|
|
|
|
// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>
|
|
template <class F>
|
|
typename std::enable_if<
|
|
isFutureOrSemiFuture<invoke_result_t<F>>::value,
|
|
SemiFuture<typename invoke_result_t<F>::value_type>>::type
|
|
makeSemiFutureWith(F&& func);
|
|
|
|
// makeSemiFutureWith(T()) -> SemiFuture<T>
|
|
// makeSemiFutureWith(void()) -> SemiFuture<Unit>
|
|
template <class F>
|
|
typename std::enable_if<
|
|
!(isFutureOrSemiFuture<invoke_result_t<F>>::value),
|
|
SemiFuture<lift_unit_t<invoke_result_t<F>>>>::type
|
|
makeSemiFutureWith(F&& func);
|
|
|
|
/// Make a failed Future from an exception_ptr.
|
|
/// Because the Future's type cannot be inferred you have to specify it, e.g.
|
|
///
|
|
/// auto f = makeSemiFuture<string>(std::current_exception());
|
|
template <class T>
|
|
[[deprecated("use makeSemiFuture(exception_wrapper)")]] SemiFuture<T>
|
|
makeSemiFuture(std::exception_ptr const& e);
|
|
|
|
/// Make a failed SemiFuture from an exception_wrapper.
|
|
template <class T>
|
|
SemiFuture<T> makeSemiFuture(exception_wrapper ew);
|
|
|
|
/** Make a SemiFuture from an exception type E that can be passed to
|
|
std::make_exception_ptr(). */
|
|
template <class T, class E>
|
|
typename std::
|
|
enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type
|
|
makeSemiFuture(E const& e);
|
|
|
|
/** Make a Future out of a Try */
|
|
template <class T>
|
|
SemiFuture<T> makeSemiFuture(Try<T> t);
|
|
|
|
/**
|
|
Make a completed Future by moving in a value. e.g.
|
|
|
|
string foo = "foo";
|
|
auto f = makeFuture(std::move(foo));
|
|
|
|
or
|
|
|
|
auto f = makeFuture<string>("foo");
|
|
|
|
NOTE: This function is deprecated. Please use makeSemiFuture and pass the
|
|
appropriate executor to .via on the returned SemiFuture to get a
|
|
valid Future where necessary.
|
|
*/
|
|
template <class T>
|
|
Future<typename std::decay<T>::type> makeFuture(T&& t);
|
|
|
|
/**
|
|
Make a completed void Future.
|
|
|
|
NOTE: This function is deprecated. Please use makeSemiFuture and pass the
|
|
appropriate executor to .via on the returned SemiFuture to get a
|
|
valid Future where necessary.
|
|
*/
|
|
Future<Unit> makeFuture();
|
|
|
|
/**
|
|
Make a Future by executing a function.
|
|
|
|
If the function returns a value of type T, makeFutureWith
|
|
returns a completed Future<T>, capturing the value returned
|
|
by the function.
|
|
|
|
If the function returns a Future<T> already, makeFutureWith
|
|
returns just that.
|
|
|
|
Either way, if the function throws, a failed Future is
|
|
returned that captures the exception.
|
|
|
|
Calling makeFutureWith(func) is equivalent to calling
|
|
makeFuture().then(func).
|
|
|
|
NOTE: This function is deprecated. Please use makeSemiFutureWith and pass the
|
|
appropriate executor to .via on the returned SemiFuture to get a
|
|
valid Future where necessary.
|
|
*/
|
|
|
|
// makeFutureWith(Future<T>()) -> Future<T>
|
|
template <class F>
|
|
typename std::
|
|
enable_if<isFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type
|
|
makeFutureWith(F&& func);
|
|
|
|
// makeFutureWith(T()) -> Future<T>
|
|
// makeFutureWith(void()) -> Future<Unit>
|
|
template <class F>
|
|
typename std::enable_if<
|
|
!(isFuture<invoke_result_t<F>>::value),
|
|
Future<lift_unit_t<invoke_result_t<F>>>>::type
|
|
makeFutureWith(F&& func);
|
|
|
|
/// Make a failed Future from an exception_ptr.
|
|
/// Because the Future's type cannot be inferred you have to specify it, e.g.
|
|
///
|
|
/// auto f = makeFuture<string>(std::current_exception());
|
|
template <class T>
|
|
[[deprecated("use makeSemiFuture(exception_wrapper)")]] Future<T> makeFuture(
|
|
std::exception_ptr const& e);
|
|
|
|
/// Make a failed Future from an exception_wrapper.
|
|
/// NOTE: This function is deprecated. Please use makeSemiFuture and pass the
|
|
/// appropriate executor to .via on the returned SemiFuture to get a
|
|
/// valid Future where necessary.
|
|
template <class T>
|
|
Future<T> makeFuture(exception_wrapper ew);
|
|
|
|
/** Make a Future from an exception type E that can be passed to
|
|
std::make_exception_ptr().
|
|
|
|
NOTE: This function is deprecated. Please use makeSemiFuture and pass the
|
|
appropriate executor to .via on the returned SemiFuture to get a
|
|
valid Future where necessary.
|
|
*/
|
|
template <class T, class E>
|
|
typename std::enable_if<std::is_base_of<std::exception, E>::value, Future<T>>::
|
|
type
|
|
makeFuture(E const& e);
|
|
|
|
/**
|
|
Make a Future out of a Try
|
|
|
|
NOTE: This function is deprecated. Please use makeSemiFuture and pass the
|
|
appropriate executor to .via on the returned SemiFuture to get a
|
|
valid Future where necessary.
|
|
*/
|
|
template <class T>
|
|
Future<T> makeFuture(Try<T> t);
|
|
|
|
/*
|
|
* Return a new Future that will call back on the given Executor.
|
|
* This is just syntactic sugar for makeFuture().via(executor)
|
|
*
|
|
* @param executor the Executor to call back on
|
|
* @param priority optionally, the priority to add with. Defaults to 0 which
|
|
* represents medium priority.
|
|
*
|
|
* @returns a void Future that will call back on the given executor
|
|
*/
|
|
inline Future<Unit> via(Executor::KeepAlive<> executor);
|
|
inline Future<Unit> via(Executor::KeepAlive<> executor, int8_t priority);
|
|
|
|
/// Execute a function via the given executor and return a future.
|
|
/// This is semantically equivalent to via(executor).then(func), but
|
|
/// easier to read and slightly more efficient.
|
|
template <class Func>
|
|
auto via(Executor::KeepAlive<>, Func&& func) -> Future<
|
|
typename isFutureOrSemiFuture<decltype(std::declval<Func>()())>::Inner>;
|
|
|
|
/** When all the input Futures complete, the returned Future will complete.
|
|
Errors do not cause early termination; this Future will always succeed
|
|
after all its Futures have finished (whether successfully or with an
|
|
error).
|
|
|
|
The Futures are moved in, so your copies are invalid. If you need to
|
|
chain further from these Futures, use the variant with an output iterator.
|
|
|
|
This function is thread-safe for Futures running on different threads. But
|
|
if you are doing anything non-trivial after, you will probably want to
|
|
follow with `via(executor)` because it will complete in whichever thread the
|
|
last Future completes in.
|
|
|
|
The return type for Future<T> input is a SemiFuture<std::vector<Try<T>>>
|
|
for collectX and collectXSemiFuture.
|
|
|
|
collectXUnsafe returns an inline Future that erases the executor from the
|
|
incoming Futures/SemiFutures. collectXUnsafe should be phased out and
|
|
replaced with collectX(...).via(e) where e is a valid non-inline executor.
|
|
*/
|
|
template <class InputIterator>
|
|
SemiFuture<std::vector<
|
|
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
|
|
collectAllSemiFuture(InputIterator first, InputIterator last);
|
|
|
|
/// Sugar for the most common case
|
|
template <class Collection>
|
|
auto collectAllSemiFuture(Collection&& c)
|
|
-> decltype(collectAllSemiFuture(c.begin(), c.end())) {
|
|
return collectAllSemiFuture(c.begin(), c.end());
|
|
}
|
|
|
|
// Unsafe variant, see above comment for details
|
|
template <class InputIterator>
|
|
Future<std::vector<
|
|
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
|
|
collectAllUnsafe(InputIterator first, InputIterator last);
|
|
|
|
// Unsafe variant sugar, see above comment for details
|
|
template <class Collection>
|
|
auto collectAllUnsafe(Collection&& c)
|
|
-> decltype(collectAllUnsafe(c.begin(), c.end())) {
|
|
return collectAllUnsafe(c.begin(), c.end());
|
|
}
|
|
|
|
template <class InputIterator>
|
|
SemiFuture<std::vector<
|
|
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
|
|
collectAll(InputIterator first, InputIterator last);
|
|
|
|
template <class Collection>
|
|
auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) {
|
|
return collectAll(c.begin(), c.end());
|
|
}
|
|
|
|
/// This version takes a varying number of Futures instead of an iterator.
|
|
/// The return type for (Future<T1>, Future<T2>, ...) input
|
|
/// is a SemiFuture<std::tuple<Try<T1>, Try<T2>, ...>>.
|
|
/// The Futures are moved in, so your copies are invalid.
|
|
template <typename... Fs>
|
|
SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
|
|
collectAllSemiFuture(Fs&&... fs);
|
|
|
|
// Unsafe variant of collectAll, see coment above for details. Returns
|
|
// a Future<std::tuple<Try<T1>, Try<T2>, ...>> on the Inline executor.
|
|
template <typename... Fs>
|
|
Future<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
|
|
collectAllUnsafe(Fs&&... fs);
|
|
|
|
template <typename... Fs>
|
|
SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
|
|
collectAll(Fs&&... fs);
|
|
|
|
/// Like collectAll, but will short circuit on the first exception. Thus, the
|
|
/// type of the returned SemiFuture is std::vector<T> instead of
|
|
/// std::vector<Try<T>>
|
|
template <class InputIterator>
|
|
SemiFuture<std::vector<
|
|
typename std::iterator_traits<InputIterator>::value_type::value_type>>
|
|
collect(InputIterator first, InputIterator last);
|
|
|
|
/// Sugar for the most common case
|
|
template <class Collection>
|
|
auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) {
|
|
return collect(c.begin(), c.end());
|
|
}
|
|
|
|
// Unsafe variant of collect. Returns a Future<std::vector<T>> that
|
|
// completes inline.
|
|
template <class InputIterator>
|
|
Future<std::vector<
|
|
typename std::iterator_traits<InputIterator>::value_type::value_type>>
|
|
collectUnsafe(InputIterator first, InputIterator last);
|
|
|
|
/// Sugar for the most common unsafe case. Returns a Future<std::vector<T>>
|
|
// that completes inline.
|
|
template <class Collection>
|
|
auto collectUnsafe(Collection&& c)
|
|
-> decltype(collectUnsafe(c.begin(), c.end())) {
|
|
return collectUnsafe(c.begin(), c.end());
|
|
}
|
|
|
|
/// Like collectAll, but will short circuit on the first exception. Thus, the
|
|
/// type of the returned SemiFuture is std::tuple<T1, T2, ...> instead of
|
|
/// std::tuple<Try<T1>, Try<T2>, ...>
|
|
template <typename... Fs>
|
|
SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect(
|
|
Fs&&... fs);
|
|
|
|
/** The result is a pair of the index of the first Future to complete and
|
|
the Try. If multiple Futures complete at the same time (or are already
|
|
complete when passed in), the "winner" is chosen non-deterministically.
|
|
|
|
This function is thread-safe for Futures running on different threads.
|
|
*/
|
|
template <class InputIterator>
|
|
SemiFuture<std::pair<
|
|
size_t,
|
|
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
|
|
collectAny(InputIterator first, InputIterator last);
|
|
// Unsafe variant of collectAny, Returns a Future that completes inline.
|
|
template <class InputIterator>
|
|
Future<std::pair<
|
|
size_t,
|
|
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
|
|
collectAnyUnsafe(InputIterator first, InputIterator last);
|
|
template <class InputIterator>
|
|
SemiFuture<std::pair<
|
|
size_t,
|
|
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
|
|
collectAnySemiFuture(InputIterator first, InputIterator last);
|
|
|
|
/// Sugar for the most common case
|
|
template <class Collection>
|
|
auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) {
|
|
return collectAny(c.begin(), c.end());
|
|
}
|
|
// Unsafe variant of common form of collectAny, Returns a Future that completes
|
|
// inline.
|
|
template <class Collection>
|
|
auto collectAnyUnsafe(Collection&& c)
|
|
-> decltype(collectAnyUnsafe(c.begin(), c.end())) {
|
|
return collectAnyUnsafe(c.begin(), c.end());
|
|
}
|
|
template <class Collection>
|
|
auto collectAnySemiFuture(Collection&& c)
|
|
-> decltype(collectAnySemiFuture(c.begin(), c.end())) {
|
|
return collectAnySemiFuture(c.begin(), c.end());
|
|
}
|
|
|
|
/** Similar to collectAny, collectAnyWithoutException return the first Future to
|
|
* complete without exceptions. If none of the future complete without
|
|
* exceptions, the last exception will be returned as a result.
|
|
*/
|
|
template <class InputIterator>
|
|
SemiFuture<std::pair<
|
|
size_t,
|
|
typename std::iterator_traits<InputIterator>::value_type::value_type>>
|
|
collectAnyWithoutException(InputIterator first, InputIterator last);
|
|
|
|
/// Sugar for the most common case
|
|
template <class Collection>
|
|
auto collectAnyWithoutException(Collection&& c)
|
|
-> decltype(collectAnyWithoutException(c.begin(), c.end())) {
|
|
return collectAnyWithoutException(c.begin(), c.end());
|
|
}
|
|
|
|
/** when n Futures have completed, the Future completes with a vector of
|
|
the index and Try of those n Futures (the indices refer to the original
|
|
order, but the result vector will be in an arbitrary order)
|
|
|
|
Not thread safe.
|
|
*/
|
|
template <class InputIterator>
|
|
SemiFuture<std::vector<std::pair<
|
|
size_t,
|
|
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
|
|
collectN(InputIterator first, InputIterator last, size_t n);
|
|
|
|
/// Sugar for the most common case
|
|
template <class Collection>
|
|
auto collectN(Collection&& c, size_t n)
|
|
-> decltype(collectN(c.begin(), c.end(), n)) {
|
|
return collectN(c.begin(), c.end(), n);
|
|
}
|
|
|
|
/** window creates up to n Futures using the values
|
|
in the collection, and then another Future for each Future
|
|
that completes
|
|
|
|
this is basically a sliding window of Futures of size n
|
|
|
|
func must return a Future for each value in input
|
|
*/
|
|
template <
|
|
class Collection,
|
|
class F,
|
|
class ItT = typename std::iterator_traits<
|
|
typename Collection::iterator>::value_type,
|
|
class Result = typename invoke_result_t<F, ItT&&>::value_type>
|
|
std::vector<Future<Result>> window(Collection input, F func, size_t n);
|
|
|
|
template <
|
|
class Collection,
|
|
class F,
|
|
class ItT = typename std::iterator_traits<
|
|
typename Collection::iterator>::value_type,
|
|
class Result = typename invoke_result_t<F, ItT&&>::value_type>
|
|
std::vector<Future<Result>>
|
|
window(Executor::KeepAlive<> executor, Collection input, F func, size_t n);
|
|
|
|
template <typename F, typename T, typename ItT>
|
|
using MaybeTryArg = typename std::
|
|
conditional<is_invocable_v<F, T&&, Try<ItT>&&>, Try<ItT>, ItT>::type;
|
|
|
|
/** repeatedly calls func on every result, e.g.
|
|
reduce(reduce(reduce(T initial, result of first), result of second), ...)
|
|
|
|
The type of the final result is a Future of the type of the initial value.
|
|
|
|
Func can either return a T, or a Future<T>
|
|
|
|
func is called in order of the input, see unorderedReduce if that is not
|
|
a requirement
|
|
*/
|
|
template <class It, class T, class F>
|
|
Future<T> reduce(It first, It last, T&& initial, F&& func);
|
|
|
|
/// Sugar for the most common case
|
|
template <class Collection, class T, class F>
|
|
auto reduce(Collection&& c, T&& initial, F&& func) -> decltype(folly::reduce(
|
|
c.begin(),
|
|
c.end(),
|
|
std::forward<T>(initial),
|
|
std::forward<F>(func))) {
|
|
return folly::reduce(
|
|
c.begin(), c.end(), std::forward<T>(initial), std::forward<F>(func));
|
|
}
|
|
|
|
/** like reduce, but calls func on finished futures as they complete
|
|
does NOT keep the order of the input
|
|
*/
|
|
template <class It, class T, class F>
|
|
Future<T> unorderedReduce(It first, It last, T initial, F func);
|
|
|
|
/// Sugar for the most common case
|
|
template <class Collection, class T, class F>
|
|
auto unorderedReduce(Collection&& c, T&& initial, F&& func)
|
|
-> decltype(folly::unorderedReduce(
|
|
c.begin(),
|
|
c.end(),
|
|
std::forward<T>(initial),
|
|
std::forward<F>(func))) {
|
|
return folly::unorderedReduce(
|
|
c.begin(), c.end(), std::forward<T>(initial), std::forward<F>(func));
|
|
}
|
|
|
|
/// Carry out the computation contained in the given future if
|
|
/// while the predicate continues to hold.
|
|
///
|
|
/// if thunk behaves like std::function<Future<T2>(void)>
|
|
/// returns Future<Unit>
|
|
/// if thunk behaves like std::function<SemiFuture<T2>(void)>
|
|
/// returns SemiFuture<Unit>
|
|
/// predicate behaves like std::function<bool(void)>
|
|
template <class P, class F>
|
|
typename std::enable_if<isFuture<invoke_result_t<F>>::value, Future<Unit>>::type
|
|
whileDo(P&& predicate, F&& thunk);
|
|
template <class P, class F>
|
|
typename std::
|
|
enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type
|
|
whileDo(P&& predicate, F&& thunk);
|
|
|
|
/// Repeat the given future (i.e., the computation it contains) n times.
|
|
///
|
|
/// thunk behaves like
|
|
/// std::function<Future<T2>(void)>
|
|
/// or
|
|
/// std::function<SemiFuture<T2>(void)>
|
|
template <class F>
|
|
auto times(int n, F&& thunk);
|
|
} // namespace folly
|
|
|
|
#if FOLLY_HAS_COROUTINES
|
|
|
|
namespace folly {
|
|
namespace detail {
|
|
|
|
template <typename T>
|
|
class FutureAwaitable {
|
|
public:
|
|
explicit FutureAwaitable(folly::Future<T>&& future) noexcept
|
|
: future_(std::move(future)) {}
|
|
|
|
bool await_ready() {
|
|
if (future_.isReady()) {
|
|
result_ = std::move(future_.getTry());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
T await_resume() {
|
|
return std::move(result_).value();
|
|
}
|
|
|
|
Try<T> await_resume_try() {
|
|
return std::move(result_);
|
|
}
|
|
|
|
FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend(
|
|
std::experimental::coroutine_handle<> h) {
|
|
// FutureAwaitable may get destroyed as soon as the callback is executed.
|
|
// Make sure the future object doesn't get destroyed until setCallback_
|
|
// returns.
|
|
auto future = std::move(future_);
|
|
future.setCallback_(
|
|
[this, h](Executor::KeepAlive<>&&, Try<T>&& result) mutable {
|
|
result_ = std::move(result);
|
|
h.resume();
|
|
});
|
|
}
|
|
|
|
private:
|
|
folly::Future<T> future_;
|
|
folly::Try<T> result_;
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
template <typename T>
|
|
inline detail::FutureAwaitable<T>
|
|
/* implicit */ operator co_await(Future<T>&& future) noexcept {
|
|
return detail::FutureAwaitable<T>(std::move(future));
|
|
}
|
|
|
|
} // namespace folly
|
|
#endif
|
|
|
|
#include <folly/futures/Future-inl.h>
|