chromium/base/task/thread_pool/thread_pool_impl_unittest.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include "base/task/thread_pool/thread_pool_impl.h"

#include <stddef.h>

#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "base/base_switches.h"
#include "base/cfi_buildflags.h"
#include "base/containers/span.h"
#include "base/debug/stack_trace.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/system/sys_info.h"
#include "base/task/task_features.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool/environment_config.h"
#include "base/task/thread_pool/test_task_factory.h"
#include "base/task/thread_pool/test_utils.h"
#include "base/task/thread_pool/worker_thread_observer.h"
#include "base/task/updateable_sequenced_task_runner.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/test/test_waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_POSIX)
#include <unistd.h>

#include "base/debug/leak_annotations.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/files/file_util.h"
#include "base/posix/eintr_wrapper.h"
#endif  // BUILDFLAG(IS_POSIX)

#if BUILDFLAG(IS_WIN)
#include "base/win/com_init_util.h"
#endif  // BUILDFLAG(IS_WIN)

namespace base {
namespace internal {

namespace {

constexpr size_t kMaxNumForegroundThreads =;
constexpr size_t kMaxNumUtilityThreads =;

struct TraitsExecutionModePair {};

// Returns true if a task with |traits| could run at background thread priority
// on this platform. Even if this returns true, it is possible that the task
// won't run at background thread priority if a native thread group is used.
bool TraitsSupportBackgroundThreadType(const TaskTraits& traits) {}

// Returns true if a task with |traits| could run at utility thread
// type on this platform. Even if this returns true, it is possible that the
// task won't run at efficient thread priority if a native thread group is used
// or the utility thread group is disabled.
bool TraitsSupportUtilityThreadType(const TaskTraits& traits) {}

// Verify that the current thread type and I/O restrictions are appropriate to
// run a Task with |traits|.
// Note: ExecutionMode is verified inside TestTaskFactory.
void VerifyTaskEnvironment(const TaskTraits& traits,
                           bool use_resource_efficient_group) {}

void VerifyTaskEnvironmentAndSignalEvent(const TaskTraits& traits,
                                         bool use_resource_efficient_group,
                                         TestWaitableEvent* event) {}

void VerifyTimeAndTaskEnvironmentAndSignalEvent(
    const TaskTraits& traits,
    bool use_resource_efficient_group,
    TimeTicks expected_time,
    TestWaitableEvent* event) {}

void VerifyOrderAndTaskEnvironmentAndSignalEvent(
    const TaskTraits& traits,
    bool use_resource_efficient_group,
    TestWaitableEvent* expected_previous_event,
    TestWaitableEvent* event) {}

scoped_refptr<TaskRunner> CreateTaskRunnerAndExecutionMode(
    ThreadPoolImpl* thread_pool,
    const TaskTraits& traits,
    TaskSourceExecutionMode execution_mode,
    SingleThreadTaskRunnerThreadMode default_single_thread_task_runner_mode =
        SingleThreadTaskRunnerThreadMode::SHARED) {}

class ThreadPostingTasks : public SimpleThread {};

// Returns a vector with a TraitsExecutionModePair for each valid combination of
// {ExecutionMode, TaskPriority, ThreadPolicy, MayBlock()}.
std::vector<TraitsExecutionModePair> GetTraitsExecutionModePairs() {}

// Returns a vector with enough TraitsExecutionModePairs to cover all valid
// combinations of task destination (background/foreground ThreadGroup,
// single-thread) and whether the task is affected by a BEST_EFFORT fence.
std::vector<TraitsExecutionModePair>
GetTraitsExecutionModePairsToCoverAllSchedulingOptions() {}

class ThreadPoolImplTestBase : public testing::Test {};

class ThreadPoolImplTest : public ThreadPoolImplTestBase,
                           public testing::WithParamInterface<
                               bool /* use_resource_efficient_thread_group */> {};

// Tests run for enough traits and execution mode combinations to cover all
// valid combinations of task destination (background/foreground ThreadGroup,
// single-thread) and whether the task is affected by a BEST_EFFORT fence.
class ThreadPoolImplTest_CoverAllSchedulingOptions
    : public ThreadPoolImplTestBase,
      public testing::WithParamInterface<
          std::tuple<bool /* use_resource_efficient_thread_group */,
                     TraitsExecutionModePair>> {};

}  // namespace

// Verifies that a Task posted via PostDelayedTask with parameterized TaskTraits
// and no delay runs on a thread with the expected priority and I/O
// restrictions. The ExecutionMode parameter is ignored by this test.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostDelayedTaskNoDelay) {}

// Verifies that a Task posted via PostDelayedTask with parameterized
// TaskTraits and a non-zero delay runs on a thread with the expected priority
// and I/O restrictions after the delay expires. The ExecutionMode parameter is
// ignored by this test.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostDelayedTaskWithDelay) {}

namespace {

scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunnerAndExecutionMode(
    ThreadPoolImpl* thread_pool,
    const TaskTraits& traits,
    TaskSourceExecutionMode execution_mode,
    SingleThreadTaskRunnerThreadMode default_single_thread_task_runner_mode =
        SingleThreadTaskRunnerThreadMode::SHARED) {}

}  // namespace

TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
       PostDelayedTaskAtViaTaskRunner) {}

// Verifies that Tasks posted via a TaskRunner with parameterized TaskTraits and
// ExecutionMode run on a thread with the expected priority and I/O restrictions
// and respect the characteristics of their ExecutionMode.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostTasksViaTaskRunner) {}

// Verifies that a task posted via PostDelayedTask without a delay doesn't run
// before Start() is called.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
       PostDelayedTaskNoDelayBeforeStart) {}

