blob: 8dbe607d6d05cb068505fea1b00779aeb1f5dfcb [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
13#include <functional>
14#include <memory>
15#include <utility>
16#include <vector>
17
18#include "rtc_base/bind.h"
19#include "rtc_base/event.h"
20#include "rtc_base/thread.h"
21#include "test/gmock.h"
22#include "test/gtest.h"
23
24namespace rtc {
25
26using ::testing::ElementsAre;
27
28class OperationTracker {
29 public:
30 OperationTracker() : background_thread_(Thread::Create()) {
31 background_thread_->Start();
32 }
33 // The caller is responsible for ensuring that no operations are pending.
34 ~OperationTracker() {}
35
36 // Creates a binding for the synchronous operation (see
37 // StartSynchronousOperation() below).
38 std::function<void(std::function<void()>)> BindSynchronousOperation(
39 Event* operation_complete_event) {
40 return [this, operation_complete_event](std::function<void()> callback) {
41 StartSynchronousOperation(operation_complete_event, std::move(callback));
42 };
43 }
44
45 // Creates a binding for the asynchronous operation (see
46 // StartAsynchronousOperation() below).
47 std::function<void(std::function<void()>)> BindAsynchronousOperation(
48 Event* unblock_operation_event,
49 Event* operation_complete_event) {
50 return [this, unblock_operation_event,
51 operation_complete_event](std::function<void()> callback) {
52 StartAsynchronousOperation(unblock_operation_event,
53 operation_complete_event, std::move(callback));
54 };
55 }
56
57 // When an operation is completed, its associated Event* is added to this
58 // list, in chronological order. This allows you to verify the order that
59 // operations are executed.
60 const std::vector<Event*>& completed_operation_events() const {
61 return completed_operation_events_;
62 }
63
64 private:
65 // This operation is completed synchronously; the callback is invoked before
66 // the function returns.
67 void StartSynchronousOperation(Event* operation_complete_event,
68 std::function<void()> callback) {
69 completed_operation_events_.push_back(operation_complete_event);
70 operation_complete_event->Set();
71 callback();
72 }
73
74 // This operation is completed asynchronously; it pings |background_thread_|,
75 // blocking that thread until |unblock_operation_event| is signaled and then
76 // completes upon posting back to the thread that the operation started on.
77 // Note that this requires the starting thread to be executing tasks (handle
78 // messages), i.e. must not be blocked.
79 void StartAsynchronousOperation(Event* unblock_operation_event,
80 Event* operation_complete_event,
81 std::function<void()> callback) {
82 Thread* current_thread = Thread::Current();
83 background_thread_->PostTask(
84 RTC_FROM_HERE, [this, current_thread, unblock_operation_event,
85 operation_complete_event, callback]() {
86 unblock_operation_event->Wait(Event::kForever);
87 current_thread->PostTask(
88 RTC_FROM_HERE, [this, operation_complete_event, callback]() {
89 completed_operation_events_.push_back(operation_complete_event);
90 operation_complete_event->Set();
91 callback();
92 });
93 });
94 }
95
96 std::unique_ptr<Thread> background_thread_;
97 std::vector<Event*> completed_operation_events_;
98};
99
100// The OperationTrackerProxy ensures all operations are chained on a separate
101// thread. This allows tests to block while chained operations are posting
102// between threads.
103class OperationTrackerProxy {
104 public:
105 OperationTrackerProxy()
106 : operations_chain_thread_(Thread::Create()),
107 operation_tracker_(nullptr),
108 operations_chain_(nullptr) {
109 operations_chain_thread_->Start();
110 }
111
112 std::unique_ptr<Event> Initialize() {
113 std::unique_ptr<Event> event = std::make_unique<Event>();
114 operations_chain_thread_->PostTask(
115 RTC_FROM_HERE, [this, event_ptr = event.get()]() {
116 operation_tracker_ = std::make_unique<OperationTracker>();
117 operations_chain_ = OperationsChain::Create();
118 event_ptr->Set();
119 });
120 return event;
121 }
122
123 std::unique_ptr<Event> ReleaseOperationChain() {
124 std::unique_ptr<Event> event = std::make_unique<Event>();
125 operations_chain_thread_->PostTask(RTC_FROM_HERE,
126 [this, event_ptr = event.get()]() {
127 operations_chain_ = nullptr;
128 event_ptr->Set();
129 });
130 return event;
131 }
132
133 // Chains a synchronous operation on the operation chain's thread.
134 std::unique_ptr<Event> PostSynchronousOperation() {
135 std::unique_ptr<Event> operation_complete_event = std::make_unique<Event>();
136 operations_chain_thread_->PostTask(
137 RTC_FROM_HERE, [this, operation_complete_event_ptr =
138 operation_complete_event.get()]() {
139 operations_chain_->ChainOperation(
140 operation_tracker_->BindSynchronousOperation(
141 operation_complete_event_ptr));
142 });
143 return operation_complete_event;
144 }
145
146 // Chains an asynchronous operation on the operation chain's thread. This
147 // involves the operation chain thread and an additional background thread.
148 std::unique_ptr<Event> PostAsynchronousOperation(
149 Event* unblock_operation_event) {
150 std::unique_ptr<Event> operation_complete_event = std::make_unique<Event>();
151 operations_chain_thread_->PostTask(
152 RTC_FROM_HERE,
153 [this, unblock_operation_event,
154 operation_complete_event_ptr = operation_complete_event.get()]() {
155 operations_chain_->ChainOperation(
156 operation_tracker_->BindAsynchronousOperation(
157 unblock_operation_event, operation_complete_event_ptr));
158 });
159 return operation_complete_event;
160 }
161
162 // The order of completed events. Touches the |operation_tracker_| on the
163 // calling thread, this is only thread safe if all chained operations have
164 // completed.
165 const std::vector<Event*>& completed_operation_events() const {
166 return operation_tracker_->completed_operation_events();
167 }
168
169 private:
170 std::unique_ptr<Thread> operations_chain_thread_;
171 std::unique_ptr<OperationTracker> operation_tracker_;
172 scoped_refptr<OperationsChain> operations_chain_;
173};
174
175TEST(OperationsChainTest, SynchronousOperation) {
176 OperationTrackerProxy operation_tracker_proxy;
177 operation_tracker_proxy.Initialize()->Wait(Event::kForever);
178
179 operation_tracker_proxy.PostSynchronousOperation()->Wait(Event::kForever);
180}
181
182TEST(OperationsChainTest, AsynchronousOperation) {
183 OperationTrackerProxy operation_tracker_proxy;
184 operation_tracker_proxy.Initialize()->Wait(Event::kForever);
185
186 Event unblock_async_operation_event;
187 auto async_operation_completed_event =
188 operation_tracker_proxy.PostAsynchronousOperation(
189 &unblock_async_operation_event);
190 // This should not be signaled until we unblock the operation.
191 EXPECT_FALSE(async_operation_completed_event->Wait(0));
192 // Unblock the operation and wait for it to complete.
193 unblock_async_operation_event.Set();
194 async_operation_completed_event->Wait(Event::kForever);
195}
196
197TEST(OperationsChainTest,
198 SynchronousOperationsAreExecutedImmediatelyWhenChainIsEmpty) {
199 // Testing synchonicity must be done without the OperationTrackerProxy to
200 // ensure messages are not processed in parallel. This test has no background
201 // threads.
202 scoped_refptr<OperationsChain> operations_chain = OperationsChain::Create();
203 OperationTracker operation_tracker;
204 Event event0;
205 operations_chain->ChainOperation(
206 operation_tracker.BindSynchronousOperation(&event0));
207 // This should already be signaled. (If it wasn't, waiting wouldn't help,
208 // because we'd be blocking the only thread that exists.)
209 EXPECT_TRUE(event0.Wait(0));
210 // Chaining another operation should also execute immediately because the
211 // chain should already be empty.
212 Event event1;
213 operations_chain->ChainOperation(
214 operation_tracker.BindSynchronousOperation(&event1));
215 EXPECT_TRUE(event1.Wait(0));
216}
217
218TEST(OperationsChainTest, AsynchronousOperationBlocksSynchronousOperation) {
219 OperationTrackerProxy operation_tracker_proxy;
220 operation_tracker_proxy.Initialize()->Wait(Event::kForever);
221
222 Event unblock_async_operation_event;
223 auto async_operation_completed_event =
224 operation_tracker_proxy.PostAsynchronousOperation(
225 &unblock_async_operation_event);
226
227 auto sync_operation_completed_event =
228 operation_tracker_proxy.PostSynchronousOperation();
229
230 unblock_async_operation_event.Set();
231
232 sync_operation_completed_event->Wait(Event::kForever);
233 // The asynchronous avent should have blocked the synchronous event, meaning
234 // this should already be signaled.
235 EXPECT_TRUE(async_operation_completed_event->Wait(0));
236}
237
238TEST(OperationsChainTest, OperationsAreExecutedInOrder) {
239 OperationTrackerProxy operation_tracker_proxy;
240 operation_tracker_proxy.Initialize()->Wait(Event::kForever);
241
242 // Chain a mix of asynchronous and synchronous operations.
243 Event operation0_unblock_event;
244 auto operation0_completed_event =
245 operation_tracker_proxy.PostAsynchronousOperation(
246 &operation0_unblock_event);
247
248 Event operation1_unblock_event;
249 auto operation1_completed_event =
250 operation_tracker_proxy.PostAsynchronousOperation(
251 &operation1_unblock_event);
252
253 auto operation2_completed_event =
254 operation_tracker_proxy.PostSynchronousOperation();
255
256 auto operation3_completed_event =
257 operation_tracker_proxy.PostSynchronousOperation();
258
259 Event operation4_unblock_event;
260 auto operation4_completed_event =
261 operation_tracker_proxy.PostAsynchronousOperation(
262 &operation4_unblock_event);
263
264 auto operation5_completed_event =
265 operation_tracker_proxy.PostSynchronousOperation();
266
267 Event operation6_unblock_event;
268 auto operation6_completed_event =
269 operation_tracker_proxy.PostAsynchronousOperation(
270 &operation6_unblock_event);
271
272 // Unblock events in reverse order. Operations 5, 3 and 2 are synchronous and
273 // don't need to be unblocked.
274 operation6_unblock_event.Set();
275 operation4_unblock_event.Set();
276 operation1_unblock_event.Set();
277 operation0_unblock_event.Set();
278 // Await all operations. The await-order shouldn't matter since they all get
279 // executed eventually.
280 operation0_completed_event->Wait(Event::kForever);
281 operation1_completed_event->Wait(Event::kForever);
282 operation2_completed_event->Wait(Event::kForever);
283 operation3_completed_event->Wait(Event::kForever);
284 operation4_completed_event->Wait(Event::kForever);
285 operation5_completed_event->Wait(Event::kForever);
286 operation6_completed_event->Wait(Event::kForever);
287
288 EXPECT_THAT(
289 operation_tracker_proxy.completed_operation_events(),
290 ElementsAre(
291 operation0_completed_event.get(), operation1_completed_event.get(),
292 operation2_completed_event.get(), operation3_completed_event.get(),
293 operation4_completed_event.get(), operation5_completed_event.get(),
294 operation6_completed_event.get()));
295}
296
297TEST(OperationsChainTest,
298 SafeToReleaseReferenceToOperationChainWhileOperationIsPending) {
299 OperationTrackerProxy operation_tracker_proxy;
300 operation_tracker_proxy.Initialize()->Wait(Event::kForever);
301
302 Event unblock_async_operation_event;
303 auto async_operation_completed_event =
304 operation_tracker_proxy.PostAsynchronousOperation(
305 &unblock_async_operation_event);
306
307 // Pending operations keep the OperationChain alive, making it safe for the
308 // test to release any references before unblocking the async operation.
309 operation_tracker_proxy.ReleaseOperationChain()->Wait(Event::kForever);
310
311 unblock_async_operation_event.Set();
312 async_operation_completed_event->Wait(Event::kForever);
313}
314
315#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
316
317TEST(OperationsChainTest, OperationNotInvokingCallbackShouldCrash) {
318 scoped_refptr<OperationsChain> operations_chain = OperationsChain::Create();
319 EXPECT_DEATH(
320 operations_chain->ChainOperation([](std::function<void()> callback) {}),
321 "");
322}
323
324TEST(OperationsChainTest, OperationInvokingCallbackMultipleTimesShouldCrash) {
325 scoped_refptr<OperationsChain> operations_chain = OperationsChain::Create();
326 EXPECT_DEATH(
327 operations_chain->ChainOperation([](std::function<void()> callback) {
328 // Signal that the operation has completed multiple times.
329 callback();
330 callback();
331 }),
332 "");
333}
334
335#endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
336
337} // namespace rtc