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 <thread>
8
9#include "flutter/fml/message_loop_task_queues.h"
10#include "flutter/fml/synchronization/count_down_latch.h"
11#include "flutter/fml/synchronization/waitable_event.h"
12#include "gtest/gtest.h"
13
14class TestWakeable : public fml::Wakeable {
15 public:
16 using WakeUpCall = std::function<void(const fml::TimePoint)>;
17
18 explicit TestWakeable(WakeUpCall call) : wake_up_call_(call) {}
19
20 void WakeUp(fml::TimePoint time_point) override { wake_up_call_(time_point); }
21
22 private:
23 WakeUpCall wake_up_call_;
24};
25
26TEST(MessageLoopTaskQueue, StartsWithNoPendingTasks) {
27 auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
28 auto queue_id = task_queue->CreateTaskQueue();
29 ASSERT_FALSE(task_queue->HasPendingTasks(queue_id));
30}
31
32TEST(MessageLoopTaskQueue, RegisterOneTask) {
33 const auto time = fml::TimePoint::Max();
34
35 auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
36 auto queue_id = task_queue->CreateTaskQueue();
37 task_queue->SetWakeable(queue_id,
38 new TestWakeable([&time](fml::TimePoint wake_time) {
39 ASSERT_TRUE(wake_time == time);
40 }));
41
42 task_queue->RegisterTask(
43 queue_id, [] {}, time);
44 ASSERT_TRUE(task_queue->HasPendingTasks(queue_id));
45 ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 1);
46}
47
48TEST(MessageLoopTaskQueue, RegisterTwoTasksAndCount) {
49 auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
50 auto queue_id = task_queue->CreateTaskQueue();
51 task_queue->RegisterTask(
52 queue_id, [] {}, fml::TimePoint::Now());
53 task_queue->RegisterTask(
54 queue_id, [] {}, fml::TimePoint::Max());
55 ASSERT_TRUE(task_queue->HasPendingTasks(queue_id));
56 ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 2);
57}
58
59TEST(MessageLoopTaskQueue, PreserveTaskOrdering) {
60 auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
61 auto queue_id = task_queue->CreateTaskQueue();
62 int test_val = 0;
63
64 // order: 0
65 task_queue->RegisterTask(
66 queue_id, [&test_val]() { test_val = 1; }, fml::TimePoint::Now());
67
68 // order: 1
69 task_queue->RegisterTask(
70 queue_id, [&test_val]() { test_val = 2; }, fml::TimePoint::Now());
71
72 std::vector<fml::closure> invocations;
73 task_queue->GetTasksToRunNow(queue_id, fml::FlushType::kAll, invocations);
74
75 int expected_value = 1;
76
77 for (auto& invocation : invocations) {
78 invocation();
79 ASSERT_TRUE(test_val == expected_value);
80 expected_value++;
81 }
82}
83
84void TestNotifyObservers(fml::TaskQueueId queue_id) {
85 auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
86 std::vector<fml::closure> observers =
87 task_queue->GetObserversToNotify(queue_id);
88 for (const auto& observer : observers) {
89 observer();
90 }
91}
92
93TEST(MessageLoopTaskQueue, AddRemoveNotifyObservers) {
94 auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
95 auto queue_id = task_queue->CreateTaskQueue();
96
97 int test_val = 0;
98 intptr_t key = 123;
99
100 task_queue->AddTaskObserver(queue_id, key, [&test_val]() { test_val = 1; });
101 TestNotifyObservers(queue_id);
102 ASSERT_TRUE(test_val == 1);
103
104 test_val = 0;
105 task_queue->RemoveTaskObserver(queue_id, key);
106 TestNotifyObservers(queue_id);
107 ASSERT_TRUE(test_val == 0);
108}
109
110TEST(MessageLoopTaskQueue, WakeUpIndependentOfTime) {
111 auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
112 auto queue_id = task_queue->CreateTaskQueue();
113
114 int num_wakes = 0;
115 task_queue->SetWakeable(
116 queue_id, new TestWakeable(
117 [&num_wakes](fml::TimePoint wake_time) { ++num_wakes; }));
118
119 task_queue->RegisterTask(
120 queue_id, []() {}, fml::TimePoint::Now());
121 task_queue->RegisterTask(
122 queue_id, []() {}, fml::TimePoint::Max());
123
124 ASSERT_TRUE(num_wakes == 2);
125}
126
127TEST(MessageLoopTaskQueue, WokenUpWithNewerTime) {
128 auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
129 auto queue_id = task_queue->CreateTaskQueue();
130 fml::CountDownLatch latch(2);
131
132 fml::TimePoint expected = fml::TimePoint::Max();
133
134 task_queue->SetWakeable(
135 queue_id, new TestWakeable([&latch, &expected](fml::TimePoint wake_time) {
136 ASSERT_TRUE(wake_time == expected);
137 latch.CountDown();
138 }));
139
140 task_queue->RegisterTask(
141 queue_id, []() {}, fml::TimePoint::Max());
142
143 const auto now = fml::TimePoint::Now();
144 expected = now;
145 task_queue->RegisterTask(
146 queue_id, []() {}, now);
147
148 latch.Wait();
149}
150
151TEST(MessageLoopTaskQueue, NotifyObserversWhileCreatingQueues) {
152 auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
153 fml::TaskQueueId queue_id = task_queues->CreateTaskQueue();
154 fml::AutoResetWaitableEvent first_observer_executing, before_second_observer;
155
156 task_queues->AddTaskObserver(queue_id, queue_id + 1, [&]() {
157 first_observer_executing.Signal();
158 before_second_observer.Wait();
159 });
160
161 for (int i = 0; i < 100; i++) {
162 task_queues->AddTaskObserver(queue_id, queue_id + i + 2, [] {});
163 }
164
165 std::thread notify_observers([&]() { TestNotifyObservers(queue_id); });
166
167 first_observer_executing.Wait();
168
169 for (int i = 0; i < 100; i++) {
170 task_queues->CreateTaskQueue();
171 }
172
173 before_second_observer.Signal();
174 notify_observers.join();
175}
176// TODO(chunhtai): This unit-test is flaky and sometimes fails asynchronizely
177// after the test has finished.
178// https://github.com/flutter/flutter/issues/43858
179TEST(MessageLoopTaskQueue, DISABLED_ConcurrentQueueAndTaskCreatingCounts) {
180 auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
181 const int base_queue_id = task_queues->CreateTaskQueue();
182
183 const int num_queues = 100;
184 std::atomic_bool created[num_queues * 3];
185 std::atomic_int num_tasks[num_queues * 3];
186 std::mutex task_count_mutex[num_queues * 3];
187 std::atomic_int done = 0;
188
189 for (int i = 0; i < num_queues * 3; i++) {
190 num_tasks[i] = 0;
191 created[i] = false;
192 }
193
194 auto creation_func = [&] {
195 for (int i = 0; i < num_queues; i++) {
196 fml::TaskQueueId queue_id = task_queues->CreateTaskQueue();
197 int limit = queue_id - base_queue_id;
198 created[limit] = true;
199
200 for (int cur_q = 1; cur_q < limit; cur_q++) {
201 if (created[cur_q]) {
202 std::scoped_lock counter(task_count_mutex[cur_q]);
203 int cur_num_tasks = rand() % 10;
204 for (int k = 0; k < cur_num_tasks; k++) {
205 task_queues->RegisterTask(
206 fml::TaskQueueId(base_queue_id + cur_q), [] {},
207 fml::TimePoint::Now());
208 }
209 num_tasks[cur_q] += cur_num_tasks;
210 }
211 }
212 }
213 done++;
214 };
215
216 std::thread creation_1(creation_func);
217 std::thread creation_2(creation_func);
218
219 while (done < 2) {
220 for (int i = 0; i < num_queues * 3; i++) {
221 if (created[i]) {
222 std::scoped_lock counter(task_count_mutex[i]);
223 int num_pending = task_queues->GetNumPendingTasks(
224 fml::TaskQueueId(base_queue_id + i));
225 int num_added = num_tasks[i];
226 ASSERT_EQ(num_pending, num_added);
227 }
228 }
229 }
230
231 creation_1.join();
232 creation_2.join();
233}
234