xref: /aosp_15_r20/external/webrtc/api/task_queue/task_queue_test.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
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 #include "api/task_queue/task_queue_test.h"
11 
12 #include <memory>
13 
14 #include "absl/cleanup/cleanup.h"
15 #include "absl/strings/string_view.h"
16 #include "api/task_queue/default_task_queue_factory.h"
17 #include "api/units/time_delta.h"
18 #include "rtc_base/event.h"
19 #include "rtc_base/ref_counter.h"
20 #include "rtc_base/time_utils.h"
21 
22 namespace webrtc {
23 namespace {
24 
CreateTaskQueue(const std::unique_ptr<webrtc::TaskQueueFactory> & factory,absl::string_view task_queue_name,TaskQueueFactory::Priority priority=TaskQueueFactory::Priority::NORMAL)25 std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
26     const std::unique_ptr<webrtc::TaskQueueFactory>& factory,
27     absl::string_view task_queue_name,
28     TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) {
29   return factory->CreateTaskQueue(task_queue_name, priority);
30 }
31 
TEST_P(TaskQueueTest,Construct)32 TEST_P(TaskQueueTest, Construct) {
33   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
34   auto queue = CreateTaskQueue(factory, "Construct");
35   EXPECT_FALSE(queue->IsCurrent());
36 }
37 
TEST_P(TaskQueueTest,PostAndCheckCurrent)38 TEST_P(TaskQueueTest, PostAndCheckCurrent) {
39   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
40   rtc::Event event;
41   auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent");
42 
43   // We're not running a task, so `queue` shouldn't be current.
44   // Note that because rtc::Thread also supports the TQ interface and
45   // TestMainImpl::Init wraps the main test thread (bugs.webrtc.org/9714), that
46   // means that TaskQueueBase::Current() will still return a valid value.
47   EXPECT_FALSE(queue->IsCurrent());
48 
49   queue->PostTask([&event, &queue] {
50     EXPECT_TRUE(queue->IsCurrent());
51     event.Set();
52   });
53   EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
54 }
55 
TEST_P(TaskQueueTest,PostCustomTask)56 TEST_P(TaskQueueTest, PostCustomTask) {
57   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
58   rtc::Event ran;
59   auto queue = CreateTaskQueue(factory, "PostCustomImplementation");
60 
61   class CustomTask {
62    public:
63     explicit CustomTask(rtc::Event* ran) : ran_(ran) {}
64 
65     void operator()() { ran_->Set(); }
66 
67    private:
68     rtc::Event* const ran_;
69   } my_task(&ran);
70 
71   queue->PostTask(my_task);
72   EXPECT_TRUE(ran.Wait(TimeDelta::Seconds(1)));
73 }
74 
TEST_P(TaskQueueTest,PostDelayedZero)75 TEST_P(TaskQueueTest, PostDelayedZero) {
76   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
77   rtc::Event event;
78   auto queue = CreateTaskQueue(factory, "PostDelayedZero");
79 
80   queue->PostDelayedTask([&event] { event.Set(); }, TimeDelta::Zero());
81   EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
82 }
83 
TEST_P(TaskQueueTest,PostFromQueue)84 TEST_P(TaskQueueTest, PostFromQueue) {
85   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
86   rtc::Event event;
87   auto queue = CreateTaskQueue(factory, "PostFromQueue");
88 
89   queue->PostTask(
90       [&event, &queue] { queue->PostTask([&event] { event.Set(); }); });
91   EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
92 }
93 
TEST_P(TaskQueueTest,PostDelayed)94 TEST_P(TaskQueueTest, PostDelayed) {
95   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
96   rtc::Event event;
97   auto queue =
98       CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH);
99 
100   int64_t start = rtc::TimeMillis();
101   queue->PostDelayedTask(
102       [&event, &queue] {
103         EXPECT_TRUE(queue->IsCurrent());
104         event.Set();
105       },
106       TimeDelta::Millis(100));
107   EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
108   int64_t end = rtc::TimeMillis();
109   // These tests are a little relaxed due to how "powerful" our test bots can
110   // be.  Most recently we've seen windows bots fire the callback after 94-99ms,
111   // which is why we have a little bit of leeway backwards as well.
112   EXPECT_GE(end - start, 90u);
113   EXPECT_NEAR(end - start, 190u, 100u);  // Accept 90-290.
114 }
115 
TEST_P(TaskQueueTest,PostMultipleDelayed)116 TEST_P(TaskQueueTest, PostMultipleDelayed) {
117   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
118   auto queue = CreateTaskQueue(factory, "PostMultipleDelayed");
119 
120   std::vector<rtc::Event> events(100);
121   for (int i = 0; i < 100; ++i) {
122     rtc::Event* event = &events[i];
123     queue->PostDelayedTask(
124         [event, &queue] {
125           EXPECT_TRUE(queue->IsCurrent());
126           event->Set();
127         },
128         TimeDelta::Millis(i));
129   }
130 
131   for (rtc::Event& e : events)
132     EXPECT_TRUE(e.Wait(TimeDelta::Seconds(1)));
133 }
134 
TEST_P(TaskQueueTest,PostDelayedAfterDestruct)135 TEST_P(TaskQueueTest, PostDelayedAfterDestruct) {
136   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
137   rtc::Event run;
138   rtc::Event deleted;
139   auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct");
140   absl::Cleanup cleanup = [&deleted] { deleted.Set(); };
141   queue->PostDelayedTask([&run, cleanup = std::move(cleanup)] { run.Set(); },
142                          TimeDelta::Millis(100));
143   // Destroy the queue.
144   queue = nullptr;
145   // Task might outlive the TaskQueue, but still should be deleted.
146   EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1)));
147   EXPECT_FALSE(run.Wait(TimeDelta::Zero()));  // and should not run.
148 }
149 
TEST_P(TaskQueueTest,PostAndReuse)150 TEST_P(TaskQueueTest, PostAndReuse) {
151   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
152   rtc::Event event;
153   auto post_queue = CreateTaskQueue(factory, "PostQueue");
154   auto reply_queue = CreateTaskQueue(factory, "ReplyQueue");
155 
156   int call_count = 0;
157 
158   class ReusedTask {
159    public:
160     ReusedTask(int* counter, TaskQueueBase* reply_queue, rtc::Event* event)
161         : counter_(*counter), reply_queue_(reply_queue), event_(*event) {
162       EXPECT_EQ(counter_, 0);
163     }
164     ReusedTask(ReusedTask&&) = default;
165     ReusedTask& operator=(ReusedTask&&) = delete;
166 
167     void operator()() && {
168       if (++counter_ == 1) {
169         reply_queue_->PostTask(std::move(*this));
170         // At this point, the object is in the moved-from state.
171       } else {
172         EXPECT_EQ(counter_, 2);
173         EXPECT_TRUE(reply_queue_->IsCurrent());
174         event_.Set();
175       }
176     }
177 
178    private:
179     int& counter_;
180     TaskQueueBase* const reply_queue_;
181     rtc::Event& event_;
182   };
183 
184   ReusedTask task(&call_count, reply_queue.get(), &event);
185   post_queue->PostTask(std::move(task));
186   EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
187 }
188 
TEST_P(TaskQueueTest,PostALot)189 TEST_P(TaskQueueTest, PostALot) {
190   // Waits until DecrementCount called `count` times. Thread safe.
191   class BlockingCounter {
192    public:
193     explicit BlockingCounter(int initial_count) : count_(initial_count) {}
194 
195     void DecrementCount() {
196       if (count_.DecRef() == rtc::RefCountReleaseStatus::kDroppedLastRef) {
197         event_.Set();
198       }
199     }
200     bool Wait(TimeDelta give_up_after) { return event_.Wait(give_up_after); }
201 
202    private:
203     webrtc_impl::RefCounter count_;
204     rtc::Event event_;
205   };
206 
207   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
208   static constexpr int kTaskCount = 0xffff;
209   rtc::Event posting_done;
210   BlockingCounter all_destroyed(kTaskCount);
211 
212   int tasks_executed = 0;
213   auto task_queue = CreateTaskQueue(factory, "PostALot");
214 
215   task_queue->PostTask([&] {
216     // Post tasks from the queue to guarantee that the 1st task won't be
217     // executed before the last one is posted.
218     for (int i = 0; i < kTaskCount; ++i) {
219       absl::Cleanup cleanup = [&] { all_destroyed.DecrementCount(); };
220       task_queue->PostTask([&tasks_executed, cleanup = std::move(cleanup)] {
221         ++tasks_executed;
222       });
223     }
224 
225     posting_done.Set();
226   });
227 
228   // Before destroying the task queue wait until all child tasks are posted.
229   posting_done.Wait(rtc::Event::kForever);
230   // Destroy the task queue.
231   task_queue = nullptr;
232 
233   // Expect all tasks are destroyed eventually. In some task queue
234   // implementations that might happen on a different thread after task queue is
235   // destroyed.
236   EXPECT_TRUE(all_destroyed.Wait(TimeDelta::Minutes(1)));
237   EXPECT_LE(tasks_executed, kTaskCount);
238 }
239 
240 // Test posting two tasks that have shared state not protected by a
241 // lock. The TaskQueue should guarantee memory read-write order and
242 // FIFO task execution order, so the second task should always see the
243 // changes that were made by the first task.
244 //
245 // If the TaskQueue doesn't properly synchronize the execution of
246 // tasks, there will be a data race, which is undefined behavior. The
247 // EXPECT calls may randomly catch this, but to make the most of this
248 // unit test, run it under TSan or some other tool that is able to
249 // directly detect data races.
TEST_P(TaskQueueTest,PostTwoWithSharedUnprotectedState)250 TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) {
251   std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
252   struct SharedState {
253     // First task will set this value to 1 and second will assert it.
254     int state = 0;
255   } state;
256 
257   auto queue = CreateTaskQueue(factory, "PostTwoWithSharedUnprotectedState");
258   rtc::Event done;
259   queue->PostTask([&state, &queue, &done] {
260     // Post tasks from queue to guarantee, that 1st task won't be
261     // executed before the second one will be posted.
262     queue->PostTask([&state] { state.state = 1; });
263     queue->PostTask([&state, &done] {
264       EXPECT_EQ(state.state, 1);
265       done.Set();
266     });
267     // Check, that state changing tasks didn't start yet.
268     EXPECT_EQ(state.state, 0);
269   });
270   EXPECT_TRUE(done.Wait(TimeDelta::Seconds(1)));
271 }
272 
273 // TaskQueueTest is a set of tests for any implementation of the TaskQueueBase.
274 // Tests are instantiated next to the concrete implementation(s).
275 // https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#creating-value-parameterized-abstract-tests
276 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TaskQueueTest);
277 
278 }  // namespace
279 }  // namespace webrtc
280