// Copyright 2021 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_SEQUENCE_MANAGER_WAKE_UP_QUEUE_H_
#define BASE_TASK_SEQUENCE_MANAGER_WAKE_UP_QUEUE_H_

#include "base/base_export.h"
#include "base/check.h"
#include "base/containers/intrusive_heap.h"
#include "base/memory/raw_ptr.h"
#include "base/task/sequence_manager/lazy_now.h"
#include "base/task/sequence_manager/task_queue_impl.h"
#include "base/time/time.h"
#include "base/values.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace base {
namespace sequence_manager {

class EnqueueOrder;

namespace internal {

class AssociatedThreadId;
class SequenceManagerImpl;
class TaskQueueImpl;

// WakeUpQueue is a queue of (wake_up, TaskQueueImpl*) pairs which
// aggregates wake-ups from multiple TaskQueueImpl into a single wake-up, and
// notifies TaskQueueImpls when wake-up times are reached.
class BASE_EXPORT WakeUpQueue {
 public:
  WakeUpQueue(const WakeUpQueue&) = delete;
  WakeUpQueue& operator=(const WakeUpQueue&) = delete;
  virtual ~WakeUpQueue();

  // Returns a wake-up for the next pending delayed task (pending delayed tasks
  // that are ripe may be ignored). If there are no such tasks (immediate tasks
  // don't count) or queues are disabled it returns nullopt.
  absl::optional<WakeUp> GetNextDelayedWakeUp() const;

  // Debug info.
  Value AsValue(TimeTicks now) const;

  bool has_pending_high_resolution_tasks() const {
    return pending_high_res_wake_up_count_;
  }

  // Returns true if there are no pending delayed tasks.
  bool empty() const { return wake_up_queue_.empty(); }

  // Moves ready delayed tasks in TaskQueues to delayed WorkQueues, consuming
  // expired wake-ups in the process.
  void MoveReadyDelayedTasksToWorkQueues(LazyNow* lazy_now,
                                         EnqueueOrder enqueue_order);

  // Schedule `queue` to wake up at certain time. Repeating calls with the same
  // `queue` invalidate previous requests. Nullopt `wake_up` cancels a
  // previously set wake up for `queue`.
  void SetNextWakeUpForQueue(internal::TaskQueueImpl* queue,
                             LazyNow* lazy_now,
                             absl::optional<WakeUp> wake_up);

  // Remove the TaskQueue from any internal data structures.
  virtual void UnregisterQueue(internal::TaskQueueImpl* queue) = 0;

  // Removes all canceled delayed tasks from the front of the queue. After
  // calling this, GetNextDelayedWakeUp() is guaranteed to return a wake up time
  // for a non-canceled task.
  void RemoveAllCanceledDelayedTasksFromFront(LazyNow* lazy_now);

 protected:
  explicit WakeUpQueue(
      scoped_refptr<internal::AssociatedThreadId> associated_thread);

  // Called every time the next `next_wake_up` changes. absl::nullopt is used to
  // cancel the next wake-up. Subclasses may use this to tell SequenceManager to
  // schedule the next wake-up at the given time.
  virtual void OnNextWakeUpChanged(LazyNow* lazy_now,
                                   absl::optional<WakeUp> next_wake_up) = 0;

  virtual const char* GetName() const = 0;

 private:
  friend class MockWakeUpQueue;

  struct ScheduledWakeUp {
    WakeUp wake_up;
    internal::TaskQueueImpl* queue;

    bool operator>(const ScheduledWakeUp& other) const {
      return wake_up.latest_time() > other.wake_up.latest_time();
    }

    void SetHeapHandle(HeapHandle handle) {
      DCHECK(handle.IsValid());
      queue->set_heap_handle(handle);
    }

    void ClearHeapHandle() {
      DCHECK(queue->heap_handle().IsValid());
      queue->set_heap_handle(HeapHandle());
    }

    HeapHandle GetHeapHandle() const { return queue->heap_handle(); }
  };

  IntrusiveHeap<ScheduledWakeUp, std::greater<>> wake_up_queue_;
  int pending_high_res_wake_up_count_ = 0;

  scoped_refptr<internal::AssociatedThreadId> associated_thread_;
};

// Default WakeUpQueue implementation that forwards wake-ups to
// `sequence_manager_`.
class BASE_EXPORT DefaultWakeUpQueue : public WakeUpQueue {
 public:
  DefaultWakeUpQueue(
      scoped_refptr<internal::AssociatedThreadId> associated_thread,
      internal::SequenceManagerImpl* sequence_manager);
  ~DefaultWakeUpQueue() override;

 private:
  // WakeUpQueue implementation:
  void OnNextWakeUpChanged(LazyNow* lazy_now,
                           absl::optional<WakeUp> wake_up) override;
  const char* GetName() const override;
  void UnregisterQueue(internal::TaskQueueImpl* queue) override;

  raw_ptr<internal::SequenceManagerImpl> sequence_manager_;  // Not owned.
};

// WakeUpQueue implementation that doesn't sends wake-ups to
// any SequenceManager, such that task queues don't cause wake-ups.
class BASE_EXPORT NonWakingWakeUpQueue : public WakeUpQueue {
 public:
  explicit NonWakingWakeUpQueue(
      scoped_refptr<internal::AssociatedThreadId> associated_thread);
  ~NonWakingWakeUpQueue() override;

 private:
  // WakeUpQueue implementation:
  void OnNextWakeUpChanged(LazyNow* lazy_now,
                           absl::optional<WakeUp> wake_up) override;
  const char* GetName() const override;
  void UnregisterQueue(internal::TaskQueueImpl* queue) override;
};

}  // namespace internal
}  // namespace sequence_manager
}  // namespace base

#endif  // BASE_TASK_SEQUENCE_MANAGER_WAKE_UP_QUEUE_H_
