| // Copyright 2019 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/libplatform/delayed-task-queue.h" |
| |
| #include "include/v8-platform.h" |
| #include "src/base/logging.h" |
| #include "src/base/platform/time.h" |
| |
| namespace v8 { |
| namespace platform { |
| |
| DelayedTaskQueue::DelayedTaskQueue(TimeFunction time_function) |
| : time_function_(time_function) {} |
| |
| DelayedTaskQueue::~DelayedTaskQueue() { |
| base::MutexGuard guard(&lock_); |
| DCHECK(terminated_); |
| DCHECK(task_queue_.empty()); |
| } |
| |
| double DelayedTaskQueue::MonotonicallyIncreasingTime() { |
| return time_function_(); |
| } |
| |
| void DelayedTaskQueue::Append(std::unique_ptr<Task> task) { |
| base::MutexGuard guard(&lock_); |
| DCHECK(!terminated_); |
| task_queue_.push(std::move(task)); |
| queues_condition_var_.NotifyOne(); |
| } |
| |
| void DelayedTaskQueue::AppendDelayed(std::unique_ptr<Task> task, |
| double delay_in_seconds) { |
| DCHECK_GE(delay_in_seconds, 0.0); |
| double deadline = MonotonicallyIncreasingTime() + delay_in_seconds; |
| { |
| base::MutexGuard guard(&lock_); |
| DCHECK(!terminated_); |
| delayed_task_queue_.emplace(deadline, std::move(task)); |
| queues_condition_var_.NotifyOne(); |
| } |
| } |
| |
| std::unique_ptr<Task> DelayedTaskQueue::GetNext() { |
| base::MutexGuard guard(&lock_); |
| for (;;) { |
| // Move delayed tasks that have hit their deadline to the main queue. |
| double now = MonotonicallyIncreasingTime(); |
| std::unique_ptr<Task> task = PopTaskFromDelayedQueue(now); |
| while (task) { |
| task_queue_.push(std::move(task)); |
| task = PopTaskFromDelayedQueue(now); |
| } |
| if (!task_queue_.empty()) { |
| std::unique_ptr<Task> result = std::move(task_queue_.front()); |
| task_queue_.pop(); |
| return result; |
| } |
| |
| if (terminated_) { |
| queues_condition_var_.NotifyAll(); |
| return nullptr; |
| } |
| |
| if (task_queue_.empty() && !delayed_task_queue_.empty()) { |
| // Wait for the next delayed task or a newly posted task. |
| double wait_in_seconds = delayed_task_queue_.begin()->first - now; |
| base::TimeDelta wait_delta = base::TimeDelta::FromMicroseconds( |
| base::TimeConstants::kMicrosecondsPerSecond * wait_in_seconds); |
| |
| // WaitFor unfortunately doesn't care about our fake time and will wait |
| // the 'real' amount of time, based on whatever clock the system call |
| // uses. |
| bool notified = queues_condition_var_.WaitFor(&lock_, wait_delta); |
| USE(notified); |
| } else { |
| queues_condition_var_.Wait(&lock_); |
| } |
| } |
| } |
| |
| // Gets the next task from the delayed queue for which the deadline has passed |
| // according to |now|. Returns nullptr if no such task exists. |
| std::unique_ptr<Task> DelayedTaskQueue::PopTaskFromDelayedQueue(double now) { |
| if (delayed_task_queue_.empty()) return nullptr; |
| |
| auto it = delayed_task_queue_.begin(); |
| if (it->first > now) return nullptr; |
| |
| std::unique_ptr<Task> result = std::move(it->second); |
| delayed_task_queue_.erase(it); |
| return result; |
| } |
| |
| void DelayedTaskQueue::Terminate() { |
| base::MutexGuard guard(&lock_); |
| DCHECK(!terminated_); |
| terminated_ = true; |
| queues_condition_var_.NotifyAll(); |
| } |
| |
| } // namespace platform |
| } // namespace v8 |