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 <algorithm>
10 #include <cstdlib>
11 #include <thread>
12 
13 #include "flutter/fml/synchronization/count_down_latch.h"
14 #include "flutter/fml/synchronization/waitable_event.h"
15 #include "flutter/fml/time/chrono_timestamp_provider.h"
16 #include "gtest/gtest.h"
17 
18 namespace fml {
19 namespace testing {
20 
21 class TestWakeable : public fml::Wakeable {
22  public:
23  using WakeUpCall = std::function<void(const fml::TimePoint)>;
24 
25  explicit TestWakeable(WakeUpCall call) : wake_up_call_(call) {}
26 
27  void WakeUp(fml::TimePoint time_point) override { wake_up_call_(time_point); }
28 
29  private:
30  WakeUpCall wake_up_call_;
31 };
32 
33 TEST(MessageLoopTaskQueue, StartsWithNoPendingTasks) {
34  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
35  auto queue_id = task_queue->CreateTaskQueue();
36  ASSERT_FALSE(task_queue->HasPendingTasks(queue_id));
37 }
38 
39 TEST(MessageLoopTaskQueue, RegisterOneTask) {
40  const auto time = fml::TimePoint::Max();
41 
42  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
43  auto queue_id = task_queue->CreateTaskQueue();
44  task_queue->SetWakeable(queue_id,
45  new TestWakeable([&time](fml::TimePoint wake_time) {
46  ASSERT_TRUE(wake_time == time);
47  }));
48 
49  task_queue->RegisterTask(
50  queue_id, [] {}, time);
51  ASSERT_TRUE(task_queue->HasPendingTasks(queue_id));
52  ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 1);
53 }
54 
55 TEST(MessageLoopTaskQueue, RegisterTwoTasksAndCount) {
56  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
57  auto queue_id = task_queue->CreateTaskQueue();
58  task_queue->RegisterTask(
59  queue_id, [] {}, ChronoTicksSinceEpoch());
60  task_queue->RegisterTask(
61  queue_id, [] {}, fml::TimePoint::Max());
62  ASSERT_TRUE(task_queue->HasPendingTasks(queue_id));
63  ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 2);
64 }
65 
66 TEST(MessageLoopTaskQueue, RegisterTasksOnMergedQueuesAndCount) {
67  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
68  auto platform_queue = task_queue->CreateTaskQueue();
69  auto raster_queue = task_queue->CreateTaskQueue();
70  // A task in platform_queue
71  task_queue->RegisterTask(
72  platform_queue, []() {}, fml::TimePoint::Now());
73  // A task in raster_queue
74  task_queue->RegisterTask(
75  raster_queue, []() {}, fml::TimePoint::Now());
76  ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 1);
77  ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 1);
78 
79  ASSERT_FALSE(task_queue->Owns(platform_queue, raster_queue));
80  task_queue->Merge(platform_queue, raster_queue);
81  ASSERT_TRUE(task_queue->Owns(platform_queue, raster_queue));
82 
83  ASSERT_TRUE(task_queue->HasPendingTasks(platform_queue));
84  ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 2);
85  // The task count of subsumed queue is 0
86  ASSERT_FALSE(task_queue->HasPendingTasks(raster_queue));
87  ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 0);
88 
89  task_queue->Unmerge(platform_queue, raster_queue);
90  ASSERT_FALSE(task_queue->Owns(platform_queue, raster_queue));
91  ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 1);
92  ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 1);
93 }
94 
95 TEST(MessageLoopTaskQueue, PreserveTaskOrdering) {
96  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
97  auto queue_id = task_queue->CreateTaskQueue();
98  int test_val = 0;
99 
100  // order: 0
101  task_queue->RegisterTask(
102  queue_id, [&test_val]() { test_val = 1; }, ChronoTicksSinceEpoch());
103 
104  // order: 1
105  task_queue->RegisterTask(
106  queue_id, [&test_val]() { test_val = 2; }, ChronoTicksSinceEpoch());
107 
108  const auto now = ChronoTicksSinceEpoch();
109  int expected_value = 1;
110  while (true) {
111  fml::closure invocation = task_queue->GetNextTaskToRun(queue_id, now);
112  if (!invocation) {
113  break;
114  }
115  invocation();
116  ASSERT_TRUE(test_val == expected_value);
117  expected_value++;
118  }
119 }
120 
121 TEST(MessageLoopTaskQueue, RegisterTasksOnMergedQueuesPreserveTaskOrdering) {
122  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
123  auto platform_queue = task_queue->CreateTaskQueue();
124  auto raster1_queue = task_queue->CreateTaskQueue();
125  auto raster2_queue = task_queue->CreateTaskQueue();
126  int test_val = 0;
127 
128  // order 0 in raster1_queue
129  task_queue->RegisterTask(
130  raster1_queue, [&test_val]() { test_val = 0; }, fml::TimePoint::Now());
131 
132  // order 1 in platform_queue
133  task_queue->RegisterTask(
134  platform_queue, [&test_val]() { test_val = 1; }, fml::TimePoint::Now());
135 
136  // order 2 in raster2_queue
137  task_queue->RegisterTask(
138  raster2_queue, [&test_val]() { test_val = 2; }, fml::TimePoint::Now());
139 
140  task_queue->Merge(platform_queue, raster1_queue);
141  ASSERT_TRUE(task_queue->Owns(platform_queue, raster1_queue));
142  task_queue->Merge(platform_queue, raster2_queue);
143  ASSERT_TRUE(task_queue->Owns(platform_queue, raster2_queue));
144  const auto now = fml::TimePoint::Now();
145  int expected_value = 0;
146  // Right order:
147  // "test_val = 0" in raster1_queue
148  // "test_val = 1" in platform_queue
149  // "test_val = 2" in raster2_queue
150  while (true) {
151  fml::closure invocation = task_queue->GetNextTaskToRun(platform_queue, now);
152  if (!invocation) {
153  break;
154  }
155  invocation();
156  ASSERT_TRUE(test_val == expected_value);
157  expected_value++;
158  }
159 }
160 
161 TEST(MessageLoopTaskQueue, UnmergeRespectTheOriginalTaskOrderingInQueues) {
162  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
163  auto platform_queue = task_queue->CreateTaskQueue();
164  auto raster_queue = task_queue->CreateTaskQueue();
165  int test_val = 0;
166 
167  // order 0 in platform_queue
168  task_queue->RegisterTask(
169  platform_queue, [&test_val]() { test_val = 0; }, fml::TimePoint::Now());
170  // order 1 in platform_queue
171  task_queue->RegisterTask(
172  platform_queue, [&test_val]() { test_val = 1; }, fml::TimePoint::Now());
173  // order 2 in raster_queue
174  task_queue->RegisterTask(
175  raster_queue, [&test_val]() { test_val = 2; }, fml::TimePoint::Now());
176  // order 3 in raster_queue
177  task_queue->RegisterTask(
178  raster_queue, [&test_val]() { test_val = 3; }, fml::TimePoint::Now());
179  // order 4 in platform_queue
180  task_queue->RegisterTask(
181  platform_queue, [&test_val]() { test_val = 4; }, fml::TimePoint::Now());
182  // order 5 in raster_queue
183  task_queue->RegisterTask(
184  raster_queue, [&test_val]() { test_val = 5; }, fml::TimePoint::Now());
185 
186  ASSERT_TRUE(task_queue->Merge(platform_queue, raster_queue));
187  ASSERT_TRUE(task_queue->Owns(platform_queue, raster_queue));
188  const auto now = fml::TimePoint::Now();
189  // The right order after merged and consumed 3 tasks:
190  // "test_val = 0" in platform_queue
191  // "test_val = 1" in platform_queue
192  // "test_val = 2" in raster_queue (running on platform)
193  for (int i = 0; i < 3; i++) {
194  fml::closure invocation = task_queue->GetNextTaskToRun(platform_queue, now);
195  ASSERT_FALSE(!invocation);
196  invocation();
197  ASSERT_TRUE(test_val == i);
198  }
199  ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 3);
200  ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 0);
201 
202  ASSERT_TRUE(task_queue->Unmerge(platform_queue, raster_queue));
203  ASSERT_FALSE(task_queue->Owns(platform_queue, raster_queue));
204 
205  // The right order after unmerged and left 3 tasks:
206  // "test_val = 3" in raster_queue
207  // "test_val = 4" in platform_queue
208  // "test_val = 5" in raster_queue
209 
210  // platform_queue has 1 task left: "test_val = 4"
211  {
212  ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 1);
213  fml::closure invocation = task_queue->GetNextTaskToRun(platform_queue, now);
214  ASSERT_FALSE(!invocation);
215  invocation();
216  ASSERT_TRUE(test_val == 4);
217  ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 0);
218  }
219 
220  // raster_queue has 2 tasks left: "test_val = 3" and "test_val = 5"
221  {
222  ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 2);
223  fml::closure invocation = task_queue->GetNextTaskToRun(raster_queue, now);
224  ASSERT_FALSE(!invocation);
225  invocation();
226  ASSERT_TRUE(test_val == 3);
227  }
228  {
229  ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 1);
230  fml::closure invocation = task_queue->GetNextTaskToRun(raster_queue, now);
231  ASSERT_FALSE(!invocation);
232  invocation();
233  ASSERT_TRUE(test_val == 5);
234  ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 0);
235  }
236 }
237 
239  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
240  std::vector<fml::closure> observers =
241  task_queue->GetObserversToNotify(queue_id);
242  for (const auto& observer : observers) {
243  observer();
244  }
245 }
246 
247 TEST(MessageLoopTaskQueue, AddRemoveNotifyObservers) {
248  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
249  auto queue_id = task_queue->CreateTaskQueue();
250 
251  int test_val = 0;
252  intptr_t key = 123;
253 
254  task_queue->AddTaskObserver(queue_id, key, [&test_val]() { test_val = 1; });
255  TestNotifyObservers(queue_id);
256  ASSERT_TRUE(test_val == 1);
257 
258  test_val = 0;
259  task_queue->RemoveTaskObserver(queue_id, key);
260  TestNotifyObservers(queue_id);
261  ASSERT_TRUE(test_val == 0);
262 }
263 
264 TEST(MessageLoopTaskQueue, WakeUpIndependentOfTime) {
265  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
266  auto queue_id = task_queue->CreateTaskQueue();
267 
268  int num_wakes = 0;
269  task_queue->SetWakeable(
270  queue_id, new TestWakeable(
271  [&num_wakes](fml::TimePoint wake_time) { ++num_wakes; }));
272 
273  task_queue->RegisterTask(
274  queue_id, []() {}, ChronoTicksSinceEpoch());
275  task_queue->RegisterTask(
276  queue_id, []() {}, fml::TimePoint::Max());
277 
278  ASSERT_TRUE(num_wakes == 2);
279 }
280 
281 TEST(MessageLoopTaskQueue, WokenUpWithNewerTime) {
282  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
283  auto queue_id = task_queue->CreateTaskQueue();
284  fml::CountDownLatch latch(2);
285 
286  fml::TimePoint expected = fml::TimePoint::Max();
287 
288  task_queue->SetWakeable(
289  queue_id, new TestWakeable([&latch, &expected](fml::TimePoint wake_time) {
290  ASSERT_TRUE(wake_time == expected);
291  latch.CountDown();
292  }));
293 
294  task_queue->RegisterTask(
295  queue_id, []() {}, fml::TimePoint::Max());
296 
297  const auto now = ChronoTicksSinceEpoch();
298  expected = now;
299  task_queue->RegisterTask(
300  queue_id, []() {}, now);
301 
302  latch.Wait();
303 }
304 
305 TEST(MessageLoopTaskQueue, NotifyObserversWhileCreatingQueues) {
306  auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
307  fml::TaskQueueId queue_id = task_queues->CreateTaskQueue();
308  fml::AutoResetWaitableEvent first_observer_executing, before_second_observer;
309 
310  task_queues->AddTaskObserver(queue_id, queue_id + 1, [&]() {
311  first_observer_executing.Signal();
312  before_second_observer.Wait();
313  });
314 
315  for (int i = 0; i < 100; i++) {
316  task_queues->AddTaskObserver(queue_id, queue_id + i + 2, [] {});
317  }
318 
319  std::thread notify_observers([&]() { TestNotifyObservers(queue_id); });
320 
321  first_observer_executing.Wait();
322 
323  for (int i = 0; i < 100; i++) {
324  task_queues->CreateTaskQueue();
325  }
326 
327  before_second_observer.Signal();
328  notify_observers.join();
329 }
330 
331 TEST(MessageLoopTaskQueue, QueueDoNotOwnItself) {
332  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
333  auto queue_id = task_queue->CreateTaskQueue();
334  ASSERT_FALSE(task_queue->Owns(queue_id, queue_id));
335 }
336 
337 TEST(MessageLoopTaskQueue, QueueDoNotOwnUnmergedTaskQueueId) {
338  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
339  ASSERT_FALSE(task_queue->Owns(task_queue->CreateTaskQueue(), _kUnmerged));
340  ASSERT_FALSE(task_queue->Owns(_kUnmerged, task_queue->CreateTaskQueue()));
341  ASSERT_FALSE(task_queue->Owns(_kUnmerged, _kUnmerged));
342 }
343 
344 TEST(MessageLoopTaskQueue, QueueOwnsMergedTaskQueueId) {
345  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
346  auto platform_queue = task_queue->CreateTaskQueue();
347  auto raster_queue = task_queue->CreateTaskQueue();
348  ASSERT_FALSE(task_queue->Owns(platform_queue, raster_queue));
349  ASSERT_FALSE(task_queue->Owns(raster_queue, platform_queue));
350  task_queue->Merge(platform_queue, raster_queue);
351  ASSERT_TRUE(task_queue->Owns(platform_queue, raster_queue));
352  ASSERT_FALSE(task_queue->Owns(raster_queue, platform_queue));
353 }
354 
355 //------------------------------------------------------------------------------
356 /// Verifies that tasks can be added to task queues concurrently.
357 ///
358 TEST(MessageLoopTaskQueue, ConcurrentQueueAndTaskCreatingCounts) {
359  auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
360 
361  // kThreadCount threads post kThreadTaskCount tasks each to kTaskQueuesCount
362  // task queues. Each thread picks a task queue randomly for each task.
363  constexpr size_t kThreadCount = 4;
364  constexpr size_t kTaskQueuesCount = 2;
365  constexpr size_t kThreadTaskCount = 500;
366 
367  std::vector<TaskQueueId> task_queue_ids;
368  for (size_t i = 0; i < kTaskQueuesCount; ++i) {
369  task_queue_ids.emplace_back(task_queues->CreateTaskQueue());
370  }
371 
372  ASSERT_EQ(task_queue_ids.size(), kTaskQueuesCount);
373 
374  fml::CountDownLatch tasks_posted_latch(kThreadCount);
375 
376  auto thread_main = [&]() {
377  for (size_t i = 0; i < kThreadTaskCount; i++) {
378  const auto current_task_queue_id =
379  task_queue_ids[std::rand() % kTaskQueuesCount];
380  const auto empty_task = []() {};
381  // The timepoint doesn't matter as the queue is never going to be drained.
382  const auto task_timepoint = ChronoTicksSinceEpoch();
383 
384  task_queues->RegisterTask(current_task_queue_id, empty_task,
385  task_timepoint);
386  }
387 
388  tasks_posted_latch.CountDown();
389  };
390 
391  std::vector<std::thread> threads;
392 
393  for (size_t i = 0; i < kThreadCount; i++) {
394  threads.emplace_back(std::thread{thread_main});
395  }
396 
397  ASSERT_EQ(threads.size(), kThreadCount);
398 
399  for (size_t i = 0; i < kThreadCount; i++) {
400  threads[i].join();
401  }
402 
403  // All tasks have been posted by now. Check that they are all pending.
404 
405  size_t pending_tasks = 0u;
406  std::for_each(task_queue_ids.begin(), task_queue_ids.end(),
407  [&](const auto& queue) {
408  pending_tasks += task_queues->GetNumPendingTasks(queue);
409  });
410 
411  ASSERT_EQ(pending_tasks, kThreadCount * kThreadTaskCount);
412 }
413 
414 TEST(MessageLoopTaskQueue, RegisterTaskWakesUpOwnerQueue) {
415  auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
416  auto platform_queue = task_queue->CreateTaskQueue();
417  auto raster_queue = task_queue->CreateTaskQueue();
418 
419  std::vector<fml::TimePoint> wakes;
420 
421  task_queue->SetWakeable(platform_queue,
422  new TestWakeable([&wakes](fml::TimePoint wake_time) {
423  wakes.push_back(wake_time);
424  }));
425 
426  task_queue->SetWakeable(raster_queue,
427  new TestWakeable([](fml::TimePoint wake_time) {
428  // The raster queue is owned by the platform queue.
429  ASSERT_FALSE(true);
430  }));
431 
434 
435  ASSERT_EQ(0UL, wakes.size());
436 
437  task_queue->RegisterTask(
438  platform_queue, []() {}, time1);
439 
440  ASSERT_EQ(1UL, wakes.size());
441  ASSERT_EQ(time1, wakes[0]);
442 
443  task_queue->Merge(platform_queue, raster_queue);
444 
445  task_queue->RegisterTask(
446  raster_queue, []() {}, time2);
447 
448  ASSERT_EQ(3UL, wakes.size());
449  ASSERT_EQ(time1, wakes[1]);
450  ASSERT_EQ(time1, wakes[2]);
451 }
452 
453 } // namespace testing
454 } // 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 const char * expected_value
static fml::RefPtr< MessageLoopTaskQueues > GetInstance()
static constexpr TimePoint Max()
Definition: time_point.h:34
static const TaskQueueId _kUnmerged
void TestNotifyObservers(fml::TaskQueueId queue_id)
fml::TimePoint ChronoTicksSinceEpoch()
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
static TimePoint Now()
Definition: time_point.cc:39