// Copyright 2018 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_SEQUENCE_MANAGER_TASKS_H_
#define BASE_TASK_SEQUENCE_MANAGER_TASKS_H_

#include "base/base_export.h"
#include "base/check.h"
#include "base/containers/intrusive_heap.h"
#include "base/dcheck_is_on.h"
#include "base/pending_task.h"
#include "base/task/delay_policy.h"
#include "base/task/sequence_manager/delayed_task_handle_delegate.h"
#include "base/task/sequence_manager/enqueue_order.h"
#include "base/task/sequenced_task_runner.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/abseil-cpp/absl/types/variant.h"

namespace base {
namespace sequence_manager {

using TaskType = uint8_t;
constexpr TaskType kTaskTypeNone = 0;

class TaskOrder;

namespace internal {

// Wrapper around PostTask method arguments and the assigned task type.
// Eventually it becomes a PendingTask once accepted by a TaskQueueImpl.
struct BASE_EXPORT PostedTask {
  explicit PostedTask(scoped_refptr<SequencedTaskRunner> task_runner,
                      OnceClosure callback,
                      Location location,
                      TimeDelta delay = base::TimeDelta(),
                      Nestable nestable = Nestable::kNestable,
                      TaskType task_type = kTaskTypeNone,
                      WeakPtr<DelayedTaskHandleDelegate>
                          delayed_task_handle_delegate = nullptr);
  explicit PostedTask(scoped_refptr<SequencedTaskRunner> task_runner,
                      OnceClosure callback,
                      Location location,
                      TimeTicks delayed_run_time,
                      subtle::DelayPolicy delay_policy,
                      Nestable nestable = Nestable::kNestable,
                      TaskType task_type = kTaskTypeNone,
                      WeakPtr<DelayedTaskHandleDelegate>
                          delayed_task_handle_delegate = nullptr);
  PostedTask(PostedTask&& move_from) noexcept;
  PostedTask(const PostedTask&) = delete;
  PostedTask& operator=(const PostedTask&) = delete;
  ~PostedTask();

  bool is_delayed() const {
    return absl::holds_alternative<TimeTicks>(delay_or_delayed_run_time)
               ? !absl::get<TimeTicks>(delay_or_delayed_run_time).is_null()
               : !absl::get<TimeDelta>(delay_or_delayed_run_time).is_zero();
  }

  OnceClosure callback;
  Location location;
  Nestable nestable = Nestable::kNestable;
  TaskType task_type = kTaskTypeNone;
  absl::variant<TimeDelta, TimeTicks> delay_or_delayed_run_time;
  subtle::DelayPolicy delay_policy = subtle::DelayPolicy::kFlexibleNoSooner;
  // The task runner this task is running on. Can be used by task runners that
  // support posting back to the "current sequence".
  scoped_refptr<SequencedTaskRunner> task_runner;
  // The delegate for the DelayedTaskHandle, if this task was posted through
  // PostCancelableDelayedTask(), nullptr otherwise.
  WeakPtr<DelayedTaskHandleDelegate> delayed_task_handle_delegate;
};

}  // namespace internal

enum class WakeUpResolution { kLow, kHigh };

// Represents a time at which a task wants to run.
struct WakeUp {
  // is_null() for immediate wake up.
  TimeTicks time;
  // These are meaningless if is_immediate().
  TimeDelta leeway;
  WakeUpResolution resolution = WakeUpResolution::kLow;
  subtle::DelayPolicy delay_policy = subtle::DelayPolicy::kFlexibleNoSooner;

  bool operator!=(const WakeUp& other) const {
    return time != other.time || leeway != other.leeway ||
           resolution != other.resolution || delay_policy != other.delay_policy;
  }

  bool operator==(const WakeUp& other) const { return !(*this != other); }

  bool is_immediate() const { return time.is_null(); }

  TimeTicks earliest_time() const;
  TimeTicks latest_time() const;
};

// PendingTask with extra metadata for SequenceManager.
struct BASE_EXPORT Task : public PendingTask {
  Task(internal::PostedTask posted_task,
       EnqueueOrder sequence_order,
       EnqueueOrder enqueue_order = EnqueueOrder(),
       TimeTicks queue_time = TimeTicks(),
       WakeUpResolution wake_up_resolution = WakeUpResolution::kLow,
       TimeDelta leeway = TimeDelta());
  Task(Task&& move_from);
  ~Task();
  Task& operator=(Task&& other);

  // SequenceManager is particularly sensitive to enqueue order,
  // so we have accessors for safety.
  EnqueueOrder enqueue_order() const {
    DCHECK(enqueue_order_);
    return enqueue_order_;
  }

  void set_enqueue_order(EnqueueOrder enqueue_order) {
    DCHECK(!enqueue_order_);
    enqueue_order_ = enqueue_order;
  }

  bool enqueue_order_set() const { return enqueue_order_; }

  TaskOrder task_order() const;

  // OK to dispatch from a nested loop.
  Nestable nestable = Nestable::kNonNestable;

  // Needs high resolution timers.
  bool is_high_res = false;

  TaskType task_type;

  // The task runner this task is running on. Can be used by task runners that
  // support posting back to the "current sequence".
  scoped_refptr<SequencedTaskRunner> task_runner;

#if DCHECK_IS_ON()
  bool cross_thread_;
#endif

  // Implement the intrusive heap contract.
  void SetHeapHandle(HeapHandle heap_handle);
  void ClearHeapHandle();
  HeapHandle GetHeapHandle() const;

  // Returns true if this task was canceled, either through weak pointer
  // invalidation or through |delayed_task_handle_delegate_|.
  bool IsCanceled() const;

  // Must be invoked before running the task. Returns true if the task must run
  // (any delayed task handle will have been invalidated by this method), false
  // if it mustn't run (e.g. delayed task handle was invalidated prior to
  // calling this method).
  bool WillRunTask();

 private:
  // `enqueue_order_` is the primary component used to order tasks (see
  // `TaskOrder`). For immediate tasks, `enqueue_order` is set when posted, but
  // for delayed tasks it's not defined until they are enqueued. This is because
  // otherwise delayed tasks could run before an immediate task posted after the
  // delayed task.
  EnqueueOrder enqueue_order_;

  // The delegate for the DelayedTaskHandle, if this task was posted through
  // `PostCancelableDelayedTask()`, not set otherwise. The task is canceled if
  // `WeakPtr::WasInvalidated` is true. Note: if the task was not posted via
  // `PostCancelableDelayedTask()`. the weak pointer won't be valid, but
  // `WeakPtr::WasInvalidated` will be false.
  WeakPtr<internal::DelayedTaskHandleDelegate> delayed_task_handle_delegate_;
};

}  // namespace sequence_manager
}  // namespace base

#endif  // BASE_TASK_SEQUENCE_MANAGER_TASKS_H_
