// 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