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 restrict the maximum number of concurrent tasks that can run in a 129 // scope. 130 class BASE_EXPORT ScopedRestrictedTasks { 131 public: 132 ScopedRestrictedTasks(); 133 ScopedRestrictedTasks(const ScopedRestrictedTasks&) = delete; 134 ScopedRestrictedTasks& operator=(const ScopedRestrictedTasks&) = delete; 135 ~ScopedRestrictedTasks(); 136 }; 137 138 // Used to allow posting `BLOCK_SHUTDOWN` tasks after shutdown in a scope. The 139 // tasks will fizzle (not run) but not trigger any checks that aim to catch 140 // this class of ordering bugs. 141 class BASE_EXPORT ScopedFizzleBlockShutdownTasks { 142 public: 143 ScopedFizzleBlockShutdownTasks(); 144 ScopedFizzleBlockShutdownTasks(const ScopedFizzleBlockShutdownTasks&) = 145 delete; 146 ScopedFizzleBlockShutdownTasks& operator=( 147 const ScopedFizzleBlockShutdownTasks&) = delete; 148 ~ScopedFizzleBlockShutdownTasks(); 149 }; 150 151 // Destroying a ThreadPoolInstance is not allowed in production; it is always 152 // leaked. In tests, it should only be destroyed after JoinForTesting() has 153 // returned. 154 virtual ~ThreadPoolInstance() = default; 155 156 // Allows the thread pool to create threads and run tasks following the 157 // |init_params| specification. 158 // 159 // If specified, |worker_thread_observer| will be notified when a worker 160 // enters and exits its main function. It must not be destroyed before 161 // JoinForTesting() has returned (must never be destroyed in production). 162 // 163 // CHECKs on failure. 164 virtual void Start( 165 const InitParams& init_params, 166 WorkerThreadObserver* worker_thread_observer = nullptr) = 0; 167 168 // Returns true if Start() was called. This will continue returning true even 169 // after Shutdown() is called. Must be called on the same sequence as Start(). 170 virtual bool WasStarted() const = 0; 171 172 // Same as WasStarted(), but can be called from any sequence. The caller must 173 // make sure this call is properly synchronized with Start(), to avoid 174 // undefined behavior. 175 virtual bool WasStartedUnsafe() const = 0; 176 177 // Synchronously shuts down the thread pool. Once this is called, only tasks 178 // posted with the BLOCK_SHUTDOWN behavior will be run. When this returns: 179 // - All SKIP_ON_SHUTDOWN tasks that were already running have completed their 180 // execution. 181 // - All posted BLOCK_SHUTDOWN tasks have completed their execution. 182 // - CONTINUE_ON_SHUTDOWN tasks might still be running. 183 // Note that an implementation can keep threads and other resources alive to 184 // support running CONTINUE_ON_SHUTDOWN after this returns. This can only be 185 // called once. Must be called on the same sequence as Start(). 186 virtual void Shutdown() = 0; 187 188 // Waits until there are no pending undelayed tasks. May be called in tests 189 // to validate that a condition is met after all undelayed tasks have run. 190 // 191 // Does not wait for delayed tasks. Waits for undelayed tasks posted from 192 // other threads during the call. Returns immediately when shutdown completes. 193 virtual void FlushForTesting() = 0; 194 195 // Returns and calls |flush_callback| when there are no incomplete undelayed 196 // tasks. |flush_callback| may be called back on any thread and should not 197 // perform a lot of work. May be used when additional work on the current 198 // thread needs to be performed during a flush. Only one 199 // FlushAsyncForTesting() may be pending at any given time. 200 virtual void FlushAsyncForTesting(OnceClosure flush_callback) = 0; 201 202 // Joins all threads. Tasks that are already running are allowed to complete 203 // their execution. This can only be called once. Using this thread pool 204 // instance to create task runners or post tasks is not permitted during or 205 // after this call. 206 virtual void JoinForTesting() = 0; 207 208 virtual void BeginFizzlingBlockShutdownTasks() = 0; 209 virtual void EndFizzlingBlockShutdownTasks() = 0; 210 211 // CreateAndStartWithDefaultParams(), Create(), and SetInstance() register a 212 // ThreadPoolInstance to handle tasks posted through the thread_pool.h API for 213 // this process. 214 // 215 // Processes that need to initialize ThreadPoolInstance with custom params or 216 // that need to allow tasks to be posted before the ThreadPoolInstance creates 217 // its threads should use Create() followed by Start(). Other processes can 218 // use CreateAndStartWithDefaultParams(). 219 // 220 // A registered ThreadPoolInstance is only deleted when a new 221 // ThreadPoolInstance is registered. The last registered ThreadPoolInstance is 222 // leaked on shutdown. The methods below must not be called when TaskRunners 223 // created by a previous ThreadPoolInstance are still alive. The methods are 224 // not thread-safe; proper synchronization is required to use the 225 // thread_pool.h API after registering a new ThreadPoolInstance. 226 227 #if !BUILDFLAG(IS_NACL) 228 // Creates and starts a thread pool using default params. |name| is used to 229 // label histograms, it must not be empty. It should identify the component 230 // that calls this. Start() is called by this method; it is invalid to call it 231 // again afterwards. CHECKs on failure. For tests, prefer 232 // base::test::TaskEnvironment (ensures isolation). 233 static void CreateAndStartWithDefaultParams(std::string_view name); 234 235 // Same as CreateAndStartWithDefaultParams() but allows callers to split the 236 // Create() and StartWithDefaultParams() calls. Start() is called by this 237 // method; it is invalid to call it again afterwards. 238 void StartWithDefaultParams(); 239 #endif // !BUILDFLAG(IS_NACL) 240 241 // Creates a ready to start thread pool. |name| is used to label histograms, 242 // it must not be empty. It should identify the component that creates the 243 // ThreadPoolInstance. The thread pool doesn't create threads until Start() is 244 // called. Tasks can be posted at any time but will not run until after 245 // Start() is called. For tests, prefer base::test::TaskEnvironment 246 // (ensures isolation). 247 static void Create(std::string_view name); 248 249 // Registers |thread_pool| to handle tasks posted through the thread_pool.h 250 // API for this process. For tests, prefer base::test::TaskEnvironment 251 // (ensures isolation). 252 static void Set(std::unique_ptr<ThreadPoolInstance> thread_pool); 253 254 // Retrieve the ThreadPoolInstance set via SetInstance() or Create(). This 255 // should be used very rarely; most users of the thread pool should use the 256 // thread_pool.h API. In particular, refrain from doing 257 // if (!ThreadPoolInstance::Get()) { 258 // ThreadPoolInstance::Set(...); 259 // base::ThreadPool::PostTask(...); 260 // } 261 // instead make sure to SetInstance() early in one determinstic place in the 262 // process' initialization phase. 263 // In doubt, consult with //base/task/thread_pool/OWNERS. 264 static ThreadPoolInstance* Get(); 265 266 private: 267 friend class ThreadPoolTestHelpers; 268 friend class gin::V8Platform; 269 friend class content::BrowserMainLoopTest_CreateThreadsInSingleProcess_Test; 270 271 // Returns the maximum number of non-single-threaded non-blocked tasks posted 272 // with |traits| that can run concurrently in this thread pool. |traits| 273 // can't contain TaskPriority::BEST_EFFORT. 274 // 275 // Do not use this method. To process n items, post n tasks that each process 276 // 1 item rather than GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() 277 // tasks that each process 278 // n/GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() items. 279 // 280 // TODO(fdoray): Remove this method. https://crbug.com/687264 281 virtual size_t GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( 282 const TaskTraits& traits) const = 0; 283 284 // Starts/stops a fence that prevents scheduling of tasks of any / BEST_EFFORT 285 // priority. Ongoing tasks will still be allowed to complete and not be 286 // waited upon. This is useful for use cases where a second component 287 // (e.g. content) needs a "single-threaded" startup phase where tasks it 288 // posts do not run before it "enables the ThreadPool" 289 // (via ThreadPoolInstance::EndFence instead of the typical 290 // ThreadPoolInstance::Start). For example, because a lightweight service 291 // manager was already running prior to launching full chrome. BeginFence 292 // does not wait for ongoing tasks as those pertain to the previous phase and 293 // cannot interfere with the upcoming "single-threaded" initialization 294 // phase. These methods must be called from the same sequence as Start(). 295 virtual void BeginFence() = 0; 296 virtual void EndFence() = 0; 297 virtual void BeginBestEffortFence() = 0; 298 virtual void EndBestEffortFence() = 0; 299 300 virtual void BeginRestrictedTasks() = 0; 301 virtual void EndRestrictedTasks() = 0; 302 }; 303 304 } // namespace base 305 306 #endif // BASE_TASK_THREAD_POOL_THREAD_POOL_INSTANCE_H_ 307