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