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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | // 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 |
179 | TEST(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 | |