// Copyright 2017 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_POOLED_SINGLE_THREAD_TASK_RUNNER_MANAGER_H_
#define BASE_TASK_THREAD_POOL_POOLED_SINGLE_THREAD_TASK_RUNNER_MANAGER_H_

#include <memory>
#include <string>
#include <vector>

#include "base/base_export.h"
#include "base/memory/raw_ptr.h"
#include "base/task/common/checked_lock.h"
#include "base/task/single_thread_task_runner_thread_mode.h"
#include "base/task/thread_pool/environment_config.h"
#include "base/task/thread_pool/tracked_ref.h"
#include "base/thread_annotations.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"

namespace base {

class TaskTraits;
class WorkerThreadObserver;
class SingleThreadTaskRunner;

namespace internal {

class DelayedTaskManager;
class WorkerThreadWaitableEvent;
class TaskTracker;

namespace {

class WorkerThreadDelegate;

}  // namespace

// Manages a group of threads which are each associated with one or more
// SingleThreadTaskRunners.
//
// SingleThreadTaskRunners using SingleThreadTaskRunnerThreadMode::SHARED are
// backed by shared WorkerThreads for each COM+task environment combination.
// These workers are lazily instantiated and then only reclaimed during
// JoinForTesting()
//
// No threads are created (and hence no tasks can run) before Start() is called.
//
// This class is thread-safe.
class BASE_EXPORT PooledSingleThreadTaskRunnerManager final {
 public:
  PooledSingleThreadTaskRunnerManager(TrackedRef<TaskTracker> task_tracker,
                                      DelayedTaskManager* delayed_task_manager);
  PooledSingleThreadTaskRunnerManager(
      const PooledSingleThreadTaskRunnerManager&) = delete;
  PooledSingleThreadTaskRunnerManager& operator=(
      const PooledSingleThreadTaskRunnerManager&) = delete;
  ~PooledSingleThreadTaskRunnerManager();

  // Starts threads for existing SingleThreadTaskRunners and allows threads to
  // be started when SingleThreadTaskRunners are created in the future.
  // `io_thread_task_runner` is used to setup FileDescriptorWatcher on worker
  // threads. `io_thread_task_runner` must refer to a Thread with
  // MessgaePumpType::IO. If specified, |worker_thread_observer| will be
  // notified when a worker enters and exits its main function. It must not be
  // destroyed before JoinForTesting() has returned (must never be destroyed in
  // production).
  void Start(scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner,
             WorkerThreadObserver* worker_thread_observer = nullptr);

  // Wakes up workers as appropriate for the new CanRunPolicy policy. Must be
  // called after an update to CanRunPolicy in TaskTracker.
  void DidUpdateCanRunPolicy();

  // Creates a SingleThreadTaskRunner which runs tasks with |traits| on a thread
  // named "ThreadPoolSingleThread[Shared]" +
  // kEnvironmentParams[GetEnvironmentIndexForTraits(traits)].name_suffix +
  // index.
  scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
      const TaskTraits& traits,
      SingleThreadTaskRunnerThreadMode thread_mode);

#if BUILDFLAG(IS_WIN)
  // Creates a SingleThreadTaskRunner which runs tasks with |traits| on a COM
  // STA thread named "ThreadPoolSingleThreadCOMSTA[Shared]" +
  // kEnvironmentParams[GetEnvironmentIndexForTraits(traits)].name_suffix +
  // index.
  scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner(
      const TaskTraits& traits,
      SingleThreadTaskRunnerThreadMode thread_mode);
#endif  // BUILDFLAG(IS_WIN)

  void JoinForTesting();

 private:
  class PooledSingleThreadTaskRunner;

  enum ContinueOnShutdown {
    IS_CONTINUE_ON_SHUTDOWN,
    IS_NOT_CONTINUE_ON_SHUTDOWN,
    CONTINUE_ON_SHUTDOWN_COUNT,
  };

  static ContinueOnShutdown TraitsToContinueOnShutdown(
      const TaskTraits& traits);

  template <typename DelegateType>
  scoped_refptr<PooledSingleThreadTaskRunner> CreateTaskRunnerImpl(
      const TaskTraits& traits,
      SingleThreadTaskRunnerThreadMode thread_mode);

  template <typename DelegateType>
  std::unique_ptr<WorkerThreadDelegate> CreateWorkerThreadDelegate(
      const std::string& name,
      int id,
      SingleThreadTaskRunnerThreadMode thread_mode);

  template <typename DelegateType>
  WorkerThreadWaitableEvent* CreateAndRegisterWorkerThread(
      const std::string& name,
      SingleThreadTaskRunnerThreadMode thread_mode,
      ThreadType thread_type_hint) EXCLUSIVE_LOCKS_REQUIRED(lock_);

  template <typename DelegateType>
  WorkerThreadWaitableEvent*& GetSharedWorkerThreadForTraits(
      const TaskTraits& traits);

  void UnregisterWorkerThread(WorkerThreadWaitableEvent* worker);

  void ReleaseSharedWorkerThreads();

  const TrackedRef<TaskTracker> task_tracker_;
  const raw_ptr<DelayedTaskManager> delayed_task_manager_;

  scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner_;

  // Optional observer notified when a worker enters and exits its main
  // function. Set in Start() and never modified afterwards.
  raw_ptr<WorkerThreadObserver> worker_thread_observer_ = nullptr;

  CheckedLock lock_;
  std::vector<scoped_refptr<WorkerThreadWaitableEvent>> workers_
      GUARDED_BY(lock_);
  int next_worker_id_ GUARDED_BY(lock_) = 0;

  // Workers for SingleThreadTaskRunnerThreadMode::SHARED tasks. It is
  // important to have separate threads for CONTINUE_ON_SHUTDOWN and non-
  // CONTINUE_ON_SHUTDOWN to avoid being in a situation where a
  // CONTINUE_ON_SHUTDOWN task effectively blocks shutdown by preventing a
  // BLOCK_SHUTDOWN task to be scheduled. https://crbug.com/829786
  WorkerThreadWaitableEvent*
      shared_worker_threads_[ENVIRONMENT_COUNT]
                            [CONTINUE_ON_SHUTDOWN_COUNT] GUARDED_BY(lock_) = {};
#if BUILDFLAG(IS_WIN)
  WorkerThreadWaitableEvent* shared_com_worker_threads_
      [ENVIRONMENT_COUNT][CONTINUE_ON_SHUTDOWN_COUNT] GUARDED_BY(lock_) = {};
#endif  // BUILDFLAG(IS_WIN)

  // Set to true when Start() is called.
  bool started_ GUARDED_BY(lock_) = false;
};

}  // namespace internal
}  // namespace base

#endif  // BASE_TASK_THREAD_POOL_POOLED_SINGLE_THREAD_TASK_RUNNER_MANAGER_H_
