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