// Verifies that a task posted via PostDelayedTask with a delay doesn't run
// before Start() is called.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
       PostDelayedTaskWithDelayBeforeStart) {}

// Verifies that a task posted via a TaskRunner doesn't run before Start() is
// called.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
       PostTaskViaTaskRunnerBeforeStart) {}

// Verify that posting tasks after the thread pool was destroyed fails but
// doesn't crash.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostTaskAfterDestroy) {}

// Verifies that FlushAsyncForTesting() calls back correctly for all trait and
// execution mode pairs.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
       FlushAsyncForTestingSimple) {}

// Verifies that BEST_EFFORT tasks don't run when the
// --disable-best-effort-tasks command-line switch is specified.
//
// Not using the same fixture as other tests because we want to append a command
// line switch before creating the pool.
TEST(ThreadPoolImplTest_Switch, DisableBestEffortTasksSwitch) {}

// Verifies that tasks only run when allowed by fences.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, Fence) {}

// Verifies that multiple fences can exist at the same time.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, MultipleFences) {}

// Verifies that a call to BeginFence() before Start() is honored.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, FenceBeforeStart) {}

// Verifies that tasks only run when allowed by BEST_EFFORT fences.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, BestEffortFence) {}

// Verifies that multiple BEST_EFFORT fences can exist at the same time.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, MultipleBestEffortFences) {}

// Verifies that a call to BeginBestEffortFence() before Start() is honored.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
       BestEffortFenceBeforeStart) {}

// Spawns threads that simultaneously post Tasks to TaskRunners with various
// TaskTraits and ExecutionModes. Verifies that each Task runs on a thread with
// the expected priority and I/O restrictions and respects the characteristics
// of its ExecutionMode.
TEST_P(ThreadPoolImplTest, MultipleTraitsExecutionModePair) {}

TEST_P(ThreadPoolImplTest,
       GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated) {}

// Verify that the RunsTasksInCurrentSequence() method of a SequencedTaskRunner
// returns false when called from a task that isn't part of the sequence.
TEST_P(ThreadPoolImplTest, SequencedRunsTasksInCurrentSequence) {}

// Verify that the RunsTasksInCurrentSequence() method of a
// SingleThreadTaskRunner returns false when called from a task that isn't part
// of the sequence.
TEST_P(ThreadPoolImplTest, SingleThreadRunsTasksInCurrentSequence) {}

#if BUILDFLAG(IS_WIN)
TEST_P(ThreadPoolImplTest, COMSTATaskRunnersRunWithCOMSTA) {
  StartThreadPool();
  auto com_sta_task_runner = thread_pool_->CreateCOMSTATaskRunner(
      {}, SingleThreadTaskRunnerThreadMode::SHARED);

  TestWaitableEvent task_ran;
  com_sta_task_runner->PostTask(
      FROM_HERE, BindOnce(
                     [](TestWaitableEvent* task_ran) {
                       win::AssertComApartmentType(win::ComApartmentType::STA);
                       task_ran->Signal();
                     },
                     Unretained(&task_ran)));
  task_ran.Wait();
}
#endif  // BUILDFLAG(IS_WIN)

