| // 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 <cstddef> | |
| #include <memory> | |
| #include <string_view> | |
| #include "base/base_export.h" | |
| #include "base/functional/callback.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 ThreadPoolTestHelpers; | |
| class WorkerThreadObserver; | |
| // 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: | |
| enum class RecordLockContention { | |
| kDisabled, | |
| kEnabled, | |
| }; | |
| 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, | |
| size_t max_num_audio_threads_in); | |
| ~InitParams(); | |
| // Maximum number of unblocked tasks that can run concurrently in the | |
| // foreground thread group. This is capped at 256 (and should likely not be | |
| // configured anywhere close to this in a browser, approaching that limit is | |
| // most useful on compute farms running tests or compiles in parallel). | |
| 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; | |
| // Maximum number of unblocked tasks that can run concurrently in the | |
| // audio thread group. | |
| size_t max_num_audio_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 | |
| }; | |
| // Used to restrict the maximum number of concurrent tasks that can run in a | |
| // scope. | |
| class BASE_EXPORT ScopedRestrictedTasks { | |
| public: | |
| ScopedRestrictedTasks(); | |
| ScopedRestrictedTasks(const ScopedRestrictedTasks&) = delete; | |
| ScopedRestrictedTasks& operator=(const ScopedRestrictedTasks&) = delete; | |
| ~ScopedRestrictedTasks(); | |
| }; | |
| // 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. | |
| // 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(std::string_view 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(); | |
| // 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. |record_lock_contention| is used to | |
| // determine if lock contention metrics are recorded depending on the | |
| // process type. 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(std::string_view name, | |
| RecordLockContention record_lock_contention = | |
| RecordLockContention::kDisabled); | |
| // 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 ScopedBestEffortExecutionFence; | |
| friend class ScopedThreadPoolExecutionFence; | |
| friend class ThreadPoolTestHelpers; | |
| friend class gin::V8Platform; | |
| friend class content::BrowserMainLoopTest_CreateThreadsInSingleProcess_Test; | |
| // Returns the maximum number of non-single-threaded tasks that can run | |
| // concurrently in the foreground thread group. | |
| // | |
| // Do not use this method. To process n items, prefer using PostJob() API. | |
| virtual size_t GetMaxConcurrentForegroundTasks() 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; | |
| virtual void BeginRestrictedTasks() = 0; | |
| virtual void EndRestrictedTasks() = 0; | |
| }; | |
| } // namespace base | |
| #endif // BASE_TASK_THREAD_POOL_THREAD_POOL_INSTANCE_H_ |