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 #include "rtc_base/operations_chain.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <atomic>
14*d9f75844SAndroid Build Coastguard Worker #include <functional>
15*d9f75844SAndroid Build Coastguard Worker #include <memory>
16*d9f75844SAndroid Build Coastguard Worker #include <utility>
17*d9f75844SAndroid Build Coastguard Worker #include <vector>
18*d9f75844SAndroid Build Coastguard Worker
19*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/event.h"
20*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/gunit.h"
21*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/thread.h"
22*d9f75844SAndroid Build Coastguard Worker #include "test/gmock.h"
23*d9f75844SAndroid Build Coastguard Worker #include "test/gtest.h"
24*d9f75844SAndroid Build Coastguard Worker
25*d9f75844SAndroid Build Coastguard Worker namespace rtc {
26*d9f75844SAndroid Build Coastguard Worker
27*d9f75844SAndroid Build Coastguard Worker using ::testing::ElementsAre;
28*d9f75844SAndroid Build Coastguard Worker
29*d9f75844SAndroid Build Coastguard Worker namespace {
30*d9f75844SAndroid Build Coastguard Worker
31*d9f75844SAndroid Build Coastguard Worker constexpr int kDefaultTimeout = 3000;
32*d9f75844SAndroid Build Coastguard Worker
33*d9f75844SAndroid Build Coastguard Worker } // namespace
34*d9f75844SAndroid Build Coastguard Worker
35*d9f75844SAndroid Build Coastguard Worker class OperationTracker {
36*d9f75844SAndroid Build Coastguard Worker public:
OperationTracker()37*d9f75844SAndroid Build Coastguard Worker OperationTracker() : background_thread_(Thread::Create()) {
38*d9f75844SAndroid Build Coastguard Worker background_thread_->Start();
39*d9f75844SAndroid Build Coastguard Worker }
40*d9f75844SAndroid Build Coastguard Worker // The caller is responsible for ensuring that no operations are pending.
~OperationTracker()41*d9f75844SAndroid Build Coastguard Worker ~OperationTracker() {}
42*d9f75844SAndroid Build Coastguard Worker
43*d9f75844SAndroid Build Coastguard Worker // Creates a binding for the synchronous operation (see
44*d9f75844SAndroid Build Coastguard Worker // StartSynchronousOperation() below).
BindSynchronousOperation(Event * operation_complete_event)45*d9f75844SAndroid Build Coastguard Worker std::function<void(std::function<void()>)> BindSynchronousOperation(
46*d9f75844SAndroid Build Coastguard Worker Event* operation_complete_event) {
47*d9f75844SAndroid Build Coastguard Worker return [this, operation_complete_event](std::function<void()> callback) {
48*d9f75844SAndroid Build Coastguard Worker StartSynchronousOperation(operation_complete_event, std::move(callback));
49*d9f75844SAndroid Build Coastguard Worker };
50*d9f75844SAndroid Build Coastguard Worker }
51*d9f75844SAndroid Build Coastguard Worker
52*d9f75844SAndroid Build Coastguard Worker // Creates a binding for the asynchronous operation (see
53*d9f75844SAndroid Build Coastguard Worker // StartAsynchronousOperation() below).
BindAsynchronousOperation(Event * unblock_operation_event,Event * operation_complete_event)54*d9f75844SAndroid Build Coastguard Worker std::function<void(std::function<void()>)> BindAsynchronousOperation(
55*d9f75844SAndroid Build Coastguard Worker Event* unblock_operation_event,
56*d9f75844SAndroid Build Coastguard Worker Event* operation_complete_event) {
57*d9f75844SAndroid Build Coastguard Worker return [this, unblock_operation_event,
58*d9f75844SAndroid Build Coastguard Worker operation_complete_event](std::function<void()> callback) {
59*d9f75844SAndroid Build Coastguard Worker StartAsynchronousOperation(unblock_operation_event,
60*d9f75844SAndroid Build Coastguard Worker operation_complete_event, std::move(callback));
61*d9f75844SAndroid Build Coastguard Worker };
62*d9f75844SAndroid Build Coastguard Worker }
63*d9f75844SAndroid Build Coastguard Worker
64*d9f75844SAndroid Build Coastguard Worker // When an operation is completed, its associated Event* is added to this
65*d9f75844SAndroid Build Coastguard Worker // list, in chronological order. This allows you to verify the order that
66*d9f75844SAndroid Build Coastguard Worker // operations are executed.
completed_operation_events() const67*d9f75844SAndroid Build Coastguard Worker const std::vector<Event*>& completed_operation_events() const {
68*d9f75844SAndroid Build Coastguard Worker return completed_operation_events_;
69*d9f75844SAndroid Build Coastguard Worker }
70*d9f75844SAndroid Build Coastguard Worker
71*d9f75844SAndroid Build Coastguard Worker private:
72*d9f75844SAndroid Build Coastguard Worker // This operation is completed synchronously; the callback is invoked before
73*d9f75844SAndroid Build Coastguard Worker // the function returns.
StartSynchronousOperation(Event * operation_complete_event,std::function<void ()> callback)74*d9f75844SAndroid Build Coastguard Worker void StartSynchronousOperation(Event* operation_complete_event,
75*d9f75844SAndroid Build Coastguard Worker std::function<void()> callback) {
76*d9f75844SAndroid Build Coastguard Worker completed_operation_events_.push_back(operation_complete_event);
77*d9f75844SAndroid Build Coastguard Worker operation_complete_event->Set();
78*d9f75844SAndroid Build Coastguard Worker callback();
79*d9f75844SAndroid Build Coastguard Worker }
80*d9f75844SAndroid Build Coastguard Worker
81*d9f75844SAndroid Build Coastguard Worker // This operation is completed asynchronously; it pings `background_thread_`,
82*d9f75844SAndroid Build Coastguard Worker // blocking that thread until `unblock_operation_event` is signaled and then
83*d9f75844SAndroid Build Coastguard Worker // completes upon posting back to the thread that the operation started on.
84*d9f75844SAndroid Build Coastguard Worker // Note that this requires the starting thread to be executing tasks (handle
85*d9f75844SAndroid Build Coastguard Worker // messages), i.e. must not be blocked.
StartAsynchronousOperation(Event * unblock_operation_event,Event * operation_complete_event,std::function<void ()> callback)86*d9f75844SAndroid Build Coastguard Worker void StartAsynchronousOperation(Event* unblock_operation_event,
87*d9f75844SAndroid Build Coastguard Worker Event* operation_complete_event,
88*d9f75844SAndroid Build Coastguard Worker std::function<void()> callback) {
89*d9f75844SAndroid Build Coastguard Worker Thread* current_thread = Thread::Current();
90*d9f75844SAndroid Build Coastguard Worker background_thread_->PostTask([this, current_thread, unblock_operation_event,
91*d9f75844SAndroid Build Coastguard Worker operation_complete_event, callback]() {
92*d9f75844SAndroid Build Coastguard Worker unblock_operation_event->Wait(Event::kForever);
93*d9f75844SAndroid Build Coastguard Worker current_thread->PostTask([this, operation_complete_event, callback]() {
94*d9f75844SAndroid Build Coastguard Worker completed_operation_events_.push_back(operation_complete_event);
95*d9f75844SAndroid Build Coastguard Worker operation_complete_event->Set();
96*d9f75844SAndroid Build Coastguard Worker callback();
97*d9f75844SAndroid Build Coastguard Worker });
98*d9f75844SAndroid Build Coastguard Worker });
99*d9f75844SAndroid Build Coastguard Worker }
100*d9f75844SAndroid Build Coastguard Worker
101*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Thread> background_thread_;
102*d9f75844SAndroid Build Coastguard Worker std::vector<Event*> completed_operation_events_;
103*d9f75844SAndroid Build Coastguard Worker };
104*d9f75844SAndroid Build Coastguard Worker
105*d9f75844SAndroid Build Coastguard Worker // The OperationTrackerProxy ensures all operations are chained on a separate
106*d9f75844SAndroid Build Coastguard Worker // thread. This allows tests to block while chained operations are posting
107*d9f75844SAndroid Build Coastguard Worker // between threads.
108*d9f75844SAndroid Build Coastguard Worker class OperationTrackerProxy {
109*d9f75844SAndroid Build Coastguard Worker public:
OperationTrackerProxy()110*d9f75844SAndroid Build Coastguard Worker OperationTrackerProxy()
111*d9f75844SAndroid Build Coastguard Worker : operations_chain_thread_(Thread::Create()),
112*d9f75844SAndroid Build Coastguard Worker operation_tracker_(nullptr),
113*d9f75844SAndroid Build Coastguard Worker operations_chain_(nullptr) {
114*d9f75844SAndroid Build Coastguard Worker operations_chain_thread_->Start();
115*d9f75844SAndroid Build Coastguard Worker }
116*d9f75844SAndroid Build Coastguard Worker
Initialize()117*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Event> Initialize() {
118*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Event> event = std::make_unique<Event>();
119*d9f75844SAndroid Build Coastguard Worker operations_chain_thread_->PostTask([this, event_ptr = event.get()]() {
120*d9f75844SAndroid Build Coastguard Worker operation_tracker_ = std::make_unique<OperationTracker>();
121*d9f75844SAndroid Build Coastguard Worker operations_chain_ = OperationsChain::Create();
122*d9f75844SAndroid Build Coastguard Worker event_ptr->Set();
123*d9f75844SAndroid Build Coastguard Worker });
124*d9f75844SAndroid Build Coastguard Worker return event;
125*d9f75844SAndroid Build Coastguard Worker }
126*d9f75844SAndroid Build Coastguard Worker
SetOnChainEmptyCallback(std::function<void ()> on_chain_empty_callback)127*d9f75844SAndroid Build Coastguard Worker void SetOnChainEmptyCallback(std::function<void()> on_chain_empty_callback) {
128*d9f75844SAndroid Build Coastguard Worker Event event;
129*d9f75844SAndroid Build Coastguard Worker operations_chain_thread_->PostTask(
130*d9f75844SAndroid Build Coastguard Worker [this, &event,
131*d9f75844SAndroid Build Coastguard Worker on_chain_empty_callback = std::move(on_chain_empty_callback)]() {
132*d9f75844SAndroid Build Coastguard Worker operations_chain_->SetOnChainEmptyCallback(
133*d9f75844SAndroid Build Coastguard Worker std::move(on_chain_empty_callback));
134*d9f75844SAndroid Build Coastguard Worker event.Set();
135*d9f75844SAndroid Build Coastguard Worker });
136*d9f75844SAndroid Build Coastguard Worker event.Wait(Event::kForever);
137*d9f75844SAndroid Build Coastguard Worker }
138*d9f75844SAndroid Build Coastguard Worker
IsEmpty()139*d9f75844SAndroid Build Coastguard Worker bool IsEmpty() {
140*d9f75844SAndroid Build Coastguard Worker Event event;
141*d9f75844SAndroid Build Coastguard Worker bool is_empty = false;
142*d9f75844SAndroid Build Coastguard Worker operations_chain_thread_->PostTask([this, &event, &is_empty]() {
143*d9f75844SAndroid Build Coastguard Worker is_empty = operations_chain_->IsEmpty();
144*d9f75844SAndroid Build Coastguard Worker event.Set();
145*d9f75844SAndroid Build Coastguard Worker });
146*d9f75844SAndroid Build Coastguard Worker event.Wait(Event::kForever);
147*d9f75844SAndroid Build Coastguard Worker return is_empty;
148*d9f75844SAndroid Build Coastguard Worker }
149*d9f75844SAndroid Build Coastguard Worker
ReleaseOperationChain()150*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Event> ReleaseOperationChain() {
151*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Event> event = std::make_unique<Event>();
152*d9f75844SAndroid Build Coastguard Worker operations_chain_thread_->PostTask([this, event_ptr = event.get()]() {
153*d9f75844SAndroid Build Coastguard Worker operations_chain_ = nullptr;
154*d9f75844SAndroid Build Coastguard Worker event_ptr->Set();
155*d9f75844SAndroid Build Coastguard Worker });
156*d9f75844SAndroid Build Coastguard Worker return event;
157*d9f75844SAndroid Build Coastguard Worker }
158*d9f75844SAndroid Build Coastguard Worker
159*d9f75844SAndroid Build Coastguard Worker // Chains a synchronous operation on the operation chain's thread.
PostSynchronousOperation()160*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Event> PostSynchronousOperation() {
161*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Event> operation_complete_event = std::make_unique<Event>();
162*d9f75844SAndroid Build Coastguard Worker operations_chain_thread_->PostTask(
163*d9f75844SAndroid Build Coastguard Worker [this,
164*d9f75844SAndroid Build Coastguard Worker operation_complete_event_ptr = operation_complete_event.get()]() {
165*d9f75844SAndroid Build Coastguard Worker operations_chain_->ChainOperation(
166*d9f75844SAndroid Build Coastguard Worker operation_tracker_->BindSynchronousOperation(
167*d9f75844SAndroid Build Coastguard Worker operation_complete_event_ptr));
168*d9f75844SAndroid Build Coastguard Worker });
169*d9f75844SAndroid Build Coastguard Worker return operation_complete_event;
170*d9f75844SAndroid Build Coastguard Worker }
171*d9f75844SAndroid Build Coastguard Worker
172*d9f75844SAndroid Build Coastguard Worker // Chains an asynchronous operation on the operation chain's thread. This
173*d9f75844SAndroid Build Coastguard Worker // involves the operation chain thread and an additional background thread.
PostAsynchronousOperation(Event * unblock_operation_event)174*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Event> PostAsynchronousOperation(
175*d9f75844SAndroid Build Coastguard Worker Event* unblock_operation_event) {
176*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Event> operation_complete_event = std::make_unique<Event>();
177*d9f75844SAndroid Build Coastguard Worker operations_chain_thread_->PostTask(
178*d9f75844SAndroid Build Coastguard Worker [this, unblock_operation_event,
179*d9f75844SAndroid Build Coastguard Worker operation_complete_event_ptr = operation_complete_event.get()]() {
180*d9f75844SAndroid Build Coastguard Worker operations_chain_->ChainOperation(
181*d9f75844SAndroid Build Coastguard Worker operation_tracker_->BindAsynchronousOperation(
182*d9f75844SAndroid Build Coastguard Worker unblock_operation_event, operation_complete_event_ptr));
183*d9f75844SAndroid Build Coastguard Worker });
184*d9f75844SAndroid Build Coastguard Worker return operation_complete_event;
185*d9f75844SAndroid Build Coastguard Worker }
186*d9f75844SAndroid Build Coastguard Worker
187*d9f75844SAndroid Build Coastguard Worker // The order of completed events. Touches the `operation_tracker_` on the
188*d9f75844SAndroid Build Coastguard Worker // calling thread, this is only thread safe if all chained operations have
189*d9f75844SAndroid Build Coastguard Worker // completed.
completed_operation_events() const190*d9f75844SAndroid Build Coastguard Worker const std::vector<Event*>& completed_operation_events() const {
191*d9f75844SAndroid Build Coastguard Worker return operation_tracker_->completed_operation_events();
192*d9f75844SAndroid Build Coastguard Worker }
193*d9f75844SAndroid Build Coastguard Worker
194*d9f75844SAndroid Build Coastguard Worker private:
195*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<Thread> operations_chain_thread_;
196*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<OperationTracker> operation_tracker_;
197*d9f75844SAndroid Build Coastguard Worker scoped_refptr<OperationsChain> operations_chain_;
198*d9f75844SAndroid Build Coastguard Worker };
199*d9f75844SAndroid Build Coastguard Worker
200*d9f75844SAndroid Build Coastguard Worker // On destruction, sets a boolean flag to true.
201*d9f75844SAndroid Build Coastguard Worker class SignalOnDestruction final {
202*d9f75844SAndroid Build Coastguard Worker public:
SignalOnDestruction(bool * destructor_called)203*d9f75844SAndroid Build Coastguard Worker SignalOnDestruction(bool* destructor_called)
204*d9f75844SAndroid Build Coastguard Worker : destructor_called_(destructor_called) {
205*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(destructor_called_);
206*d9f75844SAndroid Build Coastguard Worker }
~SignalOnDestruction()207*d9f75844SAndroid Build Coastguard Worker ~SignalOnDestruction() {
208*d9f75844SAndroid Build Coastguard Worker // Moved objects will have `destructor_called_` set to null. Destroying a
209*d9f75844SAndroid Build Coastguard Worker // moved SignalOnDestruction should not signal.
210*d9f75844SAndroid Build Coastguard Worker if (destructor_called_) {
211*d9f75844SAndroid Build Coastguard Worker *destructor_called_ = true;
212*d9f75844SAndroid Build Coastguard Worker }
213*d9f75844SAndroid Build Coastguard Worker }
214*d9f75844SAndroid Build Coastguard Worker
215*d9f75844SAndroid Build Coastguard Worker SignalOnDestruction(const SignalOnDestruction&) = delete;
216*d9f75844SAndroid Build Coastguard Worker SignalOnDestruction& operator=(const SignalOnDestruction&) = delete;
217*d9f75844SAndroid Build Coastguard Worker
218*d9f75844SAndroid Build Coastguard Worker // Move operators.
SignalOnDestruction(SignalOnDestruction && other)219*d9f75844SAndroid Build Coastguard Worker SignalOnDestruction(SignalOnDestruction&& other)
220*d9f75844SAndroid Build Coastguard Worker : SignalOnDestruction(other.destructor_called_) {
221*d9f75844SAndroid Build Coastguard Worker other.destructor_called_ = nullptr;
222*d9f75844SAndroid Build Coastguard Worker }
operator =(SignalOnDestruction && other)223*d9f75844SAndroid Build Coastguard Worker SignalOnDestruction& operator=(SignalOnDestruction&& other) {
224*d9f75844SAndroid Build Coastguard Worker destructor_called_ = other.destructor_called_;
225*d9f75844SAndroid Build Coastguard Worker other.destructor_called_ = nullptr;
226*d9f75844SAndroid Build Coastguard Worker return *this;
227*d9f75844SAndroid Build Coastguard Worker }
228*d9f75844SAndroid Build Coastguard Worker
229*d9f75844SAndroid Build Coastguard Worker private:
230*d9f75844SAndroid Build Coastguard Worker bool* destructor_called_;
231*d9f75844SAndroid Build Coastguard Worker };
232*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,SynchronousOperation)233*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest, SynchronousOperation) {
234*d9f75844SAndroid Build Coastguard Worker OperationTrackerProxy operation_tracker_proxy;
235*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.Initialize()->Wait(Event::kForever);
236*d9f75844SAndroid Build Coastguard Worker
237*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostSynchronousOperation()->Wait(Event::kForever);
238*d9f75844SAndroid Build Coastguard Worker }
239*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,AsynchronousOperation)240*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest, AsynchronousOperation) {
241*d9f75844SAndroid Build Coastguard Worker OperationTrackerProxy operation_tracker_proxy;
242*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.Initialize()->Wait(Event::kForever);
243*d9f75844SAndroid Build Coastguard Worker
244*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event;
245*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event =
246*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
247*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event);
248*d9f75844SAndroid Build Coastguard Worker // This should not be signaled until we unblock the operation.
249*d9f75844SAndroid Build Coastguard Worker EXPECT_FALSE(
250*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event->Wait(webrtc::TimeDelta::Zero()));
251*d9f75844SAndroid Build Coastguard Worker // Unblock the operation and wait for it to complete.
252*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event.Set();
253*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event->Wait(Event::kForever);
254*d9f75844SAndroid Build Coastguard Worker }
255*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,SynchronousOperationsAreExecutedImmediatelyWhenChainIsEmpty)256*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest,
257*d9f75844SAndroid Build Coastguard Worker SynchronousOperationsAreExecutedImmediatelyWhenChainIsEmpty) {
258*d9f75844SAndroid Build Coastguard Worker // Testing synchonicity must be done without the OperationTrackerProxy to
259*d9f75844SAndroid Build Coastguard Worker // ensure messages are not processed in parallel. This test has no background
260*d9f75844SAndroid Build Coastguard Worker // threads.
261*d9f75844SAndroid Build Coastguard Worker scoped_refptr<OperationsChain> operations_chain = OperationsChain::Create();
262*d9f75844SAndroid Build Coastguard Worker OperationTracker operation_tracker;
263*d9f75844SAndroid Build Coastguard Worker Event event0;
264*d9f75844SAndroid Build Coastguard Worker operations_chain->ChainOperation(
265*d9f75844SAndroid Build Coastguard Worker operation_tracker.BindSynchronousOperation(&event0));
266*d9f75844SAndroid Build Coastguard Worker // This should already be signaled. (If it wasn't, waiting wouldn't help,
267*d9f75844SAndroid Build Coastguard Worker // because we'd be blocking the only thread that exists.)
268*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(event0.Wait(webrtc::TimeDelta::Zero()));
269*d9f75844SAndroid Build Coastguard Worker // Chaining another operation should also execute immediately because the
270*d9f75844SAndroid Build Coastguard Worker // chain should already be empty.
271*d9f75844SAndroid Build Coastguard Worker Event event1;
272*d9f75844SAndroid Build Coastguard Worker operations_chain->ChainOperation(
273*d9f75844SAndroid Build Coastguard Worker operation_tracker.BindSynchronousOperation(&event1));
274*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(event1.Wait(webrtc::TimeDelta::Zero()));
275*d9f75844SAndroid Build Coastguard Worker }
276*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,AsynchronousOperationBlocksSynchronousOperation)277*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest, AsynchronousOperationBlocksSynchronousOperation) {
278*d9f75844SAndroid Build Coastguard Worker OperationTrackerProxy operation_tracker_proxy;
279*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.Initialize()->Wait(Event::kForever);
280*d9f75844SAndroid Build Coastguard Worker
281*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event;
282*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event =
283*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
284*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event);
285*d9f75844SAndroid Build Coastguard Worker
286*d9f75844SAndroid Build Coastguard Worker auto sync_operation_completed_event =
287*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostSynchronousOperation();
288*d9f75844SAndroid Build Coastguard Worker
289*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event.Set();
290*d9f75844SAndroid Build Coastguard Worker
291*d9f75844SAndroid Build Coastguard Worker sync_operation_completed_event->Wait(Event::kForever);
292*d9f75844SAndroid Build Coastguard Worker // The asynchronous avent should have blocked the synchronous event, meaning
293*d9f75844SAndroid Build Coastguard Worker // this should already be signaled.
294*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(async_operation_completed_event->Wait(webrtc::TimeDelta::Zero()));
295*d9f75844SAndroid Build Coastguard Worker }
296*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,OperationsAreExecutedInOrder)297*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest, OperationsAreExecutedInOrder) {
298*d9f75844SAndroid Build Coastguard Worker OperationTrackerProxy operation_tracker_proxy;
299*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.Initialize()->Wait(Event::kForever);
300*d9f75844SAndroid Build Coastguard Worker
301*d9f75844SAndroid Build Coastguard Worker // Chain a mix of asynchronous and synchronous operations.
302*d9f75844SAndroid Build Coastguard Worker Event operation0_unblock_event;
303*d9f75844SAndroid Build Coastguard Worker auto operation0_completed_event =
304*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
305*d9f75844SAndroid Build Coastguard Worker &operation0_unblock_event);
306*d9f75844SAndroid Build Coastguard Worker
307*d9f75844SAndroid Build Coastguard Worker Event operation1_unblock_event;
308*d9f75844SAndroid Build Coastguard Worker auto operation1_completed_event =
309*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
310*d9f75844SAndroid Build Coastguard Worker &operation1_unblock_event);
311*d9f75844SAndroid Build Coastguard Worker
312*d9f75844SAndroid Build Coastguard Worker auto operation2_completed_event =
313*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostSynchronousOperation();
314*d9f75844SAndroid Build Coastguard Worker
315*d9f75844SAndroid Build Coastguard Worker auto operation3_completed_event =
316*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostSynchronousOperation();
317*d9f75844SAndroid Build Coastguard Worker
318*d9f75844SAndroid Build Coastguard Worker Event operation4_unblock_event;
319*d9f75844SAndroid Build Coastguard Worker auto operation4_completed_event =
320*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
321*d9f75844SAndroid Build Coastguard Worker &operation4_unblock_event);
322*d9f75844SAndroid Build Coastguard Worker
323*d9f75844SAndroid Build Coastguard Worker auto operation5_completed_event =
324*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostSynchronousOperation();
325*d9f75844SAndroid Build Coastguard Worker
326*d9f75844SAndroid Build Coastguard Worker Event operation6_unblock_event;
327*d9f75844SAndroid Build Coastguard Worker auto operation6_completed_event =
328*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
329*d9f75844SAndroid Build Coastguard Worker &operation6_unblock_event);
330*d9f75844SAndroid Build Coastguard Worker
331*d9f75844SAndroid Build Coastguard Worker // Unblock events in reverse order. Operations 5, 3 and 2 are synchronous and
332*d9f75844SAndroid Build Coastguard Worker // don't need to be unblocked.
333*d9f75844SAndroid Build Coastguard Worker operation6_unblock_event.Set();
334*d9f75844SAndroid Build Coastguard Worker operation4_unblock_event.Set();
335*d9f75844SAndroid Build Coastguard Worker operation1_unblock_event.Set();
336*d9f75844SAndroid Build Coastguard Worker operation0_unblock_event.Set();
337*d9f75844SAndroid Build Coastguard Worker // Await all operations. The await-order shouldn't matter since they all get
338*d9f75844SAndroid Build Coastguard Worker // executed eventually.
339*d9f75844SAndroid Build Coastguard Worker operation0_completed_event->Wait(Event::kForever);
340*d9f75844SAndroid Build Coastguard Worker operation1_completed_event->Wait(Event::kForever);
341*d9f75844SAndroid Build Coastguard Worker operation2_completed_event->Wait(Event::kForever);
342*d9f75844SAndroid Build Coastguard Worker operation3_completed_event->Wait(Event::kForever);
343*d9f75844SAndroid Build Coastguard Worker operation4_completed_event->Wait(Event::kForever);
344*d9f75844SAndroid Build Coastguard Worker operation5_completed_event->Wait(Event::kForever);
345*d9f75844SAndroid Build Coastguard Worker operation6_completed_event->Wait(Event::kForever);
346*d9f75844SAndroid Build Coastguard Worker
347*d9f75844SAndroid Build Coastguard Worker EXPECT_THAT(
348*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.completed_operation_events(),
349*d9f75844SAndroid Build Coastguard Worker ElementsAre(
350*d9f75844SAndroid Build Coastguard Worker operation0_completed_event.get(), operation1_completed_event.get(),
351*d9f75844SAndroid Build Coastguard Worker operation2_completed_event.get(), operation3_completed_event.get(),
352*d9f75844SAndroid Build Coastguard Worker operation4_completed_event.get(), operation5_completed_event.get(),
353*d9f75844SAndroid Build Coastguard Worker operation6_completed_event.get()));
354*d9f75844SAndroid Build Coastguard Worker }
355*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,IsEmpty)356*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest, IsEmpty) {
357*d9f75844SAndroid Build Coastguard Worker OperationTrackerProxy operation_tracker_proxy;
358*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.Initialize()->Wait(Event::kForever);
359*d9f75844SAndroid Build Coastguard Worker
360*d9f75844SAndroid Build Coastguard Worker // The chain is initially empty.
361*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(operation_tracker_proxy.IsEmpty());
362*d9f75844SAndroid Build Coastguard Worker // Chain a single event.
363*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event0;
364*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event0 =
365*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
366*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event0);
367*d9f75844SAndroid Build Coastguard Worker // The chain is not empty while an event is pending.
368*d9f75844SAndroid Build Coastguard Worker EXPECT_FALSE(operation_tracker_proxy.IsEmpty());
369*d9f75844SAndroid Build Coastguard Worker // Completing the operation empties the chain.
370*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event0.Set();
371*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event0->Wait(Event::kForever);
372*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(operation_tracker_proxy.IsEmpty());
373*d9f75844SAndroid Build Coastguard Worker
374*d9f75844SAndroid Build Coastguard Worker // Chain multiple events.
375*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event1;
376*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event1 =
377*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
378*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event1);
379*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event2;
380*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event2 =
381*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
382*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event2);
383*d9f75844SAndroid Build Coastguard Worker // Again, the chain is not empty while an event is pending.
384*d9f75844SAndroid Build Coastguard Worker EXPECT_FALSE(operation_tracker_proxy.IsEmpty());
385*d9f75844SAndroid Build Coastguard Worker // Upon completing the first event, the chain is still not empty.
386*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event1.Set();
387*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event1->Wait(Event::kForever);
388*d9f75844SAndroid Build Coastguard Worker EXPECT_FALSE(operation_tracker_proxy.IsEmpty());
389*d9f75844SAndroid Build Coastguard Worker // Completing the last evenet empties the chain.
390*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event2.Set();
391*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event2->Wait(Event::kForever);
392*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(operation_tracker_proxy.IsEmpty());
393*d9f75844SAndroid Build Coastguard Worker }
394*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,OnChainEmptyCallback)395*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest, OnChainEmptyCallback) {
396*d9f75844SAndroid Build Coastguard Worker rtc::AutoThread main_thread;
397*d9f75844SAndroid Build Coastguard Worker OperationTrackerProxy operation_tracker_proxy;
398*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.Initialize()->Wait(Event::kForever);
399*d9f75844SAndroid Build Coastguard Worker
400*d9f75844SAndroid Build Coastguard Worker std::atomic<size_t> on_empty_callback_counter(0u);
401*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.SetOnChainEmptyCallback(
402*d9f75844SAndroid Build Coastguard Worker [&on_empty_callback_counter] { ++on_empty_callback_counter; });
403*d9f75844SAndroid Build Coastguard Worker
404*d9f75844SAndroid Build Coastguard Worker // Chain a single event.
405*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event0;
406*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event0 =
407*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
408*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event0);
409*d9f75844SAndroid Build Coastguard Worker // The callback is not invoked until the operation has completed.
410*d9f75844SAndroid Build Coastguard Worker EXPECT_EQ(0u, on_empty_callback_counter);
411*d9f75844SAndroid Build Coastguard Worker // Completing the operation empties the chain, invoking the callback.
412*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event0.Set();
413*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event0->Wait(Event::kForever);
414*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE_WAIT(1u == on_empty_callback_counter, kDefaultTimeout);
415*d9f75844SAndroid Build Coastguard Worker
416*d9f75844SAndroid Build Coastguard Worker // Chain multiple events.
417*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event1;
418*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event1 =
419*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
420*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event1);
421*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event2;
422*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event2 =
423*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
424*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event2);
425*d9f75844SAndroid Build Coastguard Worker // Again, the callback is not invoked until the operation has completed.
426*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE_WAIT(1u == on_empty_callback_counter, kDefaultTimeout);
427*d9f75844SAndroid Build Coastguard Worker // Upon completing the first event, the chain is still not empty, so the
428*d9f75844SAndroid Build Coastguard Worker // callback must not be invoked yet.
429*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event1.Set();
430*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event1->Wait(Event::kForever);
431*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE_WAIT(1u == on_empty_callback_counter, kDefaultTimeout);
432*d9f75844SAndroid Build Coastguard Worker // Completing the last evenet empties the chain, invoking the callback.
433*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event2.Set();
434*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event2->Wait(Event::kForever);
435*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE_WAIT(2u == on_empty_callback_counter, kDefaultTimeout);
436*d9f75844SAndroid Build Coastguard Worker }
437*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,SafeToReleaseReferenceToOperationChainWhileOperationIsPending)438*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest,
439*d9f75844SAndroid Build Coastguard Worker SafeToReleaseReferenceToOperationChainWhileOperationIsPending) {
440*d9f75844SAndroid Build Coastguard Worker OperationTrackerProxy operation_tracker_proxy;
441*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.Initialize()->Wait(Event::kForever);
442*d9f75844SAndroid Build Coastguard Worker
443*d9f75844SAndroid Build Coastguard Worker Event unblock_async_operation_event;
444*d9f75844SAndroid Build Coastguard Worker auto async_operation_completed_event =
445*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.PostAsynchronousOperation(
446*d9f75844SAndroid Build Coastguard Worker &unblock_async_operation_event);
447*d9f75844SAndroid Build Coastguard Worker
448*d9f75844SAndroid Build Coastguard Worker // Pending operations keep the OperationChain alive, making it safe for the
449*d9f75844SAndroid Build Coastguard Worker // test to release any references before unblocking the async operation.
450*d9f75844SAndroid Build Coastguard Worker operation_tracker_proxy.ReleaseOperationChain()->Wait(Event::kForever);
451*d9f75844SAndroid Build Coastguard Worker
452*d9f75844SAndroid Build Coastguard Worker unblock_async_operation_event.Set();
453*d9f75844SAndroid Build Coastguard Worker async_operation_completed_event->Wait(Event::kForever);
454*d9f75844SAndroid Build Coastguard Worker }
455*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainTest,FunctorIsNotDestroyedWhileExecuting)456*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainTest, FunctorIsNotDestroyedWhileExecuting) {
457*d9f75844SAndroid Build Coastguard Worker scoped_refptr<OperationsChain> operations_chain = OperationsChain::Create();
458*d9f75844SAndroid Build Coastguard Worker
459*d9f75844SAndroid Build Coastguard Worker bool destructor_called = false;
460*d9f75844SAndroid Build Coastguard Worker SignalOnDestruction signal_on_destruction(&destructor_called);
461*d9f75844SAndroid Build Coastguard Worker
462*d9f75844SAndroid Build Coastguard Worker operations_chain->ChainOperation(
463*d9f75844SAndroid Build Coastguard Worker [signal_on_destruction = std::move(signal_on_destruction),
464*d9f75844SAndroid Build Coastguard Worker &destructor_called](std::function<void()> callback) {
465*d9f75844SAndroid Build Coastguard Worker EXPECT_FALSE(destructor_called);
466*d9f75844SAndroid Build Coastguard Worker // Invoking the callback marks the operation as complete, popping the
467*d9f75844SAndroid Build Coastguard Worker // Operation object from the OperationsChain internal queue.
468*d9f75844SAndroid Build Coastguard Worker callback();
469*d9f75844SAndroid Build Coastguard Worker // Even though the internal Operation object has been destroyed,
470*d9f75844SAndroid Build Coastguard Worker // variables captured by this lambda expression must still be valid (the
471*d9f75844SAndroid Build Coastguard Worker // associated functor must not be deleted while executing).
472*d9f75844SAndroid Build Coastguard Worker EXPECT_FALSE(destructor_called);
473*d9f75844SAndroid Build Coastguard Worker });
474*d9f75844SAndroid Build Coastguard Worker // The lambda having executed synchronously and completed, its captured
475*d9f75844SAndroid Build Coastguard Worker // variables should now have been deleted.
476*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(destructor_called);
477*d9f75844SAndroid Build Coastguard Worker }
478*d9f75844SAndroid Build Coastguard Worker
479*d9f75844SAndroid Build Coastguard Worker #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
480*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainDeathTest,OperationNotInvokingCallbackShouldCrash)481*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainDeathTest, OperationNotInvokingCallbackShouldCrash) {
482*d9f75844SAndroid Build Coastguard Worker scoped_refptr<OperationsChain> operations_chain = OperationsChain::Create();
483*d9f75844SAndroid Build Coastguard Worker EXPECT_DEATH(
484*d9f75844SAndroid Build Coastguard Worker operations_chain->ChainOperation([](std::function<void()> callback) {}),
485*d9f75844SAndroid Build Coastguard Worker "");
486*d9f75844SAndroid Build Coastguard Worker }
487*d9f75844SAndroid Build Coastguard Worker
TEST(OperationsChainDeathTest,OperationInvokingCallbackMultipleTimesShouldCrash)488*d9f75844SAndroid Build Coastguard Worker TEST(OperationsChainDeathTest,
489*d9f75844SAndroid Build Coastguard Worker OperationInvokingCallbackMultipleTimesShouldCrash) {
490*d9f75844SAndroid Build Coastguard Worker scoped_refptr<OperationsChain> operations_chain = OperationsChain::Create();
491*d9f75844SAndroid Build Coastguard Worker EXPECT_DEATH(
492*d9f75844SAndroid Build Coastguard Worker operations_chain->ChainOperation([](std::function<void()> callback) {
493*d9f75844SAndroid Build Coastguard Worker // Signal that the operation has completed multiple times.
494*d9f75844SAndroid Build Coastguard Worker callback();
495*d9f75844SAndroid Build Coastguard Worker callback();
496*d9f75844SAndroid Build Coastguard Worker }),
497*d9f75844SAndroid Build Coastguard Worker "");
498*d9f75844SAndroid Build Coastguard Worker }
499*d9f75844SAndroid Build Coastguard Worker
500*d9f75844SAndroid Build Coastguard Worker #endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
501*d9f75844SAndroid Build Coastguard Worker
502*d9f75844SAndroid Build Coastguard Worker } // namespace rtc
503