462 lines
17 KiB
C++
462 lines
17 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 <functional>
|
|
|
|
#include <folly/Portability.h>
|
|
#include <folly/Try.h>
|
|
#include <folly/futures/detail/Core.h>
|
|
#include <folly/lang/Exception.h>
|
|
|
|
namespace folly {
|
|
|
|
class FOLLY_EXPORT PromiseException : public std::logic_error {
|
|
public:
|
|
using std::logic_error::logic_error;
|
|
};
|
|
|
|
class FOLLY_EXPORT PromiseInvalid : public PromiseException {
|
|
public:
|
|
PromiseInvalid() : PromiseException("Promise invalid") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT PromiseAlreadySatisfied : public PromiseException {
|
|
public:
|
|
PromiseAlreadySatisfied() : PromiseException("Promise already satisfied") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT FutureAlreadyRetrieved : public PromiseException {
|
|
public:
|
|
FutureAlreadyRetrieved() : PromiseException("Future already retrieved") {}
|
|
};
|
|
|
|
class FOLLY_EXPORT BrokenPromise : public PromiseException {
|
|
public:
|
|
explicit BrokenPromise(const std::string& type)
|
|
: PromiseException("Broken promise for type name `" + type + '`') {}
|
|
|
|
explicit BrokenPromise(const char* type) : BrokenPromise(std::string(type)) {}
|
|
};
|
|
|
|
// forward declaration
|
|
template <class T>
|
|
class SemiFuture;
|
|
template <class T>
|
|
class Future;
|
|
template <class T>
|
|
class Promise;
|
|
|
|
namespace futures {
|
|
namespace detail {
|
|
template <class T>
|
|
class FutureBase;
|
|
struct EmptyConstruct {};
|
|
template <typename T, typename F>
|
|
class CoreCallbackState;
|
|
template <typename T>
|
|
void setTry(Promise<T>& p, Executor::KeepAlive<>&& ka, Try<T>&& t);
|
|
} // namespace detail
|
|
} // namespace futures
|
|
|
|
/// Promises and futures provide a potentially nonblocking mechanism
|
|
/// to execute a producer/consumer operation concurrently, with
|
|
/// threading/pools controlled via an executor. There are multiple potential
|
|
/// patterns for using promises and futures including some that block the
|
|
/// caller, though that is discouraged; it should be used only when necessary.
|
|
///
|
|
/// One typical pattern uses a series of calls to set up a small, limited
|
|
/// program that...
|
|
///
|
|
/// - ...performs the desired operations (based on a lambda)...
|
|
/// - ...on an asynchronously provided input (an exception or a value)...
|
|
/// - ...lazily, when that input is ready (without blocking the caller)...
|
|
/// - ...using appropriate execution resources (determined by the executor)...
|
|
/// - ...then after constructing the 'program,' launches the asynchronous
|
|
/// producer.
|
|
///
|
|
/// That usage pattern looks roughly like this:
|
|
///
|
|
/// auto [p, f] = makePromiseContract(executor);
|
|
/// g = std::move(f).then([](MyValue&& x) {
|
|
/// ...executor runs this code if/when a MyValue is ready...
|
|
/// });
|
|
/// ...launch the async producer that eventually calls p.setResult()...
|
|
///
|
|
/// This is just one of many potential usage patterns. It has the desired
|
|
/// property of being nonblocking to the caller. Of course the `.then()`
|
|
/// code is deferred until the produced value (or exception) is ready,
|
|
/// but no code actually blocks pending completion of other operations.
|
|
///
|
|
/// The promise/future mechanism is limited to a single object of some arbitrary
|
|
/// type. It also supports a (logically) void result, i.e., in cases where the
|
|
/// continuation/consumer (the `.then()` code if using the above pattern) is not
|
|
/// expecting a value because the 'producer' is running for its side-effects.
|
|
///
|
|
/// The primary data movement is from producer to consumer, however Promise and
|
|
/// Future also provide a mechanism where the consumer can send an interruption
|
|
/// message to the producer. The meaning and response to that interruption
|
|
/// message is controlled by the promise; see `Promise::setInterruptHandler()`.
|
|
///
|
|
/// Neither Promise nor Future is thread-safe. All internal interactions
|
|
/// between a promise and its associated future are thread-safe, provided that
|
|
/// callers otherwise honor the promise's contract and the future's contract.
|
|
///
|
|
/// Logically there are up to three threads (though in practice there are often
|
|
/// fewer - one thread might take on more than one role):
|
|
///
|
|
/// - Set-up thread: thread used to construct the Promise, and often also to
|
|
/// set up the SemiFuture/Future.
|
|
/// - Producer thread: thread that produces the result.
|
|
/// - Consumer thread: thread in which the continuation is invoked (a
|
|
/// continuation is a callback provided to `.then` or to a variant).
|
|
///
|
|
/// For description purposes, the term 'shared state' is used to describe the
|
|
/// logical state shared by the promise and the future. This 'third object'
|
|
/// represents things like whether the result has been fulfilled, the value or
|
|
/// exception in that result, and the data needed to handle interruption
|
|
/// requests.
|
|
///
|
|
/// A promise can be in various logical states:
|
|
///
|
|
/// - valid vs. invalid (has vs. does not have a shared state, respectfully).
|
|
/// - fulfilled vs. unfulfilled (an invalid promise is always fulfilled; a valid
|
|
/// promise is fulfilled if the shared-state has a result).
|
|
///
|
|
/// A promise `p` may optionally have an associated future. This future, if it
|
|
/// exists, may be either a SemiFuture or a Future, and is defined as the
|
|
/// future (if any) that holds the same shared state as promise `p`.
|
|
/// The associated future is initially the future returned from
|
|
/// `p.getFuture()` or `p.getSemiFuture()`, but various operations
|
|
/// may transfer the shared state from one future to another.
|
|
template <class T>
|
|
class Promise {
|
|
public:
|
|
/// Returns an invalid promise.
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `RESULT.valid() == false`
|
|
/// - `RESULT.isFulfilled() == true`
|
|
static Promise<T> makeEmpty() noexcept;
|
|
|
|
/// Constructs a valid but unfulfilled promise.
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `valid() == true` (it will have a shared state)
|
|
/// - `isFulfilled() == false` (its shared state won't have a result)
|
|
Promise();
|
|
|
|
/// Postconditions:
|
|
///
|
|
/// - If `valid()` and `!isFulfilled()`, the associated future (if any) will
|
|
/// be completed with a `BrokenPromise` exception *as if* by
|
|
/// `setException(...)`.
|
|
/// - If `valid()`, releases, possibly destroying, the shared state.
|
|
~Promise();
|
|
|
|
// not copyable
|
|
Promise(Promise const&) = delete;
|
|
Promise& operator=(Promise const&) = delete;
|
|
|
|
/// Move ctor
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `this` will have whatever shared-state was previously held by `other`
|
|
/// (if any)
|
|
/// - `other.valid()` will be false (`other` will not have any shared state)
|
|
Promise(Promise<T>&& other) noexcept;
|
|
|
|
/// Move assignment
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - If `valid()` and `!isFulfilled()`, the associated future (if any) will
|
|
/// be completed with a `BrokenPromise` exception *as if* by
|
|
/// `setException(...)`.
|
|
/// - If `valid()`, releases, possibly destroying, the original shared state.
|
|
/// - `this` will have whatever shared-state was previously held by `other`
|
|
/// (if any)
|
|
/// - `other.valid()` will be false (`other` will not have any shared state)
|
|
Promise& operator=(Promise<T>&& other) noexcept;
|
|
|
|
/// Return a SemiFuture associated with this Promise, sharing the same shared
|
|
/// state as `this`.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws PromiseInvalid)
|
|
/// - neither getSemiFuture() nor getFuture() may have been called previously
|
|
/// on `this` Promise (else throws FutureAlreadyRetrieved)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `RESULT.valid() == true`
|
|
/// - RESULT will share the same shared-state as `this`
|
|
///
|
|
/// DEPRECATED: use `folly::makePromiseContract()` instead.
|
|
SemiFuture<T> getSemiFuture();
|
|
|
|
/// Return a Future associated with this Promise, sharing the same shared
|
|
/// state as `this`.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws PromiseInvalid)
|
|
/// - neither getSemiFuture() nor getFuture() may have been called previously
|
|
/// on `this` Promise (else throws FutureAlreadyRetrieved)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `RESULT.valid() == true`
|
|
/// - RESULT will share the same shared-state as `this`
|
|
///
|
|
/// DEPRECATED: use `folly::makePromiseContract()` instead. If you can't use
|
|
/// that, use `this->getSemiFuture()` then get a Future by calling `.via()`
|
|
/// with an appropriate executor.
|
|
Future<T> getFuture();
|
|
|
|
/// Fulfill the Promise with an exception_wrapper.
|
|
///
|
|
/// Sample usage:
|
|
///
|
|
/// Promise<MyValue> p = ...
|
|
/// ...
|
|
/// auto const ep = std::exception_ptr();
|
|
/// auto const ew = exception_wrapper::from_exception_ptr(ep);
|
|
/// p.setException(ew);
|
|
///
|
|
/// Functionally equivalent to `setTry(Try<T>(std::move(ew)))`
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws PromiseInvalid)
|
|
/// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `isFulfilled() == true`
|
|
/// - `valid() == true` (unchanged)
|
|
/// - The associated future (if any) will complete with the exception.
|
|
void setException(exception_wrapper ew);
|
|
|
|
/// Fulfill the Promise with exception `e` *as if* by
|
|
/// `setException(make_exception_wrapper<E>(e))`.
|
|
///
|
|
/// Please see `setException(exception_wrapper)` for semantics/contract.
|
|
template <class E>
|
|
typename std::enable_if<std::is_base_of<std::exception, E>::value>::type
|
|
setException(E const& e);
|
|
|
|
/// Sets a handler for the producer to receive a (logical) interruption
|
|
/// request (exception) sent from the consumer via `future.raise()`.
|
|
///
|
|
/// Details: The consumer calls `future.raise()` when it wishes to send a
|
|
/// logical interruption message (an exception), and that exception/message
|
|
/// is passed to `fn()`. The thread used to call `fn()` depends on timing
|
|
/// (see Postconditions for threading details).
|
|
///
|
|
/// Handler `fn()` can do anything you want, but if you bother to set one
|
|
/// then you probably will want to (more or less immediately) fulfill the
|
|
/// promise with an exception (or other special value) indicating how the
|
|
/// interrupt was handled.
|
|
///
|
|
/// This call silently does nothing if `isFulfilled()`.
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws PromiseInvalid)
|
|
/// - `fn` must be copyable and must be invocable with
|
|
/// `exception_wrapper const&`
|
|
/// - the code within `fn()` must be safe to run either synchronously within
|
|
/// the `setInterruptHandler()` call or asynchronously within the consumer
|
|
/// thread's call to `future.raise()`.
|
|
/// - the code within `fn()` must also be safe to run after this promise is
|
|
/// fulfilled; this may have lifetime/race-case ramifications, e.g., if the
|
|
/// code of `fn()` might access producer-resources that will be destroyed,
|
|
/// then the destruction of those producer-resources must be deferred beyond
|
|
/// the moment when this promise is fulfilled.
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - if the consumer calls `future.raise()` early enough (up to a particular
|
|
/// moment within the `setInterruptHandler()` call), `fn()` will be called
|
|
/// synchronously (in the current thread, during this call).
|
|
/// - if the consumer calls `future.raise()` after that moment within
|
|
/// `setInterruptHandler()` but before this promise is fulfilled, `fn()`
|
|
/// will be called asynchronously (in the consumer's thread, within the call
|
|
/// to `future.raise()`).
|
|
/// - if the consumer calls `future.raise()` after this promise is fulfilled,
|
|
/// `fn()` may or may not be called at all, and if it is called, it will be
|
|
/// called asynchronously (within the consumer's call to `future.raise()`).
|
|
///
|
|
/// IMPORTANT: `fn()` should return quickly since it could block this call
|
|
/// to `promise.setInterruptHandler()` and/or a concurrent call to
|
|
/// `future.raise()`. Those two functions contend on the same lock; those
|
|
/// calls could block if `fn()` is invoked within one of those while the
|
|
/// lock is held.
|
|
template <typename F>
|
|
void setInterruptHandler(F&& fn);
|
|
|
|
/// Fulfills a (logically) void Promise, that is, Promise<Unit>.
|
|
/// (If you want a void-promise, use Promise<Unit>, not Promise<void>.)
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws PromiseInvalid)
|
|
/// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `isFulfilled() == true`
|
|
/// - `valid() == true` (unchanged)
|
|
template <class B = T>
|
|
typename std::enable_if<std::is_same<Unit, B>::value, void>::type setValue() {
|
|
setTry(Try<T>(T()));
|
|
}
|
|
|
|
/// Fulfill the Promise with the specified value using perfect forwarding.
|
|
///
|
|
/// Functionally equivalent to `setTry(Try<T>(std::forward<M>(value)))`
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws PromiseInvalid)
|
|
/// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `isFulfilled() == true`
|
|
/// - `valid() == true` (unchanged)
|
|
/// - The associated future will see the value, e.g., in its continuation.
|
|
template <class M>
|
|
void setValue(M&& value);
|
|
|
|
/// Fulfill the Promise with the specified Try (value or exception).
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws PromiseInvalid)
|
|
/// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `isFulfilled() == true`
|
|
/// - `valid() == true` (unchanged)
|
|
/// - The associated future will see the result, e.g., in its continuation.
|
|
void setTry(Try<T>&& t);
|
|
|
|
/// Fulfill this Promise with the result of a function that takes no
|
|
/// arguments and returns something implicitly convertible to T.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// p.setWith([] { do something that may throw; return a T; });
|
|
///
|
|
/// Functionally equivalent to `setTry(makeTryWith(static_cast<F&&>(func)));`
|
|
///
|
|
/// Preconditions:
|
|
///
|
|
/// - `valid() == true` (else throws PromiseInvalid)
|
|
/// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)
|
|
///
|
|
/// Postconditions:
|
|
///
|
|
/// - `func()` will be run synchronously (in this thread, during this call)
|
|
/// - If `func()` returns, the return value will be captured as if via
|
|
/// `setValue()`
|
|
/// - If `func()` throws, the exception will be captured as if via
|
|
/// `setException()`
|
|
/// - `isFulfilled() == true`
|
|
/// - `valid() == true` (unchanged)
|
|
/// - The associated future will see the result, e.g., in its continuation.
|
|
template <class F>
|
|
void setWith(F&& func);
|
|
|
|
/// true if this has a shared state;
|
|
/// false if this has been consumed/moved-out.
|
|
bool valid() const noexcept {
|
|
return core_ != nullptr;
|
|
}
|
|
|
|
/// True if either this promise was fulfilled or is invalid.
|
|
///
|
|
/// - True if `!valid()`
|
|
/// - True if `valid()` and this was fulfilled (a prior call to `setValue()`,
|
|
/// `setTry()`, `setException()`, or `setWith()`)
|
|
bool isFulfilled() const noexcept;
|
|
|
|
private:
|
|
template <class>
|
|
friend class futures::detail::FutureBase;
|
|
template <class>
|
|
friend class SemiFuture;
|
|
template <class>
|
|
friend class Future;
|
|
template <class, class>
|
|
friend class futures::detail::CoreCallbackState;
|
|
friend void futures::detail::setTry<T>(
|
|
Promise<T>& p,
|
|
Executor::KeepAlive<>&& ka,
|
|
Try<T>&& t);
|
|
|
|
// Whether the Future has been retrieved (a one-time operation).
|
|
bool retrieved_;
|
|
|
|
using Core = futures::detail::Core<T>;
|
|
|
|
// Throws PromiseInvalid 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(core_);
|
|
}
|
|
Core const& getCore() const {
|
|
return getCoreImpl(core_);
|
|
}
|
|
|
|
template <typename CoreT>
|
|
static CoreT& getCoreImpl(CoreT* core) {
|
|
if (!core) {
|
|
throw_exception<PromiseInvalid>();
|
|
}
|
|
return *core;
|
|
}
|
|
|
|
/// Fulfill the Promise with the specified Try (value or exception) and
|
|
/// propagate the completing executor.
|
|
void setTry(Executor::KeepAlive<>&& ka, Try<T>&& t);
|
|
|
|
// shared core state object
|
|
// usually you should use `getCore()` instead of directly accessing `core_`.
|
|
Core* core_;
|
|
|
|
explicit Promise(futures::detail::EmptyConstruct) noexcept;
|
|
|
|
void throwIfFulfilled() const;
|
|
void detach();
|
|
};
|
|
|
|
} // namespace folly
|
|
|
|
#include <folly/futures/Future.h>
|
|
#include <folly/futures/Promise-inl.h>
|