blob: 94ff57bef77cb9f879247c912873e72710b5677e [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
21#include "api/scoped_refptr.h"
22#include "rtc_base/checks.h"
23#include "rtc_base/constructor_magic.h"
24#include "rtc_base/ref_count.h"
25#include "rtc_base/ref_counted_object.h"
26#include "rtc_base/synchronization/sequence_checker.h"
27
28namespace rtc {
29
30namespace rtc_operations_chain_internal {
31
32// Abstract base class for operations on the OperationsChain. Run() must be
33// invoked exactly once during the Operation's lifespan.
34class Operation {
35 public:
36 virtual ~Operation() {}
37
38 virtual void Run() = 0;
39};
40
41// FunctorT is the same as in OperationsChain::ChainOperation(). |callback_| is
42// passed on to the |functor_| and is used to inform the OperationsChain that
43// the operation completed. The functor is responsible for invoking the
44// callback when the operation has completed.
45template <typename FunctorT>
46class OperationWithFunctor final : public Operation {
47 public:
48 OperationWithFunctor(FunctorT&& functor, std::function<void()> callback)
49 : functor_(std::forward<FunctorT>(functor)),
50 callback_(std::move(callback)) {}
51
52 ~OperationWithFunctor() override { RTC_DCHECK(has_run_); }
53
54 void Run() override {
55 RTC_DCHECK(!has_run_);
56#ifdef RTC_DCHECK_IS_ON
57 has_run_ = true;
58#endif // RTC_DCHECK_IS_ON
59 functor_(std::move(callback_));
60 }
61
62 private:
63 typename std::remove_reference<FunctorT>::type functor_;
64 std::function<void()> callback_;
65#ifdef RTC_DCHECK_IS_ON
66 bool has_run_ = false;
67#endif // RTC_DCHECK_IS_ON
68};
69
70} // namespace rtc_operations_chain_internal
71
72// An implementation of an operations chain. An operations chain is used to
73// ensure that asynchronous tasks are executed in-order with at most one task
74// running at a time. The notion of an operation chain is defined in
75// https://w3c.github.io/webrtc-pc/#dfn-operations-chain, though unlike this
76// implementation, the referenced definition is coupled with a peer connection.
77//
78// An operation is an asynchronous task. The operation starts when its functor
79// is invoked, and completes when the callback that is passed to functor is
80// invoked by the operation. The operation must start and complete on the same
81// sequence that the operation was "chained" on. As such, the OperationsChain
82// operates in a "single-threaded" fashion, but the asynchronous operations may
83// use any number of threads to achieve "in parallel" behavior.
84//
85// When an operation is chained onto the OperationsChain, it is enqueued to be
86// executed. Operations are executed in FIFO order, where the next operation
87// does not start until the previous operation has completed. OperationsChain
88// guarantees that:
89// - If the operations chain is empty when an operation is chained, the
90// operation starts immediately, inside ChainOperation().
91// - If the operations chain is not empty when an operation is chained, the
92// operation starts upon the previous operation completing, inside the
93// callback.
94//
95// An operation is contractually obligated to invoke the completion callback
96// exactly once. Cancelling a chained operation is not supported by the
97// OperationsChain; an operation that wants to be cancellable is responsible for
98// aborting its own steps. The callback must still be invoked.
99//
100// The OperationsChain is kept-alive through reference counting if there are
101// operations pending. This, together with the contract, guarantees that all
102// operations that are chained get executed.
103class OperationsChain final : public RefCountedObject<RefCountInterface> {
104 public:
105 static scoped_refptr<OperationsChain> Create();
106 ~OperationsChain();
107
108 // Chains an operation. Chained operations are executed in FIFO order. The
109 // operation starts when |functor| is executed by the OperationsChain and is
110 // contractually obligated to invoke the callback passed to it when the
111 // operation is complete. Operations must start and complete on the same
112 // sequence that this method was invoked on.
113 //
114 // If the OperationsChain is empty, the operation starts immediately.
115 // Otherwise it starts upon the previous operation completing.
116 //
117 // Requirements of FunctorT:
118 // - FunctorT is movable.
119 // - FunctorT implements "T operator()(std::function<void()> callback)" or
120 // "T operator()(std::function<void()> callback) const" for some T (if T is
121 // not void, the return value is discarded in the invoking sequence). The
122 // operator starts the operation; when the operation is complete, "callback"
123 // MUST be invoked, and it MUST be so on the sequence that ChainOperation()
124 // was invoked on.
125 //
126 // Lambda expressions are valid functors.
127 template <typename FunctorT>
128 void ChainOperation(FunctorT&& functor) {
129 RTC_DCHECK_RUN_ON(&sequence_checker_);
130 chained_operations_.push(
131 std::make_unique<
132 rtc_operations_chain_internal::OperationWithFunctor<FunctorT>>(
133 std::forward<FunctorT>(functor), CreateOperationsChainCallback()));
134 // If this is the only operation in the chain we execute it immediately.
135 // Otherwise the callback will get invoked when the pending operation
136 // completes which will trigger the next operation to execute.
137 if (chained_operations_.size() == 1) {
138 chained_operations_.front()->Run();
139 }
140 }
141
142 private:
143 friend class CallbackHandle;
144
145 // The callback that is passed to an operation's functor (that is used to
146 // inform the OperationsChain that the operation has completed) is of type
147 // std::function<void()>, which is a copyable type. To allow the callback to
148 // be copyable, it is backed up by this reference counted handle. See
149 // CreateOperationsChainCallback().
150 class CallbackHandle final : public RefCountedObject<RefCountInterface> {
151 public:
152 explicit CallbackHandle(scoped_refptr<OperationsChain> operations_chain);
153 ~CallbackHandle();
154
155 void OnOperationComplete();
156
157 private:
158 scoped_refptr<OperationsChain> operations_chain_;
159#ifdef RTC_DCHECK_IS_ON
160 bool has_run_ = false;
161#endif // RTC_DCHECK_IS_ON
162
163 RTC_DISALLOW_COPY_AND_ASSIGN(CallbackHandle);
164 };
165
166 OperationsChain();
167
168 std::function<void()> CreateOperationsChainCallback();
169 void OnOperationComplete();
170
171 webrtc::SequenceChecker sequence_checker_;
172 // FIFO-list of operations that are chained. An operation that is executing
173 // remains on this list until it has completed by invoking the callback passed
174 // to it.
175 std::queue<std::unique_ptr<rtc_operations_chain_internal::Operation>>
176 chained_operations_ RTC_GUARDED_BY(sequence_checker_);
177
178 RTC_DISALLOW_COPY_AND_ASSIGN(OperationsChain);
179};
180
181} // namespace rtc
182
183#endif // RTC_BASE_OPERATIONS_CHAIN_H_