• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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