blob: 9416295070e8f9975ba4eedb29f48e2505ef0259 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TASK_THREAD_POOL_THREAD_GROUP_IMPL_H_
#define BASE_TASK_THREAD_POOL_THREAD_GROUP_IMPL_H_
#include <optional>
#include <string_view>
#include <vector>
#include "base/base_export.h"
#include "base/gtest_prod_util.h"
#include "base/profiler/thread_group_profiler.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/thread_pool/task_source.h"
#include "base/task/thread_pool/thread_group.h"
#include "base/task/thread_pool/tracked_ref.h"
#include "base/task/thread_pool/worker_thread.h"
#include "base/task/thread_pool/worker_thread_set.h"
#include "base/time/time.h"
namespace base {
class WorkerThreadObserver;
class ThreadGroupProfiler;
namespace internal {
class TaskTracker;
// A group of |WorkerThread|s that run |Task|s.
//
// The thread group doesn't create threads until Start() is called. Tasks can be
// posted at any time but will not run until after Start() is called.
//
// This class is thread-safe.
class BASE_EXPORT ThreadGroupImpl : public ThreadGroup {
public:
// Constructs a group without workers.
//
// |histogram_label| is used to label the thread group's histograms as
// "ThreadPool." + histogram_name + "." + |histogram_label| + extra suffixes.
// It must not be empty. |thread group_label| is used to label the thread
// group's threads, it must not be empty. |thread_type_hint| is the preferred
// thread type; the actual thread type depends on shutdown state and platform
// capabilities. |thread_group_type| is used for thread group profiler to tag
// the profiles collected on this group. |task_tracker| keeps track of tasks.
ThreadGroupImpl(std::string_view histogram_label,
std::string_view thread_group_label,
ThreadType thread_type_hint,
int64_t thread_group_type,
TrackedRef<TaskTracker> task_tracker,
TrackedRef<Delegate> delegate);
ThreadGroupImpl(const ThreadGroupImpl&) = delete;
ThreadGroupImpl& operator=(const ThreadGroupImpl&) = delete;
// Destroying a ThreadGroupImpl returned by Create() is not allowed
// in production; it is always leaked. In tests, it can only be destroyed
// after JoinForTesting() has returned.
~ThreadGroupImpl() override;
// ThreadGroup:
void Start(size_t max_tasks,
size_t max_best_effort_tasks,
TimeDelta suggested_reclaim_time,
scoped_refptr<SingleThreadTaskRunner> service_thread_task_runner,
WorkerThreadObserver* worker_thread_observer,
WorkerEnvironment worker_environment,
bool synchronous_thread_start_for_testing,
std::optional<TimeDelta> may_block_threshold) override;
void Start(size_t max_tasks,
size_t max_best_effort_tasks,
TimeDelta suggested_reclaim_time,
scoped_refptr<SingleThreadTaskRunner> service_thread_task_runner,
WorkerThreadObserver* worker_thread_observer,
WorkerEnvironment worker_environment,
bool synchronous_thread_start_for_testing = false) {
Start(max_tasks, max_best_effort_tasks, suggested_reclaim_time,
service_thread_task_runner, worker_thread_observer,
worker_environment, synchronous_thread_start_for_testing, {});
}
void JoinForTesting() override;
void DidUpdateCanRunPolicy() override;
void OnShutdownStarted() override;
// Returns the number of workers that are idle (i.e. not running tasks).
size_t NumberOfIdleWorkersLockRequiredForTesting() const
EXCLUSIVE_LOCKS_REQUIRED(lock_) override;
private:
class ScopedCommandsExecutor;
class WorkerDelegate;
friend class WorkerDelegate;
// friend tests so that they can access |blocked_workers_poll_period| and
// may_block_threshold(), both in ThreadGroup.
friend class ThreadGroupImplBlockingTest;
friend class ThreadGroupImplMayBlockTest;
FRIEND_TEST_ALL_PREFIXES(ThreadGroupImplBlockingTest,
ThreadBlockUnblockPremature);
FRIEND_TEST_ALL_PREFIXES(ThreadGroupImplBlockingTest,
ThreadBlockUnblockPrematureBestEffort);
// ThreadGroup:
void UpdateSortKey(TaskSource::Transaction transaction) override;
void PushTaskSourceAndWakeUpWorkers(
RegisteredTaskSourceAndTransaction transaction_with_task_source) override;
void EnsureEnoughWorkersLockRequired(BaseScopedCommandsExecutor* executor)
override EXCLUSIVE_LOCKS_REQUIRED(lock_);
void ScheduleAdjustMaxTasks() override;
void AdjustMaxTasks() override;
// Creates a worker and schedules its start, if needed, to maintain one idle
// worker, |max_tasks_| permitting.
void MaintainAtLeastOneIdleWorkerLockRequired(
ScopedCommandsExecutor* executor) EXCLUSIVE_LOCKS_REQUIRED(lock_);
// Creates a worker, adds it to the thread group, schedules its start and
// returns it. Cannot be called before Start().
scoped_refptr<WorkerThread> CreateAndRegisterWorkerLockRequired(
ScopedCommandsExecutor* executor) EXCLUSIVE_LOCKS_REQUIRED(lock_);
// Returns the number of workers that are awake (i.e. not on the idle set).
size_t GetNumAwakeWorkersLockRequired() const EXCLUSIVE_LOCKS_REQUIRED(lock_);
bool IsOnIdleSetLockRequired(WorkerThread* worker) const
EXCLUSIVE_LOCKS_REQUIRED(lock_);
size_t worker_sequence_num_ GUARDED_BY(lock_) = 0;
// Ordered set of idle workers; the order uses pointer comparison, this is
// arbitrary but stable. Initially, all workers are on this set. A worker is
// removed from the set before its WakeUp() function is called and when it
// receives work from GetWork() (a worker calls GetWork() when its sleep
// timeout expires, even if its WakeUp() method hasn't been called). A worker
// is inserted on this set when it receives nullptr from GetWork().
WorkerThreadSet idle_workers_set_ GUARDED_BY(lock_);
// This is used in ThreadGroupProfiler to tag as metadata on profiles
// collected for worker threads within this thread group.
const int64_t thread_group_type_;
// This is set in Start() if profiling is enabled, before any worker thread is
// created. If profiling is not enabled, this will remain std::nullopt. If
// created the ThreadGroupProfiler instance will exist until ThreadGroupImpl
// destruction.
std::optional<ThreadGroupProfiler> thread_group_profiler_;
// Ensures recently cleaned up workers (ref.
// WorkerDelegate::CleanupLockRequired()) had time to exit as
// they have a raw reference to |this| (and to TaskTracker) which can
// otherwise result in racy use-after-frees per no longer being part of
// |workers_| and hence not being explicitly joined in JoinForTesting():
// https://crbug.com/810464. Uses AtomicRefCount to make its only public
// method thread-safe.
TrackedRefFactory<ThreadGroupImpl> tracked_ref_factory_;
};
} // namespace internal
} // namespace base
#endif // BASE_TASK_THREAD_POOL_THREAD_GROUP_IMPL_H_