Commit 9d6a5522 authored by Marshall Cline's avatar Marshall Cline Committed by Facebook Github Bot

add method-level contracts to SemiFuture & Future

Summary:
method-level contracts

(Note: this ignores all push blocking failures!)

Reviewed By: yfeldblum

Differential Revision: D8132350

fbshipit-source-id: d79b1d88a38ea97de8d0e841e822805aff3959ae
parent 71bfdd3e
...@@ -104,7 +104,13 @@ class FutureBase { ...@@ -104,7 +104,13 @@ class FutureBase {
public: public:
typedef T value_type; typedef T value_type;
/// Construct a Future from a value (perfect forwarding) /// Construct from a value (perfect forwarding)
///
/// Postconditions:
///
/// - `valid() == true`
/// - `isReady() == true`
/// - `hasValue() == true`
template < template <
class T2 = T, class T2 = T,
typename = typename std::enable_if< typename = typename std::enable_if<
...@@ -112,6 +118,13 @@ class FutureBase { ...@@ -112,6 +118,13 @@ class FutureBase {
!isSemiFuture<typename std::decay<T2>::type>::value>::type> !isSemiFuture<typename std::decay<T2>::type>::value>::type>
/* implicit */ FutureBase(T2&& val); /* implicit */ FutureBase(T2&& val);
/// Construct a (logical) FutureBase-of-void.
///
/// Postconditions:
///
/// - `valid() == true`
/// - `isReady() == true`
/// - `hasValue() == true`
template <class T2 = T> template <class T2 = T>
/* implicit */ FutureBase( /* implicit */ FutureBase(
typename std::enable_if<std::is_same<Unit, T2>::value>::type*); typename std::enable_if<std::is_same<Unit, T2>::value>::type*);
...@@ -133,44 +146,97 @@ class FutureBase { ...@@ -133,44 +146,97 @@ class FutureBase {
~FutureBase(); ~FutureBase();
/// true if this has a shared state; /// true if this has a shared state;
/// false if this has been consumed/moved-out. /// false if this has been either moved-out or created without a shared state.
bool valid() const noexcept { bool valid() const noexcept {
return core_ != nullptr; return core_ != nullptr;
} }
/// Returns a reference to the result, with a reference category and const- /// Returns a reference to the result value if it is ready, with a reference
/// qualification equivalent to the reference category and const-qualification /// category and const-qualification like those of the future.
/// of the receiver.
/// ///
/// If moved-from, throws FutureInvalid. /// Does not `wait()`; see `get()` for that.
/// ///
/// If !isReady(), throws FutureNotReady. /// Preconditions:
/// ///
/// If an exception has been captured, throws that exception. /// - `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& value() &;
T const& value() const&; T const& value() const&;
T&& value() &&; T&& value() &&;
T const&& value() const&&; T const&& value() const&&;
/// Returns a reference to the try of the result. Throws as for value if /// Returns a reference to the result's Try if it is ready, with a reference
/// future is not valid. /// 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>& result() &;
Try<T> const& result() const&; Try<T> const& result() const&;
Try<T>&& result() &&; Try<T>&& result() &&;
Try<T> const&& result() const&&; Try<T> const&& result() const&&;
/** True when the result (or exception) is ready. */ /// True when the result (or exception) is ready; see value(), result(), etc.
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
bool isReady() const; bool isReady() const;
/// sugar for getTry().hasValue() /// True if the result is an exception (not a value) 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; bool hasValue() const;
/// sugar for getTry().hasException() /// True if the result is ready (`isReady() == true`) and the result is an
/// exception (not a value).
///
/// Equivalent to result().hasException()
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
/// - `isReady() == true` (else throws FutureNotReady)
bool hasException() const; bool hasException() const;
/// If the promise has been fulfilled, return an Optional with the Try<T>. /// Returns either an Optional holding the result or an empty Optional
/// Otherwise return an empty Optional. /// depending on whether or not (respectively) the promise has been
/// Note that this moves the Try<T> out. /// 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(); Optional<Try<T>> poll();
/// This is not the method you're looking for. /// This is not the method you're looking for.
...@@ -181,22 +247,84 @@ class FutureBase { ...@@ -181,22 +247,84 @@ class FutureBase {
template <class F> template <class F>
void setCallback_(F&& func); void setCallback_(F&& func);
/// 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> template <class E>
void raise(E&& exception) { void raise(E&& exception) {
raise(make_exception_wrapper<typename std::remove_reference<E>::type>( raise(make_exception_wrapper<typename std::remove_reference<E>::type>(
std::forward<E>(exception))); std::forward<E>(exception)));
} }
/// Raise an interrupt. If the promise holder has an interrupt /// Raises a FutureCancellation interrupt.
/// handler it will be called and potentially stop asynchronous work from /// See `raise(exception_wrapper)` for details.
/// being done. This is advisory only - a promise holder may not set an
/// interrupt handler, or may do anything including ignore. But, if you know
/// your future supports this the most likely result is stopping or
/// preventing the asynchronous operation (if in time), and the promise
/// holder setting an exception on the future. (That may happen
/// asynchronously, of course.)
void raise(exception_wrapper interrupt);
void cancel() { void cancel() {
raise(FutureCancellation()); raise(FutureCancellation());
} }
...@@ -265,6 +393,10 @@ class FutureBase { ...@@ -265,6 +393,10 @@ class FutureBase {
return getCore().getExecutor(); return getCore().getExecutor();
} }
// 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(Executor* x, int8_t priority = Executor::MID_PRI) { void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) {
getCore().setExecutor(x, priority); getCore().setExecutor(x, priority);
} }
...@@ -295,7 +427,7 @@ void convertFuture(SemiFuture<T>&& sf, Future<T>& f); ...@@ -295,7 +427,7 @@ void convertFuture(SemiFuture<T>&& sf, Future<T>& f);
} // namespace detail } // namespace detail
} // namespace futures } // namespace futures
/// The interface (along with SemiFuture) for the consumer-side of a /// The interface (along with Future) for the consumer-side of a
/// producer/consumer pair. /// producer/consumer pair.
/// ///
/// Future vs. SemiFuture: /// Future vs. SemiFuture:
...@@ -317,7 +449,7 @@ void convertFuture(SemiFuture<T>&& sf, Future<T>& f); ...@@ -317,7 +449,7 @@ void convertFuture(SemiFuture<T>&& sf, Future<T>& f);
/// - the two styles cannot be mixed within the same future; use one or the /// - the two styles cannot be mixed within the same future; use one or the
/// other. /// other.
/// ///
/// SemiFuture/Future also provide a back-channel so an interruption request can /// SemiFuture/Future also provide a back-channel so an interrupt can
/// be sent from consumer to producer; see SemiFuture/Future's `raise()` /// be sent from consumer to producer; see SemiFuture/Future's `raise()`
/// and Promise's `setInterruptHandler()`. /// and Promise's `setInterruptHandler()`.
/// ///
...@@ -333,14 +465,25 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -333,14 +465,25 @@ class SemiFuture : private futures::detail::FutureBase<T> {
public: public:
~SemiFuture(); ~SemiFuture();
static SemiFuture<T> makeEmpty(); // equivalent to moved-from /// Creates/returns an invalid SemiFuture, that is, one with no shared state.
///
/// Postcondition:
///
/// - `RESULT.valid() == false`
static SemiFuture<T> makeEmpty();
// Export public interface of FutureBase /// Type of the value that the producer, when successful, produces.
// FutureBase is inherited privately to avoid subclasses being cast to
// a FutureBase pointer
using typename Base::value_type; using typename Base::value_type;
/// Construct a SemiFuture from a value (perfect forwarding) /// 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 < template <
class T2 = T, class T2 = T,
typename = typename std::enable_if< typename = typename std::enable_if<
...@@ -348,11 +491,27 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -348,11 +491,27 @@ class SemiFuture : private futures::detail::FutureBase<T> {
!isSemiFuture<typename std::decay<T2>::type>::value>::type> !isSemiFuture<typename std::decay<T2>::type>::value>::type>
/* implicit */ SemiFuture(T2&& val) : Base(std::forward<T2>(val)) {} /* 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> template <class T2 = T>
/* implicit */ SemiFuture( /* implicit */ SemiFuture(
typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr) typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr)
: Base(p) {} : 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 < template <
class... Args, class... Args,
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>:: typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
...@@ -381,57 +540,141 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -381,57 +540,141 @@ class SemiFuture : private futures::detail::FutureBase<T> {
SemiFuture& operator=(SemiFuture&&) noexcept; SemiFuture& operator=(SemiFuture&&) noexcept;
SemiFuture& operator=(Future<T>&&) noexcept; SemiFuture& operator=(Future<T>&&) noexcept;
/// Block until the future is fulfilled. Returns the value (moved out), or /// Blocks until the promise is fulfilled, either by value (which is returned)
/// throws the exception. The future must not already have a callback. /// or exception (which is thrown).
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
/// - must not have a continuation, e.g., via `.then()` or similar
///
/// Postconditions:
///
/// - `valid() == false`
T get() &&; T get() &&;
/// Block until the future is fulfilled, or until timed out. Returns the /// Blocks until the semifuture is fulfilled, or until `dur` elapses. Returns
/// value (moved out), or throws the exception (which might be a FutureTimeout /// the value (moved-out), or throws the exception (which might be a
/// FutureTimeout).
/// exception). /// exception).
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
T get(Duration dur) &&; T get(Duration dur) &&;
/// Block until the future is fulfilled. Returns the Try of the value (moved /// Blocks until the future is fulfilled. Returns the Try of the result
/// out). /// (moved-out).
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
Try<T> getTry() &&; Try<T> getTry() &&;
/// Block until the future is fulfilled, or until timed out. Returns the /// Blocks until the future is fulfilled, or until `dur` elapses.
/// Try of the value (moved out) or may throw a FutureTimeout exception. /// Returns the Try of the result (moved-out), or throws FutureTimeout
/// exception.
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - if returns (no exception), moves-out the Try; treat `*this` as if
/// `!valid()`.
/// - on FutureTimeout exception, `valid()` remains true.
Try<T> getTry(Duration dur) &&; Try<T> getTry(Duration dur) &&;
/// Block until this Future is complete. Returns a reference to this Future. /// 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() &; SemiFuture<T>& wait() &;
/// Overload of wait() for rvalue Futures /// 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() &&; SemiFuture<T>&& wait() &&;
/// Block until this Future is complete or until the given Duration passes. /// Blocks until the future is fulfilled, or `dur` elapses.
/// Returns a reference to this Future ///
SemiFuture<T>& wait(Duration) &; /// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == true`
/// - `&RESULT == this`
/// - `isReady()` will be indeterminate - may or may not be true
SemiFuture<T>& wait(Duration dur) &;
/// Overload of wait(Duration) for rvalue Futures /// Blocks until the future is fulfilled, or `dur` elapses.
SemiFuture<T>&& wait(Duration) &&; ///
/// 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
SemiFuture<T>&& wait(Duration dur) &&;
/// Returns a Future which will call back on the other side of executor. /// Returns a Future which will call back on the other side of executor.
///
/// The ref-qualifier allows for `this` to be moved out so we
/// don't get access-after-free situations in chaining.
/// https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/
Future<T> via(Executor* executor, int8_t priority = Executor::MID_PRI) &&; Future<T> via(Executor* executor, int8_t priority = Executor::MID_PRI) &&;
Future<T> via( Future<T> via(
Executor::KeepAlive<> executor, Executor::KeepAlive<> executor,
int8_t priority = Executor::MID_PRI) &&; int8_t priority = Executor::MID_PRI) &&;
/** /// Defer work to run on the consumer of the future.
* Defer work to run on the consumer of the future. /// Function must take a Try as a parameter.
* Function must take a Try as a parameter. /// This work will be run either on an executor that the caller sets on the
* This work will be run either on an executor that the caller sets on the /// SemiFuture, or inline with the call to .get().
* SemiFuture, or inline with the call to .get(). ///
* NB: This is a custom method because boost-blocking executors is a /// NB: This is a custom method because boost-blocking executors is a
* special-case for work deferral in folly. With more general boost-blocking /// 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 /// support all executors would boost block and we would simply use some form
* of driveable executor here. /// of driveable executor here.
*/ ///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
/// - `RESULT.valid() == true`
template <typename F> template <typename F>
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type> SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
defer(F&& func) &&; defer(F&& func) &&;
...@@ -441,9 +684,16 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -441,9 +684,16 @@ class SemiFuture : private futures::detail::FutureBase<T> {
return std::move(*this).defer(&func); return std::move(*this).defer(&func);
} }
/** /// Defer for functions taking a T rather than a Try<T>.
* Defer for functions taking a T rather than a Try<T>. ///
*/ /// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
/// - `RESULT.valid() == true`
template <typename F> template <typename F>
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type> SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
deferValue(F&& func) &&; deferValue(F&& func) &&;
...@@ -453,11 +703,13 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -453,11 +703,13 @@ class SemiFuture : private futures::detail::FutureBase<T> {
return std::move(*this).deferValue(&func); return std::move(*this).deferValue(&func);
} }
/// Set an error continuation for this SemiFuture. The continuation should /// Set an error continuation for this SemiFuture where the continuation can
/// take a single argument of the type that you want to catch, and should /// be called with a known exception type and returns a `T`, `Future<T>`, or
/// return a `T`, `Future<T>` or `SemiFuture<`T`> (see overload below). /// `SemiFuture<T>`.
/// For instance, ///
/// Example:
/// ///
/// ```
/// makeSemiFuture() /// makeSemiFuture()
/// .defer([] { /// .defer([] {
/// throw std::runtime_error("oh no!"); /// throw std::runtime_error("oh no!");
...@@ -465,10 +717,18 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -465,10 +717,18 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/// }) /// })
/// .deferError<std::runtime_error>([] (auto const& e) { /// .deferError<std::runtime_error>([] (auto const& e) {
/// LOG(INFO) << "std::runtime_error: " << e.what(); /// LOG(INFO) << "std::runtime_error: " << e.what();
/// return -1; // or makeSemiFuture<int>(-1) /// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)
/// }); /// });
/// Overload of deferError where continuation can be called with a known /// ```
/// exception type and returns T, Future<T> or SemiFuture<T> ///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
/// - `RESULT.valid() == true`
template <class ExceptionType, class F> template <class ExceptionType, class F>
SemiFuture<T> deferError(F&& func) &&; SemiFuture<T> deferError(F&& func) &&;
...@@ -477,8 +737,32 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -477,8 +737,32 @@ class SemiFuture : private futures::detail::FutureBase<T> {
return std::move(*this).template deferError<ExceptionType>(&func); return std::move(*this).template deferError<ExceptionType>(&func);
} }
/// Overload of deferError where continuation can be called with /// Set an error continuation for this SemiFuture where the continuation can
/// exception_wrapper&& and returns T, Future<T> or SemiFuture<T> /// 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> template <class F>
SemiFuture<T> deferError(F&& func) &&; SemiFuture<T> deferError(F&& func) &&;
...@@ -500,10 +784,22 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -500,10 +784,22 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/// Return a future that completes inline, as if the future had no executor. /// Return a future that completes inline, as if the future had no executor.
/// Intended for porting legacy code without behavioural change, and for rare /// Intended for porting legacy code without behavioural change, and for rare
/// cases where this is really the intended behaviour. /// cases where this is really the intended behaviour.
/// 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 /// Future is unsafe in the sense that the executor it completes on is
/// non-deterministic in the standard case. /// non-deterministic in the standard case.
/// For new code, or to update code that temporarily uses this, please /// For new code, or to update code that temporarily uses this, please
/// use via and pass a meaningful executor. /// use via and pass a meaningful executor.
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
/// - `RESULT.valid() == true`
Future<T> toUnsafeFuture() &&; Future<T> toUnsafeFuture() &&;
#if FOLLY_HAS_COROUTINES #if FOLLY_HAS_COROUTINES
...@@ -562,6 +858,7 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -562,6 +858,7 @@ class SemiFuture : private futures::detail::FutureBase<T> {
explicit SemiFuture(futures::detail::EmptyConstruct) noexcept explicit SemiFuture(futures::detail::EmptyConstruct) noexcept
: Base(futures::detail::EmptyConstruct{}) {} : Base(futures::detail::EmptyConstruct{}) {}
// Throws FutureInvalid if !this->core_
DeferredExecutor* getDeferredExecutor() const; DeferredExecutor* getDeferredExecutor() const;
static void releaseDeferredExecutor(corePtr core); static void releaseDeferredExecutor(corePtr core);
...@@ -596,7 +893,7 @@ std::pair<Promise<T>, SemiFuture<T>> makePromiseContract() { ...@@ -596,7 +893,7 @@ std::pair<Promise<T>, SemiFuture<T>> makePromiseContract() {
/// - the two styles cannot be mixed within the same future; use one or the /// - the two styles cannot be mixed within the same future; use one or the
/// other. /// other.
/// ///
/// SemiFuture/Future also provide a back-channel so an interruption request can /// SemiFuture/Future also provide a back-channel so an interrupt can
/// be sent from consumer to producer; see SemiFuture/Future's `raise()` /// be sent from consumer to producer; see SemiFuture/Future's `raise()`
/// and Promise's `setInterruptHandler()`. /// and Promise's `setInterruptHandler()`.
/// ///
...@@ -608,12 +905,17 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -608,12 +905,17 @@ class Future : private futures::detail::FutureBase<T> {
using Base = futures::detail::FutureBase<T>; using Base = futures::detail::FutureBase<T>;
public: public:
// Export public interface of FutureBase /// Type of the value that the producer, when successful, produces.
// FutureBase is inherited privately to avoid subclasses being cast to
// a FutureBase pointer
using typename Base::value_type; using typename Base::value_type;
/// Construct a Future from a value (perfect forwarding) /// Construct a Future from a value (perfect forwarding)
///
/// Postconditions:
///
/// - `valid() == true`
/// - `isReady() == true`
/// - `hasValue() == true`
/// - `value()`, `get()`, `result()` will return the forwarded `T`
template < template <
class T2 = T, class T2 = T,
typename = typename std::enable_if< typename = typename std::enable_if<
...@@ -621,11 +923,27 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -621,11 +923,27 @@ class Future : private futures::detail::FutureBase<T> {
!isSemiFuture<typename std::decay<T2>::type>::value>::type> !isSemiFuture<typename std::decay<T2>::type>::value>::type>
/* implicit */ Future(T2&& val) : Base(std::forward<T2>(val)) {} /* 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> template <class T2 = T>
/* implicit */ Future( /* implicit */ Future(
typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr) typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr)
: Base(p) {} : 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 < template <
class... Args, class... Args,
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>:: typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
...@@ -673,7 +991,12 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -673,7 +991,12 @@ class Future : private futures::detail::FutureBase<T> {
using Base::value; using Base::value;
using Base::result; using Base::result;
static Future<T> makeEmpty(); // equivalent to moved-from /// Creates/returns an invalid Future, that is, one with no shared state.
///
/// Postcondition:
///
/// - `RESULT.valid() == false`
static Future<T> makeEmpty();
// not copyable // not copyable
Future& operator=(Future const&) = delete; Future& operator=(Future const&) = delete;
...@@ -681,13 +1004,18 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -681,13 +1004,18 @@ class Future : private futures::detail::FutureBase<T> {
// movable // movable
Future& operator=(Future&&) noexcept; Future& operator=(Future&&) noexcept;
/// Call e->drive() repeatedly until the future is fulfilled. Examples /// Call e->drive() repeatedly until the future is fulfilled.
/// of DrivableExecutor include EventBase and ManualExecutor. Returns the ///
/// value (moved out), or throws the exception. /// Examples of DrivableExecutor include EventBase and ManualExecutor.
///
/// Returns the fulfilled value (moved-out) or throws the fulfilled exception.
T getVia(DrivableExecutor* e); T getVia(DrivableExecutor* e);
/// getVia but will wait only until timed out. Returns the /// Call e->drive() repeatedly until the future is fulfilled, or `dur`
/// Try of the value (moved out) or may throw a FutureTimeout exception. /// elapses.
///
/// Returns the fulfilled value (moved-out), throws the fulfilled exception,
/// or on timeout throws FutureTimeout.
T getVia(TimedDrivableExecutor* e, Duration dur); T getVia(TimedDrivableExecutor* e, Duration dur);
/// Call e->drive() repeatedly until the future is fulfilled. Examples /// Call e->drive() repeatedly until the future is fulfilled. Examples
...@@ -695,8 +1023,8 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -695,8 +1023,8 @@ class Future : private futures::detail::FutureBase<T> {
/// reference to the Try of the value. /// reference to the Try of the value.
Try<T>& getTryVia(DrivableExecutor* e); Try<T>& getTryVia(DrivableExecutor* e);
/// getTryVia but will wait only until timed out. Returns the /// getTryVia but will wait only until `dur` elapses. Returns the
/// Try of the value (moved out) or may throw a FutureTimeout exception. /// Try of the value (moved-out) or may throw a FutureTimeout exception.
Try<T>& getTryVia(TimedDrivableExecutor* e, Duration dur); Try<T>& getTryVia(TimedDrivableExecutor* e, Duration dur);
/// Unwraps the case of a Future<Future<T>> instance, and returns a simple /// Unwraps the case of a Future<Future<T>> instance, and returns a simple
...@@ -708,44 +1036,59 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -708,44 +1036,59 @@ class Future : private futures::detail::FutureBase<T> {
/// Returns a Future which will call back on the other side of executor. /// Returns a Future which will call back on the other side of executor.
/// ///
/// The ref-qualifier allows for `this` to be moved out so we /// Preconditions:
/// don't get access-after-free situations in chaining. ///
/// https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/ /// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
/// - `RESULT.valid() == true`
Future<T> via(Executor* executor, int8_t priority = Executor::MID_PRI) &&; Future<T> via(Executor* executor, int8_t priority = Executor::MID_PRI) &&;
Future<T> via( Future<T> via(
Executor::KeepAlive<> executor, Executor::KeepAlive<> executor,
int8_t priority = Executor::MID_PRI) &&; int8_t priority = Executor::MID_PRI) &&;
/// This variant creates a new future, where the ref-qualifier && version /// Returns a Future which will call back on the other side of executor.
/// moves `this` out. This one is less efficient but avoids confusing users ///
/// when "return f.via(x);" fails. /// 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* executor, int8_t priority = Executor::MID_PRI) &; Future<T> via(Executor* executor, int8_t priority = Executor::MID_PRI) &;
Future<T> via( Future<T> via(
Executor::KeepAlive<> executor, Executor::KeepAlive<> executor,
int8_t priority = Executor::MID_PRI) &; int8_t priority = Executor::MID_PRI) &;
/** When this Future has completed, execute func which is a function that /// When this Future has completed, execute func which is a function that
takes one of: /// can be called with either `T&&` or `Try<T>&&`.
(const) Try<T>&& ///
(const) Try<T>& /// Func shall return either another Future or a value.
(const) Try<T> ///
(const) T&& /// A Future for the return type of func is returned.
(const) T& ///
(const) T /// ```
(void) /// Future<string> f2 = f1.then([](Try<T>&&) { return string("foo"); });
/// ```
Func shall return either another Future or a value. ///
/// Preconditions:
A Future for the return type of func is returned. ///
/// - `valid() == true` (else throws FutureInvalid)
Future<string> f2 = f1.then([](Try<T>&&) { return string("foo"); }); ///
/// Postconditions:
The Future given to the functor is ready, and the functor may call ///
value(), which may rethrow if this has captured an exception. If func /// - Calling code should act as if `valid() == false`,
throws, the exception will be captured in the Future that is returned. /// i.e., as if `*this` was moved into RESULT.
*/ /// - `RESULT.valid() == true`
template <typename F, typename R = futures::detail::callableResult<T, F>> template <typename F, typename R = futures::detail::callableResult<T, F>>
typename R::Return then(F&& func) { typename R::Return then(F&& func) {
return this->template thenImplementation<F, R>( return this->template thenImplementation<F, R>(
...@@ -762,6 +1105,16 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -762,6 +1105,16 @@ class Future : private futures::detail::FutureBase<T> {
/// This is just sugar for /// This is just sugar for
/// ///
/// f1.then(std::bind(&Worker::doWork, w)); /// f1.then(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> template <typename R, typename Caller, typename... Args>
Future<typename isFuture<R>::Inner> then( Future<typename isFuture<R>::Inner> then(
R (Caller::*func)(Args...), R (Caller::*func)(Args...),
...@@ -779,6 +1132,16 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -779,6 +1132,16 @@ class Future : private futures::detail::FutureBase<T> {
/// ///
/// In the former both b and c execute via x. In the latter, only b executes /// 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. /// 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 Executor, class Arg, class... Args> template <class Executor, class Arg, class... Args>
auto then(Executor* x, Arg&& arg, Args&&... args) { auto then(Executor* x, Arg&& arg, Args&&... args) {
auto oldX = this->getExecutor(); auto oldX = this->getExecutor();
...@@ -787,33 +1150,56 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -787,33 +1150,56 @@ class Future : private futures::detail::FutureBase<T> {
.via(oldX); .via(oldX);
} }
/** When this Future has completed, execute func which is a function that /// When this Future has completed, execute func which is a function that
takes one of: /// can be called with `Try<T>&&` (often a lambda with parameter type
(const) Try<T>&& /// `auto&&` or `auto`).
(const) Try<T>& ///
(const) Try<T> /// Func shall return either another Future or a value.
///
Func shall return either another Future or a value. /// A Future for the return type of func is returned.
///
A Future for the return type of func is returned. /// ```
/// Future<string> f2 = std::move(f1).thenTry([](auto&& t) {
Future<string> f2 = f1.then([](Try<T>&&) { return string("foo"); }); /// ...
/// return string("foo");
The Future given to the functor is ready, and the functor may call /// });
value(), which may rethrow if this has captured an exception. If func /// ```
throws, the exception will be captured in the Future that is returned. ///
*/ /// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
/// - `RESULT.valid() == true`
template <typename F> template <typename F>
Future<typename futures::detail::tryCallableResult<T, F>::value_type> thenTry( Future<typename futures::detail::tryCallableResult<T, F>::value_type> thenTry(
F&& func) &&; F&& func) &&;
/** When this Future has completed, execute func which is a function that /// When this Future has completed, execute func which is a function that
takes one of: /// can be called with `T&&` (often a lambda with parameter type
(const) T&& /// `auto&&` or `auto`).
(const) T& ///
(const) T /// Func shall return either another Future or a value.
(void) ///
*/ /// 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> template <typename F>
Future<typename futures::detail::valueCallableResult<T, F>::value_type> Future<typename futures::detail::valueCallableResult<T, F>::value_type>
thenValue(F&& func) &&; thenValue(F&& func) &&;
...@@ -823,11 +1209,13 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -823,11 +1209,13 @@ class Future : private futures::detail::FutureBase<T> {
return std::move(*this).thenValue(&func); return std::move(*this).thenValue(&func);
} }
/// Set an error callback for this Future. The callback should take a /// Set an error continuation for this Future where the continuation can
/// single argument of the type that you want to catch, and should return /// be called with a known exception type and returns a `T`, `Future<T>`, or
/// T, SemiFuture<T> or Future<T> /// `SemiFuture<T>`.
/// (see overload below). For instance, ///
/// Example:
/// ///
/// ```
/// makeFuture() /// makeFuture()
/// .thenTry([] { /// .thenTry([] {
/// throw std::runtime_error("oh no!"); /// throw std::runtime_error("oh no!");
...@@ -835,10 +1223,18 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -835,10 +1223,18 @@ class Future : private futures::detail::FutureBase<T> {
/// }) /// })
/// .thenError<std::runtime_error>([] (auto const& e) { /// .thenError<std::runtime_error>([] (auto const& e) {
/// LOG(INFO) << "std::runtime_error: " << e.what(); /// LOG(INFO) << "std::runtime_error: " << e.what();
/// return -1; // or makeSemiFuture<int>(-1) /// return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)
/// }); /// });
/// Overload of thenError where continuation can be called with a known /// ```
/// exception type and returns T or Future<T> ///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
/// - `RESULT.valid() == true`
template <class ExceptionType, class F> template <class ExceptionType, class F>
Future<T> thenError(F&& func) &&; Future<T> thenError(F&& func) &&;
...@@ -847,8 +1243,32 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -847,8 +1243,32 @@ class Future : private futures::detail::FutureBase<T> {
return std::move(*this).template thenError<ExceptionType>(&func); return std::move(*this).template thenError<ExceptionType>(&func);
} }
/// Overload of thenError where continuation can be called with /// Set an error continuation for this Future where the continuation can
/// exception_wrapper&& and returns T or Future<T> /// 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> template <class F>
Future<T> thenError(F&& func) &&; Future<T> thenError(F&& func) &&;
...@@ -860,16 +1280,36 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -860,16 +1280,36 @@ class Future : private futures::detail::FutureBase<T> {
/// Convenience method for ignoring the value and creating a Future<Unit>. /// Convenience method for ignoring the value and creating a Future<Unit>.
/// Exceptions still propagate. /// Exceptions still propagate.
/// This function is identical to .unit(). /// 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(); Future<Unit> then();
/// Convenience method for ignoring the value and creating a Future<Unit>. /// Convenience method for ignoring the value and creating a Future<Unit>.
/// Exceptions still propagate. /// Exceptions still propagate.
/// This function is identical to parameterless .then(). /// 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() { Future<Unit> unit() {
return then(); return then();
} }
/// Set an error callback for this Future. The callback should take a single /// 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 /// 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 /// the same type as this Future, or a Future of that type (see overload
/// below). For instance, /// below). For instance,
...@@ -883,6 +1323,16 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -883,6 +1323,16 @@ class Future : private futures::detail::FutureBase<T> {
/// LOG(INFO) << "std::runtime_error: " << e.what(); /// LOG(INFO) << "std::runtime_error: " << e.what();
/// return -1; // or makeFuture<int>(-1) /// 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> template <class F>
typename std::enable_if< typename std::enable_if<
!is_invocable<F, exception_wrapper>::value && !is_invocable<F, exception_wrapper>::value &&
...@@ -890,7 +1340,17 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -890,7 +1340,17 @@ class Future : private futures::detail::FutureBase<T> {
Future<T>>::type Future<T>>::type
onError(F&& func); onError(F&& func);
/// Overload of onError where the error callback returns a Future<T> /// 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> template <class F>
typename std::enable_if< typename std::enable_if<
!is_invocable<F, exception_wrapper>::value && !is_invocable<F, exception_wrapper>::value &&
...@@ -899,6 +1359,16 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -899,6 +1359,16 @@ class Future : private futures::detail::FutureBase<T> {
onError(F&& func); onError(F&& func);
/// Overload of onError that takes exception_wrapper and returns Future<T> /// 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> template <class F>
typename std::enable_if< typename std::enable_if<
is_invocable<F, exception_wrapper>::value && is_invocable<F, exception_wrapper>::value &&
...@@ -907,6 +1377,16 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -907,6 +1377,16 @@ class Future : private futures::detail::FutureBase<T> {
onError(F&& func); onError(F&& func);
/// Overload of onError that takes exception_wrapper and returns T /// 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> template <class F>
typename std::enable_if< typename std::enable_if<
is_invocable<F, exception_wrapper>::value && is_invocable<F, exception_wrapper>::value &&
...@@ -918,37 +1398,90 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -918,37 +1398,90 @@ class Future : private futures::detail::FutureBase<T> {
/// the value/exception is passed through to the resulting Future. /// the value/exception is passed through to the resulting Future.
/// func shouldn't throw, but if it does it will be captured and propagated, /// func shouldn't throw, but if it does it will be captured and propagated,
/// and discard any value/exception that this Future has obtained. /// 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> template <class F>
Future<T> ensure(F&& func); Future<T> ensure(F&& func);
/// Like onError, but for timeouts. example: /// Like onError, but for timeouts. example:
/// ///
/// Future<int> f = makeFuture<int>(42) /// ```
/// .delayed(long_time) /// Future<int> f = makeFuture<int>(42)
/// .onTimeout(short_time, /// .delayed(long_time)
/// []() -> int{ return -1; }); /// .onTimeout(short_time,
/// [] { return -1; });
/// ```
/// ///
/// or perhaps /// or perhaps
/// ///
/// Future<int> f = makeFuture<int>(42) /// ```
/// .delayed(long_time) /// Future<int> f = makeFuture<int>(42)
/// .onTimeout(short_time, /// .delayed(long_time)
/// []() { return makeFuture<int>(some_exception); }); /// .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> template <class F>
Future<T> onTimeout(Duration, F&& func, Timekeeper* = nullptr); Future<T> onTimeout(Duration, F&& func, Timekeeper* = nullptr);
/// Throw FutureTimeout if this Future does not complete within the given /// Throw FutureTimeout if this Future does not complete within the given
/// duration from now. The optional Timekeeper is as with futures::sleep(). /// duration from now. 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(Duration, Timekeeper* = nullptr); Future<T> within(Duration, Timekeeper* = nullptr);
/// Throw the given exception if this Future does not complete within the /// Throw the given exception if this Future does not complete within the
/// given duration from now. The optional Timekeeper is as with /// given duration from now. The optional Timekeeper is as with
/// futures::sleep(). /// 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> template <class E>
Future<T> within(Duration, E exception, Timekeeper* = nullptr); Future<T> within(Duration, E exception, Timekeeper* = nullptr);
/// Delay the completion of this Future for at least this duration from /// Delay the completion of this Future for at least this duration from
/// now. The optional Timekeeper is as with futures::sleep(). /// now. The optional Timekeeper is as with futures::sleep().
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
/// - `RESULT.valid() == true`
Future<T> delayed(Duration, Timekeeper* = nullptr) &&; Future<T> delayed(Duration, Timekeeper* = nullptr) &&;
/// Delay the completion of this Future for at least this duration from /// Delay the completion of this Future for at least this duration from
...@@ -957,45 +1490,145 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -957,45 +1490,145 @@ class Future : private futures::detail::FutureBase<T> {
/// WARNING: Returned future may complete on Timekeeper thread. /// WARNING: Returned future may complete on Timekeeper thread.
Future<T> delayedUnsafe(Duration, Timekeeper* = nullptr); Future<T> delayedUnsafe(Duration, Timekeeper* = nullptr);
/// Block until the future is fulfilled. Returns the value (moved out), or /// Blocks until the future is fulfilled. Returns the value (moved-out), or
/// throws the exception. The future must not already have a callback. /// throws the exception. The future must not already have a continuation.
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
T get(); T get();
/// Block until the future is fulfilled, or until timed out. Returns the /// Blocks until the future is fulfilled, or until `dur` elapses. Returns the
/// value (moved out), or throws the exception (which might be a FutureTimeout /// value (moved-out), or throws the exception (which might be a FutureTimeout
/// exception). /// exception).
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == false`
T get(Duration dur); T get(Duration dur);
/** A reference to the Try of the value */ /// A reference to the Try of the value
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
/// - `isReady() == true` (else throws FutureNotReady)
Try<T>& getTry(); Try<T>& getTry();
/// Block until this Future is complete. Returns a reference to this Future. /// Blocks until this Future is complete.
///
/// Preconditions:
///
/// - `valid() == true` (else throws FutureInvalid)
///
/// Postconditions:
///
/// - `valid() == true`
/// - `&RESULT == this`
/// - `isReady() == true`
Future<T>& wait() &; Future<T>& wait() &;
/// Overload of wait() for rvalue Futures /// 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() &&; Future<T>&& wait() &&;
/// Block until this Future is complete or until the given Duration passes. /// Blocks until this Future is complete, or `dur` elapses.
/// Returns a reference to this Future ///
Future<T>& wait(Duration) &; /// 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(Duration dur) &;
/// Overload of wait(Duration) for rvalue Futures /// Blocks until this Future is complete or until `dur` passes.
Future<T>&& wait(Duration) &&; ///
/// 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(Duration dur) &&;
/// Call e->drive() repeatedly until the future is fulfilled. Examples /// Call e->drive() repeatedly until the future is fulfilled. Examples
/// of DrivableExecutor include EventBase and ManualExecutor. Returns a /// of DrivableExecutor include EventBase and ManualExecutor. Returns a
/// reference to this Future so that you can chain calls if desired. /// reference to this Future so that you can chain calls if desired.
/// value (moved out), or throws the exception. /// 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) &; Future<T>& waitVia(DrivableExecutor* e) &;
/// Overload of waitVia() for rvalue Futures /// 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) &&; Future<T>&& waitVia(DrivableExecutor* e) &&;
/// As waitVia but may return early after dur passes. /// 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, Duration dur) &; Future<T>& waitVia(TimedDrivableExecutor* e, Duration dur) &;
/// Overload of waitVia() for rvalue Futures /// Overload of waitVia() for rvalue Futures
/// As waitVia but may return early after dur passes. /// 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, Duration dur) &&; Future<T>&& waitVia(TimedDrivableExecutor* e, Duration dur) &&;
/// If the value in this Future is equal to the given Future, when they have /// If the value in this Future is equal to the given Future, when they have
...@@ -1007,22 +1640,56 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1007,22 +1640,56 @@ class Future : private futures::detail::FutureBase<T> {
/// predicate behaves like std::function<bool(T const&)> /// predicate behaves like std::function<bool(T const&)>
/// If the predicate does not obtain with the value, the result /// If the predicate does not obtain with the value, the result
/// is a folly::FuturePredicateDoesNotObtain exception /// 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> template <class F>
Future<T> filter(F&& predicate); Future<T> filter(F&& predicate);
/// Like reduce, but works on a Future<std::vector<T / Try<T>>>, for example /// Like reduce, but works on a Future<std::vector<T / Try<T>>>, for example
/// the result of collect or collectAll /// 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> template <class I, class F>
Future<I> reduce(I&& initial, F&& func); Future<I> reduce(I&& initial, F&& func);
/// Create a Future chain from a sequence of callbacks. i.e. /// Create a Future chain from a sequence of continuations. i.e.
/// ///
/// f.then(a).then(b).then(c) /// ```
/// f.then(a).then(b).then(c)
/// ```
/// ///
/// where f is a Future<A> and the result of the chain is a Future<D> /// where f is a Future<A> and the result of the chain is a Future<D>
/// becomes /// becomes
/// ///
/// f.thenMulti(a, b, c); /// ```
/// f.thenMulti(a, b, c);
/// ```
///
/// 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 Callback, class... Callbacks> template <class Callback, class... Callbacks>
auto thenMulti(Callback&& fn, Callbacks&&... fns) { auto thenMulti(Callback&& fn, Callbacks&&... fns) {
// thenMulti with two callbacks is just then(a).thenMulti(b, ...) // thenMulti with two callbacks is just then(a).thenMulti(b, ...)
...@@ -1030,6 +1697,17 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1030,6 +1697,17 @@ class Future : private futures::detail::FutureBase<T> {
.thenMulti(std::forward<Callbacks>(fns)...); .thenMulti(std::forward<Callbacks>(fns)...);
} }
/// Create a Future chain from a sequence of callbacks.
///
/// 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 Callback> template <class Callback>
auto thenMulti(Callback&& fn) { auto thenMulti(Callback&& fn) {
// thenMulti with one callback is just a then // thenMulti with one callback is just a then
...@@ -1038,12 +1716,26 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1038,12 +1716,26 @@ class Future : private futures::detail::FutureBase<T> {
/// Create a Future chain from a sequence of callbacks. i.e. /// Create a Future chain from a sequence of callbacks. i.e.
/// ///
/// f.via(executor).then(a).then(b).then(c).via(oldExecutor) /// ```
/// f.via(executor).then(a).then(b).then(c).via(oldExecutor)
/// ```
/// ///
/// where f is a Future<A> and the result of the chain is a Future<D> /// where f is a Future<A> and the result of the chain is a Future<D>
/// becomes /// becomes
/// ///
/// f.thenMultiWithExecutor(executor, a, b, c); /// ```
/// f.thenMultiWithExecutor(executor, a, b, c);
/// ```
///
/// 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 Callback, class... Callbacks> template <class Callback, class... Callbacks>
auto thenMultiWithExecutor(Executor* x, Callback&& fn, Callbacks&&... fns) { auto thenMultiWithExecutor(Executor* x, Callback&& fn, Callbacks&&... fns) {
// thenMultiExecutor with two callbacks is // thenMultiExecutor with two callbacks is
...@@ -1061,8 +1753,13 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1061,8 +1753,13 @@ class Future : private futures::detail::FutureBase<T> {
return then(x, std::forward<Callback>(fn)); return then(x, std::forward<Callback>(fn));
} }
// Convert this Future to a SemiFuture to safely export from a library /// Moves-out `*this`, creating/returning a corresponding SemiFuture.
// without exposing a continuation interface /// 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() { SemiFuture<T> semi() {
return SemiFuture<T>{std::move(*this)}; return SemiFuture<T>{std::move(*this)};
} }
...@@ -1090,8 +1787,7 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1090,8 +1787,7 @@ class Future : private futures::detail::FutureBase<T> {
template <class T2> template <class T2>
friend Future<T2> makeFuture(Try<T2>&&); friend Future<T2> makeFuture(Try<T2>&&);
/// Repeat the given future (i.e., the computation it contains) /// Repeat the given future (i.e., the computation it contains) n times.
/// n times.
/// ///
/// thunk behaves like std::function<Future<T2>(void)> /// thunk behaves like std::function<Future<T2>(void)>
template <class F> template <class F>
...@@ -1127,7 +1823,7 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1127,7 +1823,7 @@ class Future : private futures::detail::FutureBase<T> {
/// Delays that are used to trigger timeouts of async operations), then you /// Delays that are used to trigger timeouts of async operations), then you
/// can and should cancel them to reclaim resources. /// can and should cancel them to reclaim resources.
/// ///
/// Users will typically get one of these via Future::sleep(Duration) or /// Users will typically get one of these via Future::sleep(Duration dur) or
/// use them implicitly behind the scenes by passing a timeout to some Future /// use them implicitly behind the scenes by passing a timeout to some Future
/// operation. /// operation.
/// ///
...@@ -1153,12 +1849,12 @@ class Timekeeper { ...@@ -1153,12 +1849,12 @@ class Timekeeper {
/// elapsed time. Exceptional errors can happen but they must be /// elapsed time. Exceptional errors can happen but they must be
/// exceptional. Use the steady (monotonic) clock. /// exceptional. Use the steady (monotonic) clock.
/// ///
/// You may cancel this Future to reclaim resources. /// The consumer thread may cancel this Future to reclaim resources.
/// ///
/// This future probably completes on the timer thread. You should almost /// This future probably completes on the timer thread. You should almost
/// certainly follow it with a via() call or the accuracy of other timers /// certainly follow it with a via() call or the accuracy of other timers
/// will suffer. /// will suffer.
virtual Future<Unit> after(Duration) = 0; virtual Future<Unit> after(Duration dur) = 0;
/// Returns a future that will complete at the requested time. /// Returns a future that will complete at the requested time.
/// ///
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment