// Copyright 2017 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_LAZY_TASK_RUNNER_H_
#define BASE_TASK_SCHEDULER_LAZY_TASK_RUNNER_H_

#include <vector>

#include "base/atomicops.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/lazy_instance_helpers.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/task_scheduler/scheduler_lock.h"
#include "base/task_scheduler/single_thread_task_runner_thread_mode.h"
#include "base/task_scheduler/task_traits.h"
#include "build/build_config.h"

// Lazy(Sequenced|SingleThread|COMSTA)TaskRunner lazily creates a TaskRunner.
//
// Lazy(Sequenced|SingleThread|COMSTA)TaskRunner is meant to be instantiated in
// an anonymous namespace (no static initializer is generated) and used to post
// tasks to the same sequence/thread from pieces of code that don't have a
// better way of sharing a TaskRunner. It is important to use this class
// instead of a self-managed global variable or LazyInstance so that the
// TaskRunners do not outlive the scope of the ScopedTaskEnvironment in unit
// tests (otherwise the next test in the same process will die in use-after-
// frees).
//
// IMPORTANT: Only use this API as a last resort. Prefer storing a
// (Sequenced|SingleThread)TaskRunner returned by
// base::Create(Sequenced|SingleThread|COMSTA)TaskRunnerWithTraits() as a member
// on an object accessible by all PostTask() call sites.
//
// Example usage 1:
//
// namespace {
// base::LazySequencedTaskRunner g_sequenced_task_runner =
//     LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(
//         base::TaskTraits(base::MayBlock(),
//                          base::TaskPriority::USER_VISIBLE));
// }  // namespace
//
// void SequencedFunction() {
//   // Different invocations of this function post to the same
//   // MayBlock() SequencedTaskRunner.
//   g_sequenced_task_runner.Get()->PostTask(FROM_HERE, base::BindOnce(...));
// }
//
// Example usage 2:
//
// namespace {
// base::LazySequencedTaskRunner g_sequenced_task_task_runner =
//     LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER({base::MayBlock()});
// }  // namespace
//
// // Code from different files can access the SequencedTaskRunner via this
// // function.
// scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() {
//   return g_sequenced_task_runner.Get();
// }

namespace base {

namespace internal {
template <typename TaskRunnerType, bool com_sta>
class BASE_EXPORT LazyTaskRunner;
}  // namespace internal

// Lazy SequencedTaskRunner.
using LazySequencedTaskRunner =
    internal::LazyTaskRunner<SequencedTaskRunner, false>;

// Lazy SingleThreadTaskRunner.
using LazySingleThreadTaskRunner =
    internal::LazyTaskRunner<SingleThreadTaskRunner, false>;

#if defined(OS_WIN)
// Lazy COM-STA enabled SingleThreadTaskRunner.
using LazyCOMSTATaskRunner =
    internal::LazyTaskRunner<SingleThreadTaskRunner, true>;
#endif

// Helper macros to generate a variable name by concatenation.
#define LAZY_TASK_RUNNER_CONCATENATE_INTERNAL2(a, b) a##b
#define LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(a, b) \
  LAZY_TASK_RUNNER_CONCATENATE_INTERNAL2(a, b)

// Use the macros below to initialize a LazyTaskRunner. These macros verify that
// their arguments are constexpr, which is important to prevent the generation
// of a static initializer.

// |traits| are TaskTraits used when creating the SequencedTaskRunner.
#define LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(traits)                 \
  base::LazySequencedTaskRunner::CreateInternal(traits);               \
  ALLOW_UNUSED_TYPE constexpr base::TaskTraits                         \
      LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(kVerifyTraitsAreConstexpr, \
                                            __LINE__) = traits

// |traits| are TaskTraits used when creating the SingleThreadTaskRunner.
// |thread_mode| specifies whether the SingleThreadTaskRunner can share its
// thread with other SingleThreadTaskRunners.
#define LAZY_SINGLE_THREAD_TASK_RUNNER_INITIALIZER(traits, thread_mode)   \
  base::LazySingleThreadTaskRunner::CreateInternal(traits, thread_mode);  \
  ALLOW_UNUSED_TYPE constexpr base::TaskTraits                            \
      LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(kVerifyTraitsAreConstexpr,    \
                                            __LINE__) = traits;           \
  ALLOW_UNUSED_TYPE constexpr base::SingleThreadTaskRunnerThreadMode      \
      LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(kVerifyThreadModeIsConstexpr, \
                                            __LINE__) = thread_mode

// |traits| are TaskTraits used when creating the COM STA
// SingleThreadTaskRunner. |thread_mode| specifies whether the COM STA
// SingleThreadTaskRunner can share its thread with other
// SingleThreadTaskRunners.
#define LAZY_COM_STA_TASK_RUNNER_INITIALIZER(traits, thread_mode)         \
  base::LazyCOMSTATaskRunner::CreateInternal(traits, thread_mode);        \
  ALLOW_UNUSED_TYPE constexpr base::TaskTraits                            \
      LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(kVerifyTraitsAreConstexpr,    \
                                            __LINE__) = traits;           \
  ALLOW_UNUSED_TYPE constexpr base::SingleThreadTaskRunnerThreadMode      \
      LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(kVerifyThreadModeIsConstexpr, \
                                            __LINE__) = thread_mode

namespace internal {

template <typename TaskRunnerType, bool com_sta>
class BASE_EXPORT LazyTaskRunner {
 public:
  // Use the macros above rather than a direct call to this.
  //
  // |traits| are TaskTraits to use to create the TaskRunner. If this
  // LazyTaskRunner is specialized to create a SingleThreadTaskRunner,
  // |thread_mode| specifies whether the SingleThreadTaskRunner can share its
  // thread with other SingleThreadTaskRunner. Otherwise, it is unused.
  static constexpr LazyTaskRunner CreateInternal(
      const TaskTraits& traits,
      SingleThreadTaskRunnerThreadMode thread_mode =
          SingleThreadTaskRunnerThreadMode::SHARED) {
    return LazyTaskRunner(traits, thread_mode);
  }

