blob: bd8fd7817fb598e0231f1d0b71f4adc54a071690 [file] [log] [blame]
// 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