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