1 // Copyright 2016 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_TASK_THREAD_POOL_THREAD_POOL_INSTANCE_H_ 6 #define BASE_TASK_THREAD_POOL_THREAD_POOL_INSTANCE_H_ 7 8 #include <memory> 9 #include <string_view> 10 11 #include "base/base_export.h" 12 #include "base/functional/callback.h" 13 #include "base/gtest_prod_util.h" 14 #include "base/task/sequenced_task_runner.h" 15 #include "base/task/single_thread_task_runner.h" 16 #include "base/task/single_thread_task_runner_thread_mode.h" 17 #include "base/task/task_runner.h" 18 #include "base/task/task_traits.h" 19 #include "base/time/time.h" 20 #include "build/build_config.h" 21 22 namespace gin { 23 class V8Platform; 24 } 25 26 namespace content { 27 // Can't use the FRIEND_TEST_ALL_PREFIXES macro because the test is in a 28 // different namespace. 29 class BrowserMainLoopTest_CreateThreadsInSingleProcess_Test; 30 } // namespace content 31 32 namespace base { 33 34 class WorkerThreadObserver; 35 class ThreadPoolTestHelpers; 36 37 // Interface for a thread pool and static methods to manage the instance used 38 // by the thread_pool.h API. 39 // 40 // The thread pool doesn't create threads until Start() is called. Tasks can 41 // be posted at any time but will not run until after Start() is called. 42 // 43 // The instance methods of this class are thread-safe unless otherwise noted. 44 // 45 // Note: All thread pool users should go through base/task/thread_pool.h instead 46 // of this interface except for the one callsite per process which manages the 47 // process's instance. 48 class BASE_EXPORT ThreadPoolInstance { 49 public: 50 struct BASE_EXPORT InitParams { 51 enum class CommonThreadPoolEnvironment { 52 // Use the default environment (no environment). 53 DEFAULT, 54 #if BUILDFLAG(IS_WIN) 55 // Place the pool's workers in a COM MTA. 56 COM_MTA, 57 #endif // BUILDFLAG(IS_WIN) 58 }; 59 60 InitParams(size_t max_num_foreground_threads_in); 61 InitParams(size_t max_num_foreground_threads_in, 62 size_t max_num_utility_threads_in); 63 ~InitParams(); 64 65 // Maximum number of unblocked tasks that can run concurrently in the 66 // foreground thread group. This is capped at 256 (and should likely not be 67 // configured anywhere close to this in a browser, approaching that limit is 68 // most useful on compute farms running tests or compiles in parallel). 69 size_t max_num_foreground_threads; 70 71 // Maximum number of unblocked tasks that can run concurrently in the 72 // utility thread group. 73 size_t max_num_utility_threads; 74 75 // Whether COM is initialized when running sequenced and parallel tasks. 76 CommonThreadPoolEnvironment common_thread_pool_environment = 77 CommonThreadPoolEnvironment::DEFAULT; 78 79 // An experiment conducted in July 2019 revealed that on Android, changing 80 // the reclaim time from 30 seconds to 5 minutes: 81 // - Reduces jank by 5% at 99th percentile 82 // - Reduces first input delay by 5% at 99th percentile 83 // - Reduces input delay by 3% at 50th percentile 84 // - Reduces navigation to first contentful paint by 2-3% at 25-95th 85 // percentiles 86 // On Windows and Mac, we instead see no impact or small regressions. 87 // 88 // TODO(scheduler-dev): Conduct experiments to find the optimal value for 89 // each process type on each platform. In particular, due to regressions at 90 // high percentiles for *HeartbeatLatencyMicroseconds.Renderer* histograms, 91 // it was suggested that we might want a different reclaim time in 92 // renderers. Note that the regression is not present in 93 // *TaskLatencyMicroseconds.Renderer* histograms. 94 TimeDelta suggested_reclaim_time = 95 #if BUILDFLAG(IS_ANDROID) 96 Minutes(5); 97 #else 98 Seconds(30); 99 #endif 100 }; 101 102 // A Scoped(BestEffort)ExecutionFence prevents new tasks of any/BEST_EFFORT 103 // priority from being scheduled in ThreadPoolInstance within its scope. 104 // Multiple fences can exist at the same time. Upon destruction of all 105 // Scoped(BestEffort)ExecutionFences, tasks that were preeempted are released. 106 // Note: the constructor of Scoped(BestEffort)ExecutionFence will not wait for 107 // currently running tasks (as they were posted before entering this scope and 108 // do not violate the contract; some of them could be CONTINUE_ON_SHUTDOWN and 109 // waiting for them to complete is ill-advised). 110 class BASE_EXPORT ScopedExecutionFence { 111 public: 112 ScopedExecutionFence(); 113 ScopedExecutionFence(const ScopedExecutionFence&) = delete; 114 ScopedExecutionFence& operator=(const ScopedExecutionFence&) = delete; 115 ~ScopedExecutionFence(); 116 }; 117 118 class BASE_EXPORT ScopedBestEffortExecutionFence { 119 public: 120 ScopedBestEffortExecutionFence(); 121 ScopedBestEffortExecutionFence(const ScopedBestEffortExecutionFence&) = 122 delete; 123 ScopedBestEffortExecutionFence& operator=( 124 const ScopedBestEffortExecutionFence&) = delete; 125 ~ScopedBestEffortExecutionFence(); 126 }; 127 128 // Used to allow posting `BLOCK_SHUTDOWN` tasks after shutdown in a scope. The 129 // tasks will fizzle (not run) but not trigger any checks that aim to catch 130 // this class of ordering bugs. 131 class BASE_EXPORT ScopedFizzleBlockShutdownTasks { 132 public: 133 ScopedFizzleBlockShutdownTasks(); 134 ScopedFizzleBlockShutdownTasks(const ScopedFizzleBlockShutdownTasks&) = 135 delete; 136 ScopedFizzleBlockShutdownTasks& operator=( 137 const ScopedFizzleBlockShutdownTasks&) = delete; 138 ~ScopedFizzleBlockShutdownTasks(); 139 }; 140 141 // Destroying a ThreadPoolInstance is not allowed in production; it is always 142 // leaked. In tests, it should only be destroyed after JoinForTesting() has 143 // returned. 144 virtual ~ThreadPoolInstance() = default; 145 146 // Allows the thread pool to create threads and run tasks following the 147 // |init_params| specification. 148 // 149 // If specified, |worker_thread_observer| will be notified when a worker 150 // enters and exits its main function. It must not be destroyed before 151 // JoinForTesting() has returned (must never be destroyed in production). 152 // 153 // CHECKs on failure. 154 virtual void Start( 155 const InitParams& init_params, 156 WorkerThreadObserver* worker_thread_observer = nullptr) = 0; 157 158 // Returns true if Start() was called. This will continue returning true even 159 // after Shutdown() is called. Must be called on the same sequence as Start(). 160 virtual bool WasStarted() const = 0; 161 162 // Same as WasStarted(), but can be called from any sequence. The caller must 163 // make sure this call is properly synchronized with Start(), to avoid 164 // undefined behavior. 165 virtual bool WasStartedUnsafe() const = 0; 166 167 // Synchronously shuts down the thread pool. Once this is called, only tasks 168 // posted with the BLOCK_SHUTDOWN behavior will be run. When this returns: 169 // - All SKIP_ON_SHUTDOWN tasks that were already running have completed their 170 // execution. 171 // - All posted BLOCK_SHUTDOWN tasks have completed their execution. 172 // - CONTINUE_ON_SHUTDOWN tasks might still be running. 173 // Note that an implementation can keep threads and other resources alive to 174 // support running CONTINUE_ON_SHUTDOWN after this returns. This can only be 175 // called once. Must be called on the same sequence as Start(). 176 virtual void Shutdown() = 0; 177 178 // Waits until there are no pending undelayed tasks. May be called in tests 179 // to validate that a condition is met after all undelayed tasks have run. 180 // 181 // Does not wait for delayed tasks. Waits for undelayed tasks posted from 182 // other threads during the call. Returns immediately when shutdown completes. 183 virtual void FlushForTesting() = 0; 184 185 // Returns and calls |flush_callback| when there are no incomplete undelayed 186 // tasks. |flush_callback| may be called back on any thread and should not 187 // perform a lot of work. May be used when additional work on the current 188 // thread needs to be performed during a flush. Only one 189 // FlushAsyncForTesting() may be pending at any given time. 190 virtual void FlushAsyncForTesting(OnceClosure flush_callback) = 0; 191 192 // Joins all threads. Tasks that are already running are allowed to complete 193 // their execution. This can only be called once. Using this thread pool 194 // instance to create task runners or post tasks is not permitted during or 195 // after this call. 196 virtual void JoinForTesting() = 0; 197 198 virtual void BeginFizzlingBlockShutdownTasks() = 0; 199 virtual void EndFizzlingBlockShutdownTasks() = 0; 200 201 // CreateAndStartWithDefaultParams(), Create(), and SetInstance() register a 202 // ThreadPoolInstance to handle tasks posted through the thread_pool.h API for 203 // this process. 204 // 205 // Processes that need to initialize ThreadPoolInstance with custom params or 206 // that need to allow tasks to be posted before the ThreadPoolInstance creates 207 // its threads should use Create() followed by Start(). Other processes can 208 // use CreateAndStartWithDefaultParams(). 209 // 210 // A registered ThreadPoolInstance is only deleted when a new 211 // ThreadPoolInstance is registered. The last registered ThreadPoolInstance is 212 // leaked on shutdown. The methods below must not be called when TaskRunners 213 // created by a previous ThreadPoolInstance are still alive. The methods are 214 // not thread-safe; proper synchronization is required to use the 215 // thread_pool.h API after registering a new ThreadPoolInstance. 216 217 #if !BUILDFLAG(IS_NACL) 218 // Creates and starts a thread pool using default params. |name| is used to 219 // label histograms, it must not be empty. It should identify the component 220 // that calls this. Start() is called by this method; it is invalid to call it 221 // again afterwards. CHECKs on failure. For tests, prefer 222 // base::test::TaskEnvironment (ensures isolation). 223 static void CreateAndStartWithDefaultParams(std::string_view name); 224 225 // Same as CreateAndStartWithDefaultParams() but allows callers to split the 226 // Create() and StartWithDefaultParams() calls. Start() is called by this 227 // method; it is invalid to call it again afterwards. 228 void StartWithDefaultParams(); 229 #endif // !BUILDFLAG(IS_NACL) 230 231 // Creates a ready to start thread pool. |name| is used to label histograms, 232 // it must not be empty. It should identify the component that creates the 233 // ThreadPoolInstance. The thread pool doesn't create threads until Start() is 234 // called. Tasks can be posted at any time but will not run until after 235 // Start() is called. For tests, prefer base::test::TaskEnvironment 236 // (ensures isolation). 237 static void Create(std::string_view name); 238 239 // Registers |thread_pool| to handle tasks posted through the thread_pool.h 240 // API for this process. For tests, prefer base::test::TaskEnvironment 241 // (ensures isolation). 242 static void Set(std::unique_ptr<ThreadPoolInstance> thread_pool); 243 244 // Retrieve the ThreadPoolInstance set via SetInstance() or Create(). This 245 // should be used very rarely; most users of the thread pool should use the 246 // thread_pool.h API. In particular, refrain from doing 247 // if (!ThreadPoolInstance::Get()) { 248 // ThreadPoolInstance::Set(...); 249 // base::ThreadPool::PostTask(...); 250 // } 251 // instead make sure to SetInstance() early in one determinstic place in the 252 // process' initialization phase. 253 // In doubt, consult with //base/task/thread_pool/OWNERS. 254 static ThreadPoolInstance* Get(); 255 256 private: 257 friend class ThreadPoolTestHelpers; 258 friend class gin::V8Platform; 259 friend class content::BrowserMainLoopTest_CreateThreadsInSingleProcess_Test; 260 261 // Returns the maximum number of non-single-threaded non-blocked tasks posted 262 // with |traits| that can run concurrently in this thread pool. |traits| 263 // can't contain TaskPriority::BEST_EFFORT. 264 // 265 // Do not use this method. To process n items, post n tasks that each process 266 // 1 item rather than GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() 267 // tasks that each process 268 // n/GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() items. 269 // 270 // TODO(fdoray): Remove this method. https://crbug.com/687264 271 virtual size_t GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( 272 const TaskTraits& traits) const = 0; 273 274 // Starts/stops a fence that prevents scheduling of tasks of any / BEST_EFFORT 275 // priority. Ongoing tasks will still be allowed to complete and not be 276 // waited upon. This is useful for use cases where a second component 277 // (e.g. content) needs a "single-threaded" startup phase where tasks it 278 // posts do not run before it "enables the ThreadPool" 279 // (via ThreadPoolInstance::EndFence instead of the typical 280 // ThreadPoolInstance::Start). For example, because a lightweight service 281 // manager was already running prior to launching full chrome. BeginFence 282 // does not wait for ongoing tasks as those pertain to the previous phase and 283 // cannot interfere with the upcoming "single-threaded" initialization 284 // phase. These methods must be called from the same sequence as Start(). 285 virtual void BeginFence() = 0; 286 virtual void EndFence() = 0; 287 virtual void BeginBestEffortFence() = 0; 288 virtual void EndBestEffortFence() = 0; 289 }; 290 291 } // namespace base 292 293 #endif // BASE_TASK_THREAD_POOL_THREAD_POOL_INSTANCE_H_ 294