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