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