blob: e6fb4f99d3d68a23e2ab18ec9636573789409074 [file] [log] [blame]
Henrik Boström27c29362019-10-21 15:21:55 +02001/*
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 10:20:11 +020021#include "absl/types/optional.h"
Niels Möller95129102022-01-13 11:00:05 +010022#include "api/ref_counted_base.h"
Henrik Boström27c29362019-10-21 15:21:55 +020023#include "api/scoped_refptr.h"
Artem Titovd15a5752021-02-10 14:31:24 +010024#include "api/sequence_checker.h"
Henrik Boström27c29362019-10-21 15:21:55 +020025#include "rtc_base/checks.h"
26#include "rtc_base/constructor_magic.h"
27#include "rtc_base/ref_count.h"
28#include "rtc_base/ref_counted_object.h"
Mirko Bonadei20e4c802020-11-23 11:07:42 +010029#include "rtc_base/system/no_unique_address.h"
Henrik Boström27c29362019-10-21 15:21:55 +020030
31namespace rtc {
32
33namespace rtc_operations_chain_internal {
34
35// Abstract base class for operations on the OperationsChain. Run() must be
36// invoked exactly once during the Operation's lifespan.
37class Operation {
38 public:
39 virtual ~Operation() {}
40
41 virtual void Run() = 0;
42};
43
Artem Titov96e3b992021-07-26 16:03:14 +020044// FunctorT is the same as in OperationsChain::ChainOperation(). `callback_` is
45// passed on to the `functor_` and is used to inform the OperationsChain that
Henrik Boström27c29362019-10-21 15:21:55 +020046// the operation completed. The functor is responsible for invoking the
47// callback when the operation has completed.
48template <typename FunctorT>
49class OperationWithFunctor final : public Operation {
50 public:
51 OperationWithFunctor(FunctorT&& functor, std::function<void()> callback)
52 : functor_(std::forward<FunctorT>(functor)),
53 callback_(std::move(callback)) {}
54
Tomas Gunnarsson36992362020-10-05 21:41:36 +020055 ~OperationWithFunctor() override {
56#if RTC_DCHECK_IS_ON
57 RTC_DCHECK(has_run_);
58#endif // RTC_DCHECK_IS_ON
59 }
Henrik Boström27c29362019-10-21 15:21:55 +020060
61 void Run() override {
Tomas Gunnarsson36992362020-10-05 21:41:36 +020062#if RTC_DCHECK_IS_ON
Henrik Boström27c29362019-10-21 15:21:55 +020063 RTC_DCHECK(!has_run_);
Henrik Boström27c29362019-10-21 15:21:55 +020064 has_run_ = true;
65#endif // RTC_DCHECK_IS_ON
Henrik Boströmee6f4f62019-11-06 12:36:12 +010066 // The functor being executed may invoke the callback synchronously,
Artem Titov96e3b992021-07-26 16:03:14 +020067 // marking the operation as complete. As such, `this` OperationWithFunctor
68 // object may get deleted here, including destroying `functor_`. To
Henrik Boströmee6f4f62019-11-06 12:36:12 +010069 // protect the functor from self-destruction while running, it is moved to
70 // a local variable.
71 auto functor = std::move(functor_);
72 functor(std::move(callback_));
Artem Titov96e3b992021-07-26 16:03:14 +020073 // `this` may now be deleted; don't touch any member variables.
Henrik Boström27c29362019-10-21 15:21:55 +020074 }
75
76 private:
77 typename std::remove_reference<FunctorT>::type functor_;
78 std::function<void()> callback_;
Tomas Gunnarsson36992362020-10-05 21:41:36 +020079#if RTC_DCHECK_IS_ON
Henrik Boström27c29362019-10-21 15:21:55 +020080 bool has_run_ = false;
81#endif // RTC_DCHECK_IS_ON
82};
83
84} // namespace rtc_operations_chain_internal
85
86// An implementation of an operations chain. An operations chain is used to
87// ensure that asynchronous tasks are executed in-order with at most one task
88// running at a time. The notion of an operation chain is defined in
89// https://w3c.github.io/webrtc-pc/#dfn-operations-chain, though unlike this
90// implementation, the referenced definition is coupled with a peer connection.
91//
92// An operation is an asynchronous task. The operation starts when its functor
93// is invoked, and completes when the callback that is passed to functor is
94// invoked by the operation. The operation must start and complete on the same
95// sequence that the operation was "chained" on. As such, the OperationsChain
96// operates in a "single-threaded" fashion, but the asynchronous operations may
97// use any number of threads to achieve "in parallel" behavior.
98//
99// When an operation is chained onto the OperationsChain, it is enqueued to be
100// executed. Operations are executed in FIFO order, where the next operation
101// does not start until the previous operation has completed. OperationsChain
102// guarantees that:
103// - If the operations chain is empty when an operation is chained, the
104// operation starts immediately, inside ChainOperation().
105// - If the operations chain is not empty when an operation is chained, the
106// operation starts upon the previous operation completing, inside the
107// callback.
108//
109// An operation is contractually obligated to invoke the completion callback
110// exactly once. Cancelling a chained operation is not supported by the
111// OperationsChain; an operation that wants to be cancellable is responsible for
112// aborting its own steps. The callback must still be invoked.
113//
114// The OperationsChain is kept-alive through reference counting if there are
115// operations pending. This, together with the contract, guarantees that all
116// operations that are chained get executed.
Niels Möller95129102022-01-13 11:00:05 +0100117class OperationsChain final : public RefCountedNonVirtual<OperationsChain> {
Henrik Boström27c29362019-10-21 15:21:55 +0200118 public:
119 static scoped_refptr<OperationsChain> Create();
120 ~OperationsChain();
121
Henrik Boströme574a312020-08-25 10:20:11 +0200122 void SetOnChainEmptyCallback(std::function<void()> on_chain_empty_callback);
123 bool IsEmpty() const;
124
Henrik Boström27c29362019-10-21 15:21:55 +0200125 // Chains an operation. Chained operations are executed in FIFO order. The
Artem Titov96e3b992021-07-26 16:03:14 +0200126 // operation starts when `functor` is executed by the OperationsChain and is
Henrik Boström27c29362019-10-21 15:21:55 +0200127 // contractually obligated to invoke the callback passed to it when the
128 // operation is complete. Operations must start and complete on the same
129 // sequence that this method was invoked on.
130 //
131 // If the OperationsChain is empty, the operation starts immediately.
132 // Otherwise it starts upon the previous operation completing.
133 //
134 // Requirements of FunctorT:
135 // - FunctorT is movable.
136 // - FunctorT implements "T operator()(std::function<void()> callback)" or
137 // "T operator()(std::function<void()> callback) const" for some T (if T is
138 // not void, the return value is discarded in the invoking sequence). The
139 // operator starts the operation; when the operation is complete, "callback"
140 // MUST be invoked, and it MUST be so on the sequence that ChainOperation()
141 // was invoked on.
142 //
143 // Lambda expressions are valid functors.
144 template <typename FunctorT>
145 void ChainOperation(FunctorT&& functor) {
146 RTC_DCHECK_RUN_ON(&sequence_checker_);
147 chained_operations_.push(
148 std::make_unique<
149 rtc_operations_chain_internal::OperationWithFunctor<FunctorT>>(
150 std::forward<FunctorT>(functor), CreateOperationsChainCallback()));
151 // If this is the only operation in the chain we execute it immediately.
152 // Otherwise the callback will get invoked when the pending operation
153 // completes which will trigger the next operation to execute.
154 if (chained_operations_.size() == 1) {
155 chained_operations_.front()->Run();
156 }
157 }
158
159 private:
160 friend class CallbackHandle;
161
162 // The callback that is passed to an operation's functor (that is used to
163 // inform the OperationsChain that the operation has completed) is of type
164 // std::function<void()>, which is a copyable type. To allow the callback to
165 // be copyable, it is backed up by this reference counted handle. See
166 // CreateOperationsChainCallback().
Niels Möller95129102022-01-13 11:00:05 +0100167 class CallbackHandle final : public RefCountedNonVirtual<CallbackHandle> {
Henrik Boström27c29362019-10-21 15:21:55 +0200168 public:
169 explicit CallbackHandle(scoped_refptr<OperationsChain> operations_chain);
170 ~CallbackHandle();
171
172 void OnOperationComplete();
173
174 private:
175 scoped_refptr<OperationsChain> operations_chain_;
Tomas Gunnarsson36992362020-10-05 21:41:36 +0200176#if RTC_DCHECK_IS_ON
Henrik Boström27c29362019-10-21 15:21:55 +0200177 bool has_run_ = false;
178#endif // RTC_DCHECK_IS_ON
179
180 RTC_DISALLOW_COPY_AND_ASSIGN(CallbackHandle);
181 };
182
183 OperationsChain();
184
185 std::function<void()> CreateOperationsChainCallback();
186 void OnOperationComplete();
187
Mirko Bonadei20e4c802020-11-23 11:07:42 +0100188 RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
Henrik Boström27c29362019-10-21 15:21:55 +0200189 // FIFO-list of operations that are chained. An operation that is executing
190 // remains on this list until it has completed by invoking the callback passed
191 // to it.
192 std::queue<std::unique_ptr<rtc_operations_chain_internal::Operation>>
193 chained_operations_ RTC_GUARDED_BY(sequence_checker_);
Henrik Boströme574a312020-08-25 10:20:11 +0200194 absl::optional<std::function<void()>> on_chain_empty_callback_
195 RTC_GUARDED_BY(sequence_checker_);
Henrik Boström27c29362019-10-21 15:21:55 +0200196
197 RTC_DISALLOW_COPY_AND_ASSIGN(OperationsChain);
198};
199
200} // namespace rtc
201
202#endif // RTC_BASE_OPERATIONS_CHAIN_H_