#include "base/task/single_thread_task_executor.h"
#include <stddef.h>
#include <stdint.h>
#include <optional>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_pump_for_io.h"
#include "base/message_loop/message_pump_type.h"
#include "base/pending_task.h"
#include "base/posix/eintr_wrapper.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_observer.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/java_handler_thread.h"
#include "base/android/jni_android.h"
#include "base/test/android/java_handler_thread_helpers.h"
#endif
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "base/message_loop/message_pump_win.h"
#include "base/process/memory.h"
#include "base/win/current_module.h"
#include "base/win/message_window.h"
#include "base/win/scoped_handle.h"
#endif
using ::testing::IsNull;
using ::testing::NotNull;
namespace base {
namespace {
class Foo : public RefCounted<Foo> {
public:
Foo() = default;
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
void Test0() { ++test_count_; }
void Test1ConstRef(const std::string& a) {
++test_count_;
result_.append(a);
}
void Test1Ptr(std::string* a) {
++test_count_;
result_.append(*a);
}
void Test1Int(int a) { test_count_ += a; }
void Test2Ptr(std::string* a, std::string* b) {
++test_count_;
result_.append(*a);
result_.append(*b);
}
void Test2Mixed(const std::string& a, std::string* b) {
++test_count_;
result_.append(a);
result_.append(*b);
}
int test_count() const { return test_count_; }
const std::string& result() const LIFETIME_BOUND { return result_; }
private:
friend class RefCounted<Foo>;
~Foo() = default;
int test_count_ = 0;
std::string result_;
};
static void SlowFunc(TimeDelta pause,
int* quit_counter,
base::OnceClosure quit_closure) {
PlatformThread::Sleep(pause);
if (--(*quit_counter) == 0) {
std::move(quit_closure).Run();
}
}
static void RecordRunTimeFunc(TimeTicks* run_time,
int* quit_counter,
base::OnceClosure quit_closure) {
*run_time = TimeTicks::Now();
SlowFunc(Milliseconds(10), quit_counter, std::move(quit_closure));
}
enum TaskType {
MESSAGEBOX,
ENDDIALOG,
RECURSIVE,
TIMEDMESSAGELOOP,
QUITMESSAGELOOP,
ORDERED,
PUMPS,
SLEEP,
RUNS,
};
struct TaskItem {
TaskItem(TaskType t, int c, bool s) : type(t), cookie(c), start(s) {}
TaskType type;
int cookie;
bool start;
bool operator==(const TaskItem& other) const {
return type == other.type && cookie == other.cookie && start == other.start;
}
};
std::ostream& operator<<(std::ostream& os, TaskType type) {
switch (type) {
case MESSAGEBOX:
os << "MESSAGEBOX";
break;
case ENDDIALOG:
os << "ENDDIALOG";
break;
case RECURSIVE:
os << "RECURSIVE";
break;
case TIMEDMESSAGELOOP:
os << "TIMEDMESSAGELOOP";
break;
case QUITMESSAGELOOP:
os << "QUITMESSAGELOOP";
break;
case ORDERED:
os << "ORDERED";
break;
case PUMPS:
os << "PUMPS";
break;
case SLEEP:
os << "SLEEP";
break;
default:
NOTREACHED();
}
return os;
}
std::ostream& operator<<(std::ostream& os, const TaskItem& item) {
if (item.start) {
return os << item.type << " " << item.cookie << " starts";
}
return os << item.type << " " << item.cookie << " ends";
}
class TaskList {
public:
void RecordStart(TaskType type, int cookie) {
TaskItem item(type, cookie, true);
DVLOG(1) << item;
task_list_.push_back(item);
}
void RecordEnd(TaskType type, int cookie) {
TaskItem item(type, cookie, false);
DVLOG(1) << item;
task_list_.push_back(item);
}
size_t Size() { return task_list_.size(); }
TaskItem Get(int n) { return task_list_[n]; }
private:
std::vector<TaskItem> task_list_;
};
class DummyTaskObserver : public TaskObserver {
public:
explicit DummyTaskObserver(int num_tasks)
: num_tasks_started_(0), num_tasks_processed_(0), num_tasks_(num_tasks) {}
DummyTaskObserver(int num_tasks, int num_tasks_started)
: num_tasks_started_(num_tasks_started),
num_tasks_processed_(0),
num_tasks_(num_tasks) {}
DummyTaskObserver(const DummyTaskObserver&) = delete;
DummyTaskObserver& operator=(const DummyTaskObserver&) = delete;
~DummyTaskObserver() override = default;
void WillProcessTask(const PendingTask& pending_task,
bool ) override {
num_tasks_started_++;
EXPECT_LE(num_tasks_started_, num_tasks_);
EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1);
}
void DidProcessTask(const PendingTask& pending_task) override {
num_tasks_processed_++;
EXPECT_LE(num_tasks_started_, num_tasks_);
EXPECT_EQ(num_tasks_started_, num_tasks_processed_);
}
int num_tasks_started() const { return num_tasks_started_; }
int num_tasks_processed() const { return num_tasks_processed_; }
private:
int num_tasks_started_;
int num_tasks_processed_;
const int num_tasks_;
};
void RecursiveFunc(TaskList* order, int cookie, int depth) {
order->RecordStart(RECURSIVE, cookie);
if (depth > 0) {
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&RecursiveFunc, order, cookie, depth - 1));
}
order->RecordEnd(RECURSIVE, cookie);
}
void QuitFunc(TaskList* order, int cookie, base::OnceClosure quit_closure) {
order->RecordStart(QUITMESSAGELOOP, cookie);
std::move(quit_closure).Run();
order->RecordEnd(QUITMESSAGELOOP, cookie);
}
#if BUILDFLAG(IS_WIN)
void SubPumpFunc(OnceClosure on_done) {
CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop
allow_nestable_tasks;
MSG msg;
while (::GetMessage(&msg, NULL, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
std::move(on_done).Run();
}
const wchar_t kMessageBoxTitle[] = L"SingleThreadTaskExecutor Unit Test";
void MessageBoxFunc(TaskList* order, int cookie, bool is_reentrant) {
order->RecordStart(MESSAGEBOX, cookie);
std::optional<CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop>
maybe_allow_nesting;
if (is_reentrant) {
maybe_allow_nesting.emplace();
}
::MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK);
order->RecordEnd(MESSAGEBOX, cookie);
}
void EndDialogFunc(TaskList* order, int cookie) {
order->RecordStart(ENDDIALOG, cookie);
HWND window = GetActiveWindow();
if (window != NULL) {
EXPECT_NE(::EndDialog(window, IDCONTINUE), 0);
order->RecordEnd(ENDDIALOG, cookie);
}
}
void RecursiveFuncWin(scoped_refptr<SingleThreadTaskRunner> task_runner,
HANDLE event,
bool expect_window,
TaskList* order,
bool message_box_is_reentrant,
base::OnceClosure quit_closure) {
task_runner->PostTask(FROM_HERE, BindOnce(&RecursiveFunc, order, 1, 2));
task_runner->PostTask(
FROM_HERE, BindOnce(&MessageBoxFunc, order, 2, message_box_is_reentrant));
task_runner->PostTask(FROM_HERE, BindOnce(&RecursiveFunc, order, 3, 2));
task_runner->PostTask(FROM_HERE, BindOnce(&EndDialogFunc, order, 4));
task_runner->PostTask(FROM_HERE,
BindOnce(&QuitFunc, order, 5, std::move(quit_closure)));
ASSERT_TRUE(SetEvent(event));
for (; expect_window;) {
HWND window = ::FindWindowW(L"#32770", kMessageBoxTitle);
if (window) {
for (;;) {
HWND button = ::FindWindowExW(window, NULL, L"Button", NULL);
if (button != NULL) {
EXPECT_EQ(0, ::SendMessage(button, WM_LBUTTONDOWN, 0, 0));
EXPECT_EQ(0, ::SendMessage(button, WM_LBUTTONUP, 0, 0));
break;
}
}
break;
}
}
}
#endif
void Post128KTasksThenQuit(SingleThreadTaskRunner* executor_task_runner,
TimeTicks begin_ticks,
TimeTicks last_post_ticks,
TimeDelta slowest_delay,
OnceClosure on_done,
int num_posts_done = 0) {
const int kNumTimes = 128000;
const auto now = TimeTicks::Now();
const auto scheduling_delay = now - last_post_ticks;
if (scheduling_delay > slowest_delay) {
slowest_delay = scheduling_delay;
}
if (num_posts_done == kNumTimes) {
std::move(on_done).Run();
return;
} else if (now - begin_ticks >= TestTimeouts::action_max_timeout()) {
ADD_FAILURE() << "Couldn't run all tasks."
<< "\nNumber of tasks remaining: "
<< kNumTimes - num_posts_done
<< "\nSlowest scheduling delay: " << slowest_delay
<< "\nAverage per task: "
<< (now - begin_ticks) / num_posts_done;
std::move(on_done).Run();
return;
}
executor_task_runner->PostTask(
FROM_HERE,
BindOnce(&Post128KTasksThenQuit, Unretained(executor_task_runner),
begin_ticks, now, slowest_delay, std::move(on_done),
num_posts_done + 1));
}
#if BUILDFLAG(IS_WIN)
class TestIOHandler : public MessagePumpForIO::IOHandler {
public:
TestIOHandler(const wchar_t* name, HANDLE signal);
void OnIOCompleted(MessagePumpForIO::IOContext* context,
DWORD bytes_transfered,
DWORD error) override;
void Init();
OVERLAPPED* context() { return context_.GetOverlapped(); }
DWORD size() { return sizeof(buffer_); }
private:
char buffer_[48];
MessagePumpForIO::IOContext context_;
HANDLE signal_;
win::ScopedHandle file_;
};
TestIOHandler::TestIOHandler(const wchar_t* name, HANDLE signal)
: MessagePumpForIO::IOHandler(FROM_HERE), signal_(signal) {
UNSAFE_TODO(memset(buffer_, 0, sizeof(buffer_)));
file_.Set(CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, NULL));
EXPECT_TRUE(file_.is_valid());
}
void TestIOHandler::Init() {
const bool success =
CurrentIOThread::Get()->RegisterIOHandler(file_.get(), this);
ASSERT_TRUE(success);
DWORD read;
EXPECT_FALSE(ReadFile(file_.get(), buffer_, size(), &read, context()));
EXPECT_EQ(static_cast<DWORD>(ERROR_IO_PENDING), GetLastError());
}
void TestIOHandler::OnIOCompleted(MessagePumpForIO::IOContext* context,
DWORD bytes_transfered,
DWORD error) {
ASSERT_TRUE(context == &context_);
ASSERT_TRUE(SetEvent(signal_));
}
void RunTest_IOHandler() {
win::ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL));
ASSERT_TRUE(callback_called.is_valid());
const wchar_t* kPipeName = L"\\\\.\\pipe\\iohandler_pipe";
win::ScopedHandle server(
CreateNamedPipe(kPipeName, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
ASSERT_TRUE(server.is_valid());
Thread thread("IOHandler test");
Thread::Options options;
options.message_pump_type = MessagePumpType::IO;
ASSERT_TRUE(thread.StartWithOptions(std::move(options)));
TestIOHandler handler(kPipeName, callback_called.get());
thread.task_runner()->PostTask(
FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler)));
PlatformThread::Sleep(Milliseconds(100));
const char buffer[] = "Hello there!";
DWORD written;
EXPECT_TRUE(WriteFile(server.get(), buffer, sizeof(buffer), &written, NULL));
DWORD result = WaitForSingleObject(callback_called.get(), 1000);
EXPECT_EQ(WAIT_OBJECT_0, result);
thread.Stop();
}
#endif
}
class SingleThreadTaskExecutorTypedTest
: public ::testing::TestWithParam<MessagePumpType> {
public:
SingleThreadTaskExecutorTypedTest() = default;
SingleThreadTaskExecutorTypedTest(const SingleThreadTaskExecutorTypedTest&) =
delete;
SingleThreadTaskExecutorTypedTest& operator=(
const SingleThreadTaskExecutorTypedTest&) = delete;
~SingleThreadTaskExecutorTypedTest() override = default;
static std::string ParamInfoToString(
::testing::TestParamInfo<MessagePumpType> param_info) {
switch (param_info.param) {
case MessagePumpType::DEFAULT:
return "default_pump";
case MessagePumpType::IO:
return "IO_pump";
case MessagePumpType::UI:
return "UI_pump";
case MessagePumpType::CUSTOM:
break;
#if BUILDFLAG(IS_ANDROID)
case MessagePumpType::JAVA:
break;
#endif
#if BUILDFLAG(IS_APPLE)
case MessagePumpType::NS_RUNLOOP:
break;
#endif
}
NOTREACHED();
}
};
TEST_P(SingleThreadTaskExecutorTypedTest, PostTask) {
SingleThreadTaskExecutor executor(GetParam());
base::RunLoop loop;
scoped_refptr<Foo> foo(new Foo());
std::string a("a"), b("b"), c("c"), d("d");
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&Foo::Test0, foo));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&Foo::Test1Ptr, foo, &b));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&Foo::Test1Int, foo, 100));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&Foo::Test2Ptr, foo, &a, &c));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&Foo::Test2Mixed, foo, a, &d));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(loop.QuitWhenIdleClosure()));
loop.Run();
EXPECT_EQ(foo->test_count(), 105);
EXPECT_EQ(foo->result(), "abacad");
}
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_Basic) {
SingleThreadTaskExecutor executor(GetParam());
const TimeDelta kDelay = Milliseconds(100);
int num_tasks = 1;
TimeTicks run_time;
base::RunLoop loop;
TimeTicks time_before_run = TimeTicks::Now();
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks,
loop.QuitWhenIdleClosure()),
kDelay);
loop.Run();
TimeTicks time_after_run = TimeTicks::Now();
EXPECT_EQ(0, num_tasks);
EXPECT_LT(kDelay, time_after_run - time_before_run);
}
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InDelayOrder) {
SingleThreadTaskExecutor executor(GetParam());
int num_tasks = 2;
TimeTicks run_time1, run_time2;
base::RunLoop loop;
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks,
loop.QuitWhenIdleClosure()),
Milliseconds(200));
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks,
loop.QuitWhenIdleClosure()),
Milliseconds(10));
loop.Run();
EXPECT_EQ(0, num_tasks);
EXPECT_TRUE(run_time2 < run_time1);
}
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder) {
SingleThreadTaskExecutor executor(GetParam());
const TimeDelta kDelay = Milliseconds(100);
int num_tasks = 2;
TimeTicks run_time1, run_time2;
base::RunLoop loop;
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks,
loop.QuitWhenIdleClosure()),
kDelay);
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks,
loop.QuitWhenIdleClosure()),
kDelay);
loop.Run();
EXPECT_EQ(0, num_tasks);
EXPECT_TRUE(run_time1 < run_time2);
}
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder_2) {
SingleThreadTaskExecutor executor(GetParam());
const TimeDelta kPause = Milliseconds(50);
int num_tasks = 2;
TimeTicks run_time;
base::RunLoop loop;
executor.task_runner()->PostTask(
FROM_HERE,
BindOnce(&SlowFunc, kPause, &num_tasks, loop.QuitWhenIdleClosure()));
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks,
loop.QuitWhenIdleClosure()),
Milliseconds(10));
TimeTicks time_before_run = TimeTicks::Now();
loop.Run();
TimeTicks time_after_run = TimeTicks::Now();
EXPECT_EQ(0, num_tasks);
EXPECT_LT(kPause, time_after_run - time_before_run);
}
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder_3) {
SingleThreadTaskExecutor executor(GetParam());
int num_tasks = 11;
TimeTicks run_time1, run_time2;
base::RunLoop loop;
for (int i = 1; i < num_tasks; ++i) {
executor.task_runner()->PostTask(
FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks,
loop.QuitWhenIdleClosure()));
}
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks,
loop.QuitWhenIdleClosure()),
Milliseconds(1));
loop.Run();
EXPECT_EQ(0, num_tasks);
EXPECT_TRUE(run_time2 > run_time1);
}
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_SharedTimer) {
SingleThreadTaskExecutor executor(GetParam());
int num_tasks = 1;
TimeTicks run_time1, run_time2;
base::RunLoop loop;
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks,
loop.QuitWhenIdleClosure()),
Seconds(1000));
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks,
loop.QuitWhenIdleClosure()),
Milliseconds(10));
TimeTicks start_time = TimeTicks::Now();
loop.Run();
EXPECT_EQ(0, num_tasks);
TimeDelta total_time = TimeTicks::Now() - start_time;
EXPECT_GT(5000, total_time.InMilliseconds());
PlatformThread::Sleep(Milliseconds(100));
RunLoop().RunUntilIdle();
EXPECT_TRUE(run_time1.is_null());
EXPECT_FALSE(run_time2.is_null());
}
namespace {
class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> {
public:
RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted)
: post_on_delete_(post_on_delete), was_deleted_(was_deleted) {}
void Run() {}
private:
friend class RefCounted<RecordDeletionProbe>;
~RecordDeletionProbe() {
*was_deleted_ = true;
if (post_on_delete_.get()) {
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&RecordDeletionProbe::Run, post_on_delete_));
}
}
scoped_refptr<RecordDeletionProbe> post_on_delete_;
raw_ptr<bool> was_deleted_;
};
}
*/
TEST_P(SingleThreadTaskExecutorTypedTest, DISABLED_EnsureDeletion) {
bool a_was_deleted = false;
bool b_was_deleted = false;
{
SingleThreadTaskExecutor executor(GetParam());
executor.task_runner()->PostTask(
FROM_HERE, BindOnce(&RecordDeletionProbe::Run,
new RecordDeletionProbe(nullptr, &a_was_deleted)));
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordDeletionProbe::Run,
new RecordDeletionProbe(nullptr, &b_was_deleted)),
Milliseconds(1000));
}
EXPECT_TRUE(a_was_deleted);
EXPECT_TRUE(b_was_deleted);
}
*/
TEST_P(SingleThreadTaskExecutorTypedTest, DISABLED_EnsureDeletion_Chain) {
bool a_was_deleted = false;
bool b_was_deleted = false;
bool c_was_deleted = false;
{
SingleThreadTaskExecutor executor(GetParam());
RecordDeletionProbe* a = new RecordDeletionProbe(nullptr, &a_was_deleted);
RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted);
RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted);
executor.task_runner()->PostTask(FROM_HERE,
BindOnce(&RecordDeletionProbe::Run, c));
}
EXPECT_TRUE(a_was_deleted);
EXPECT_TRUE(b_was_deleted);
EXPECT_TRUE(c_was_deleted);
}
namespace {
void NestingFunc(int* depth, base::OnceClosure quit_closure) {
if (*depth > 0) {
*depth -= 1;
base::RunLoop loop1{base::RunLoop::Type::kNestableTasksAllowed};
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&NestingFunc, depth, loop1.QuitWhenIdleClosure()));
loop1.Run();
}
std::move(quit_closure).Run();
}
}
TEST_P(SingleThreadTaskExecutorTypedTest, Nesting) {
SingleThreadTaskExecutor executor(GetParam());
base::RunLoop loop;
int depth = 50;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&NestingFunc, &depth, loop.QuitWhenIdleClosure()));
loop.Run();
EXPECT_EQ(depth, 0);
}
TEST_P(SingleThreadTaskExecutorTypedTest, Recursive) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
base::RunLoop loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&QuitFunc, &order, 3, loop.QuitWhenIdleClosure()));
loop.Run();
ASSERT_EQ(14U, order.Size());
EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
}
namespace {
void OrderedFunc(TaskList* order, int cookie) {
order->RecordStart(ORDERED, cookie);
order->RecordEnd(ORDERED, cookie);
}
}
TEST_P(SingleThreadTaskExecutorTypedTest, NonNestableWithNoNesting) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
base::RunLoop loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostNonNestableTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 1));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&QuitFunc, &order, 3, loop.QuitWhenIdleClosure()));
loop.Run();
ASSERT_EQ(6U, order.Size());
EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true));
EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false));
EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true));
EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false));
EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
}
namespace {
void FuncThatPumps(TaskList* order, int cookie) {
order->RecordStart(PUMPS, cookie);
RunLoop(RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
order->RecordEnd(PUMPS, cookie);
}
void SleepFunc(TaskList* order, int cookie, TimeDelta delay) {
order->RecordStart(SLEEP, cookie);
PlatformThread::Sleep(delay);
order->RecordEnd(SLEEP, cookie);
}
}
TEST_P(SingleThreadTaskExecutorTypedTest, NonNestableDelayedInNestedLoop) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
base::RunLoop loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatPumps, &order, 1));
SingleThreadTaskRunner::GetCurrentDefault()->PostNonNestableTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 3));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&SleepFunc, &order, 4, Milliseconds(50)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 5));
SingleThreadTaskRunner::GetCurrentDefault()->PostNonNestableTask(
FROM_HERE, BindOnce(&QuitFunc, &order, 6, loop.QuitWhenIdleClosure()));
loop.Run();
ASSERT_EQ(12U, order.Size());
EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true));
EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true));
EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false));
EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true));
EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false));
EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true));
EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false));
EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false));
EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true));
EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false));
EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true));
EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false));
}
namespace {
void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) {
order->RecordStart(RUNS, cookie);
run_loop->Run();
order->RecordEnd(RUNS, cookie);
}
void FuncThatQuitsNow(base::OnceClosure quit_closure) {
std::move(quit_closure).Run();
}
}
TEST_P(SingleThreadTaskExecutorTypedTest, QuitNow) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
RunLoop outer_run_loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatQuitsNow, nested_run_loop.QuitClosure()));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 3));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatQuitsNow, outer_run_loop.QuitClosure()));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 4));
outer_run_loop.Run();
ASSERT_EQ(6U, order.Size());
int task_index = 0;
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
}
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitTop) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop outer_run_loop;
RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, outer_run_loop.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_run_loop.QuitClosure());
outer_run_loop.Run();
ASSERT_EQ(4U, order.Size());
int task_index = 0;
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
}
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitNested) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop outer_run_loop;
RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_run_loop.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, outer_run_loop.QuitClosure());
outer_run_loop.Run();
ASSERT_EQ(4U, order.Size());
int task_index = 0;
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
}
void QuitAndRunNestedLoop(TaskList* order,
int cookie,
RunLoop* outer_run_loop,
RunLoop* nested_run_loop) {
order->RecordStart(RUNS, cookie);
outer_run_loop->Quit();
nested_run_loop->Run();
order->RecordEnd(RUNS, cookie);
}
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopNestedAfterQuit) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop outer_run_loop;
RunLoop nested_run_loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_run_loop.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&QuitAndRunNestedLoop, &order, 1, &outer_run_loop,
&nested_run_loop));
outer_run_loop.Run();
ASSERT_EQ(2U, order.Size());
int task_index = 0;
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
}
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitBogus) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop outer_run_loop;
RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
RunLoop bogus_run_loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, bogus_run_loop.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, outer_run_loop.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_run_loop.QuitClosure());
outer_run_loop.Run();
ASSERT_EQ(4U, order.Size());
int task_index = 0;
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
}
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitDeep) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop outer_run_loop;
RunLoop nested_loop1(RunLoop::Type::kNestableTasksAllowed);
RunLoop nested_loop2(RunLoop::Type::kNestableTasksAllowed);
RunLoop nested_loop3(RunLoop::Type::kNestableTasksAllowed);
RunLoop nested_loop4(RunLoop::Type::kNestableTasksAllowed);
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_loop1)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatRuns, &order, 2, Unretained(&nested_loop2)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatRuns, &order, 3, Unretained(&nested_loop3)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatRuns, &order, 4, Unretained(&nested_loop4)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 5));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, outer_run_loop.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 6));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_loop1.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 7));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_loop2.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 8));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_loop3.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 9));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_loop4.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 10));
outer_run_loop.Run();
ASSERT_EQ(18U, order.Size());
int task_index = 0;
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
}
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderBefore) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop run_loop;
run_loop.Quit();
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 1));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatQuitsNow,
run_loop.QuitClosure()));
run_loop.Run();
ASSERT_EQ(0U, order.Size());
}
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderDuring) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop run_loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 1));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
run_loop.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatQuitsNow,
run_loop.QuitClosure()));
run_loop.Run();
ASSERT_EQ(2U, order.Size());
int task_index = 0;
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false));
EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
}
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderAfter) {
SingleThreadTaskExecutor executor(GetParam());
TaskList order;
RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
RunLoop outer_run_loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatQuitsNow, nested_run_loop.QuitClosure()));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 3));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, nested_run_loop.QuitClosure());
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&OrderedFunc, &order, 4));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&FuncThatQuitsNow, outer_run_loop.QuitClosure()));
outer_run_loop.Run();
ASSERT_EQ(8U, order.Size());
int task_index = 0;
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true));
EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false));
EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
}
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_RecursivePostsDoNotFloodPipe DISABLED_RecursivePostsDoNotFloodPipe
#else
#define MAYBE_RecursivePostsDoNotFloodPipe RecursivePostsDoNotFloodPipe
#endif
TEST_P(SingleThreadTaskExecutorTypedTest, MAYBE_RecursivePostsDoNotFloodPipe) {
SingleThreadTaskExecutor executor(GetParam());
const auto begin_ticks = TimeTicks::Now();
RunLoop run_loop;
Post128KTasksThenQuit(executor.task_runner().get(), begin_ticks, begin_ticks,
TimeDelta(), run_loop.QuitClosure());
run_loop.Run();
}
TEST_P(SingleThreadTaskExecutorTypedTest,
ApplicationTasksAllowedInNativeNestedLoopAtTopLevel) {
SingleThreadTaskExecutor executor(GetParam());
EXPECT_TRUE(
CurrentThread::Get()->ApplicationTasksAllowedInNativeNestedLoop());
}
TEST_P(SingleThreadTaskExecutorTypedTest, NestableTasksDisallowedByDefault) {
SingleThreadTaskExecutor executor(GetParam());
RunLoop run_loop;
executor.task_runner()->PostTask(
FROM_HERE,
BindOnce(
[](RunLoop* run_loop) {
EXPECT_FALSE(CurrentThread::Get()
->ApplicationTasksAllowedInNativeNestedLoop());
run_loop->Quit();
},
Unretained(&run_loop)));
run_loop.Run();
}
TEST_P(SingleThreadTaskExecutorTypedTest,
NestableTasksProcessedWhenRunLoopAllows) {
SingleThreadTaskExecutor executor(GetParam());
RunLoop run_loop;
executor.task_runner()->PostTask(
FROM_HERE,
BindOnce(
[](RunLoop* run_loop) {
RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
BindOnce(
[](RunLoop* nested_run_loop) {
EXPECT_FALSE(
CurrentThread::Get()
->ApplicationTasksAllowedInNativeNestedLoop());
nested_run_loop->Quit();
},
Unretained(&nested_run_loop)));
nested_run_loop.Run();
run_loop->Quit();
},
Unretained(&run_loop)));
run_loop.Run();
}
TEST_P(SingleThreadTaskExecutorTypedTest, IsIdleForTesting) {
SingleThreadTaskExecutor executor(GetParam());
EXPECT_TRUE(CurrentThread::Get()->IsIdleForTesting());
executor.task_runner()->PostTask(FROM_HERE, BindOnce([] {}));
executor.task_runner()->PostDelayedTask(FROM_HERE, BindOnce([] {}),
Milliseconds(10));
EXPECT_FALSE(CurrentThread::Get()->IsIdleForTesting());
RunLoop().RunUntilIdle();
EXPECT_TRUE(CurrentThread::Get()->IsIdleForTesting());
PlatformThread::Sleep(Milliseconds(20));
EXPECT_TRUE(CurrentThread::Get()->IsIdleForTesting());
}
TEST_P(SingleThreadTaskExecutorTypedTest, IsIdleForTestingNonNestableTask) {
SingleThreadTaskExecutor executor(GetParam());
RunLoop run_loop;
EXPECT_TRUE(CurrentThread::Get()->IsIdleForTesting());
bool nested_task_run = false;
executor.task_runner()->PostTask(
FROM_HERE, BindLambdaForTesting([&] {
RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
executor.task_runner()->PostNonNestableTask(
FROM_HERE, BindLambdaForTesting([&] { nested_task_run = true; }));
executor.task_runner()->PostTask(
FROM_HERE, BindLambdaForTesting([&] {
EXPECT_FALSE(nested_task_run);
EXPECT_TRUE(CurrentThread::Get()->IsIdleForTesting());
}));
nested_run_loop.RunUntilIdle();
EXPECT_FALSE(nested_task_run);
EXPECT_FALSE(CurrentThread::Get()->IsIdleForTesting());
}));
run_loop.RunUntilIdle();
EXPECT_TRUE(nested_task_run);
EXPECT_TRUE(CurrentThread::Get()->IsIdleForTesting());
}
INSTANTIATE_TEST_SUITE_P(All,
SingleThreadTaskExecutorTypedTest,
::testing::Values(MessagePumpType::DEFAULT,
MessagePumpType::UI,
MessagePumpType::IO),
SingleThreadTaskExecutorTypedTest::ParamInfoToString);
#if BUILDFLAG(IS_WIN)
TEST(SingleThreadTaskExecutorTest, WmQuitIsIgnored) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
::PostQuitMessage(0);
bool task_was_run = false;
RunLoop run_loop;
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(
[](bool* flag, OnceClosure closure) {
*flag = true;
std::move(closure).Run();
},
&task_was_run, run_loop.QuitClosure()),
TestTimeouts::tiny_timeout());
run_loop.Run();
EXPECT_TRUE(task_was_run);
}
TEST(SingleThreadTaskExecutorTest, PostDelayedTask_SharedTimer_SubPump) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
int num_tasks = 1;
TimeTicks run_time;
RunLoop run_loop;
executor.task_runner()->PostTask(
FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
executor.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks,
run_loop.QuitWhenIdleClosure()),
Seconds(1000));
executor.task_runner()->PostDelayedTask(
FROM_HERE, BindOnce(&::PostQuitMessage, 0), Milliseconds(10));
Time start_time = Time::Now();
run_loop.Run();
EXPECT_EQ(1, num_tasks);
TimeDelta total_time = Time::Now() - start_time;
EXPECT_GT(5000, total_time.InMilliseconds());
PlatformThread::Sleep(Milliseconds(100));
RunLoop().RunUntilIdle();
EXPECT_TRUE(run_time.is_null());
}
namespace {
bool QuitOnSystemTimer(UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result) {
if (message == static_cast<UINT>(WM_TIMER)) {
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&::PostQuitMessage, 0));
}
*result = 0;
return true;
}
bool DelayedQuitOnSystemTimer(UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result) {
if (message == static_cast<UINT>(WM_TIMER)) {
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, BindOnce(&::PostQuitMessage, 0), Milliseconds(10));
}
*result = 0;
return true;
}
}
TEST(SingleThreadTaskExecutorTest, PostImmediateTaskFromSystemPump) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
RunLoop run_loop;
win::MessageWindow local_message_window;
local_message_window.Create(BindRepeating(&QuitOnSystemTimer));
ASSERT_TRUE(::SetTimer(local_message_window.hwnd(), 0, 20, nullptr));
executor.task_runner()->PostTask(
FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
run_loop.Run();
}
TEST(SingleThreadTaskExecutorTest, PostDelayedTaskFromSystemPump) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
RunLoop run_loop;
win::MessageWindow local_message_window;
local_message_window.Create(BindRepeating(&DelayedQuitOnSystemTimer));
ASSERT_TRUE(::SetTimer(local_message_window.hwnd(), 0, 20, nullptr));
executor.task_runner()->PostTask(
FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
run_loop.Run();
}
TEST(SingleThreadTaskExecutorTest, WmQuitIsVisibleToSubPump) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
RunLoop run_loop;
executor.task_runner()->PostTask(
FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0));
executor.task_runner()->PostTask(FROM_HERE, DoNothing());
run_loop.Run();
}
TEST(SingleThreadTaskExecutorTest,
RepostingWmQuitDoesntStarveUpcomingNativeLoop) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
RunLoop run_loop;
executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0));
executor.task_runner()->PostTask(FROM_HERE, DoNothing());
executor.task_runner()->PostTask(FROM_HERE, DoNothing());
executor.task_runner()->PostTask(
FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0));
run_loop.Run();
}
TEST(SingleThreadTaskExecutorTest,
DISABLED_UnwindingMultipleSubPumpsDoesntStarveApplicationTasks) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
RunLoop run_loop;
executor.task_runner()->PostTask(
FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
executor.task_runner()->PostTask(FROM_HERE,
BindOnce(&SubPumpFunc, DoNothing()));
executor.task_runner()->PostTask(FROM_HERE,
BindOnce(&SubPumpFunc, DoNothing()));
executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0));
executor.task_runner()->PostTask(FROM_HERE, DoNothing());
executor.task_runner()->PostTask(FROM_HERE, DoNothing());
executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0));
executor.task_runner()->PostTask(FROM_HERE, DoNothing());
executor.task_runner()->PostTask(FROM_HERE, DoNothing());
bool last_task_ran = false;
executor.task_runner()->PostTask(
FROM_HERE, BindOnce([](bool* to_set) { *to_set = true; },
Unretained(&last_task_ran)));
executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0));
run_loop.Run();
EXPECT_TRUE(last_task_ran);
}
namespace {
void RunTest_NestingDenial2(MessagePumpType message_pump_type) {
SingleThreadTaskExecutor executor(message_pump_type);
base::RunLoop loop;
Thread worker("NestingDenial2_worker");
Thread::Options options;
options.message_pump_type = message_pump_type;
ASSERT_EQ(true, worker.StartWithOptions(std::move(options)));
TaskList order;
win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
worker.task_runner()->PostTask(
FROM_HERE,
BindOnce(&RecursiveFuncWin, SingleThreadTaskRunner::GetCurrentDefault(),
event.get(), true, &order, false, loop.QuitWhenIdleClosure()));
WaitForSingleObject(event.get(), INFINITE);
loop.Run();
ASSERT_EQ(17u, order.Size());
EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true));
EXPECT_EQ(order.Get(3), TaskItem(MESSAGEBOX, 2, false));
EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, true));
EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 3, false));
EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, true));
EXPECT_EQ(order.Get(7), TaskItem(QUITMESSAGELOOP, 5, true));
EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, false));
EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, false));
EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 3, true));
EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, false));
EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, false));
EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 3, true));
EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, false));
}
}
TEST(SingleThreadTaskExecutorTest, DISABLED_NestingDenial2) {
RunTest_NestingDenial2(MessagePumpType::DEFAULT);
RunTest_NestingDenial2(MessagePumpType::UI);
RunTest_NestingDenial2(MessagePumpType::IO);
}
TEST(SingleThreadTaskExecutorTest, NestingSupport2) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
base::RunLoop loop;
Thread worker("NestingSupport2_worker");
Thread::Options options;
options.message_pump_type = MessagePumpType::UI;
ASSERT_EQ(true, worker.StartWithOptions(std::move(options)));
TaskList order;
win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
worker.task_runner()->PostTask(
FROM_HERE,
BindOnce(&RecursiveFuncWin, SingleThreadTaskRunner::GetCurrentDefault(),
event.get(), false, &order, true, loop.QuitWhenIdleClosure()));
WaitForSingleObject(event.get(), INFINITE);
loop.Run();
ASSERT_EQ(18u, order.Size());
EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true));
EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 3, true));
EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, false));
EXPECT_EQ(order.Get(5), TaskItem(ENDDIALOG, 4, true));
EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, false));
EXPECT_EQ(order.Get(7), TaskItem(MESSAGEBOX, 2, false));
is called in the main thread, if it is faster than getting to the
PostTask(FROM_HERE, BindOnce(&QuitFunc) execution, the order of task
execution can change. We don't care anyway that the order isn't correct.
EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, true));
EXPECT_EQ(order.Get(9), TaskItem(QUITMESSAGELOOP, 5, false));
EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
*/
EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, true));
EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 3, false));
EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, true));
EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 1, false));
EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, true));
EXPECT_EQ(order.Get(17), TaskItem(RECURSIVE, 3, false));
}
#endif
#if BUILDFLAG(IS_WIN)
TEST(SingleThreadTaskExecutorTest, IOHandler) {
RunTest_IOHandler();
}
#endif
namespace {
class DestructionObserverProbe : public RefCounted<DestructionObserverProbe> {
public:
DestructionObserverProbe(bool* task_destroyed,
bool* destruction_observer_called)
: task_destroyed_(task_destroyed),
destruction_observer_called_(destruction_observer_called) {}
virtual void Run() {
ADD_FAILURE();
}
private:
friend class RefCounted<DestructionObserverProbe>;
virtual ~DestructionObserverProbe() {
EXPECT_FALSE(*destruction_observer_called_);
*task_destroyed_ = true;
}
raw_ptr<bool> task_destroyed_;
raw_ptr<bool> destruction_observer_called_;
};
class MLDestructionObserver : public CurrentThread::DestructionObserver {
public:
MLDestructionObserver(bool* task_destroyed, bool* destruction_observer_called)
: task_destroyed_(task_destroyed),
destruction_observer_called_(destruction_observer_called) {}
void WillDestroyCurrentMessageLoop() override {
task_destroyed_before_message_loop_ = *task_destroyed_;
*destruction_observer_called_ = true;
}
bool task_destroyed_before_message_loop() const {
return task_destroyed_before_message_loop_;
}
private:
raw_ptr<bool> task_destroyed_;
raw_ptr<bool> destruction_observer_called_;
bool task_destroyed_before_message_loop_ = false;
};
}
TEST(SingleThreadTaskExecutorTest, DestructionObserverTest) {
auto executor = std::make_unique<SingleThreadTaskExecutor>();
const TimeDelta kDelay = Milliseconds(100);
bool task_destroyed = false;
bool destruction_observer_called = false;
MLDestructionObserver observer(&task_destroyed, &destruction_observer_called);
CurrentThread::Get()->AddDestructionObserver(&observer);
executor->task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(&DestructionObserverProbe::Run,
base::MakeRefCounted<DestructionObserverProbe>(
&task_destroyed, &destruction_observer_called)),
kDelay);
executor.reset();
EXPECT_TRUE(observer.task_destroyed_before_message_loop());
EXPECT_TRUE(task_destroyed);
EXPECT_TRUE(destruction_observer_called);
}
TEST(SingleThreadTaskExecutorTest, ThreadMainTaskRunner) {
SingleThreadTaskExecutor executor;
base::RunLoop loop;
scoped_refptr<Foo> foo(new Foo());
std::string a("a");
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindOnce(loop.QuitWhenIdleClosure()));
loop.Run();
EXPECT_EQ(foo->test_count(), 1);
EXPECT_EQ(foo->result(), "a");
}
TEST(SingleThreadTaskExecutorTest, type) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
EXPECT_EQ(executor.type(), MessagePumpType::UI);
}
#if BUILDFLAG(IS_WIN)
void EmptyFunction() {}
void PostMultipleTasks() {
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&EmptyFunction));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&EmptyFunction));
}
static const int kSignalMsg = WM_USER + 2;
static base::RunLoop* g_loop_to_quit_from_message_handler = nullptr;
void PostWindowsMessage(HWND message_hwnd) {
PostMessage(message_hwnd, kSignalMsg, 0, 2);
}
void EndTest(bool* did_run, HWND hwnd) {
*did_run = true;
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
constexpr int kMyMessageFilterCode = 0x5002;
LRESULT CALLBACK TestWndProcThunk(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam) {
if (message == WM_CLOSE) {
EXPECT_TRUE(DestroyWindow(hwnd));
}
if (message != kSignalMsg) {
return DefWindowProc(hwnd, message, wparam, lparam);
}
switch (lparam) {
case 1:
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&PostMultipleTasks));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&PostWindowsMessage, hwnd));
break;
case 2:
CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop
allow_nestable_tasks;
bool did_run = false;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&EndTest, &did_run, hwnd));
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
if (!CallMsgFilter(&msg, kMyMessageFilterCode)) {
DispatchMessage(&msg);
}
if (msg.message == WM_CLOSE) {
break;
}
}
EXPECT_TRUE(did_run);
g_loop_to_quit_from_message_handler->QuitWhenIdle();
break;
}
return 0;
}
TEST(SingleThreadTaskExecutorTest, AlwaysHaveUserMessageWhenNesting) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
RunLoop loop;
HINSTANCE instance = CURRENT_MODULE();
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = TestWndProcThunk;
wc.hInstance = instance;
wc.lpszClassName = L"SingleThreadTaskExecutorTest_HWND";
ATOM atom = RegisterClassEx(&wc);
ASSERT_TRUE(atom);
g_loop_to_quit_from_message_handler = &loop;
HWND message_hwnd = CreateWindow(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0,
HWND_MESSAGE, 0, instance, 0);
ASSERT_TRUE(message_hwnd) << GetLastError();
ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1));
loop.Run();
ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance));
g_loop_to_quit_from_message_handler = nullptr;
}
#endif
TEST(SingleThreadTaskExecutorTest,
ApplicationTasksAllowedInNativeNestedLoopExplicitlyInScope) {
SingleThreadTaskExecutor executor(MessagePumpType::UI);
RunLoop run_loop;
executor.task_runner()->PostTask(
FROM_HERE,
BindOnce(
[](RunLoop* run_loop) {
{
CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop
allow_nestable_tasks;
EXPECT_TRUE(CurrentThread::Get()
->ApplicationTasksAllowedInNativeNestedLoop());
}
EXPECT_FALSE(CurrentThread::Get()
->ApplicationTasksAllowedInNativeNestedLoop());
run_loop->Quit();
},
Unretained(&run_loop)));
run_loop.Run();
}
TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageSetGet) {
SingleThreadTaskExecutor executor;
SequenceLocalStorageSlot<int> slot;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindLambdaForTesting([&] { slot.emplace(11); }));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindLambdaForTesting([&] { EXPECT_EQ(*slot, 11); }));
RunLoop().RunUntilIdle();
EXPECT_EQ(*slot, 11);
}
TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageDifferentMessageLoops) {
SequenceLocalStorageSlot<int> slot;
{
SingleThreadTaskExecutor executor;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindLambdaForTesting([&] { slot.emplace(11); }));
RunLoop().RunUntilIdle();
EXPECT_EQ(*slot, 11);
}
SingleThreadTaskExecutor executor;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, BindLambdaForTesting([&] { EXPECT_FALSE(slot); }));
RunLoop().RunUntilIdle();
EXPECT_NE(slot.GetOrCreateValue(), 11);
}
namespace {
class PostTaskOnDestroy {
public:
explicit PostTaskOnDestroy(int times) : times_remaining_(times) {}
PostTaskOnDestroy(const PostTaskOnDestroy&) = delete;
PostTaskOnDestroy& operator=(const PostTaskOnDestroy&) = delete;
~PostTaskOnDestroy() { PostTaskWithPostingDestructor(times_remaining_); }
static void PostTaskWithPostingDestructor(int times) {
if (times > 0) {
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, DoNothingWithBoundArgs(
std::make_unique<PostTaskOnDestroy>(times - 1)));
}
}
private:
const int times_remaining_;
};
}
TEST(SingleThreadTaskExecutorDestructionTest,
DestroysFineWithPostTaskOnDestroy) {
SingleThreadTaskExecutor executor;
PostTaskOnDestroy::PostTaskWithPostingDestructor(10);
}
}