  // Returns the TaskRunner held by this instance. Creates it if it didn't
  // already exist. Thread-safe.
  scoped_refptr<TaskRunnerType> Get();

 private:
  constexpr LazyTaskRunner(const TaskTraits& traits,
                           SingleThreadTaskRunnerThreadMode thread_mode =
                               SingleThreadTaskRunnerThreadMode::SHARED)
      : traits_(traits), thread_mode_(thread_mode) {}

  // Releases the TaskRunner held by this instance.
  void Reset();

  // Creates and returns a new TaskRunner.
  scoped_refptr<TaskRunnerType> Create();

  // Creates a new TaskRunner via Create(), adds an explicit ref to it, and
  // returns it raw. Used as an adapter for lazy instance helpers. Static and
  // takes |this| as an explicit param to match the void* signature of
  // GetOrCreateLazyPointer().
  static TaskRunnerType* CreateRaw(void* void_self);

  // TaskTraits to create the TaskRunner.
  const TaskTraits traits_;

  // SingleThreadTaskRunnerThreadMode to create the TaskRunner.
  const SingleThreadTaskRunnerThreadMode thread_mode_;

  // Can have 3 states:
  // - This instance does not hold a TaskRunner: 0
  // - This instance is creating a TaskRunner: kLazyInstanceStateCreating
  // - This instance holds a TaskRunner: Pointer to the TaskRunner.
  // LazyInstance's internals are reused to handle transition between states.
  subtle::AtomicWord state_ = 0;

  // No DISALLOW_COPY_AND_ASSIGN since that prevents static initialization with
  // Visual Studio (warning C4592: 'symbol will be dynamically initialized
  // (implementation limitation))'.
};

// When a LazyTaskRunner becomes active (invokes Get()), it adds a callback to
// the current ScopedLazyTaskRunnerListForTesting, if any. Callbacks run when
// the ScopedLazyTaskRunnerListForTesting is destroyed. In a test process, a
// ScopedLazyTaskRunnerListForTesting must be instantiated before any
// LazyTaskRunner becomes active.
class BASE_EXPORT ScopedLazyTaskRunnerListForTesting {
 public:
  ScopedLazyTaskRunnerListForTesting();
  ~ScopedLazyTaskRunnerListForTesting();

 private:
  friend class LazyTaskRunner<SequencedTaskRunner, false>;
  friend class LazyTaskRunner<SingleThreadTaskRunner, false>;

#if defined(OS_WIN)
  friend class LazyTaskRunner<SingleThreadTaskRunner, true>;
#endif

  // Add |callback| to the list of callbacks to run on destruction.
  void AddCallback(OnceClosure callback);

  // Synchronizes accesses to |callbacks_|.
  SchedulerLock lock_;

  // List of callbacks to run on destruction.
  std::vector<OnceClosure> callbacks_;

  DISALLOW_COPY_AND_ASSIGN(ScopedLazyTaskRunnerListForTesting);
};

}  // namespace internal
}  // namespace base

#endif  // BASE_TASK_SCHEDULER_LAZY_TASK_RUNNER_H_
