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