TEST_P(ThreadPoolImplTest, DelayedTasksNotRunAfterShutdown) {}

#if BUILDFLAG(IS_POSIX)

TEST_P(ThreadPoolImplTest, FileDescriptorWatcherNoOpsAfterShutdown) {}
#endif  // BUILDFLAG(IS_POSIX)

#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)

// Verify that FileDescriptorWatcher::WatchReadable() can be called from task
// running on a task_runner with GetExecutionMode() without a crash.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, FileDescriptorWatcher) {}

#endif

// Verify that tasks posted on the same sequence access the same values on
// SequenceLocalStorage, and tasks on different sequences see different values.
TEST_P(ThreadPoolImplTest, SequenceLocalStorage) {}

TEST_P(ThreadPoolImplTest, FlushAsyncNoTasks) {}

namespace {

// Verifies that all strings passed as argument are found on the current stack.
// Ignores failures if this configuration doesn't have symbols.
void VerifyHasStringsOnStack(const std::string& pool_str,
                             const std::string& shutdown_behavior_str) {}

}  // namespace

#if BUILDFLAG(IS_POSIX)
// Many POSIX bots flakily crash on |debug::StackTrace().ToString()|,
// https://crbug.com/840429.
#define MAYBE_IdentifiableStacks
#elif BUILDFLAG(IS_WIN) && \
    (defined(ADDRESS_SANITIZER) || BUILDFLAG(CFI_CAST_CHECK))
// Hangs on WinASan and WinCFI (grabbing StackTrace() too slow?),
// https://crbug.com/845010#c7.
#define MAYBE_IdentifiableStacks
#else
#define MAYBE_IdentifiableStacks
#endif

// Integration test that verifies that workers have a frame on their stacks
// which easily identifies the type of worker and shutdown behavior (useful to
// diagnose issues from logs without memory dumps).
TEST_P(ThreadPoolImplTest, MAYBE_IdentifiableStacks) {}

TEST_P(ThreadPoolImplTest, WorkerThreadObserver) {}

// Verify a basic EnqueueJobTaskSource() runs the worker task.
TEST_P(ThreadPoolImplTest, ScheduleJobTaskSource) {}

// Verify that calling ShouldYield() returns true for a job task source that
// needs to change thread group because of a priority update.
TEST_P(ThreadPoolImplTest, ThreadGroupChangeShouldYield) {}

namespace {

class MustBeDestroyed {};

}  // namespace

// Regression test for https://crbug.com/945087.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
       NoLeakWhenPostingNestedTask) {}

namespace {

struct TaskRunnerAndEvents {};

// Create a series of sample task runners that will post tasks at various
// initial priorities, then update priority.
std::vector<std::unique_ptr<TaskRunnerAndEvents>> CreateTaskRunnersAndEvents(
    ThreadPoolImplTest* test,
    ThreadPolicy thread_policy) {}

// Update the priority of a sequence when it is not scheduled.
void TestUpdatePrioritySequenceNotScheduled(ThreadPoolImplTest* test,
                                            ThreadPolicy thread_policy) {}

// Update the priority of a sequence when it is scheduled, i.e. not currently
// in a priority queue.
void TestUpdatePrioritySequenceScheduled(ThreadPoolImplTest* test,
                                         ThreadPolicy thread_policy) {}

}  // namespace

TEST_P(ThreadPoolImplTest,
       UpdatePrioritySequenceNotScheduled_PreferBackground) {}

TEST_P(ThreadPoolImplTest,
       UpdatePrioritySequenceNotScheduled_MustUseForeground) {}

TEST_P(ThreadPoolImplTest, UpdatePrioritySequenceScheduled_PreferBackground) {}

TEST_P(ThreadPoolImplTest, UpdatePrioritySequenceScheduled_MustUseForeground) {}

// Verify that a ThreadPolicy has to be specified in TaskTraits to increase
// TaskPriority from BEST_EFFORT.
TEST_P(ThreadPoolImplTest, UpdatePriorityFromBestEffortNoThreadPolicy) {}

INSTANTIATE_TEST_SUITE_P();

INSTANTIATE_TEST_SUITE_P();

}  // namespace internal
}  // namespace base