/* * Copyright (c) Meta Platforms, Inc. and 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 <atomic> #include <mutex> #include <stdexcept> #include <utility> #include <vector> #include <glog/logging.h> #include <folly/Executor.h> #include <folly/Function.h> #include <folly/Optional.h> #include <folly/ScopeGuard.h> #include <folly/Try.h> #include <folly/Utility.h> #include <folly/futures/detail/Types.h> #include <folly/io/async/Request.h> #include <folly/lang/Assume.h> #include <folly/lang/Exception.h> #include <folly/synchronization/AtomicUtil.h> namespace folly { namespace futures { namespace detail { /// See `Core` for details enum class State : uint8_t { … }; constexpr State operator&(State a, State b) { … } constexpr State operator|(State a, State b) { … } constexpr State operator^(State a, State b) { … } constexpr State operator~(State a) { … } class DeferredExecutor; class UniqueDeleter { … }; DeferredWrapper; /** * Wrapper type that represents either a KeepAlive or a DeferredExecutor. * Acts as if a type-safe tagged union of the two using knowledge that the two * can safely be distinguished. */ class KeepAliveOrDeferred { … }; inline bool KeepAliveOrDeferred::isDeferred() const noexcept { … } inline bool KeepAliveOrDeferred::isKeepAlive() const noexcept { … } /** * Defer work until executor is actively boosted. */ class DeferredExecutor final { … }; class InterruptHandler { … }; template <class F> class InterruptHandlerImpl final : public InterruptHandler { … }; /// The shared state object for Future and Promise. /// /// Nomenclature: /// /// - "result": a `Try` object which, when set, contains a `T` or exception. /// - "move-out the result": used to mean the `Try` object and/or its contents /// are moved-out by a move-constructor or move-assignment. After the result /// is set, Core itself never modifies (including moving out) the result; /// however the docs refer to both since caller-code can move-out the result /// implicitly (see below for examples) whereas other types of modifications /// are more explicit in the caller code. /// - "callback": a function provided by the future which Core may invoke. The /// thread in which the callback is invoked depends on the executor; if there /// is no executor or an inline executor the thread-choice depends on timing. /// - "executor": an object which may in the future invoke a provided function /// (some executors may, as a matter of policy, simply destroy provided /// functions without executing them). /// - "consumer thread": the thread which currently owns the Future and which /// may provide the callback and/or the interrupt. /// - "producer thread": the thread which owns the Future and which may provide /// the result and which may provide the interrupt handler. /// - "interrupt": if provided, an object managed by (if non-empty) /// `exception_wrapper`. /// - "interrupt handler": if provided, a function-object passed to /// `promise.setInterruptHandler()`. Core invokes the interrupt handler with /// the interrupt when both are provided (and, best effort, if there is not /// yet any result). /// /// Core holds three sets of data, each of which is concurrency-controlled: /// /// - The primary producer-to-consumer info-flow: this info includes the result, /// callback, executor, and a priority for running the callback. Management of /// and concurrency control for this info is by an FSM based on `enum class /// State`. All state transitions are atomic; other producer-to-consumer data /// is sometimes modified within those transitions; see below for details. /// - The consumer-to-producer interrupt-request flow: this info includes an /// interrupt-handler and an interrupt. /// - Lifetime control info: this includes two reference counts, both which are /// internally synchronized (atomic). /// /// The FSM to manage the primary producer-to-consumer info-flow has these /// allowed (atomic) transitions: /// /// +----------------------------------------------------------------+ /// | ---> OnlyResult ----- | /// | / \ | /// | (setResult()) (setCallback()) | /// | / \ | /// | Start ---------> ------> Done | /// | \ \ / | /// | \ (setCallback()) (setResult()) | /// | \ \ / | /// | \ ---> OnlyCallback --- | /// | \ or OnlyCallbackAllowInline | /// | \ \ | /// | (setProxy()) (setProxy()) | /// | \ \ | /// | \ ------> Empty | /// | \ / | /// | \ (setCallback()) | /// | \ / | /// | --------> Proxy ---------- | /// +----------------------------------------------------------------+ /// /// States and the corresponding producer-to-consumer data status & ownership: /// /// - Start: has neither result nor callback. While in this state, the producer /// thread may set the result (`setResult()`) or the consumer thread may set /// the callback (`setCallback()`). /// - OnlyResult: producer thread has set the result and must never access it. /// The result is logically owned by, and possibly modified or moved-out by, /// the consumer thread. Callers of the future object can do arbitrary /// modifications, including moving-out, via continuations or via non-const /// and/or rvalue-qualified `future.result()`, `future.value()`, etc. /// Future/SemiFuture proper also move-out the result in some cases, e.g., /// in `wait()`, `get()`, when passing through values or results from core to /// core, as `then-value` and `then-error`, etc. /// - OnlyCallback: consumer thread has set a callback/continuation. From this /// point forward only the producer thread can safely access that callback /// (see `setResult()` and `doCallback()` where the producer thread can both /// read and modify the callback). /// - OnlyCallbackAllowInline: as for OnlyCallback but the core is allowed to /// run the callback inline with the setResult call, and therefore in the /// execution context and on the executor that executed the callback on the /// previous core, rather than adding the callback to the current Core's /// executor. This will only happen if the executor on which the previous /// callback is executing, and on which it is calling setResult, is the same /// as the executor the current core would add the callback to. /// - Proxy: producer thread has set a proxy core which the callback should be /// proxied to. /// - Done: callback can be safely accessed only within `doCallback()`, which /// gets called on exactly one thread exactly once just after the transition /// to Done. The future object will have determined whether that callback /// has/will move-out the result, but either way the result remains logically /// owned exclusively by the consumer thread (the code of Future/SemiFuture, /// of the continuation, and/or of callers of `future.result()`, etc.). /// - Empty: the core successfully proxied the callback and is now empty. /// /// Start state: /// /// - Start: e.g., `Core<X>::make()`. /// - (See also `Core<X>::make(x)` which logically transitions Start => /// OnlyResult within the underlying constructor.) /// /// Terminal states: /// /// - OnlyResult: a terminal state when a callback is never attached, and also /// sometimes when a callback is provided, e.g., sometimes when /// `future.wait()` and/or `future.get()` are used. /// - Done: a terminal state when `future.then()` is used, and sometimes also /// when `future.wait()` and/or `future.get()` are used. /// - Proxy: a terminal state if proxy core was set, but callback was never set. /// - Empty: a terminal state when proxying a callback was successful. /// /// Notes and caveats: /// /// - Unfortunately, there are things that users can do to break concurrency and /// we can't detect that. However users should be ok if they follow move /// semantics religiously wrt threading. /// - Futures and/or Promises can and usually will migrate between threads, /// though this usually happens within the API code. For example, an async /// operation will probably make a promise-future pair (see overloads of /// `makePromiseContract()`), then move the Promise into another thread that /// will eventually fulfill it. /// - Things get slightly more complicated with executors and via, but the /// principle is the same. /// - In general, as long as the user doesn't access a future or promise object /// from more than one thread at a time there won't be any problems. // /// Implementation is split between CoreBase and Core<T>. T-independent bits are /// in CoreBase in order to minimize the instantiation cost of Core<T>. class CoreBase { … }; template <typename T> class ResultHolder { … }; template <typename T> class Core final : private ResultHolder<T>, public CoreBase { … }; inline Executor* CoreBase::getExecutor() const { … } inline DeferredExecutor* CoreBase::getDeferredExecutor() const { … } #if FOLLY_USE_EXTERN_FUTURE_UNIT // limited to the instances unconditionally forced by the futures library extern template class Core<folly::Unit>; #endif } // namespace detail } // namespace futures } // namespace folly