// Copyright 2020 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/task/thread_pool.h" #include "base/functional/bind.h" #include "base/location.h" #include "base/run_loop.h" #include "base/task/bind_post_task.h" #include "base/task/single_thread_task_executor.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/test/bind.h" #include "base/test/task_environment.h" #include "base/test/test_waitable_event.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { TEST(ThreadPool, PostTaskAndReplyWithResultThreeArgs) { base::test::TaskEnvironment env; base::RunLoop run_loop; base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce([]() { return 3; }), base::OnceCallback( base::BindLambdaForTesting([&run_loop](int x) { EXPECT_EQ(x, 3); run_loop.Quit(); }))); run_loop.Run(); } TEST(ThreadPool, PostTaskAndReplyWithResultFourArgs) { base::test::TaskEnvironment env; base::RunLoop run_loop; base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, /*traits=*/{}, base::BindOnce([]() { return 3; }), base::OnceCallback( base::BindLambdaForTesting([&run_loop](int x) { EXPECT_EQ(x, 3); run_loop.Quit(); }))); run_loop.Run(); } TEST(ThreadPool, BindPostTaskFizzlesOnShutdown) { // Tests that a callback bound to a BLOCK_SHUTDOWN sequence doesn't trigger a // DCHECK when it's deleted without running. base::ThreadPoolInstance::CreateAndStartWithDefaultParams( "BindPostTaskFizzlesOnShutdownTest"); { // Creating this callback and deleting it after the thread pool is shutdown // used to trigger a DCHECK in task_tracker because the // BindPostTaskTrampoline destructor has to delete its state on the sequence // it's bound to. There is a DCHECK that ensures BLOCK_SHUTDOWN tasks aren't // posted after shutdown, but BindPostTaskTrampoline uses // base::ThreadPoolInstance::ScopedFizzleBlockShutdownTasks to avoid // triggering it. auto bound_callback = base::BindPostTask(base::ThreadPool::CreateSequencedTaskRunner( {TaskShutdownBehavior::BLOCK_SHUTDOWN}), base::BindOnce([]() { ADD_FAILURE(); })); base::ThreadPoolInstance::Get()->Shutdown(); } ThreadPoolInstance::Get()->JoinForTesting(); ThreadPoolInstance::Set(nullptr); } TEST(ThreadPool, PostTaskAndReplyFizzlesOnShutdown) { // Tests that a PostTaskAndReply from a BLOCK_SHUTDOWN sequence doesn't // trigger a DCHECK when it's not run at shutdown. base::ThreadPoolInstance::CreateAndStartWithDefaultParams( "PostTaskAndReplyFizzlesOnShutdown"); { base::SingleThreadTaskExecutor executor; auto blocking_task_runner = base::ThreadPool::CreateSequencedTaskRunner( {TaskShutdownBehavior::BLOCK_SHUTDOWN}); base::RunLoop run_loop; // The setup that this test is exercising is as follows: // - A task is posted using PostTaskAndReply from a BLOCK_SHUTDOWN sequence // to the main thread // - The task is not run on the main thread // - Shutdown happens, the ThreadPool is shutdown // - The main thread destroys its un-run tasks // - ~PostTaskAndReplyRelay calls `DeleteSoon` to delete its reply's state // on the sequence it's bound to // - TaskTracker ensures that no BLOCK_SHUTDOWN tasks can be posted after // shutdown. ~PostTaskAndReplyRelay avoids triggering this DCHECK by using a // base::ThreadPoolInstance::ScopedFizzleBlockShutdownTasks // Post a task to the BLOCK_SHUTDOWN thread pool sequence to setup the test. base::TestWaitableEvent event; blocking_task_runner->PostTask( FROM_HERE, base::BindLambdaForTesting([&]() { // Enqueue a task whose only purpose is to exit the run loop, ensuring // the following task is never run. executor.task_runner()->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() { event.Wait(); run_loop.Quit(); })); // Post the task for which the reply will trigger the `DeleteSoon` // from `~PostTaskAndReplyRelay`. executor.task_runner()->PostTaskAndReply( FROM_HERE, base::BindOnce([]() { ADD_FAILURE(); }), base::BindOnce([]() { ADD_FAILURE(); })); event.Signal(); })); // Run until the first task posted to the SingleThreadTaskExecutor quits the // run loop, resulting in the `PostTaskAndReply` being queued but not run. run_loop.Run(); base::ThreadPoolInstance::Get()->Shutdown(); } ThreadPoolInstance::Get()->JoinForTesting(); ThreadPoolInstance::Set(nullptr); } } // namespace base