blob: b598cb935335a2dbc963a7b36cfe0e46a01c3767 [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
Henrik Boström27c29362019-10-21 15:21:55 +020019#include "rtc_base/event.h"
Henrik Boströme8b00a12020-08-25 17:11:20 +020020#include "rtc_base/gunit.h"
Henrik Boström27c29362019-10-21 15:21:55 +020021#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 17:11:20 +020029namespace {
30
31constexpr int kDefaultTimeout = 3000;
32
33} // namespace
34
Henrik Boström27c29362019-10-21 15:21:55 +020035class 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
Artem Titov96e3b992021-07-26 16:03:14 +020081 // This operation is completed asynchronously; it pings `background_thread_`,
82 // blocking that thread until `unblock_operation_event` is signaled and then
Henrik Boström27c29362019-10-21 15:21:55 +020083 // 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();
Henrik Boström2deee4b2022-01-20 11:58:05 +010090 background_thread_->PostTask([this, current_thread, unblock_operation_event,
91 operation_complete_event, callback]() {
92 unblock_operation_event->Wait(Event::kForever);
93 current_thread->PostTask([this, operation_complete_event, callback]() {
94 completed_operation_events_.push_back(operation_complete_event);
95 operation_complete_event->Set();
96 callback();
97 });
98 });
Henrik Boström27c29362019-10-21 15:21:55 +020099 }
100
101 std::unique_ptr<Thread> background_thread_;
102 std::vector<Event*> completed_operation_events_;
103};
104
105// The OperationTrackerProxy ensures all operations are chained on a separate
106// thread. This allows tests to block while chained operations are posting
107// between threads.
108class OperationTrackerProxy {
109 public:
110 OperationTrackerProxy()
111 : operations_chain_thread_(Thread::Create()),
112 operation_tracker_(nullptr),
113 operations_chain_(nullptr) {
114 operations_chain_thread_->Start();
115 }
116
117 std::unique_ptr<Event> Initialize() {
118 std::unique_ptr<Event> event = std::make_unique<Event>();
Henrik Boström2deee4b2022-01-20 11:58:05 +0100119 operations_chain_thread_->PostTask([this, event_ptr = event.get()]() {
120 operation_tracker_ = std::make_unique<OperationTracker>();
121 operations_chain_ = OperationsChain::Create();
122 event_ptr->Set();
123 });
Henrik Boström27c29362019-10-21 15:21:55 +0200124 return event;
125 }
126
Henrik Boströme574a312020-08-25 10:20:11 +0200127 void SetOnChainEmptyCallback(std::function<void()> on_chain_empty_callback) {
128 Event event;
129 operations_chain_thread_->PostTask(
Henrik Boströme574a312020-08-25 10:20:11 +0200130 [this, &event,
131 on_chain_empty_callback = std::move(on_chain_empty_callback)]() {
132 operations_chain_->SetOnChainEmptyCallback(
133 std::move(on_chain_empty_callback));
134 event.Set();
135 });
136 event.Wait(Event::kForever);
137 }
138
139 bool IsEmpty() {
140 Event event;
141 bool is_empty = false;
Henrik Boström2deee4b2022-01-20 11:58:05 +0100142 operations_chain_thread_->PostTask([this, &event, &is_empty]() {
143 is_empty = operations_chain_->IsEmpty();
144 event.Set();
145 });
Henrik Boströme574a312020-08-25 10:20:11 +0200146 event.Wait(Event::kForever);
147 return is_empty;
148 }
149
Henrik Boström27c29362019-10-21 15:21:55 +0200150 std::unique_ptr<Event> ReleaseOperationChain() {
151 std::unique_ptr<Event> event = std::make_unique<Event>();
Henrik Boström2deee4b2022-01-20 11:58:05 +0100152 operations_chain_thread_->PostTask([this, event_ptr = event.get()]() {
153 operations_chain_ = nullptr;
154 event_ptr->Set();
155 });
Henrik Boström27c29362019-10-21 15:21:55 +0200156 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(
Henrik Boström2deee4b2022-01-20 11:58:05 +0100163 [this,
164 operation_complete_event_ptr = operation_complete_event.get()]() {
Henrik Boström27c29362019-10-21 15:21:55 +0200165 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(
Henrik Boström27c29362019-10-21 15:21:55 +0200178 [this, unblock_operation_event,
179 operation_complete_event_ptr = operation_complete_event.get()]() {
180 operations_chain_->ChainOperation(
181 operation_tracker_->BindAsynchronousOperation(
182 unblock_operation_event, operation_complete_event_ptr));
183 });
184 return operation_complete_event;
185 }
186
Artem Titov96e3b992021-07-26 16:03:14 +0200187 // The order of completed events. Touches the `operation_tracker_` on the
Henrik Boström27c29362019-10-21 15:21:55 +0200188 // calling thread, this is only thread safe if all chained operations have
189 // completed.
190 const std::vector<Event*>& completed_operation_events() const {
191 return operation_tracker_->completed_operation_events();
192 }
193
194 private:
195 std::unique_ptr<Thread> operations_chain_thread_;
196 std::unique_ptr<OperationTracker> operation_tracker_;
197 scoped_refptr<OperationsChain> operations_chain_;
198};
199
Henrik Boströmee6f4f62019-11-06 12:36:12 +0100200// On destruction, sets a boolean flag to true.
201class SignalOnDestruction final {
202 public:
203 SignalOnDestruction(bool* destructor_called)
204 : destructor_called_(destructor_called) {
205 RTC_DCHECK(destructor_called_);
206 }
207 ~SignalOnDestruction() {
Artem Titov96e3b992021-07-26 16:03:14 +0200208 // Moved objects will have `destructor_called_` set to null. Destroying a
Henrik Boströmee6f4f62019-11-06 12:36:12 +0100209 // moved SignalOnDestruction should not signal.
210 if (destructor_called_) {
211 *destructor_called_ = true;
212 }
213 }
214
Byoungchan Lee14af7622022-01-12 05:24:58 +0900215 SignalOnDestruction(const SignalOnDestruction&) = delete;
216 SignalOnDestruction& operator=(const SignalOnDestruction&) = delete;
217
Henrik Boströmee6f4f62019-11-06 12:36:12 +0100218 // Move operators.
219 SignalOnDestruction(SignalOnDestruction&& other)
220 : SignalOnDestruction(other.destructor_called_) {
221 other.destructor_called_ = nullptr;
222 }
223 SignalOnDestruction& operator=(SignalOnDestruction&& other) {
224 destructor_called_ = other.destructor_called_;
225 other.destructor_called_ = nullptr;
226 return *this;
227 }
228
229 private:
230 bool* destructor_called_;
Henrik Boströmee6f4f62019-11-06 12:36:12 +0100231};
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);
Henrik Boströme8b00a12020-08-25 17:11:20 +0200412 EXPECT_TRUE_WAIT(1u == on_empty_callback_counter, kDefaultTimeout);
Henrik Boströme574a312020-08-25 10:20:11 +0200413
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.
Henrik Boströme8b00a12020-08-25 17:11:20 +0200424 EXPECT_TRUE_WAIT(1u == on_empty_callback_counter, kDefaultTimeout);
Henrik Boströme574a312020-08-25 10:20:11 +0200425 // 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);
Henrik Boströme8b00a12020-08-25 17:11:20 +0200429 EXPECT_TRUE_WAIT(1u == on_empty_callback_counter, kDefaultTimeout);
Henrik Boströme574a312020-08-25 10:20:11 +0200430 // Completing the last evenet empties the chain, invoking the callback.
431 unblock_async_operation_event2.Set();
432 async_operation_completed_event2->Wait(Event::kForever);
Henrik Boströme8b00a12020-08-25 17:11:20 +0200433 EXPECT_TRUE_WAIT(2u == on_empty_callback_counter, kDefaultTimeout);
Henrik Boströme574a312020-08-25 10:20:11 +0200434}
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