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
14 class TestWakeable : public fml::Wakeable {
15 public:
16 using WakeUpCall = std::function<void(const fml::TimePoint)>;
17
TestWakeable(WakeUpCall call)18 TestWakeable(WakeUpCall call) : wake_up_call_(call) {}
19
WakeUp(fml::TimePoint time_point)20 void WakeUp(fml::TimePoint time_point) override { wake_up_call_(time_point); }
21
22 private:
23 WakeUpCall wake_up_call_;
24 };
25
TEST(MessageLoopTaskQueue,StartsWithNoPendingTasks)26 TEST(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
TEST(MessageLoopTaskQueue,RegisterOneTask)32 TEST(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
TEST(MessageLoopTaskQueue,RegisterTwoTasksAndCount)48 TEST(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
TEST(MessageLoopTaskQueue,PreserveTaskOrdering)59 TEST(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
TestNotifyObservers(fml::TaskQueueId queue_id)84 void 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
TEST(MessageLoopTaskQueue,AddRemoveNotifyObservers)93 TEST(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
TEST(MessageLoopTaskQueue,WakeUpIndependentOfTime)110 TEST(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
TEST(MessageLoopTaskQueue,WokenUpWithNewerTime)127 TEST(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
TEST(MessageLoopTaskQueue,NotifyObserversWhileCreatingQueues)151 TEST(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
TEST(MessageLoopTaskQueue,ConcurrentQueueAndTaskCreatingCounts)177 TEST(MessageLoopTaskQueue, ConcurrentQueueAndTaskCreatingCounts) {
178 auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
179 const int base_queue_id = task_queues->CreateTaskQueue();
180
181 const int num_queues = 100;
182 std::atomic_bool created[num_queues * 3];
183 std::atomic_int num_tasks[num_queues * 3];
184 std::mutex task_count_mutex[num_queues * 3];
185 std::atomic_int done = 0;
186
187 for (int i = 0; i < num_queues * 3; i++) {
188 num_tasks[i] = 0;
189 created[i] = false;
190 }
191
192 auto creation_func = [&] {
193 for (int i = 0; i < num_queues; i++) {
194 fml::TaskQueueId queue_id = task_queues->CreateTaskQueue();
195 created[queue_id - base_queue_id] = true;
196
197 for (int cur_q = 1; cur_q < i; cur_q++) {
198 if (created[cur_q - base_queue_id]) {
199 std::scoped_lock counter(task_count_mutex[cur_q - base_queue_id]);
200 int cur_num_tasks = rand() % 10;
201 for (int k = 0; k < cur_num_tasks; k++) {
202 task_queues->RegisterTask(
203 fml::TaskQueueId(cur_q), [] {}, fml::TimePoint::Now());
204 }
205 num_tasks[cur_q - base_queue_id] += cur_num_tasks;
206 }
207 }
208 }
209 done++;
210 };
211
212 std::thread creation_1(creation_func);
213 std::thread creation_2(creation_func);
214
215 while (done < 2) {
216 for (int i = 0; i < num_queues * 3; i++) {
217 if (created[i]) {
218 std::scoped_lock counter(task_count_mutex[i]);
219 int num_pending = task_queues->GetNumPendingTasks(
220 fml::TaskQueueId(base_queue_id + i));
221 int num_added = num_tasks[i];
222 ASSERT_EQ(num_pending, num_added);
223 }
224 }
225 }
226
227 creation_1.join();
228 creation_2.join();
229 }
230