// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_TASK_SCHEDULER_TASK_SCHEDULER_H_
#define BASE_TASK_SCHEDULER_TASK_SCHEDULER_H_

#include <memory>
#include <vector>

#include "base/base_export.h"
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_piece.h"
#include "base/task_runner.h"
#include "base/task_scheduler/scheduler_worker_pool_params.h"
#include "base/task_scheduler/single_thread_task_runner_thread_mode.h"
#include "base/task_scheduler/task_traits.h"
#include "base/time/time.h"
#include "build/build_config.h"

namespace gin {
class V8Platform;
}

namespace content {
// Can't use the FRIEND_TEST_ALL_PREFIXES macro because the test is in a
// different namespace.
class BrowserMainLoopTest_CreateThreadsInSingleProcess_Test;
}  // namespace content

namespace base {

class HistogramBase;
class Location;
class SchedulerWorkerObserver;

// Interface for a task scheduler and static methods to manage the instance used
// by the post_task.h API.
//
// The task scheduler doesn't create threads until Start() is called. Tasks can
// be posted at any time but will not run until after Start() is called.
//
// The instance methods of this class are thread-safe.
//
// Note: All base/task_scheduler users should go through post_task.h instead of
// TaskScheduler except for the one callsite per process which manages the
// process's instance.
class BASE_EXPORT TaskScheduler {
 public:
  struct BASE_EXPORT InitParams {
    enum class SharedWorkerPoolEnvironment {
      // Use the default environment (no environment).
      DEFAULT,
#if defined(OS_WIN)
      // Place the worker in a COM MTA.
      COM_MTA,
#endif  // defined(OS_WIN)
    };

    InitParams(
        const SchedulerWorkerPoolParams& background_worker_pool_params_in,
        const SchedulerWorkerPoolParams&
            background_blocking_worker_pool_params_in,
        const SchedulerWorkerPoolParams& foreground_worker_pool_params_in,
        const SchedulerWorkerPoolParams&
            foreground_blocking_worker_pool_params_in,
        SharedWorkerPoolEnvironment shared_worker_pool_environment_in =
            SharedWorkerPoolEnvironment::DEFAULT);
    ~InitParams();

    SchedulerWorkerPoolParams background_worker_pool_params;
    SchedulerWorkerPoolParams background_blocking_worker_pool_params;
    SchedulerWorkerPoolParams foreground_worker_pool_params;
    SchedulerWorkerPoolParams foreground_blocking_worker_pool_params;
    SharedWorkerPoolEnvironment shared_worker_pool_environment;
  };

  // Destroying a TaskScheduler is not allowed in production; it is always
  // leaked. In tests, it should only be destroyed after JoinForTesting() has
  // returned.
  virtual ~TaskScheduler() = default;

  // Allows the task scheduler to create threads and run tasks following the
  // |init_params| specification.
  //
  // If specified, |scheduler_worker_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).
  //
  // CHECKs on failure.
  virtual void Start(
      const InitParams& init_params,
      SchedulerWorkerObserver* scheduler_worker_observer = nullptr) = 0;

  // Posts |task| with a |delay| and specific |traits|. |delay| can be zero.
  // For one off tasks that don't require a TaskRunner.
  virtual void PostDelayedTaskWithTraits(const Location& from_here,
                                         const TaskTraits& traits,
                                         OnceClosure task,
                                         TimeDelta delay) = 0;

  // Returns a TaskRunner whose PostTask invocations result in scheduling tasks
  // using |traits|. Tasks may run in any order and in parallel.
  virtual scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits(
      const TaskTraits& traits) = 0;

  // Returns a SequencedTaskRunner whose PostTask invocations result in
  // scheduling tasks using |traits|. Tasks run one at a time in posting order.
  virtual scoped_refptr<SequencedTaskRunner>
  CreateSequencedTaskRunnerWithTraits(const TaskTraits& traits) = 0;

  // Returns a SingleThreadTaskRunner whose PostTask invocations result in
  // scheduling tasks using |traits|. Tasks run on a single thread in posting
  // order.
  virtual scoped_refptr<SingleThreadTaskRunner>
  CreateSingleThreadTaskRunnerWithTraits(
      const TaskTraits& traits,
      SingleThreadTaskRunnerThreadMode thread_mode) = 0;

#if defined(OS_WIN)
  // Returns a SingleThreadTaskRunner whose PostTask invocations result in
  // scheduling tasks using |traits| in a COM Single-Threaded Apartment. Tasks
  // run in the same Single-Threaded Apartment in posting order for the returned
  // SingleThreadTaskRunner. There is not necessarily a one-to-one
  // correspondence between SingleThreadTaskRunners and Single-Threaded
  // Apartments. The implementation is free to share apartments or create new
  // apartments as necessary. In either case, care should be taken to make sure
  // COM pointers are not smuggled across apartments.
  virtual scoped_refptr<SingleThreadTaskRunner>
  CreateCOMSTATaskRunnerWithTraits(
      const TaskTraits& traits,
      SingleThreadTaskRunnerThreadMode thread_mode) = 0;
#endif  // defined(OS_WIN)

