| /* |
| * Copyright 2019 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #ifndef RTC_BASE_OPERATIONS_CHAIN_H_ |
| #define RTC_BASE_OPERATIONS_CHAIN_H_ |
| |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <queue> |
| #include <set> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "api/ref_counted_base.h" |
| #include "api/scoped_refptr.h" |
| #include "api/sequence_checker.h" |
| #include "rtc_base/checks.h" |
| #include "rtc_base/ref_count.h" |
| #include "rtc_base/ref_counted_object.h" |
| #include "rtc_base/system/no_unique_address.h" |
| |
| namespace rtc { |
| |
| namespace rtc_operations_chain_internal { |
| |
| // Abstract base class for operations on the OperationsChain. Run() must be |
| // invoked exactly once during the Operation's lifespan. |
| class Operation { |
| public: |
| virtual ~Operation() {} |
| |
| virtual void Run() = 0; |
| }; |
| |
| // FunctorT is the same as in OperationsChain::ChainOperation(). `callback_` is |
| // passed on to the `functor_` and is used to inform the OperationsChain that |
| // the operation completed. The functor is responsible for invoking the |
| // callback when the operation has completed. |
| template <typename FunctorT> |
| class OperationWithFunctor final : public Operation { |
| public: |
| OperationWithFunctor(FunctorT&& functor, std::function<void()> callback) |
| : functor_(std::forward<FunctorT>(functor)), |
| callback_(std::move(callback)) {} |
| |
| ~OperationWithFunctor() override { |
| #if RTC_DCHECK_IS_ON |
| RTC_DCHECK(has_run_); |
| #endif // RTC_DCHECK_IS_ON |
| } |
| |
| void Run() override { |
| #if RTC_DCHECK_IS_ON |
| RTC_DCHECK(!has_run_); |
| has_run_ = true; |
| #endif // RTC_DCHECK_IS_ON |
| // The functor being executed may invoke the callback synchronously, |
| // marking the operation as complete. As such, `this` OperationWithFunctor |
| // object may get deleted here, including destroying `functor_`. To |
| // protect the functor from self-destruction while running, it is moved to |
| // a local variable. |
| auto functor = std::move(functor_); |
| functor(std::move(callback_)); |
| // `this` may now be deleted; don't touch any member variables. |
| } |
| |
| private: |
| typename std::remove_reference<FunctorT>::type functor_; |
| std::function<void()> callback_; |
| #if RTC_DCHECK_IS_ON |
| bool has_run_ = false; |
| #endif // RTC_DCHECK_IS_ON |
| }; |
| |
| } // namespace rtc_operations_chain_internal |
| |
| // An implementation of an operations chain. An operations chain is used to |
| // ensure that asynchronous tasks are executed in-order with at most one task |
| // running at a time. The notion of an operation chain is defined in |
| // https://w3c.github.io/webrtc-pc/#dfn-operations-chain, though unlike this |
| // implementation, the referenced definition is coupled with a peer connection. |
| // |
| // An operation is an asynchronous task. The operation starts when its functor |
| // is invoked, and completes when the callback that is passed to functor is |
| // invoked by the operation. The operation must start and complete on the same |
| // sequence that the operation was "chained" on. As such, the OperationsChain |
| // operates in a "single-threaded" fashion, but the asynchronous operations may |
| // use any number of threads to achieve "in parallel" behavior. |
| // |
| // When an operation is chained onto the OperationsChain, it is enqueued to be |
| // executed. Operations are executed in FIFO order, where the next operation |
| // does not start until the previous operation has completed. OperationsChain |
| // guarantees that: |
| // - If the operations chain is empty when an operation is chained, the |
| // operation starts immediately, inside ChainOperation(). |
| // - If the operations chain is not empty when an operation is chained, the |
| // operation starts upon the previous operation completing, inside the |
| // callback. |
| // |
| // An operation is contractually obligated to invoke the completion callback |
| // exactly once. Cancelling a chained operation is not supported by the |
| // OperationsChain; an operation that wants to be cancellable is responsible for |
| // aborting its own steps. The callback must still be invoked. |
| // |
| // The OperationsChain is kept-alive through reference counting if there are |
| // operations pending. This, together with the contract, guarantees that all |
| // operations that are chained get executed. |
| class OperationsChain final : public RefCountedNonVirtual<OperationsChain> { |
| public: |
| static scoped_refptr<OperationsChain> Create(); |
| ~OperationsChain(); |
| |
| OperationsChain(const OperationsChain&) = delete; |
| OperationsChain& operator=(const OperationsChain&) = delete; |
| |
| void SetOnChainEmptyCallback(std::function<void()> on_chain_empty_callback); |
| bool IsEmpty() const; |
| |
| // Chains an operation. Chained operations are executed in FIFO order. The |
| // operation starts when `functor` is executed by the OperationsChain and is |
| // contractually obligated to invoke the callback passed to it when the |
| // operation is complete. Operations must start and complete on the same |
| // sequence that this method was invoked on. |
| // |
| // If the OperationsChain is empty, the operation starts immediately. |
| // Otherwise it starts upon the previous operation completing. |
| // |
| // Requirements of FunctorT: |
| // - FunctorT is movable. |
| // - FunctorT implements "T operator()(std::function<void()> callback)" or |
| // "T operator()(std::function<void()> callback) const" for some T (if T is |
| // not void, the return value is discarded in the invoking sequence). The |
| // operator starts the operation; when the operation is complete, "callback" |
| // MUST be invoked, and it MUST be so on the sequence that ChainOperation() |
| // was invoked on. |
| // |
| // Lambda expressions are valid functors. |
| template <typename FunctorT> |
| void ChainOperation(FunctorT&& functor) { |
| RTC_DCHECK_RUN_ON(&sequence_checker_); |
| chained_operations_.push( |
| std::make_unique< |
| rtc_operations_chain_internal::OperationWithFunctor<FunctorT>>( |
| std::forward<FunctorT>(functor), CreateOperationsChainCallback())); |
| // If this is the only operation in the chain we execute it immediately. |
| // Otherwise the callback will get invoked when the pending operation |
| // completes which will trigger the next operation to execute. |
| if (chained_operations_.size() == 1) { |
| chained_operations_.front()->Run(); |
| } |
| } |
| |
| private: |
| friend class CallbackHandle; |
| |
| // The callback that is passed to an operation's functor (that is used to |
| // inform the OperationsChain that the operation has completed) is of type |
| // std::function<void()>, which is a copyable type. To allow the callback to |
| // be copyable, it is backed up by this reference counted handle. See |
| // CreateOperationsChainCallback(). |
| class CallbackHandle final : public RefCountedNonVirtual<CallbackHandle> { |
| public: |
| explicit CallbackHandle(scoped_refptr<OperationsChain> operations_chain); |
| ~CallbackHandle(); |
| |
| CallbackHandle(const CallbackHandle&) = delete; |
| CallbackHandle& operator=(const CallbackHandle&) = delete; |
| |
| void OnOperationComplete(); |
| |
| private: |
| scoped_refptr<OperationsChain> operations_chain_; |
| #if RTC_DCHECK_IS_ON |
| bool has_run_ = false; |
| #endif // RTC_DCHECK_IS_ON |
| }; |
| |
| OperationsChain(); |
| |
| std::function<void()> CreateOperationsChainCallback(); |
| void OnOperationComplete(); |
| |
| RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_; |
| // FIFO-list of operations that are chained. An operation that is executing |
| // remains on this list until it has completed by invoking the callback passed |
| // to it. |
| std::queue<std::unique_ptr<rtc_operations_chain_internal::Operation>> |
| chained_operations_ RTC_GUARDED_BY(sequence_checker_); |
| std::optional<std::function<void()>> on_chain_empty_callback_ |
| RTC_GUARDED_BY(sequence_checker_); |
| }; |
| |
| } // namespace rtc |
| |
| #endif // RTC_BASE_OPERATIONS_CHAIN_H_ |