|  | /* | 
|  | *  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 <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/system/no_unique_address.h" | 
|  | #include "rtc_base/thread_annotations.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | 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 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 webrtc | 
|  |  | 
|  |  | 
|  | #endif  // RTC_BASE_OPERATIONS_CHAIN_H_ |