#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
IsNull;
NotNull;
namespace base {
namespace {
class Foo : public RefCounted<Foo> { … };
static void SlowFunc(TimeDelta pause,
int* quit_counter,
base::OnceClosure quit_closure) { … }
static void RecordRunTimeFunc(TimeTicks* run_time,
int* quit_counter,
base::OnceClosure quit_closure) { … }
enum TaskType { … };
struct TaskItem { … };
std::ostream& operator<<(std::ostream& os, TaskType type) { … }
std::ostream& operator<<(std::ostream& os, const TaskItem& item) { … }
class TaskList { … };
class DummyTaskObserver : public TaskObserver { … };
void RecursiveFunc(TaskList* order, int cookie, int depth) { … }
void QuitFunc(TaskList* order, int cookie, base::OnceClosure quit_closure) { … }
#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) { … }
#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_.overlapped; }
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) {
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() {
CurrentIOThread::Get()->RegisterIOHandler(file_.get(), this);
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> { … };
TEST_P(SingleThreadTaskExecutorTypedTest, PostTask) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_Basic) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InDelayOrder) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder_2) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder_3) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_SharedTimer) { … }
namespace {
class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> { … };
}
TEST_P(SingleThreadTaskExecutorTypedTest, DISABLED_EnsureDeletion) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, DISABLED_EnsureDeletion_Chain) { … }
namespace {
void NestingFunc(int* depth, base::OnceClosure quit_closure) { … }
}
TEST_P(SingleThreadTaskExecutorTypedTest, Nesting) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, Recursive) { … }
namespace {
void OrderedFunc(TaskList* order, int cookie) { … }
}
TEST_P(SingleThreadTaskExecutorTypedTest, NonNestableWithNoNesting) { … }
namespace {
void FuncThatPumps(TaskList* order, int cookie) { … }
void SleepFunc(TaskList* order, int cookie, TimeDelta delay) { … }
}
TEST_P(SingleThreadTaskExecutorTypedTest, NonNestableDelayedInNestedLoop) { … }
namespace {
void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) { … }
void FuncThatQuitsNow(base::OnceClosure quit_closure) { … }
}
TEST_P(SingleThreadTaskExecutorTypedTest, QuitNow) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitTop) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitNested) { … }
void QuitAndRunNestedLoop(TaskList* order,
int cookie,
RunLoop* outer_run_loop,
RunLoop* nested_run_loop) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopNestedAfterQuit) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitBogus) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitDeep) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderBefore) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderDuring) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderAfter) { … }
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_RecursivePostsDoNotFloodPipe …
#else
#define MAYBE_RecursivePostsDoNotFloodPipe …
#endif
TEST_P(SingleThreadTaskExecutorTypedTest, MAYBE_RecursivePostsDoNotFloodPipe) { … }
TEST_P(SingleThreadTaskExecutorTypedTest,
ApplicationTasksAllowedInNativeNestedLoopAtTopLevel) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, NestableTasksDisallowedByDefault) { … }
TEST_P(SingleThreadTaskExecutorTypedTest,
NestableTasksProcessedWhenRunLoopAllows) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, IsIdleForTesting) { … }
TEST_P(SingleThreadTaskExecutorTypedTest, IsIdleForTestingNonNestableTask) { … }
INSTANTIATE_TEST_SUITE_P(…);
#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));
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> { … };
class MLDestructionObserver : public CurrentThread::DestructionObserver { … };
}
TEST(SingleThreadTaskExecutorTest, DestructionObserverTest) { … }
TEST(SingleThreadTaskExecutorTest, ThreadMainTaskRunner) { … }
TEST(SingleThreadTaskExecutorTest, type) { … }
#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);
}
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) { … }
TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageSetGet) { … }
TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageDifferentMessageLoops) { … }
namespace {
class PostTaskOnDestroy { … };
}
TEST(SingleThreadTaskExecutorDestructionTest,
DestroysFineWithPostTaskOnDestroy) { … }
}