Flutter Engine
message_loop_task_queues_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #define FML_USED_ON_EMBEDDER
6 
7 #include "flutter/fml/message_loop_task_queues.h"
8 
9 #include <thread>
10 
11 #include "flutter/fml/synchronization/count_down_latch.h"
12 #include "flutter/fml/synchronization/waitable_event.h"
13 #include "gtest/gtest.h"
14 
15 namespace fml {
16 namespace testing {
17 
18 class TestWakeable : public fml::Wakeable {
19  public:
20  using WakeUpCall = std::function<void(const fml::TimePoint)>;
21 
22  explicit TestWakeable(WakeUpCall call) : wake_up_call_(call) {}
23 
24  void WakeUp(fml::TimePoint time_point) override { wake_up_call_(time_point); }
25 
26  private:
27  WakeUpCall wake_up_call_;
28 };
29 
30 TEST(MessageLoopTaskQueue, StartsWithNoPendingTasks) {
31  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
32  auto queue_id = task_queue->CreateTaskQueue();
33  ASSERT_FALSE(task_queue->HasPendingTasks(queue_id));
34 }
35 
36 TEST(MessageLoopTaskQueue, RegisterOneTask) {
37  const auto time = fml::TimePoint::Max();
38 
39  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
40  auto queue_id = task_queue->CreateTaskQueue();
41  task_queue->SetWakeable(queue_id,
42  new TestWakeable([&time](fml::TimePoint wake_time) {
43  ASSERT_TRUE(wake_time == time);
44  }));
45 
46  task_queue->RegisterTask(
47  queue_id, [] {}, time);
48  ASSERT_TRUE(task_queue->HasPendingTasks(queue_id));
49  ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 1);
50 }
51 
52 TEST(MessageLoopTaskQueue, RegisterTwoTasksAndCount) {
53  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
54  auto queue_id = task_queue->CreateTaskQueue();
55  task_queue->RegisterTask(
56  queue_id, [] {}, fml::TimePoint::Now());
57  task_queue->RegisterTask(
58  queue_id, [] {}, fml::TimePoint::Max());
59  ASSERT_TRUE(task_queue->HasPendingTasks(queue_id));
60  ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 2);
61 }
62 
63 TEST(MessageLoopTaskQueue, PreserveTaskOrdering) {
64  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
65  auto queue_id = task_queue->CreateTaskQueue();
66  int test_val = 0;
67 
68  // order: 0
69  task_queue->RegisterTask(
70  queue_id, [&test_val]() { test_val = 1; }, fml::TimePoint::Now());
71 
72  // order: 1
73  task_queue->RegisterTask(
74  queue_id, [&test_val]() { test_val = 2; }, fml::TimePoint::Now());
75 
76  const auto now = fml::TimePoint::Now();
77  int expected_value = 1;
78  for (;;) {
79  fml::closure invocation = task_queue->GetNextTaskToRun(queue_id, now);
80  if (!invocation) {
81  break;
82  }
83  invocation();
84  ASSERT_TRUE(test_val == expected_value);
85  expected_value++;
86  }
87 }
88 
90  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
91  std::vector<fml::closure> observers =
92  task_queue->GetObserversToNotify(queue_id);
93  for (const auto& observer : observers) {
94  observer();
95  }
96 }
97 
98 TEST(MessageLoopTaskQueue, AddRemoveNotifyObservers) {
99  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
100  auto queue_id = task_queue->CreateTaskQueue();
101 
102  int test_val = 0;
103  intptr_t key = 123;
104 
105  task_queue->AddTaskObserver(queue_id, key, [&test_val]() { test_val = 1; });
106  TestNotifyObservers(queue_id);
107  ASSERT_TRUE(test_val == 1);
108 
109  test_val = 0;
110  task_queue->RemoveTaskObserver(queue_id, key);
111  TestNotifyObservers(queue_id);
112  ASSERT_TRUE(test_val == 0);
113 }
114 
115 TEST(MessageLoopTaskQueue, WakeUpIndependentOfTime) {
116  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
117  auto queue_id = task_queue->CreateTaskQueue();
118 
119  int num_wakes = 0;
120  task_queue->SetWakeable(
121  queue_id, new TestWakeable(
122  [&num_wakes](fml::TimePoint wake_time) { ++num_wakes; }));
123 
124  task_queue->RegisterTask(
125  queue_id, []() {}, fml::TimePoint::Now());
126  task_queue->RegisterTask(
127  queue_id, []() {}, fml::TimePoint::Max());
128 
129  ASSERT_TRUE(num_wakes == 2);
130 }
131 
132 TEST(MessageLoopTaskQueue, WokenUpWithNewerTime) {
133  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
134  auto queue_id = task_queue->CreateTaskQueue();
135  fml::CountDownLatch latch(2);
136 
137  fml::TimePoint expected = fml::TimePoint::Max();
138 
139  task_queue->SetWakeable(
140  queue_id, new TestWakeable([&latch, &expected](fml::TimePoint wake_time) {
141  ASSERT_TRUE(wake_time == expected);
142  latch.CountDown();
143  }));
144 
145  task_queue->RegisterTask(
146  queue_id, []() {}, fml::TimePoint::Max());
147 
148  const auto now = fml::TimePoint::Now();
149  expected = now;
150  task_queue->RegisterTask(
151  queue_id, []() {}, now);
152 
153  latch.Wait();
154 }
155 
156 TEST(MessageLoopTaskQueue, NotifyObserversWhileCreatingQueues) {
157  auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
158  fml::TaskQueueId queue_id = task_queues->CreateTaskQueue();
159  fml::AutoResetWaitableEvent first_observer_executing, before_second_observer;
160 
161  task_queues->AddTaskObserver(queue_id, queue_id + 1, [&]() {
162  first_observer_executing.Signal();
163  before_second_observer.Wait();
164  });
165 
166  for (int i = 0; i < 100; i++) {
167  task_queues->AddTaskObserver(queue_id, queue_id + i + 2, [] {});
168  }
169 
170  std::thread notify_observers([&]() { TestNotifyObservers(queue_id); });
171 
172  first_observer_executing.Wait();
173 
174  for (int i = 0; i < 100; i++) {
175  task_queues->CreateTaskQueue();
176  }
177 
178  before_second_observer.Signal();
179  notify_observers.join();
180 }
181 
182 TEST(MessageLoopTaskQueue, QueueDoNotOwnItself) {
183  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
184  auto queue_id = task_queue->CreateTaskQueue();
185  ASSERT_FALSE(task_queue->Owns(queue_id, queue_id));
186 }
187 
188 // TODO(chunhtai): This unit-test is flaky and sometimes fails asynchronizely
189 // after the test has finished.
190 // https://github.com/flutter/flutter/issues/43858
191 TEST(MessageLoopTaskQueue, DISABLED_ConcurrentQueueAndTaskCreatingCounts) {
192  auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
193  const int base_queue_id = task_queues->CreateTaskQueue();
194 
195  const int num_queues = 100;
196  std::atomic_bool created[num_queues * 3];
197  std::atomic_int num_tasks[num_queues * 3];
198  std::mutex task_count_mutex[num_queues * 3];
199  std::atomic_int done = 0;
200 
201  for (int i = 0; i < num_queues * 3; i++) {
202  num_tasks[i] = 0;
203  created[i] = false;
204  }
205 
206  auto creation_func = [&] {
207  for (int i = 0; i < num_queues; i++) {
208  fml::TaskQueueId queue_id = task_queues->CreateTaskQueue();
209  int limit = queue_id - base_queue_id;
210  created[limit] = true;
211 
212  for (int cur_q = 1; cur_q < limit; cur_q++) {
213  if (created[cur_q]) {
214  std::scoped_lock counter(task_count_mutex[cur_q]);
215  int cur_num_tasks = rand() % 10;
216  for (int k = 0; k < cur_num_tasks; k++) {
217  task_queues->RegisterTask(
218  fml::TaskQueueId(base_queue_id + cur_q), [] {},
220  }
221  num_tasks[cur_q] += cur_num_tasks;
222  }
223  }
224  }
225  done++;
226  };
227 
228  std::thread creation_1(creation_func);
229  std::thread creation_2(creation_func);
230 
231  while (done < 2) {
232  for (int i = 0; i < num_queues * 3; i++) {
233  if (created[i]) {
234  std::scoped_lock counter(task_count_mutex[i]);
235  int num_pending = task_queues->GetNumPendingTasks(
236  fml::TaskQueueId(base_queue_id + i));
237  int num_added = num_tasks[i];
238  ASSERT_EQ(num_pending, num_added);
239  }
240  }
241  }
242 
243  creation_1.join();
244  creation_2.join();
245 }
246 
247 TEST(MessageLoopTaskQueue, RegisterTaskWakesUpOwnerQueue) {
248  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
249  auto platform_queue = task_queue->CreateTaskQueue();
250  auto raster_queue = task_queue->CreateTaskQueue();
251 
252  std::vector<fml::TimePoint> wakes;
253 
254  task_queue->SetWakeable(platform_queue,
255  new TestWakeable([&wakes](fml::TimePoint wake_time) {
256  wakes.push_back(wake_time);
257  }));
258 
259  task_queue->SetWakeable(raster_queue,
260  new TestWakeable([](fml::TimePoint wake_time) {
261  // The raster queue is owned by the platform queue.
262  ASSERT_FALSE(true);
263  }));
264 
267 
268  ASSERT_EQ(0UL, wakes.size());
269 
270  task_queue->RegisterTask(
271  platform_queue, []() {}, time1);
272 
273  ASSERT_EQ(1UL, wakes.size());
274  ASSERT_EQ(time1, wakes[0]);
275 
276  task_queue->Merge(platform_queue, raster_queue);
277 
278  task_queue->RegisterTask(
279  raster_queue, []() {}, time2);
280 
281  ASSERT_EQ(3UL, wakes.size());
282  ASSERT_EQ(time1, wakes[1]);
283  ASSERT_EQ(time1, wakes[2]);
284 }
285 
286 } // namespace testing
287 } // namespace fml
std::function< void(const fml::TimePoint)> WakeUpCall
void WakeUp(fml::TimePoint time_point) override
Definition: ascii_trie.cc:9
std::function< void()> closure
Definition: closure.h:14
TEST(BacktraceTest, CanGatherBacktrace)
static fml::RefPtr< MessageLoopTaskQueues > GetInstance()
static constexpr TimePoint Max()
Definition: time_point.h:32
void TestNotifyObservers(fml::TaskQueueId queue_id)
const char * expected_value
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
static TimePoint Now()
Definition: time_point.cc:26