xref: /aosp_15_r20/external/cronet/base/task/thread_pool/thread_group_impl.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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_GROUP_IMPL_H_
6 #define BASE_TASK_THREAD_POOL_THREAD_GROUP_IMPL_H_
7 
8 #include <optional>
9 #include <string_view>
10 #include <vector>
11 
12 #include "base/base_export.h"
13 #include "base/gtest_prod_util.h"
14 #include "base/synchronization/condition_variable.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/task/thread_pool/task_source.h"
17 #include "base/task/thread_pool/thread_group.h"
18 #include "base/task/thread_pool/thread_group_worker_delegate.h"
19 #include "base/task/thread_pool/tracked_ref.h"
20 #include "base/task/thread_pool/worker_thread_set.h"
21 #include "base/task/thread_pool/worker_thread_waitable_event.h"
22 #include "base/time/time.h"
23 
24 namespace base {
25 
26 class WorkerThreadObserver;
27 
28 namespace internal {
29 
30 class TaskTracker;
31 
32 // A group of |WorkerThreadWaitableEvent|s that run |Task|s.
33 //
34 // The thread group doesn't create threads until Start() is called. Tasks can be
35 // posted at any time but will not run until after Start() is called.
36 //
37 // This class is thread-safe.
38 class BASE_EXPORT ThreadGroupImpl : public ThreadGroup {
39  public:
40   // Constructs a group without workers.
41   //
42   // |histogram_label| is used to label the thread group's histograms as
43   // "ThreadPool." + histogram_name + "." + |histogram_label| + extra suffixes.
44   // It must not be empty. |thread group_label| is used to label the thread
45   // group's threads, it must not be empty. |thread_type_hint| is the preferred
46   // thread type; the actual thread type depends on shutdown state and platform
47   // capabilities. |task_tracker| keeps track of tasks.
48   ThreadGroupImpl(std::string_view histogram_label,
49                   std::string_view thread_group_label,
50                   ThreadType thread_type_hint,
51                   TrackedRef<TaskTracker> task_tracker,
52                   TrackedRef<Delegate> delegate);
53 
54   ThreadGroupImpl(const ThreadGroupImpl&) = delete;
55   ThreadGroupImpl& operator=(const ThreadGroupImpl&) = delete;
56   // Destroying a ThreadGroupImpl returned by Create() is not allowed
57   // in production; it is always leaked. In tests, it can only be destroyed
58   // after JoinForTesting() has returned.
59   ~ThreadGroupImpl() override;
60 
61   // ThreadGroup:
62   void Start(size_t max_tasks,
63              size_t max_best_effort_tasks,
64              TimeDelta suggested_reclaim_time,
65              scoped_refptr<SingleThreadTaskRunner> service_thread_task_runner,
66              WorkerThreadObserver* worker_thread_observer,
67              WorkerEnvironment worker_environment,
68              bool synchronous_thread_start_for_testing = false,
69              std::optional<TimeDelta> may_block_threshold =
70                  std::optional<TimeDelta>()) override;
71   void JoinForTesting() override;
72   void DidUpdateCanRunPolicy() override;
73   void OnShutdownStarted() override;
74   std::unique_ptr<BaseScopedCommandsExecutor> GetExecutor() override;
75   // Returns the number of workers that are idle (i.e. not running tasks).
76   size_t NumberOfIdleWorkersLockRequiredForTesting() const
77       EXCLUSIVE_LOCKS_REQUIRED(lock_) override;
78 
79  protected:
80  private:
81   class ScopedCommandsExecutor;
82   class WaitableEventWorkerDelegate;
83   friend class WaitableEventWorkerDelegate;
84 
85   // friend tests so that they can access |blocked_workers_poll_period| and
86   // may_block_threshold(), both in ThreadGroup.
87   friend class ThreadGroupImplBlockingTest;
88   friend class ThreadGroupImplMayBlockTest;
89   FRIEND_TEST_ALL_PREFIXES(ThreadGroupImplBlockingTest,
90                            ThreadBlockUnblockPremature);
91   FRIEND_TEST_ALL_PREFIXES(ThreadGroupImplBlockingTest,
92                            ThreadBlockUnblockPrematureBestEffort);
93 
94   // ThreadGroup:
95   void UpdateSortKey(TaskSource::Transaction transaction) override;
96   void PushTaskSourceAndWakeUpWorkers(
97       RegisteredTaskSourceAndTransaction transaction_with_task_source) override;
98   void EnsureEnoughWorkersLockRequired(BaseScopedCommandsExecutor* executor)
99       override EXCLUSIVE_LOCKS_REQUIRED(lock_);
100   ThreadGroupWorkerDelegate* GetWorkerDelegate(WorkerThread* worker) override;
101 
102   // Creates a worker and schedules its start, if needed, to maintain one idle
103   // worker, |max_tasks_| permitting.
104   void MaintainAtLeastOneIdleWorkerLockRequired(
105       ScopedCommandsExecutor* executor) EXCLUSIVE_LOCKS_REQUIRED(lock_);
106 
107   // Creates a worker, adds it to the thread group, schedules its start and
108   // returns it. Cannot be called before Start().
109   scoped_refptr<WorkerThreadWaitableEvent> CreateAndRegisterWorkerLockRequired(
110       ScopedCommandsExecutor* executor) EXCLUSIVE_LOCKS_REQUIRED(lock_);
111 
112   bool IsOnIdleSetLockRequired(WorkerThread* worker) const
113       EXCLUSIVE_LOCKS_REQUIRED(lock_);
114 
115   // Returns the number of workers that are awake (i.e. not on the idle set).
116   size_t GetNumAwakeWorkersLockRequired() const EXCLUSIVE_LOCKS_REQUIRED(lock_);
117 
118   bool IsOnIdleSetLockRequired(WorkerThreadWaitableEvent* worker) const
119       EXCLUSIVE_LOCKS_REQUIRED(lock_);
120 
121   size_t worker_sequence_num_ GUARDED_BY(lock_) = 0;
122 
123   // Ordered set of idle workers; the order uses pointer comparison, this is
124   // arbitrary but stable. Initially, all workers are on this set. A worker is
125   // removed from the set before its WakeUp() function is called and when it
126   // receives work from GetWork() (a worker calls GetWork() when its sleep
127   // timeout expires, even if its WakeUp() method hasn't been called). A worker
128   // is inserted on this set when it receives nullptr from GetWork().
129   WorkerThreadSet idle_workers_set_ GUARDED_BY(lock_);
130 
131   // Ensures recently cleaned up workers (ref.
132   // WaitableEventWorkerDelegate::CleanupLockRequired()) had time to exit as
133   // they have a raw reference to |this| (and to TaskTracker) which can
134   // otherwise result in racy use-after-frees per no longer being part of
135   // |workers_| and hence not being explicitly joined in JoinForTesting():
136   // https://crbug.com/810464. Uses AtomicRefCount to make its only public
137   // method thread-safe.
138   TrackedRefFactory<ThreadGroup> tracked_ref_factory_;
139 };
140 
141 }  // namespace internal
142 }  // namespace base
143 
144 #endif  // BASE_TASK_THREAD_POOL_THREAD_GROUP_IMPL_H_
145