1// Copyright 2019 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/libplatform/delayed-task-queue.h"
6
7#include "include/v8-platform.h"
8#include "src/base/logging.h"
9#include "src/base/platform/time.h"
10
11namespace v8 {
12namespace platform {
13
14DelayedTaskQueue::DelayedTaskQueue(TimeFunction time_function)
15    : time_function_(time_function) {}
16
17DelayedTaskQueue::~DelayedTaskQueue() {
18  base::MutexGuard guard(&lock_);
19  DCHECK(terminated_);
20  DCHECK(task_queue_.empty());
21}
22
23double DelayedTaskQueue::MonotonicallyIncreasingTime() {
24  return time_function_();
25}
26
27void DelayedTaskQueue::Append(std::unique_ptr<Task> task) {
28  base::MutexGuard guard(&lock_);
29  DCHECK(!terminated_);
30  task_queue_.push(std::move(task));
31  queues_condition_var_.NotifyOne();
32}
33
34void DelayedTaskQueue::AppendDelayed(std::unique_ptr<Task> task,
35                                     double delay_in_seconds) {
36  DCHECK_GE(delay_in_seconds, 0.0);
37  double deadline = MonotonicallyIncreasingTime() + delay_in_seconds;
38  {
39    base::MutexGuard guard(&lock_);
40    DCHECK(!terminated_);
41    delayed_task_queue_.emplace(deadline, std::move(task));
42    queues_condition_var_.NotifyOne();
43  }
44}
45
46std::unique_ptr<Task> DelayedTaskQueue::GetNext() {
47  base::MutexGuard guard(&lock_);
48  for (;;) {
49    // Move delayed tasks that have hit their deadline to the main queue.
50    double now = MonotonicallyIncreasingTime();
51    std::unique_ptr<Task> task = PopTaskFromDelayedQueue(now);
52    while (task) {
53      task_queue_.push(std::move(task));
54      task = PopTaskFromDelayedQueue(now);
55    }
56    if (!task_queue_.empty()) {
57      std::unique_ptr<Task> result = std::move(task_queue_.front());
58      task_queue_.pop();
59      return result;
60    }
61
62    if (terminated_) {
63      queues_condition_var_.NotifyAll();
64      return nullptr;
65    }
66
67    if (task_queue_.empty() && !delayed_task_queue_.empty()) {
68      // Wait for the next delayed task or a newly posted task.
69      double wait_in_seconds = delayed_task_queue_.begin()->first - now;
70      base::TimeDelta wait_delta = base::TimeDelta::FromMicroseconds(
71          base::TimeConstants::kMicrosecondsPerSecond * wait_in_seconds);
72
73      // WaitFor unfortunately doesn't care about our fake time and will wait
74      // the 'real' amount of time, based on whatever clock the system call
75      // uses.
76      bool notified = queues_condition_var_.WaitFor(&lock_, wait_delta);
77      USE(notified);
78    } else {
79      queues_condition_var_.Wait(&lock_);
80    }
81  }
82}
83
84// Gets the next task from the delayed queue for which the deadline has passed
85// according to |now|. Returns nullptr if no such task exists.
86std::unique_ptr<Task> DelayedTaskQueue::PopTaskFromDelayedQueue(double now) {
87  if (delayed_task_queue_.empty()) return nullptr;
88
89  auto it = delayed_task_queue_.begin();
90  if (it->first > now) return nullptr;
91
92  std::unique_ptr<Task> result = std::move(it->second);
93  delayed_task_queue_.erase(it);
94  return result;
95}
96
97void DelayedTaskQueue::Terminate() {
98  base::MutexGuard guard(&lock_);
99  DCHECK(!terminated_);
100  terminated_ = true;
101  queues_condition_var_.NotifyAll();
102}
103
104}  // namespace platform
105}  // namespace v8
106