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(MessageLoopTaskQueueMergeUnmerge, |
27 | AfterMergePrimaryTasksServicedOnPrimary) { |
28 | auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); |
29 | |
30 | auto queue_id_1 = task_queue->CreateTaskQueue(); |
31 | auto queue_id_2 = task_queue->CreateTaskQueue(); |
32 | |
33 | task_queue->RegisterTask( |
34 | queue_id_1, []() {}, fml::TimePoint::Now()); |
35 | ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_1)); |
36 | |
37 | task_queue->Merge(queue_id_1, queue_id_2); |
38 | task_queue->RegisterTask( |
39 | queue_id_1, []() {}, fml::TimePoint::Now()); |
40 | |
41 | ASSERT_EQ(2u, task_queue->GetNumPendingTasks(queue_id_1)); |
42 | ASSERT_EQ(0u, task_queue->GetNumPendingTasks(queue_id_2)); |
43 | } |
44 | |
45 | TEST(MessageLoopTaskQueueMergeUnmerge, |
46 | AfterMergeSecondaryTasksAlsoServicedOnPrimary) { |
47 | auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); |
48 | |
49 | auto queue_id_1 = task_queue->CreateTaskQueue(); |
50 | auto queue_id_2 = task_queue->CreateTaskQueue(); |
51 | |
52 | task_queue->RegisterTask( |
53 | queue_id_2, []() {}, fml::TimePoint::Now()); |
54 | ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_2)); |
55 | |
56 | task_queue->Merge(queue_id_1, queue_id_2); |
57 | ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_1)); |
58 | ASSERT_EQ(0u, task_queue->GetNumPendingTasks(queue_id_2)); |
59 | } |
60 | |
61 | TEST(MessageLoopTaskQueueMergeUnmerge, MergeUnmergeTasksPreserved) { |
62 | auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); |
63 | |
64 | auto queue_id_1 = task_queue->CreateTaskQueue(); |
65 | auto queue_id_2 = task_queue->CreateTaskQueue(); |
66 | |
67 | task_queue->RegisterTask( |
68 | queue_id_1, []() {}, fml::TimePoint::Now()); |
69 | task_queue->RegisterTask( |
70 | queue_id_2, []() {}, fml::TimePoint::Now()); |
71 | |
72 | ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_1)); |
73 | ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_2)); |
74 | |
75 | task_queue->Merge(queue_id_1, queue_id_2); |
76 | |
77 | ASSERT_EQ(2u, task_queue->GetNumPendingTasks(queue_id_1)); |
78 | ASSERT_EQ(0u, task_queue->GetNumPendingTasks(queue_id_2)); |
79 | |
80 | task_queue->Unmerge(queue_id_1); |
81 | |
82 | ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_1)); |
83 | ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_2)); |
84 | } |
85 | |
86 | TEST(MessageLoopTaskQueueMergeUnmerge, MergeFailIfAlreadyMergedOrSubsumed) { |
87 | auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); |
88 | |
89 | auto queue_id_1 = task_queue->CreateTaskQueue(); |
90 | auto queue_id_2 = task_queue->CreateTaskQueue(); |
91 | auto queue_id_3 = task_queue->CreateTaskQueue(); |
92 | |
93 | task_queue->Merge(queue_id_1, queue_id_2); |
94 | |
95 | ASSERT_FALSE(task_queue->Merge(queue_id_1, queue_id_3)); |
96 | ASSERT_FALSE(task_queue->Merge(queue_id_2, queue_id_3)); |
97 | } |
98 | |
99 | TEST(MessageLoopTaskQueueMergeUnmerge, UnmergeFailsOnSubsumed) { |
100 | auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); |
101 | |
102 | auto queue_id_1 = task_queue->CreateTaskQueue(); |
103 | auto queue_id_2 = task_queue->CreateTaskQueue(); |
104 | |
105 | task_queue->Merge(queue_id_1, queue_id_2); |
106 | |
107 | ASSERT_FALSE(task_queue->Unmerge(queue_id_2)); |
108 | } |
109 | |
110 | TEST(MessageLoopTaskQueueMergeUnmerge, MergeInvokesBothWakeables) { |
111 | auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); |
112 | |
113 | auto queue_id_1 = task_queue->CreateTaskQueue(); |
114 | auto queue_id_2 = task_queue->CreateTaskQueue(); |
115 | |
116 | fml::CountDownLatch latch(2); |
117 | |
118 | task_queue->SetWakeable( |
119 | queue_id_1, |
120 | new TestWakeable([&](fml::TimePoint wake_time) { latch.CountDown(); })); |
121 | task_queue->SetWakeable( |
122 | queue_id_2, |
123 | new TestWakeable([&](fml::TimePoint wake_time) { latch.CountDown(); })); |
124 | |
125 | task_queue->RegisterTask( |
126 | queue_id_1, []() {}, fml::TimePoint::Now()); |
127 | |
128 | task_queue->Merge(queue_id_1, queue_id_2); |
129 | |
130 | std::vector<fml::closure> invocations; |
131 | task_queue->GetTasksToRunNow(queue_id_1, fml::FlushType::kAll, invocations); |
132 | |
133 | latch.Wait(); |
134 | } |
135 | |
136 | TEST(MessageLoopTaskQueueMergeUnmerge, |
137 | MergeUnmergeInvokesBothWakeablesSeparately) { |
138 | auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); |
139 | |
140 | auto queue_id_1 = task_queue->CreateTaskQueue(); |
141 | auto queue_id_2 = task_queue->CreateTaskQueue(); |
142 | |
143 | fml::AutoResetWaitableEvent latch_1, latch_2; |
144 | |
145 | task_queue->SetWakeable( |
146 | queue_id_1, |
147 | new TestWakeable([&](fml::TimePoint wake_time) { latch_1.Signal(); })); |
148 | task_queue->SetWakeable( |
149 | queue_id_2, |
150 | new TestWakeable([&](fml::TimePoint wake_time) { latch_2.Signal(); })); |
151 | |
152 | task_queue->RegisterTask( |
153 | queue_id_1, []() {}, fml::TimePoint::Now()); |
154 | task_queue->RegisterTask( |
155 | queue_id_2, []() {}, fml::TimePoint::Now()); |
156 | |
157 | task_queue->Merge(queue_id_1, queue_id_2); |
158 | task_queue->Unmerge(queue_id_1); |
159 | |
160 | std::vector<fml::closure> invocations; |
161 | |
162 | task_queue->GetTasksToRunNow(queue_id_1, fml::FlushType::kAll, invocations); |
163 | latch_1.Wait(); |
164 | |
165 | task_queue->GetTasksToRunNow(queue_id_2, fml::FlushType::kAll, invocations); |
166 | latch_2.Wait(); |
167 | } |
168 | |
169 | TEST(MessageLoopTaskQueueMergeUnmerge, GetTasksToRunNowBlocksMerge) { |
170 | auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); |
171 | |
172 | auto queue_id_1 = task_queue->CreateTaskQueue(); |
173 | auto queue_id_2 = task_queue->CreateTaskQueue(); |
174 | |
175 | fml::AutoResetWaitableEvent wake_up_start, wake_up_end, merge_start, |
176 | merge_end; |
177 | |
178 | task_queue->RegisterTask( |
179 | queue_id_1, []() {}, fml::TimePoint::Now()); |
180 | task_queue->SetWakeable(queue_id_1, |
181 | new TestWakeable([&](fml::TimePoint wake_time) { |
182 | wake_up_start.Signal(); |
183 | wake_up_end.Wait(); |
184 | })); |
185 | |
186 | std::thread tasks_to_run_now_thread([&]() { |
187 | std::vector<fml::closure> invocations; |
188 | task_queue->GetTasksToRunNow(queue_id_1, fml::FlushType::kAll, invocations); |
189 | }); |
190 | |
191 | wake_up_start.Wait(); |
192 | bool merge_done = false; |
193 | |
194 | std::thread merge_thread([&]() { |
195 | merge_start.Signal(); |
196 | task_queue->Merge(queue_id_1, queue_id_2); |
197 | merge_done = true; |
198 | merge_end.Signal(); |
199 | }); |
200 | |
201 | merge_start.Wait(); |
202 | ASSERT_FALSE(merge_done); |
203 | wake_up_end.Signal(); |
204 | |
205 | merge_end.Wait(); |
206 | ASSERT_TRUE(merge_done); |
207 | |
208 | tasks_to_run_now_thread.join(); |
209 | merge_thread.join(); |
210 | } |
211 | |