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 11 namespace v8 { 12 namespace platform { 13 DelayedTaskQueue(TimeFunction time_function)14DelayedTaskQueue::DelayedTaskQueue(TimeFunction time_function) 15 : time_function_(time_function) {} 16 ~DelayedTaskQueue()17DelayedTaskQueue::~DelayedTaskQueue() { 18 base::MutexGuard guard(&lock_); 19 DCHECK(terminated_); 20 DCHECK(task_queue_.empty()); 21 } 22 MonotonicallyIncreasingTime()23double DelayedTaskQueue::MonotonicallyIncreasingTime() { 24 return time_function_(); 25 } 26 Append(std::unique_ptr<Task> task)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 AppendDelayed(std::unique_ptr<Task> task,double delay_in_seconds)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 GetNext()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. PopTaskFromDelayedQueue(double now)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 Terminate()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