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(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
45TEST(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
61TEST(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
86TEST(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
99TEST(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
110TEST(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
136TEST(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
169TEST(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