/* * 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 #include #include #include #include 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 SemiFuture; template class Future; template class Promise; namespace futures { namespace detail { template class FutureBase; struct EmptyConstruct {}; template class CoreCallbackState; template void setTry(Promise& p, Executor::KeepAlive<>&& ka, Try&& 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 Promise { public: /// Returns an invalid promise. /// /// Postconditions: /// /// - `RESULT.valid() == false` /// - `RESULT.isFulfilled() == true` static Promise 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&& 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&& 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 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 getFuture(); /// Fulfill the Promise with an exception_wrapper. /// /// Sample usage: /// /// Promise p = ... /// ... /// auto const ep = std::exception_ptr(); /// auto const ew = exception_wrapper::from_exception_ptr(ep); /// p.setException(ew); /// /// Functionally equivalent to `setTry(Try(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))`. /// /// Please see `setException(exception_wrapper)` for semantics/contract. template typename std::enable_if::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 void setInterruptHandler(F&& fn); /// Fulfills a (logically) void Promise, that is, Promise. /// (If you want a void-promise, use Promise, not Promise.) /// /// Preconditions: /// /// - `valid() == true` (else throws PromiseInvalid) /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied) /// /// Postconditions: /// /// - `isFulfilled() == true` /// - `valid() == true` (unchanged) template typename std::enable_if::value, void>::type setValue() { setTry(Try(T())); } /// Fulfill the Promise with the specified value using perfect forwarding. /// /// Functionally equivalent to `setTry(Try(std::forward(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 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); /// 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(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 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 friend class futures::detail::FutureBase; template friend class SemiFuture; template friend class Future; template friend class futures::detail::CoreCallbackState; friend void futures::detail::setTry( Promise& p, Executor::KeepAlive<>&& ka, Try&& t); // Whether the Future has been retrieved (a one-time operation). bool retrieved_; using Core = futures::detail::Core; // 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 static CoreT& getCoreImpl(CoreT* core) { if (!core) { throw_exception(); } return *core; } /// Fulfill the Promise with the specified Try (value or exception) and /// propagate the completing executor. void setTry(Executor::KeepAlive<>&& ka, Try&& 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 #include