xref: /aosp_15_r20/external/webrtc/rtc_base/operations_chain.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright 2019 The WebRTC Project Authors. All rights reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #ifndef RTC_BASE_OPERATIONS_CHAIN_H_
12*d9f75844SAndroid Build Coastguard Worker #define RTC_BASE_OPERATIONS_CHAIN_H_
13*d9f75844SAndroid Build Coastguard Worker 
14*d9f75844SAndroid Build Coastguard Worker #include <functional>
15*d9f75844SAndroid Build Coastguard Worker #include <memory>
16*d9f75844SAndroid Build Coastguard Worker #include <queue>
17*d9f75844SAndroid Build Coastguard Worker #include <set>
18*d9f75844SAndroid Build Coastguard Worker #include <type_traits>
19*d9f75844SAndroid Build Coastguard Worker #include <utility>
20*d9f75844SAndroid Build Coastguard Worker 
21*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h"
22*d9f75844SAndroid Build Coastguard Worker #include "api/ref_counted_base.h"
23*d9f75844SAndroid Build Coastguard Worker #include "api/scoped_refptr.h"
24*d9f75844SAndroid Build Coastguard Worker #include "api/sequence_checker.h"
25*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
26*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/ref_count.h"
27*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/ref_counted_object.h"
28*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/system/no_unique_address.h"
29*d9f75844SAndroid Build Coastguard Worker 
30*d9f75844SAndroid Build Coastguard Worker namespace rtc {
31*d9f75844SAndroid Build Coastguard Worker 
32*d9f75844SAndroid Build Coastguard Worker namespace rtc_operations_chain_internal {
33*d9f75844SAndroid Build Coastguard Worker 
34*d9f75844SAndroid Build Coastguard Worker // Abstract base class for operations on the OperationsChain. Run() must be
35*d9f75844SAndroid Build Coastguard Worker // invoked exactly once during the Operation's lifespan.
36*d9f75844SAndroid Build Coastguard Worker class Operation {
37*d9f75844SAndroid Build Coastguard Worker  public:
~Operation()38*d9f75844SAndroid Build Coastguard Worker   virtual ~Operation() {}
39*d9f75844SAndroid Build Coastguard Worker 
40*d9f75844SAndroid Build Coastguard Worker   virtual void Run() = 0;
41*d9f75844SAndroid Build Coastguard Worker };
42*d9f75844SAndroid Build Coastguard Worker 
43*d9f75844SAndroid Build Coastguard Worker // FunctorT is the same as in OperationsChain::ChainOperation(). `callback_` is
44*d9f75844SAndroid Build Coastguard Worker // passed on to the `functor_` and is used to inform the OperationsChain that
45*d9f75844SAndroid Build Coastguard Worker // the operation completed. The functor is responsible for invoking the
46*d9f75844SAndroid Build Coastguard Worker // callback when the operation has completed.
47*d9f75844SAndroid Build Coastguard Worker template <typename FunctorT>
48*d9f75844SAndroid Build Coastguard Worker class OperationWithFunctor final : public Operation {
49*d9f75844SAndroid Build Coastguard Worker  public:
OperationWithFunctor(FunctorT && functor,std::function<void ()> callback)50*d9f75844SAndroid Build Coastguard Worker   OperationWithFunctor(FunctorT&& functor, std::function<void()> callback)
51*d9f75844SAndroid Build Coastguard Worker       : functor_(std::forward<FunctorT>(functor)),
52*d9f75844SAndroid Build Coastguard Worker         callback_(std::move(callback)) {}
53*d9f75844SAndroid Build Coastguard Worker 
~OperationWithFunctor()54*d9f75844SAndroid Build Coastguard Worker   ~OperationWithFunctor() override {
55*d9f75844SAndroid Build Coastguard Worker #if RTC_DCHECK_IS_ON
56*d9f75844SAndroid Build Coastguard Worker     RTC_DCHECK(has_run_);
57*d9f75844SAndroid Build Coastguard Worker #endif  // RTC_DCHECK_IS_ON
58*d9f75844SAndroid Build Coastguard Worker   }
59*d9f75844SAndroid Build Coastguard Worker 
Run()60*d9f75844SAndroid Build Coastguard Worker   void Run() override {
61*d9f75844SAndroid Build Coastguard Worker #if RTC_DCHECK_IS_ON
62*d9f75844SAndroid Build Coastguard Worker     RTC_DCHECK(!has_run_);
63*d9f75844SAndroid Build Coastguard Worker     has_run_ = true;
64*d9f75844SAndroid Build Coastguard Worker #endif  // RTC_DCHECK_IS_ON
65*d9f75844SAndroid Build Coastguard Worker     // The functor being executed may invoke the callback synchronously,
66*d9f75844SAndroid Build Coastguard Worker     // marking the operation as complete. As such, `this` OperationWithFunctor
67*d9f75844SAndroid Build Coastguard Worker     // object may get deleted here, including destroying `functor_`. To
68*d9f75844SAndroid Build Coastguard Worker     // protect the functor from self-destruction while running, it is moved to
69*d9f75844SAndroid Build Coastguard Worker     // a local variable.
70*d9f75844SAndroid Build Coastguard Worker     auto functor = std::move(functor_);
71*d9f75844SAndroid Build Coastguard Worker     functor(std::move(callback_));
72*d9f75844SAndroid Build Coastguard Worker     // `this` may now be deleted; don't touch any member variables.
73*d9f75844SAndroid Build Coastguard Worker   }
74*d9f75844SAndroid Build Coastguard Worker 
75*d9f75844SAndroid Build Coastguard Worker  private:
76*d9f75844SAndroid Build Coastguard Worker   typename std::remove_reference<FunctorT>::type functor_;
77*d9f75844SAndroid Build Coastguard Worker   std::function<void()> callback_;
78*d9f75844SAndroid Build Coastguard Worker #if RTC_DCHECK_IS_ON
79*d9f75844SAndroid Build Coastguard Worker   bool has_run_ = false;
80*d9f75844SAndroid Build Coastguard Worker #endif  // RTC_DCHECK_IS_ON
81*d9f75844SAndroid Build Coastguard Worker };
82*d9f75844SAndroid Build Coastguard Worker 
83*d9f75844SAndroid Build Coastguard Worker }  // namespace rtc_operations_chain_internal
84*d9f75844SAndroid Build Coastguard Worker 
85*d9f75844SAndroid Build Coastguard Worker // An implementation of an operations chain. An operations chain is used to
86*d9f75844SAndroid Build Coastguard Worker // ensure that asynchronous tasks are executed in-order with at most one task
87*d9f75844SAndroid Build Coastguard Worker // running at a time. The notion of an operation chain is defined in
88*d9f75844SAndroid Build Coastguard Worker // https://w3c.github.io/webrtc-pc/#dfn-operations-chain, though unlike this
89*d9f75844SAndroid Build Coastguard Worker // implementation, the referenced definition is coupled with a peer connection.
90*d9f75844SAndroid Build Coastguard Worker //
91*d9f75844SAndroid Build Coastguard Worker // An operation is an asynchronous task. The operation starts when its functor
92*d9f75844SAndroid Build Coastguard Worker // is invoked, and completes when the callback that is passed to functor is
93*d9f75844SAndroid Build Coastguard Worker // invoked by the operation. The operation must start and complete on the same
94*d9f75844SAndroid Build Coastguard Worker // sequence that the operation was "chained" on. As such, the OperationsChain
95*d9f75844SAndroid Build Coastguard Worker // operates in a "single-threaded" fashion, but the asynchronous operations may
96*d9f75844SAndroid Build Coastguard Worker // use any number of threads to achieve "in parallel" behavior.
97*d9f75844SAndroid Build Coastguard Worker //
98*d9f75844SAndroid Build Coastguard Worker // When an operation is chained onto the OperationsChain, it is enqueued to be
99*d9f75844SAndroid Build Coastguard Worker // executed. Operations are executed in FIFO order, where the next operation
100*d9f75844SAndroid Build Coastguard Worker // does not start until the previous operation has completed. OperationsChain
101*d9f75844SAndroid Build Coastguard Worker // guarantees that:
102*d9f75844SAndroid Build Coastguard Worker // - If the operations chain is empty when an operation is chained, the
103*d9f75844SAndroid Build Coastguard Worker //   operation starts immediately, inside ChainOperation().
104*d9f75844SAndroid Build Coastguard Worker // - If the operations chain is not empty when an operation is chained, the
105*d9f75844SAndroid Build Coastguard Worker //   operation starts upon the previous operation completing, inside the
106*d9f75844SAndroid Build Coastguard Worker //   callback.
107*d9f75844SAndroid Build Coastguard Worker //
108*d9f75844SAndroid Build Coastguard Worker // An operation is contractually obligated to invoke the completion callback
109*d9f75844SAndroid Build Coastguard Worker // exactly once. Cancelling a chained operation is not supported by the
110*d9f75844SAndroid Build Coastguard Worker // OperationsChain; an operation that wants to be cancellable is responsible for
111*d9f75844SAndroid Build Coastguard Worker // aborting its own steps. The callback must still be invoked.
112*d9f75844SAndroid Build Coastguard Worker //
113*d9f75844SAndroid Build Coastguard Worker // The OperationsChain is kept-alive through reference counting if there are
114*d9f75844SAndroid Build Coastguard Worker // operations pending. This, together with the contract, guarantees that all
115*d9f75844SAndroid Build Coastguard Worker // operations that are chained get executed.
116*d9f75844SAndroid Build Coastguard Worker class OperationsChain final : public RefCountedNonVirtual<OperationsChain> {
117*d9f75844SAndroid Build Coastguard Worker  public:
118*d9f75844SAndroid Build Coastguard Worker   static scoped_refptr<OperationsChain> Create();
119*d9f75844SAndroid Build Coastguard Worker   ~OperationsChain();
120*d9f75844SAndroid Build Coastguard Worker 
121*d9f75844SAndroid Build Coastguard Worker   OperationsChain(const OperationsChain&) = delete;
122*d9f75844SAndroid Build Coastguard Worker   OperationsChain& operator=(const OperationsChain&) = delete;
123*d9f75844SAndroid Build Coastguard Worker 
124*d9f75844SAndroid Build Coastguard Worker   void SetOnChainEmptyCallback(std::function<void()> on_chain_empty_callback);
125*d9f75844SAndroid Build Coastguard Worker   bool IsEmpty() const;
126*d9f75844SAndroid Build Coastguard Worker 
127*d9f75844SAndroid Build Coastguard Worker   // Chains an operation. Chained operations are executed in FIFO order. The
128*d9f75844SAndroid Build Coastguard Worker   // operation starts when `functor` is executed by the OperationsChain and is
129*d9f75844SAndroid Build Coastguard Worker   // contractually obligated to invoke the callback passed to it when the
130*d9f75844SAndroid Build Coastguard Worker   // operation is complete. Operations must start and complete on the same
131*d9f75844SAndroid Build Coastguard Worker   // sequence that this method was invoked on.
132*d9f75844SAndroid Build Coastguard Worker   //
133*d9f75844SAndroid Build Coastguard Worker   // If the OperationsChain is empty, the operation starts immediately.
134*d9f75844SAndroid Build Coastguard Worker   // Otherwise it starts upon the previous operation completing.
135*d9f75844SAndroid Build Coastguard Worker   //
136*d9f75844SAndroid Build Coastguard Worker   // Requirements of FunctorT:
137*d9f75844SAndroid Build Coastguard Worker   // - FunctorT is movable.
138*d9f75844SAndroid Build Coastguard Worker   // - FunctorT implements "T operator()(std::function<void()> callback)" or
139*d9f75844SAndroid Build Coastguard Worker   //   "T operator()(std::function<void()> callback) const" for some T (if T is
140*d9f75844SAndroid Build Coastguard Worker   //   not void, the return value is discarded in the invoking sequence). The
141*d9f75844SAndroid Build Coastguard Worker   //   operator starts the operation; when the operation is complete, "callback"
142*d9f75844SAndroid Build Coastguard Worker   //   MUST be invoked, and it MUST be so on the sequence that ChainOperation()
143*d9f75844SAndroid Build Coastguard Worker   //   was invoked on.
144*d9f75844SAndroid Build Coastguard Worker   //
145*d9f75844SAndroid Build Coastguard Worker   // Lambda expressions are valid functors.
146*d9f75844SAndroid Build Coastguard Worker   template <typename FunctorT>
ChainOperation(FunctorT && functor)147*d9f75844SAndroid Build Coastguard Worker   void ChainOperation(FunctorT&& functor) {
148*d9f75844SAndroid Build Coastguard Worker     RTC_DCHECK_RUN_ON(&sequence_checker_);
149*d9f75844SAndroid Build Coastguard Worker     chained_operations_.push(
150*d9f75844SAndroid Build Coastguard Worker         std::make_unique<
151*d9f75844SAndroid Build Coastguard Worker             rtc_operations_chain_internal::OperationWithFunctor<FunctorT>>(
152*d9f75844SAndroid Build Coastguard Worker             std::forward<FunctorT>(functor), CreateOperationsChainCallback()));
153*d9f75844SAndroid Build Coastguard Worker     // If this is the only operation in the chain we execute it immediately.
154*d9f75844SAndroid Build Coastguard Worker     // Otherwise the callback will get invoked when the pending operation
155*d9f75844SAndroid Build Coastguard Worker     // completes which will trigger the next operation to execute.
156*d9f75844SAndroid Build Coastguard Worker     if (chained_operations_.size() == 1) {
157*d9f75844SAndroid Build Coastguard Worker       chained_operations_.front()->Run();
158*d9f75844SAndroid Build Coastguard Worker     }
159*d9f75844SAndroid Build Coastguard Worker   }
160*d9f75844SAndroid Build Coastguard Worker 
161*d9f75844SAndroid Build Coastguard Worker  private:
162*d9f75844SAndroid Build Coastguard Worker   friend class CallbackHandle;
163*d9f75844SAndroid Build Coastguard Worker 
164*d9f75844SAndroid Build Coastguard Worker   // The callback that is passed to an operation's functor (that is used to
165*d9f75844SAndroid Build Coastguard Worker   // inform the OperationsChain that the operation has completed) is of type
166*d9f75844SAndroid Build Coastguard Worker   // std::function<void()>, which is a copyable type. To allow the callback to
167*d9f75844SAndroid Build Coastguard Worker   // be copyable, it is backed up by this reference counted handle. See
168*d9f75844SAndroid Build Coastguard Worker   // CreateOperationsChainCallback().
169*d9f75844SAndroid Build Coastguard Worker   class CallbackHandle final : public RefCountedNonVirtual<CallbackHandle> {
170*d9f75844SAndroid Build Coastguard Worker    public:
171*d9f75844SAndroid Build Coastguard Worker     explicit CallbackHandle(scoped_refptr<OperationsChain> operations_chain);
172*d9f75844SAndroid Build Coastguard Worker     ~CallbackHandle();
173*d9f75844SAndroid Build Coastguard Worker 
174*d9f75844SAndroid Build Coastguard Worker     CallbackHandle(const CallbackHandle&) = delete;
175*d9f75844SAndroid Build Coastguard Worker     CallbackHandle& operator=(const CallbackHandle&) = delete;
176*d9f75844SAndroid Build Coastguard Worker 
177*d9f75844SAndroid Build Coastguard Worker     void OnOperationComplete();
178*d9f75844SAndroid Build Coastguard Worker 
179*d9f75844SAndroid Build Coastguard Worker    private:
180*d9f75844SAndroid Build Coastguard Worker     scoped_refptr<OperationsChain> operations_chain_;
181*d9f75844SAndroid Build Coastguard Worker #if RTC_DCHECK_IS_ON
182*d9f75844SAndroid Build Coastguard Worker     bool has_run_ = false;
183*d9f75844SAndroid Build Coastguard Worker #endif  // RTC_DCHECK_IS_ON
184*d9f75844SAndroid Build Coastguard Worker   };
185*d9f75844SAndroid Build Coastguard Worker 
186*d9f75844SAndroid Build Coastguard Worker   OperationsChain();
187*d9f75844SAndroid Build Coastguard Worker 
188*d9f75844SAndroid Build Coastguard Worker   std::function<void()> CreateOperationsChainCallback();
189*d9f75844SAndroid Build Coastguard Worker   void OnOperationComplete();
190*d9f75844SAndroid Build Coastguard Worker 
191*d9f75844SAndroid Build Coastguard Worker   RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
192*d9f75844SAndroid Build Coastguard Worker   // FIFO-list of operations that are chained. An operation that is executing
193*d9f75844SAndroid Build Coastguard Worker   // remains on this list until it has completed by invoking the callback passed
194*d9f75844SAndroid Build Coastguard Worker   // to it.
195*d9f75844SAndroid Build Coastguard Worker   std::queue<std::unique_ptr<rtc_operations_chain_internal::Operation>>
196*d9f75844SAndroid Build Coastguard Worker       chained_operations_ RTC_GUARDED_BY(sequence_checker_);
197*d9f75844SAndroid Build Coastguard Worker   absl::optional<std::function<void()>> on_chain_empty_callback_
198*d9f75844SAndroid Build Coastguard Worker       RTC_GUARDED_BY(sequence_checker_);
199*d9f75844SAndroid Build Coastguard Worker };
200*d9f75844SAndroid Build Coastguard Worker 
201*d9f75844SAndroid Build Coastguard Worker }  // namespace rtc
202*d9f75844SAndroid Build Coastguard Worker 
203*d9f75844SAndroid Build Coastguard Worker #endif  // RTC_BASE_OPERATIONS_CHAIN_H_
204