292 lines
13 KiB
C
292 lines
13 KiB
C
|
|
// 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.
|
||
|
|
|
||
|
|
#ifndef BASE_TASK_THREAD_POOL_THREAD_POOL_INSTANCE_H_
|
||
|
|
#define BASE_TASK_THREAD_POOL_THREAD_POOL_INSTANCE_H_
|
||
|
|
|
||
|
|
#include <memory>
|
||
|
|
|
||
|
|
#include "base/base_export.h"
|
||
|
|
#include "base/functional/callback.h"
|
||
|
|
#include "base/gtest_prod_util.h"
|
||
|
|
#include "base/strings/string_piece.h"
|
||
|
|
#include "base/task/sequenced_task_runner.h"
|
||
|
|
#include "base/task/single_thread_task_runner.h"
|
||
|
|
#include "base/task/single_thread_task_runner_thread_mode.h"
|
||
|
|
#include "base/task/task_runner.h"
|
||
|
|
#include "base/task/task_traits.h"
|
||
|
|
#include "base/time/time.h"
|
||
|
|
#include "build/build_config.h"
|
||
|
|
|
||
|
|
namespace gin {
|
||
|
|
class V8Platform;
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace content {
|
||
|
|
// Can't use the FRIEND_TEST_ALL_PREFIXES macro because the test is in a
|
||
|
|
// different namespace.
|
||
|
|
class BrowserMainLoopTest_CreateThreadsInSingleProcess_Test;
|
||
|
|
} // namespace content
|
||
|
|
|
||
|
|
namespace base {
|
||
|
|
|
||
|
|
class WorkerThreadObserver;
|
||
|
|
class ThreadPoolTestHelpers;
|
||
|
|
|
||
|
|
// Interface for a thread pool and static methods to manage the instance used
|
||
|
|
// by the thread_pool.h API.
|
||
|
|
//
|
||
|
|
// The thread pool doesn't create threads until Start() is called. Tasks can
|
||
|
|
// be posted at any time but will not run until after Start() is called.
|
||
|
|
//
|
||
|
|
// The instance methods of this class are thread-safe unless otherwise noted.
|
||
|
|
//
|
||
|
|
// Note: All thread pool users should go through base/task/thread_pool.h instead
|
||
|
|
// of this interface except for the one callsite per process which manages the
|
||
|
|
// process's instance.
|
||
|
|
class BASE_EXPORT ThreadPoolInstance {
|
||
|
|
public:
|
||
|
|
struct BASE_EXPORT InitParams {
|
||
|
|
enum class CommonThreadPoolEnvironment {
|
||
|
|
// Use the default environment (no environment).
|
||
|
|
DEFAULT,
|
||
|
|
#if BUILDFLAG(IS_WIN)
|
||
|
|
// Place the pool's workers in a COM MTA.
|
||
|
|
COM_MTA,
|
||
|
|
#endif // BUILDFLAG(IS_WIN)
|
||
|
|
};
|
||
|
|
|
||
|
|
InitParams(size_t max_num_foreground_threads_in);
|
||
|
|
InitParams(size_t max_num_foreground_threads_in,
|
||
|
|
size_t max_num_utility_threads_in);
|
||
|
|
~InitParams();
|
||
|
|
|
||
|
|
// Maximum number of unblocked tasks that can run concurrently in the
|
||
|
|
// foreground thread group.
|
||
|
|
size_t max_num_foreground_threads;
|
||
|
|
|
||
|
|
// Maximum number of unblocked tasks that can run concurrently in the
|
||
|
|
// utility thread group.
|
||
|
|
size_t max_num_utility_threads;
|
||
|
|
|
||
|
|
// Whether COM is initialized when running sequenced and parallel tasks.
|
||
|
|
CommonThreadPoolEnvironment common_thread_pool_environment =
|
||
|
|
CommonThreadPoolEnvironment::DEFAULT;
|
||
|
|
|
||
|
|
// An experiment conducted in July 2019 revealed that on Android, changing
|
||
|
|
// the reclaim time from 30 seconds to 5 minutes:
|
||
|
|
// - Reduces jank by 5% at 99th percentile
|
||
|
|
// - Reduces first input delay by 5% at 99th percentile
|
||
|
|
// - Reduces input delay by 3% at 50th percentile
|
||
|
|
// - Reduces navigation to first contentful paint by 2-3% at 25-95th
|
||
|
|
// percentiles
|
||
|
|
// On Windows and Mac, we instead see no impact or small regressions.
|
||
|
|
//
|
||
|
|
// TODO(scheduler-dev): Conduct experiments to find the optimal value for
|
||
|
|
// each process type on each platform. In particular, due to regressions at
|
||
|
|
// high percentiles for *HeartbeatLatencyMicroseconds.Renderer* histograms,
|
||
|
|
// it was suggested that we might want a different reclaim time in
|
||
|
|
// renderers. Note that the regression is not present in
|
||
|
|
// *TaskLatencyMicroseconds.Renderer* histograms.
|
||
|
|
TimeDelta suggested_reclaim_time =
|
||
|
|
#if BUILDFLAG(IS_ANDROID)
|
||
|
|
Minutes(5);
|
||
|
|
#else
|
||
|
|
Seconds(30);
|
||
|
|
#endif
|
||
|
|
};
|
||
|
|
|
||
|
|
// A Scoped(BestEffort)ExecutionFence prevents new tasks of any/BEST_EFFORT
|
||
|
|
// priority from being scheduled in ThreadPoolInstance within its scope.
|
||
|
|
// Multiple fences can exist at the same time. Upon destruction of all
|
||
|
|
// Scoped(BestEffort)ExecutionFences, tasks that were preeempted are released.
|
||
|
|
// Note: the constructor of Scoped(BestEffort)ExecutionFence will not wait for
|
||
|
|
// currently running tasks (as they were posted before entering this scope and
|
||
|
|
// do not violate the contract; some of them could be CONTINUE_ON_SHUTDOWN and
|
||
|
|
// waiting for them to complete is ill-advised).
|
||
|
|
class BASE_EXPORT ScopedExecutionFence {
|
||
|
|
public:
|
||
|
|
ScopedExecutionFence();
|
||
|
|
ScopedExecutionFence(const ScopedExecutionFence&) = delete;
|
||
|
|
ScopedExecutionFence& operator=(const ScopedExecutionFence&) = delete;
|
||
|
|
~ScopedExecutionFence();
|
||
|
|
};
|
||
|
|
|
||
|
|
class BASE_EXPORT ScopedBestEffortExecutionFence {
|
||
|
|
public:
|
||
|
|
ScopedBestEffortExecutionFence();
|
||
|
|
ScopedBestEffortExecutionFence(const ScopedBestEffortExecutionFence&) =
|
||
|
|
delete;
|
||
|
|
ScopedBestEffortExecutionFence& operator=(
|
||
|
|
const ScopedBestEffortExecutionFence&) = delete;
|
||
|
|
~ScopedBestEffortExecutionFence();
|
||
|
|
};
|
||
|
|
|
||
|
|
// Used to allow posting `BLOCK_SHUTDOWN` tasks after shutdown in a scope. The
|
||
|
|
// tasks will fizzle (not run) but not trigger any checks that aim to catch
|
||
|
|
// this class of ordering bugs.
|
||
|
|
class BASE_EXPORT ScopedFizzleBlockShutdownTasks {
|
||
|
|
public:
|
||
|
|
ScopedFizzleBlockShutdownTasks();
|
||
|
|
ScopedFizzleBlockShutdownTasks(const ScopedFizzleBlockShutdownTasks&) =
|
||
|
|
delete;
|
||
|
|
ScopedFizzleBlockShutdownTasks& operator=(
|
||
|
|
const ScopedFizzleBlockShutdownTasks&) = delete;
|
||
|
|
~ScopedFizzleBlockShutdownTasks();
|
||
|
|
};
|
||
|
|
|
||
|
|
// Destroying a ThreadPoolInstance is not allowed in production; it is always
|
||
|
|
// leaked. In tests, it should only be destroyed after JoinForTesting() has
|
||
|
|
// returned.
|
||
|
|
virtual ~ThreadPoolInstance() = default;
|
||
|
|
|
||
|
|
// Allows the thread pool to create threads and run tasks following the
|
||
|
|
// |init_params| specification.
|
||
|
|
//
|
||
|
|
// If specified, |worker_thread_observer| will be notified when a worker
|
||
|
|
// enters and exits its main function. It must not be destroyed before
|
||
|
|
// JoinForTesting() has returned (must never be destroyed in production).
|
||
|
|
//
|
||
|
|
// CHECKs on failure.
|
||
|
|
virtual void Start(
|
||
|
|
const InitParams& init_params,
|
||
|
|
WorkerThreadObserver* worker_thread_observer = nullptr) = 0;
|
||
|
|
|
||
|
|
// Returns true if Start() was called. This will continue returning true even
|
||
|
|
// after Shutdown() is called. Must be called on the same sequence as Start().
|
||
|
|
virtual bool WasStarted() const = 0;
|
||
|
|
|
||
|
|
// Same as WasStarted(), but can be called from any sequence. The caller must
|
||
|
|
// make sure this call is properly synchronized with Start(), to avoid
|
||
|
|
// undefined behavior.
|
||
|
|
virtual bool WasStartedUnsafe() const = 0;
|
||
|
|
|
||
|
|
// Synchronously shuts down the thread pool. Once this is called, only tasks
|
||
|
|
// posted with the BLOCK_SHUTDOWN behavior will be run. When this returns:
|
||
|
|
// - All SKIP_ON_SHUTDOWN tasks that were already running have completed their
|
||
|
|
// execution.
|
||
|
|
// - All posted BLOCK_SHUTDOWN tasks have completed their execution.
|
||
|
|
// - CONTINUE_ON_SHUTDOWN tasks might still be running.
|
||
|
|
// Note that an implementation can keep threads and other resources alive to
|
||
|
|
// support running CONTINUE_ON_SHUTDOWN after this returns. This can only be
|
||
|
|
// called once. Must be called on the same sequence as Start().
|
||
|
|
virtual void Shutdown() = 0;
|
||
|
|
|
||
|
|
// Waits until there are no pending undelayed tasks. May be called in tests
|
||
|
|
// to validate that a condition is met after all undelayed tasks have run.
|
||
|
|
//
|
||
|
|
// Does not wait for delayed tasks. Waits for undelayed tasks posted from
|
||
|
|
// other threads during the call. Returns immediately when shutdown completes.
|
||
|
|
virtual void FlushForTesting() = 0;
|
||
|
|
|
||
|
|
// Returns and calls |flush_callback| when there are no incomplete undelayed
|
||
|
|
// tasks. |flush_callback| may be called back on any thread and should not
|
||
|
|
// perform a lot of work. May be used when additional work on the current
|
||
|
|
// thread needs to be performed during a flush. Only one
|
||
|
|
// FlushAsyncForTesting() may be pending at any given time.
|
||
|
|
virtual void FlushAsyncForTesting(OnceClosure flush_callback) = 0;
|
||
|
|
|
||
|
|
// Joins all threads. Tasks that are already running are allowed to complete
|
||
|
|
// their execution. This can only be called once. Using this thread pool
|
||
|
|
// instance to create task runners or post tasks is not permitted during or
|
||
|
|
// after this call.
|
||
|
|
virtual void JoinForTesting() = 0;
|
||
|
|
|
||
|
|
virtual void BeginFizzlingBlockShutdownTasks() = 0;
|
||
|
|
virtual void EndFizzlingBlockShutdownTasks() = 0;
|
||
|
|
|
||
|
|
// CreateAndStartWithDefaultParams(), Create(), and SetInstance() register a
|
||
|
|
// ThreadPoolInstance to handle tasks posted through the thread_pool.h API for
|
||
|
|
// this process.
|
||
|
|
//
|
||
|
|
// Processes that need to initialize ThreadPoolInstance with custom params or
|
||
|
|
// that need to allow tasks to be posted before the ThreadPoolInstance creates
|
||
|
|
// its threads should use Create() followed by Start(). Other processes can
|
||
|
|
// use CreateAndStartWithDefaultParams().
|
||
|
|
//
|
||
|
|
// A registered ThreadPoolInstance is only deleted when a new
|
||
|
|
// ThreadPoolInstance is registered. The last registered ThreadPoolInstance is
|
||
|
|
// leaked on shutdown. The methods below must not be called when TaskRunners
|
||
|
|
// created by a previous ThreadPoolInstance are still alive. The methods are
|
||
|
|
// not thread-safe; proper synchronization is required to use the
|
||
|
|
// thread_pool.h API after registering a new ThreadPoolInstance.
|
||
|
|
|
||
|
|
#if !BUILDFLAG(IS_NACL)
|
||
|
|
// Creates and starts a thread pool using default params. |name| is used to
|
||
|
|
// label histograms, it must not be empty. It should identify the component
|
||
|
|
// that calls this. Start() is called by this method; it is invalid to call it
|
||
|
|
// again afterwards. CHECKs on failure. For tests, prefer
|
||
|
|
// base::test::TaskEnvironment (ensures isolation).
|
||
|
|
static void CreateAndStartWithDefaultParams(StringPiece name);
|
||
|
|
|
||
|
|
// Same as CreateAndStartWithDefaultParams() but allows callers to split the
|
||
|
|
// Create() and StartWithDefaultParams() calls. Start() is called by this
|
||
|
|
// method; it is invalid to call it again afterwards.
|
||
|
|
void StartWithDefaultParams();
|
||
|
|
#endif // !BUILDFLAG(IS_NACL)
|
||
|
|
|
||
|
|
// Creates a ready to start thread pool. |name| is used to label histograms,
|
||
|
|
// it must not be empty. It should identify the component that creates the
|
||
|
|
// ThreadPoolInstance. The thread pool doesn't create threads until Start() is
|
||
|
|
// called. Tasks can be posted at any time but will not run until after
|
||
|
|
// Start() is called. For tests, prefer base::test::TaskEnvironment
|
||
|
|
// (ensures isolation).
|
||
|
|
static void Create(StringPiece name);
|
||
|
|
|
||
|
|
// Registers |thread_pool| to handle tasks posted through the thread_pool.h
|
||
|
|
// API for this process. For tests, prefer base::test::TaskEnvironment
|
||
|
|
// (ensures isolation).
|
||
|
|
static void Set(std::unique_ptr<ThreadPoolInstance> thread_pool);
|
||
|
|
|
||
|
|
// Retrieve the ThreadPoolInstance set via SetInstance() or Create(). This
|
||
|
|
// should be used very rarely; most users of the thread pool should use the
|
||
|
|
// thread_pool.h API. In particular, refrain from doing
|
||
|
|
// if (!ThreadPoolInstance::Get()) {
|
||
|
|
// ThreadPoolInstance::Set(...);
|
||
|
|
// base::ThreadPool::PostTask(...);
|
||
|
|
// }
|
||
|
|
// instead make sure to SetInstance() early in one determinstic place in the
|
||
|
|
// process' initialization phase.
|
||
|
|
// In doubt, consult with //base/task/thread_pool/OWNERS.
|
||
|
|
static ThreadPoolInstance* Get();
|
||
|
|
|
||
|
|
private:
|
||
|
|
friend class ThreadPoolTestHelpers;
|
||
|
|
friend class gin::V8Platform;
|
||
|
|
friend class content::BrowserMainLoopTest_CreateThreadsInSingleProcess_Test;
|
||
|
|
|
||
|
|
// Returns the maximum number of non-single-threaded non-blocked tasks posted
|
||
|
|
// with |traits| that can run concurrently in this thread pool. |traits|
|
||
|
|
// can't contain TaskPriority::BEST_EFFORT.
|
||
|
|
//
|
||
|
|
// Do not use this method. To process n items, post n tasks that each process
|
||
|
|
// 1 item rather than GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated()
|
||
|
|
// tasks that each process
|
||
|
|
// n/GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() items.
|
||
|
|
//
|
||
|
|
// TODO(fdoray): Remove this method. https://crbug.com/687264
|
||
|
|
virtual size_t GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
|
||
|
|
const TaskTraits& traits) const = 0;
|
||
|
|
|
||
|
|
// Starts/stops a fence that prevents scheduling of tasks of any / BEST_EFFORT
|
||
|
|
// priority. Ongoing tasks will still be allowed to complete and not be
|
||
|
|
// waited upon. This is useful for use cases where a second component
|
||
|
|
// (e.g. content) needs a "single-threaded" startup phase where tasks it
|
||
|
|
// posts do not run before it "enables the ThreadPool"
|
||
|
|
// (via ThreadPoolInstance::EndFence instead of the typical
|
||
|
|
// ThreadPoolInstance::Start). For example, because a lightweight service
|
||
|
|
// manager was already running prior to launching full chrome. BeginFence
|
||
|
|
// does not wait for ongoing tasks as those pertain to the previous phase and
|
||
|
|
// cannot interfere with the upcoming "single-threaded" initialization
|
||
|
|
// phase. These methods must be called from the same sequence as Start().
|
||
|
|
virtual void BeginFence() = 0;
|
||
|
|
virtual void EndFence() = 0;
|
||
|
|
virtual void BeginBestEffortFence() = 0;
|
||
|
|
virtual void EndBestEffortFence() = 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace base
|
||
|
|
|
||
|
|
#endif // BASE_TASK_THREAD_POOL_THREAD_POOL_INSTANCE_H_
|