• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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