mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Cleans up header order/grouping for consistency: associated header, C/C++ system/standard library headers, library headers, platform-specific #includes. Adds <cstring> where strlen, memcpy are being used: there are a bunch of places we use them transitively. Applies linter-required cleanups. Disables linter on one file due to included RapidJson header. See https://github.com/flutter/flutter/issues/65676 This patch does not cover flutter/shell/platform/darwin. There's a separate, slightly more intensive cleanup for those in progress.
288 lines
8.6 KiB
C++
288 lines
8.6 KiB
C++
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#define FML_USED_ON_EMBEDDER
|
|
|
|
#include "flutter/fml/message_loop_task_queues.h"
|
|
|
|
#include <thread>
|
|
|
|
#include "flutter/fml/synchronization/count_down_latch.h"
|
|
#include "flutter/fml/synchronization/waitable_event.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace fml {
|
|
namespace testing {
|
|
|
|
class TestWakeable : public fml::Wakeable {
|
|
public:
|
|
using WakeUpCall = std::function<void(const fml::TimePoint)>;
|
|
|
|
explicit TestWakeable(WakeUpCall call) : wake_up_call_(call) {}
|
|
|
|
void WakeUp(fml::TimePoint time_point) override { wake_up_call_(time_point); }
|
|
|
|
private:
|
|
WakeUpCall wake_up_call_;
|
|
};
|
|
|
|
TEST(MessageLoopTaskQueue, StartsWithNoPendingTasks) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto queue_id = task_queue->CreateTaskQueue();
|
|
ASSERT_FALSE(task_queue->HasPendingTasks(queue_id));
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, RegisterOneTask) {
|
|
const auto time = fml::TimePoint::Max();
|
|
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto queue_id = task_queue->CreateTaskQueue();
|
|
task_queue->SetWakeable(queue_id,
|
|
new TestWakeable([&time](fml::TimePoint wake_time) {
|
|
ASSERT_TRUE(wake_time == time);
|
|
}));
|
|
|
|
task_queue->RegisterTask(
|
|
queue_id, [] {}, time);
|
|
ASSERT_TRUE(task_queue->HasPendingTasks(queue_id));
|
|
ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 1);
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, RegisterTwoTasksAndCount) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto queue_id = task_queue->CreateTaskQueue();
|
|
task_queue->RegisterTask(
|
|
queue_id, [] {}, fml::TimePoint::Now());
|
|
task_queue->RegisterTask(
|
|
queue_id, [] {}, fml::TimePoint::Max());
|
|
ASSERT_TRUE(task_queue->HasPendingTasks(queue_id));
|
|
ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 2);
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, PreserveTaskOrdering) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto queue_id = task_queue->CreateTaskQueue();
|
|
int test_val = 0;
|
|
|
|
// order: 0
|
|
task_queue->RegisterTask(
|
|
queue_id, [&test_val]() { test_val = 1; }, fml::TimePoint::Now());
|
|
|
|
// order: 1
|
|
task_queue->RegisterTask(
|
|
queue_id, [&test_val]() { test_val = 2; }, fml::TimePoint::Now());
|
|
|
|
const auto now = fml::TimePoint::Now();
|
|
int expected_value = 1;
|
|
for (;;) {
|
|
fml::closure invocation = task_queue->GetNextTaskToRun(queue_id, now);
|
|
if (!invocation) {
|
|
break;
|
|
}
|
|
invocation();
|
|
ASSERT_TRUE(test_val == expected_value);
|
|
expected_value++;
|
|
}
|
|
}
|
|
|
|
void TestNotifyObservers(fml::TaskQueueId queue_id) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
std::vector<fml::closure> observers =
|
|
task_queue->GetObserversToNotify(queue_id);
|
|
for (const auto& observer : observers) {
|
|
observer();
|
|
}
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, AddRemoveNotifyObservers) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto queue_id = task_queue->CreateTaskQueue();
|
|
|
|
int test_val = 0;
|
|
intptr_t key = 123;
|
|
|
|
task_queue->AddTaskObserver(queue_id, key, [&test_val]() { test_val = 1; });
|
|
TestNotifyObservers(queue_id);
|
|
ASSERT_TRUE(test_val == 1);
|
|
|
|
test_val = 0;
|
|
task_queue->RemoveTaskObserver(queue_id, key);
|
|
TestNotifyObservers(queue_id);
|
|
ASSERT_TRUE(test_val == 0);
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, WakeUpIndependentOfTime) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto queue_id = task_queue->CreateTaskQueue();
|
|
|
|
int num_wakes = 0;
|
|
task_queue->SetWakeable(
|
|
queue_id, new TestWakeable(
|
|
[&num_wakes](fml::TimePoint wake_time) { ++num_wakes; }));
|
|
|
|
task_queue->RegisterTask(
|
|
queue_id, []() {}, fml::TimePoint::Now());
|
|
task_queue->RegisterTask(
|
|
queue_id, []() {}, fml::TimePoint::Max());
|
|
|
|
ASSERT_TRUE(num_wakes == 2);
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, WokenUpWithNewerTime) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto queue_id = task_queue->CreateTaskQueue();
|
|
fml::CountDownLatch latch(2);
|
|
|
|
fml::TimePoint expected = fml::TimePoint::Max();
|
|
|
|
task_queue->SetWakeable(
|
|
queue_id, new TestWakeable([&latch, &expected](fml::TimePoint wake_time) {
|
|
ASSERT_TRUE(wake_time == expected);
|
|
latch.CountDown();
|
|
}));
|
|
|
|
task_queue->RegisterTask(
|
|
queue_id, []() {}, fml::TimePoint::Max());
|
|
|
|
const auto now = fml::TimePoint::Now();
|
|
expected = now;
|
|
task_queue->RegisterTask(
|
|
queue_id, []() {}, now);
|
|
|
|
latch.Wait();
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, NotifyObserversWhileCreatingQueues) {
|
|
auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
|
|
fml::TaskQueueId queue_id = task_queues->CreateTaskQueue();
|
|
fml::AutoResetWaitableEvent first_observer_executing, before_second_observer;
|
|
|
|
task_queues->AddTaskObserver(queue_id, queue_id + 1, [&]() {
|
|
first_observer_executing.Signal();
|
|
before_second_observer.Wait();
|
|
});
|
|
|
|
for (int i = 0; i < 100; i++) {
|
|
task_queues->AddTaskObserver(queue_id, queue_id + i + 2, [] {});
|
|
}
|
|
|
|
std::thread notify_observers([&]() { TestNotifyObservers(queue_id); });
|
|
|
|
first_observer_executing.Wait();
|
|
|
|
for (int i = 0; i < 100; i++) {
|
|
task_queues->CreateTaskQueue();
|
|
}
|
|
|
|
before_second_observer.Signal();
|
|
notify_observers.join();
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, QueueDoNotOwnItself) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto queue_id = task_queue->CreateTaskQueue();
|
|
ASSERT_FALSE(task_queue->Owns(queue_id, queue_id));
|
|
}
|
|
|
|
// TODO(chunhtai): This unit-test is flaky and sometimes fails asynchronizely
|
|
// after the test has finished.
|
|
// https://github.com/flutter/flutter/issues/43858
|
|
TEST(MessageLoopTaskQueue, DISABLED_ConcurrentQueueAndTaskCreatingCounts) {
|
|
auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
|
|
const int base_queue_id = task_queues->CreateTaskQueue();
|
|
|
|
const int num_queues = 100;
|
|
std::atomic_bool created[num_queues * 3];
|
|
std::atomic_int num_tasks[num_queues * 3];
|
|
std::mutex task_count_mutex[num_queues * 3];
|
|
std::atomic_int done = 0;
|
|
|
|
for (int i = 0; i < num_queues * 3; i++) {
|
|
num_tasks[i] = 0;
|
|
created[i] = false;
|
|
}
|
|
|
|
auto creation_func = [&] {
|
|
for (int i = 0; i < num_queues; i++) {
|
|
fml::TaskQueueId queue_id = task_queues->CreateTaskQueue();
|
|
int limit = queue_id - base_queue_id;
|
|
created[limit] = true;
|
|
|
|
for (int cur_q = 1; cur_q < limit; cur_q++) {
|
|
if (created[cur_q]) {
|
|
std::scoped_lock counter(task_count_mutex[cur_q]);
|
|
int cur_num_tasks = rand() % 10;
|
|
for (int k = 0; k < cur_num_tasks; k++) {
|
|
task_queues->RegisterTask(
|
|
fml::TaskQueueId(base_queue_id + cur_q), [] {},
|
|
fml::TimePoint::Now());
|
|
}
|
|
num_tasks[cur_q] += cur_num_tasks;
|
|
}
|
|
}
|
|
}
|
|
done++;
|
|
};
|
|
|
|
std::thread creation_1(creation_func);
|
|
std::thread creation_2(creation_func);
|
|
|
|
while (done < 2) {
|
|
for (int i = 0; i < num_queues * 3; i++) {
|
|
if (created[i]) {
|
|
std::scoped_lock counter(task_count_mutex[i]);
|
|
int num_pending = task_queues->GetNumPendingTasks(
|
|
fml::TaskQueueId(base_queue_id + i));
|
|
int num_added = num_tasks[i];
|
|
ASSERT_EQ(num_pending, num_added);
|
|
}
|
|
}
|
|
}
|
|
|
|
creation_1.join();
|
|
creation_2.join();
|
|
}
|
|
|
|
TEST(MessageLoopTaskQueue, RegisterTaskWakesUpOwnerQueue) {
|
|
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
|
|
auto platform_queue = task_queue->CreateTaskQueue();
|
|
auto raster_queue = task_queue->CreateTaskQueue();
|
|
|
|
std::vector<fml::TimePoint> wakes;
|
|
|
|
task_queue->SetWakeable(platform_queue,
|
|
new TestWakeable([&wakes](fml::TimePoint wake_time) {
|
|
wakes.push_back(wake_time);
|
|
}));
|
|
|
|
task_queue->SetWakeable(raster_queue,
|
|
new TestWakeable([](fml::TimePoint wake_time) {
|
|
// The raster queue is owned by the platform queue.
|
|
ASSERT_FALSE(true);
|
|
}));
|
|
|
|
auto time1 = fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(1);
|
|
auto time2 = fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(2);
|
|
|
|
ASSERT_EQ(0UL, wakes.size());
|
|
|
|
task_queue->RegisterTask(
|
|
platform_queue, []() {}, time1);
|
|
|
|
ASSERT_EQ(1UL, wakes.size());
|
|
ASSERT_EQ(time1, wakes[0]);
|
|
|
|
task_queue->Merge(platform_queue, raster_queue);
|
|
|
|
task_queue->RegisterTask(
|
|
raster_queue, []() {}, time2);
|
|
|
|
ASSERT_EQ(3UL, wakes.size());
|
|
ASSERT_EQ(time1, wakes[1]);
|
|
ASSERT_EQ(time1, wakes[2]);
|
|
}
|
|
|
|
} // namespace testing
|
|
} // namespace fml
|