blob: 02e798786a121047390532d150f2233e5e7736ee [file] [log] [blame]
// 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.h"
#include <iostream>
#include <thread>
#include "flutter/fml/build_config.h"
#include "flutter/fml/concurrent_message_loop.h"
#include "flutter/fml/synchronization/count_down_latch.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/task_runner.h"
#include "gtest/gtest.h"
#define TIMESENSITIVE(x) TimeSensitiveTest_##x
#if OS_WIN
#define PLATFORM_SPECIFIC_CAPTURE(...) [ __VA_ARGS__, count ]
#else
#define PLATFORM_SPECIFIC_CAPTURE(...) [__VA_ARGS__]
#endif
TEST(MessageLoop, GetCurrent) {
std::thread thread([]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
ASSERT_TRUE(fml::MessageLoop::GetCurrent().GetTaskRunner());
});
thread.join();
}
TEST(MessageLoop, DifferentThreadsHaveDifferentLoops) {
fml::MessageLoop* loop1 = nullptr;
fml::AutoResetWaitableEvent latch1;
fml::AutoResetWaitableEvent term1;
std::thread thread1([&loop1, &latch1, &term1]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop1 = &fml::MessageLoop::GetCurrent();
latch1.Signal();
term1.Wait();
});
fml::MessageLoop* loop2 = nullptr;
fml::AutoResetWaitableEvent latch2;
fml::AutoResetWaitableEvent term2;
std::thread thread2([&loop2, &latch2, &term2]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop2 = &fml::MessageLoop::GetCurrent();
latch2.Signal();
term2.Wait();
});
latch1.Wait();
latch2.Wait();
ASSERT_FALSE(loop1 == loop2);
term1.Signal();
term2.Signal();
thread1.join();
thread2.join();
}
TEST(MessageLoop, CanRunAndTerminate) {
bool started = false;
bool terminated = false;
std::thread thread([&started, &terminated]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = fml::MessageLoop::GetCurrent();
ASSERT_TRUE(loop.GetTaskRunner());
loop.GetTaskRunner()->PostTask([&terminated]() {
fml::MessageLoop::GetCurrent().Terminate();
terminated = true;
});
loop.Run();
started = true;
});
thread.join();
ASSERT_TRUE(started);
ASSERT_TRUE(terminated);
}
TEST(MessageLoop, NonDelayedTasksAreRunInOrder) {
const size_t count = 100;
bool started = false;
bool terminated = false;
std::thread thread([&started, &terminated, count]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = fml::MessageLoop::GetCurrent();
size_t current = 0;
for (size_t i = 0; i < count; i++) {
loop.GetTaskRunner()->PostTask(
PLATFORM_SPECIFIC_CAPTURE(&terminated, i, &current)() {
ASSERT_EQ(current, i);
current++;
if (count == i + 1) {
fml::MessageLoop::GetCurrent().Terminate();
terminated = true;
}
});
}
loop.Run();
ASSERT_EQ(current, count);
started = true;
});
thread.join();
ASSERT_TRUE(started);
ASSERT_TRUE(terminated);
}
TEST(MessageLoop, DelayedTasksAtSameTimeAreRunInOrder) {
const size_t count = 100;
bool started = false;
bool terminated = false;
std::thread thread([&started, &terminated, count]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = fml::MessageLoop::GetCurrent();
size_t current = 0;
const auto now_plus_some =
fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(2);
for (size_t i = 0; i < count; i++) {
loop.GetTaskRunner()->PostTaskForTime(
PLATFORM_SPECIFIC_CAPTURE(&terminated, i, &current)() {
ASSERT_EQ(current, i);
current++;
if (count == i + 1) {
fml::MessageLoop::GetCurrent().Terminate();
terminated = true;
}
},
now_plus_some);
}
loop.Run();
ASSERT_EQ(current, count);
started = true;
});
thread.join();
ASSERT_TRUE(started);
ASSERT_TRUE(terminated);
}
TEST(MessageLoop, CheckRunsTaskOnCurrentThread) {
fml::RefPtr<fml::TaskRunner> runner;
fml::AutoResetWaitableEvent latch;
std::thread thread([&runner, &latch]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = fml::MessageLoop::GetCurrent();
runner = loop.GetTaskRunner();
latch.Signal();
ASSERT_TRUE(loop.GetTaskRunner()->RunsTasksOnCurrentThread());
});
latch.Wait();
ASSERT_TRUE(runner);
ASSERT_FALSE(runner->RunsTasksOnCurrentThread());
thread.join();
}
TEST(MessageLoop, TIMESENSITIVE(SingleDelayedTaskByDelta)) {
bool checked = false;
std::thread thread([&checked]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = fml::MessageLoop::GetCurrent();
auto begin = fml::TimePoint::Now();
loop.GetTaskRunner()->PostDelayedTask(
[begin, &checked]() {
auto delta = fml::TimePoint::Now() - begin;
auto ms = delta.ToMillisecondsF();
ASSERT_GE(ms, 3);
ASSERT_LE(ms, 7);
checked = true;
fml::MessageLoop::GetCurrent().Terminate();
},
fml::TimeDelta::FromMilliseconds(5));
loop.Run();
});
thread.join();
ASSERT_TRUE(checked);
}
TEST(MessageLoop, TIMESENSITIVE(SingleDelayedTaskForTime)) {
bool checked = false;
std::thread thread([&checked]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = fml::MessageLoop::GetCurrent();
auto begin = fml::TimePoint::Now();
loop.GetTaskRunner()->PostTaskForTime(
[begin, &checked]() {
auto delta = fml::TimePoint::Now() - begin;
auto ms = delta.ToMillisecondsF();
ASSERT_GE(ms, 3);
ASSERT_LE(ms, 7);
checked = true;
fml::MessageLoop::GetCurrent().Terminate();
},
fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(5));
loop.Run();
});
thread.join();
ASSERT_TRUE(checked);
}
TEST(MessageLoop, TIMESENSITIVE(MultipleDelayedTasksWithIncreasingDeltas)) {
const auto count = 10;
int checked = false;
std::thread thread(PLATFORM_SPECIFIC_CAPTURE(&checked)() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = fml::MessageLoop::GetCurrent();
for (int target_ms = 0 + 2; target_ms < count + 2; target_ms++) {
auto begin = fml::TimePoint::Now();
loop.GetTaskRunner()->PostDelayedTask(
PLATFORM_SPECIFIC_CAPTURE(begin, target_ms, &checked)() {
auto delta = fml::TimePoint::Now() - begin;
auto ms = delta.ToMillisecondsF();
ASSERT_GE(ms, target_ms - 2);
ASSERT_LE(ms, target_ms + 2);
checked++;
if (checked == count) {
fml::MessageLoop::GetCurrent().Terminate();
}
},
fml::TimeDelta::FromMilliseconds(target_ms));
}
loop.Run();
});
thread.join();
ASSERT_EQ(checked, count);
}
TEST(MessageLoop, TIMESENSITIVE(MultipleDelayedTasksWithDecreasingDeltas)) {
const auto count = 10;
int checked = false;
std::thread thread(PLATFORM_SPECIFIC_CAPTURE(&checked)() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = fml::MessageLoop::GetCurrent();
for (int target_ms = count + 2; target_ms > 0 + 2; target_ms--) {
auto begin = fml::TimePoint::Now();
loop.GetTaskRunner()->PostDelayedTask(
PLATFORM_SPECIFIC_CAPTURE(begin, target_ms, &checked)() {
auto delta = fml::TimePoint::Now() - begin;
auto ms = delta.ToMillisecondsF();
ASSERT_GE(ms, target_ms - 2);
ASSERT_LE(ms, target_ms + 2);
checked++;
if (checked == count) {
fml::MessageLoop::GetCurrent().Terminate();
}
},
fml::TimeDelta::FromMilliseconds(target_ms));
}
loop.Run();
});
thread.join();
ASSERT_EQ(checked, count);
}
TEST(MessageLoop, TaskObserverFire) {
bool started = false;
bool terminated = false;
std::thread thread([&started, &terminated]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
const size_t count = 25;
auto& loop = fml::MessageLoop::GetCurrent();
size_t task_count = 0;
size_t obs_count = 0;
auto obs = PLATFORM_SPECIFIC_CAPTURE(&obs_count)() { obs_count++; };
for (size_t i = 0; i < count; i++) {
loop.GetTaskRunner()->PostTask(
PLATFORM_SPECIFIC_CAPTURE(&terminated, i, &task_count)() {
ASSERT_EQ(task_count, i);
task_count++;
if (count == i + 1) {
fml::MessageLoop::GetCurrent().Terminate();
terminated = true;
}
});
}
loop.AddTaskObserver(0, obs);
loop.Run();
ASSERT_EQ(task_count, count);
ASSERT_EQ(obs_count, count);
started = true;
});
thread.join();
ASSERT_TRUE(started);
ASSERT_TRUE(terminated);
}
TEST(MessageLoop, ConcurrentMessageLoopHasNonZeroWorkers) {
auto loop = fml::ConcurrentMessageLoop::Create(
0u /* explicitly specify zero workers */);
ASSERT_GT(loop->GetWorkerCount(), 0u);
}
TEST(MessageLoop, CanCreateAndShutdownConcurrentMessageLoopsOverAndOver) {
for (size_t i = 0; i < 10; ++i) {
auto loop = fml::ConcurrentMessageLoop::Create(i + 1);
ASSERT_EQ(loop->GetWorkerCount(), i + 1);
}
}
TEST(MessageLoop, CanCreateConcurrentMessageLoop) {
auto loop = fml::ConcurrentMessageLoop::Create();
auto task_runner = loop->GetTaskRunner();
const size_t kCount = 10;
fml::CountDownLatch latch(kCount);
std::mutex thread_ids_mutex;
std::set<std::thread::id> thread_ids;
for (size_t i = 0; i < kCount; ++i) {
task_runner->PostTask([&]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Ran on thread: " << std::this_thread::get_id() << std::endl;
std::scoped_lock lock(thread_ids_mutex);
thread_ids.insert(std::this_thread::get_id());
latch.CountDown();
});
}
latch.Wait();
ASSERT_GE(thread_ids.size(), 1u);
}