191 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  *  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 <queue>
 | |
| #include <set>
 | |
| #include <type_traits>
 | |
| #include <utility>
 | |
| 
 | |
| #include "api/scoped_refptr.h"
 | |
| #include "rtc_base/checks.h"
 | |
| #include "rtc_base/constructor_magic.h"
 | |
| #include "rtc_base/ref_count.h"
 | |
| #include "rtc_base/ref_counted_object.h"
 | |
| #include "rtc_base/synchronization/sequence_checker.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 { RTC_DCHECK(has_run_); }
 | |
| 
 | |
|   void Run() override {
 | |
|     RTC_DCHECK(!has_run_);
 | |
| #ifdef RTC_DCHECK_IS_ON
 | |
|     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_;
 | |
| #ifdef 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 RefCountedObject<RefCountInterface> {
 | |
|  public:
 | |
|   static scoped_refptr<OperationsChain> Create();
 | |
|   ~OperationsChain();
 | |
| 
 | |
|   // 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 RefCountedObject<RefCountInterface> {
 | |
|    public:
 | |
|     explicit CallbackHandle(scoped_refptr<OperationsChain> operations_chain);
 | |
|     ~CallbackHandle();
 | |
| 
 | |
|     void OnOperationComplete();
 | |
| 
 | |
|    private:
 | |
|     scoped_refptr<OperationsChain> operations_chain_;
 | |
| #ifdef RTC_DCHECK_IS_ON
 | |
|     bool has_run_ = false;
 | |
| #endif  // RTC_DCHECK_IS_ON
 | |
| 
 | |
|     RTC_DISALLOW_COPY_AND_ASSIGN(CallbackHandle);
 | |
|   };
 | |
| 
 | |
|   OperationsChain();
 | |
| 
 | |
|   std::function<void()> CreateOperationsChainCallback();
 | |
|   void OnOperationComplete();
 | |
| 
 | |
|   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_);
 | |
| 
 | |
|   RTC_DISALLOW_COPY_AND_ASSIGN(OperationsChain);
 | |
| };
 | |
| 
 | |
| }  // namespace rtc
 | |
| 
 | |
| #endif  // RTC_BASE_OPERATIONS_CHAIN_H_
 |