  // Returns a vector of all histograms available in this task scheduler.
  virtual std::vector<const HistogramBase*> GetHistograms() const = 0;

  // Synchronously shuts down the scheduler. Once this is called, only tasks
  // posted with the BLOCK_SHUTDOWN behavior will be run. When this returns:
  // - All SKIP_ON_SHUTDOWN tasks that were already running have completed their
  //   execution.
  // - All posted BLOCK_SHUTDOWN tasks have completed their execution.
  // - CONTINUE_ON_SHUTDOWN tasks might still be running.
  // Note that an implementation can keep threads and other resources alive to
  // support running CONTINUE_ON_SHUTDOWN after this returns. This can only be
  // called once.
  virtual void Shutdown() = 0;

  // Waits until there are no pending undelayed tasks. May be called in tests
  // to validate that a condition is met after all undelayed tasks have run.
  //
  // Does not wait for delayed tasks. Waits for undelayed tasks posted from
  // other threads during the call. Returns immediately when shutdown completes.
  virtual void FlushForTesting() = 0;

  // Returns and calls |flush_callback| when there are no incomplete undelayed
  // tasks. |flush_callback| may be called back on any thread and should not
  // perform a lot of work. May be used when additional work on the current
  // thread needs to be performed during a flush. Only one
  // FlushAsyncForTesting() may be pending at any given time.
  virtual void FlushAsyncForTesting(OnceClosure flush_callback) = 0;

  // Joins all threads. Tasks that are already running are allowed to complete
  // their execution. This can only be called once. Using this task scheduler
  // instance to create task runners or post tasks is not permitted during or
  // after this call.
  virtual void JoinForTesting() = 0;

// CreateAndStartWithDefaultParams(), Create(), and SetInstance() register a
// TaskScheduler to handle tasks posted through the post_task.h API for this
// process.
//
// Processes that need to initialize TaskScheduler with custom params or that
// need to allow tasks to be posted before the TaskScheduler creates its
// threads should use Create() followed by Start(). Other processes can use
// CreateAndStartWithDefaultParams().
//
// A registered TaskScheduler is only deleted when a new TaskScheduler is
// registered. The last registered TaskScheduler is leaked on shutdown. The
// methods below must not be called when TaskRunners created by a previous
// TaskScheduler are still alive. The methods are not thread-safe; proper
// synchronization is required to use the post_task.h API after registering a
// new TaskScheduler.

#if !defined(OS_NACL)
  // Creates and starts a task scheduler using default params. |name| is used to
  // label histograms, it must not be empty. It should identify the component
  // that calls this. Start() is called by this method; it is invalid to call it
  // again afterwards. CHECKs on failure. For tests, prefer
  // base::test::ScopedTaskEnvironment (ensures isolation).
  static void CreateAndStartWithDefaultParams(StringPiece name);

  // Same as CreateAndStartWithDefaultParams() but allows callers to split the
  // Create() and StartWithDefaultParams() calls.
  void StartWithDefaultParams();
#endif  // !defined(OS_NACL)

  // Creates a ready to start task scheduler. |name| is used to label
  // histograms, it must not be empty. It should identify the component that
  // creates the TaskScheduler. The task scheduler doesn't create threads until
  // Start() is called. Tasks can be posted at any time but will not run until
  // after Start() is called. For tests, prefer
  // base::test::ScopedTaskEnvironment (ensures isolation).
  static void Create(StringPiece name);

  // Registers |task_scheduler| to handle tasks posted through the post_task.h
  // API for this process. For tests, prefer base::test::ScopedTaskEnvironment
  // (ensures isolation).
  static void SetInstance(std::unique_ptr<TaskScheduler> task_scheduler);

  // Retrieve the TaskScheduler set via SetInstance() or
  // CreateAndSet(Simple|Default)TaskScheduler(). This should be used very
  // rarely; most users of TaskScheduler should use the post_task.h API. In
  // particular, refrain from doing
  //   if (!TaskScheduler::GetInstance()) {
  //     TaskScheduler::SetInstance(...);
  //     base::PostTask(...);
  //   }
  // instead make sure to SetInstance() early in one determinstic place in the
  // process' initialization phase.
  // In doubt, consult with //base/task_scheduler/OWNERS.
  static TaskScheduler* GetInstance();

 private:
  friend class gin::V8Platform;
  friend class content::BrowserMainLoopTest_CreateThreadsInSingleProcess_Test;

  // Returns the maximum number of non-single-threaded non-blocked tasks posted
  // with |traits| that can run concurrently in this TaskScheduler. |traits|
  // can't contain TaskPriority::BACKGROUND.
  //
  // Do not use this method. To process n items, post n tasks that each process
  // 1 item rather than GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated()
  // tasks that each process
  // n/GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() items.
  //
  // TODO(fdoray): Remove this method. https://crbug.com/687264
  virtual int GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
      const TaskTraits& traits) const = 0;
};

}  // namespace base

#endif  // BASE_TASK_SCHEDULER_TASK_SCHEDULER_H_