// Copyright 2015 The Chromium 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 "base/task/sequence_manager/time_domain.h" #include "base/task/sequence_manager/associated_thread_id.h" #include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/sequence_manager/task_queue_impl.h" #include "base/task/sequence_manager/work_queue.h" #include "base/threading/thread_checker.h" namespace base { namespace sequence_manager { TimeDomain::TimeDomain() : sequence_manager_(nullptr), associated_thread_(MakeRefCounted()) {} TimeDomain::~TimeDomain() { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); } void TimeDomain::OnRegisterWithSequenceManager( internal::SequenceManagerImpl* sequence_manager) { DCHECK(sequence_manager); DCHECK(!sequence_manager_); sequence_manager_ = sequence_manager; associated_thread_ = sequence_manager_->associated_thread(); } SequenceManager* TimeDomain::sequence_manager() const { DCHECK(sequence_manager_); return sequence_manager_; } // TODO(kraynov): https://crbug.com/857101 Consider making an interface // for SequenceManagerImpl which will expose SetNextDelayedDoWork and // MaybeScheduleImmediateWork methods to make the functions below pure-virtual. void TimeDomain::SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) { sequence_manager_->SetNextDelayedDoWork(lazy_now, run_time); } void TimeDomain::RequestDoWork() { sequence_manager_->MaybeScheduleImmediateWork(FROM_HERE); } void TimeDomain::UnregisterQueue(internal::TaskQueueImpl* queue) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK_EQ(queue->GetTimeDomain(), this); LazyNow lazy_now(CreateLazyNow()); SetNextWakeUpForQueue(queue, nullopt, &lazy_now); } void TimeDomain::SetNextWakeUpForQueue( internal::TaskQueueImpl* queue, Optional wake_up, LazyNow* lazy_now) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK_EQ(queue->GetTimeDomain(), this); DCHECK(queue->IsQueueEnabled() || !wake_up); Optional previous_wake_up; if (!delayed_wake_up_queue_.empty()) previous_wake_up = delayed_wake_up_queue_.Min().wake_up.time; if (wake_up) { // Insert a new wake-up into the heap. if (queue->heap_handle().IsValid()) { // O(log n) delayed_wake_up_queue_.ChangeKey(queue->heap_handle(), {wake_up.value(), queue}); } else { // O(log n) delayed_wake_up_queue_.insert({wake_up.value(), queue}); } } else { // Remove a wake-up from heap if present. if (queue->heap_handle().IsValid()) delayed_wake_up_queue_.erase(queue->heap_handle()); } Optional new_wake_up; if (!delayed_wake_up_queue_.empty()) new_wake_up = delayed_wake_up_queue_.Min().wake_up.time; // TODO(kraynov): https://crbug.com/857101 Review the relationship with // SequenceManager's time. Right now it's not an issue since // VirtualTimeDomain doesn't invoke SequenceManager itself. if (new_wake_up) { if (new_wake_up != previous_wake_up) { // Update the wake-up. SetNextDelayedDoWork(lazy_now, new_wake_up.value()); } } else { if (previous_wake_up) { // No new wake-up to be set, cancel the previous one. SetNextDelayedDoWork(lazy_now, TimeTicks::Max()); } } } void TimeDomain::WakeUpReadyDelayedQueues(LazyNow* lazy_now) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); // Wake up any queues with pending delayed work. Note std::multimap stores // the elements sorted by key, so the begin() iterator points to the earliest // queue to wake-up. while (!delayed_wake_up_queue_.empty() && delayed_wake_up_queue_.Min().wake_up.time <= lazy_now->Now()) { internal::TaskQueueImpl* queue = delayed_wake_up_queue_.Min().queue; queue->WakeUpForDelayedWork(lazy_now); } } Optional TimeDomain::NextScheduledRunTime() const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); if (delayed_wake_up_queue_.empty()) return nullopt; return delayed_wake_up_queue_.Min().wake_up.time; } void TimeDomain::AsValueInto(trace_event::TracedValue* state) const { state->BeginDictionary(); state->SetString("name", GetName()); state->SetInteger("registered_delay_count", delayed_wake_up_queue_.size()); if (!delayed_wake_up_queue_.empty()) { TimeDelta delay = delayed_wake_up_queue_.Min().wake_up.time - Now(); state->SetDouble("next_delay_ms", delay.InMillisecondsF()); } AsValueIntoInternal(state); state->EndDictionary(); } void TimeDomain::AsValueIntoInternal(trace_event::TracedValue* state) const { // Can be overriden to trace some additional state. } } // namespace sequence_manager } // namespace base