Commit af790bba authored by Alex Clarke's avatar Alex Clarke Committed by Commit Bot

Advance virtual time via SequencedTaskSource

This is necessary for using SequenceManager inside
ScopedTaskEnvironment.  It also makes Virtual Time a bit
cleaner from an interface PoV.

Bug: 863341, 891670
Change-Id: Iaa8300b6c4b2f889b4cfe185959e3a280730636d
Reviewed-on: https://chromium-review.googlesource.com/c/1329969
Commit-Queue: Alex Clarke <alexclarke@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607254}
parent 55c56063
...@@ -39,6 +39,10 @@ Optional<TimeDelta> RealTimeDomain::DelayTillNextTask(LazyNow* lazy_now) { ...@@ -39,6 +39,10 @@ Optional<TimeDelta> RealTimeDomain::DelayTillNextTask(LazyNow* lazy_now) {
return delay; return delay;
} }
bool RealTimeDomain::MaybeFastForwardToNextTask() {
return false;
}
const char* RealTimeDomain::GetName() const { const char* RealTimeDomain::GetName() const {
return "RealTimeDomain"; return "RealTimeDomain";
} }
......
...@@ -22,6 +22,7 @@ class BASE_EXPORT RealTimeDomain : public TimeDomain { ...@@ -22,6 +22,7 @@ class BASE_EXPORT RealTimeDomain : public TimeDomain {
LazyNow CreateLazyNow() const override; LazyNow CreateLazyNow() const override;
TimeTicks Now() const override; TimeTicks Now() const override;
Optional<TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override; Optional<TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
bool MaybeFastForwardToNextTask() override;
protected: protected:
const char* GetName() const override; const char* GetName() const override;
......
...@@ -515,6 +515,15 @@ bool SequenceManagerImpl::HasPendingHighResolutionTasks() { ...@@ -515,6 +515,15 @@ bool SequenceManagerImpl::HasPendingHighResolutionTasks() {
return false; return false;
} }
bool SequenceManagerImpl::OnSystemIdle() {
bool have_work_to_do = false;
for (TimeDomain* time_domain : main_thread_only().time_domains) {
if (time_domain->MaybeFastForwardToNextTask())
have_work_to_do = true;
};
return have_work_to_do;
}
void SequenceManagerImpl::WillQueueTask(Task* pending_task) { void SequenceManagerImpl::WillQueueTask(Task* pending_task) {
controller_->WillQueueTask(pending_task); controller_->WillQueueTask(pending_task);
} }
......
...@@ -131,6 +131,7 @@ class BASE_EXPORT SequenceManagerImpl ...@@ -131,6 +131,7 @@ class BASE_EXPORT SequenceManagerImpl
void DidRunTask() override; void DidRunTask() override;
TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override; TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override;
bool HasPendingHighResolutionTasks() override; bool HasPendingHighResolutionTasks() override;
bool OnSystemIdle() override;
// Methods needed for MessageLoopCurrent support. // Methods needed for MessageLoopCurrent support.
// TOOD(alexclarke): Introduce MessageLoopBase and make SequenceManagerImpl // TOOD(alexclarke): Introduce MessageLoopBase and make SequenceManagerImpl
......
...@@ -34,6 +34,11 @@ class SequencedTaskSource { ...@@ -34,6 +34,11 @@ class SequencedTaskSource {
// Return true if there are any pending tasks in the task source which require // Return true if there are any pending tasks in the task source which require
// high resolution timing. // high resolution timing.
virtual bool HasPendingHighResolutionTasks() = 0; virtual bool HasPendingHighResolutionTasks() = 0;
// Called when we have run out of immediate work. If more immediate work
// becomes available as a result of any processing done by this callback,
// return true to schedule a future DoWork.
virtual bool OnSystemIdle() = 0;
}; };
} // namespace internal } // namespace internal
......
...@@ -28,6 +28,10 @@ Optional<TimeDelta> MockTimeDomain::DelayTillNextTask(LazyNow* lazy_now) { ...@@ -28,6 +28,10 @@ Optional<TimeDelta> MockTimeDomain::DelayTillNextTask(LazyNow* lazy_now) {
return nullopt; return nullopt;
} }
bool MockTimeDomain::MaybeFastForwardToNextTask() {
return false;
}
void MockTimeDomain::SetNextDelayedDoWork(LazyNow* lazy_now, void MockTimeDomain::SetNextDelayedDoWork(LazyNow* lazy_now,
TimeTicks run_time) {} TimeTicks run_time) {}
......
...@@ -24,6 +24,7 @@ class MockTimeDomain : public TimeDomain { ...@@ -24,6 +24,7 @@ class MockTimeDomain : public TimeDomain {
TimeTicks Now() const override; TimeTicks Now() const override;
Optional<TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override; Optional<TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
void SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) override; void SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) override;
bool MaybeFastForwardToNextTask() override;
const char* GetName() const override; const char* GetName() const override;
private: private:
......
...@@ -229,13 +229,17 @@ void ThreadControllerImpl::DoWork(WorkType work_type) { ...@@ -229,13 +229,17 @@ void ThreadControllerImpl::DoWork(WorkType work_type) {
DCHECK_GE(any_sequence().do_work_running_count, 0); DCHECK_GE(any_sequence().do_work_running_count, 0);
LazyNow lazy_now(time_source_); LazyNow lazy_now(time_source_);
TimeDelta delay_till_next_task = sequence_->DelayTillNextTask(&lazy_now); TimeDelta delay_till_next_task = sequence_->DelayTillNextTask(&lazy_now);
if (delay_till_next_task <= TimeDelta()) { // The OnSystemIdle callback allows the TimeDomains to advance virtual time
// in which case we now have immediate word to do.
if (delay_till_next_task <= TimeDelta() || sequence_->OnSystemIdle()) {
// The next task needs to run immediately, post a continuation if needed. // The next task needs to run immediately, post a continuation if needed.
if (!any_sequence().immediate_do_work_posted) { if (!any_sequence().immediate_do_work_posted) {
any_sequence().immediate_do_work_posted = true; any_sequence().immediate_do_work_posted = true;
task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_); task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
} }
} else if (delay_till_next_task < TimeDelta::Max()) { return;
}
if (delay_till_next_task < TimeDelta::Max()) {
// The next task needs to run after a delay, post a continuation if // The next task needs to run after a delay, post a continuation if
// needed. // needed.
TimeTicks next_task_at = lazy_now.Now() + delay_till_next_task; TimeTicks next_task_at = lazy_now.Now() + delay_till_next_task;
...@@ -246,10 +250,10 @@ void ThreadControllerImpl::DoWork(WorkType work_type) { ...@@ -246,10 +250,10 @@ void ThreadControllerImpl::DoWork(WorkType work_type) {
FROM_HERE, cancelable_delayed_do_work_closure_.callback(), FROM_HERE, cancelable_delayed_do_work_closure_.callback(),
delay_till_next_task); delay_till_next_task);
} }
} else { return;
// There is no next task scheduled.
main_sequence_only().next_delayed_do_work = TimeTicks::Max();
} }
// There is no next task scheduled.
main_sequence_only().next_delayed_do_work = TimeTicks::Max();
} }
} }
......
...@@ -284,9 +284,6 @@ bool ThreadControllerWithMessagePumpImpl::InTopLevelDoWork() const { ...@@ -284,9 +284,6 @@ bool ThreadControllerWithMessagePumpImpl::InTopLevelDoWork() const {
} }
bool ThreadControllerWithMessagePumpImpl::DoIdleWork() { bool ThreadControllerWithMessagePumpImpl::DoIdleWork() {
// RunLoop::Delegate knows whether we called Run() or RunUntilIdle().
if (ShouldQuitWhenIdle())
Quit();
#if defined(OS_WIN) #if defined(OS_WIN)
bool need_high_res_mode = bool need_high_res_mode =
main_thread_only().task_source->HasPendingHighResolutionTasks(); main_thread_only().task_source->HasPendingHighResolutionTasks();
...@@ -299,6 +296,14 @@ bool ThreadControllerWithMessagePumpImpl::DoIdleWork() { ...@@ -299,6 +296,14 @@ bool ThreadControllerWithMessagePumpImpl::DoIdleWork() {
Time::ActivateHighResolutionTimer(need_high_res_mode); Time::ActivateHighResolutionTimer(need_high_res_mode);
} }
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
if (main_thread_only().task_source->OnSystemIdle())
return true; // Pretend we have done work to ensure DoWork is called.
// RunLoop::Delegate knows whether we called Run() or RunUntilIdle().
if (ShouldQuitWhenIdle())
Quit();
return false; return false;
} }
......
...@@ -105,6 +105,8 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource { ...@@ -105,6 +105,8 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource {
bool HasPendingHighResolutionTasks() override { return false; } bool HasPendingHighResolutionTasks() override { return false; }
bool OnSystemIdle() override { return false; }
private: private:
TickClock* clock_; TickClock* clock_;
std::queue<PendingTask> tasks_; std::queue<PendingTask> tasks_;
......
...@@ -59,6 +59,10 @@ class BASE_EXPORT TimeDomain { ...@@ -59,6 +59,10 @@ class BASE_EXPORT TimeDomain {
void AsValueInto(trace_event::TracedValue* state) const; void AsValueInto(trace_event::TracedValue* state) const;
bool HasPendingHighResolutionTasks() const; bool HasPendingHighResolutionTasks() const;
// This is the signal that virtual time should step forward. Returns true if
// time advanced and there is now a task to run.
virtual bool MaybeFastForwardToNextTask() = 0;
protected: protected:
TimeDomain(); TimeDomain();
......
...@@ -49,6 +49,8 @@ class TestTimeDomain : public TimeDomain { ...@@ -49,6 +49,8 @@ class TestTimeDomain : public TimeDomain {
return Optional<TimeDelta>(); return Optional<TimeDelta>();
} }
bool MaybeFastForwardToNextTask() override { return false; }
void AsValueIntoInternal(trace_event::TracedValue* state) const override {} void AsValueIntoInternal(trace_event::TracedValue* state) const override {}
const char* GetName() const override { return "Test"; } const char* GetName() const override { return "Test"; }
......
...@@ -54,5 +54,9 @@ base::Optional<base::TimeDelta> ThrottledTimeDomain::DelayTillNextTask( ...@@ -54,5 +54,9 @@ base::Optional<base::TimeDelta> ThrottledTimeDomain::DelayTillNextTask(
return base::nullopt; return base::nullopt;
} }
bool ThrottledTimeDomain::MaybeFastForwardToNextTask() {
return false;
}
} // namespace scheduler } // namespace scheduler
} // namespace blink } // namespace blink
...@@ -27,6 +27,7 @@ class PLATFORM_EXPORT ThrottledTimeDomain ...@@ -27,6 +27,7 @@ class PLATFORM_EXPORT ThrottledTimeDomain
base::TimeTicks Now() const override; base::TimeTicks Now() const override;
base::Optional<base::TimeDelta> DelayTillNextTask( base::Optional<base::TimeDelta> DelayTillNextTask(
base::sequence_manager::LazyNow* lazy_now) override; base::sequence_manager::LazyNow* lazy_now) override;
bool MaybeFastForwardToNextTask() override;
protected: protected:
const char* GetName() const override; const char* GetName() const override;
......
...@@ -83,15 +83,25 @@ AutoAdvancingVirtualTimeDomain::DelayTillNextTask( ...@@ -83,15 +83,25 @@ AutoAdvancingVirtualTimeDomain::DelayTillNextTask(
if (run_time <= Now()) if (run_time <= Now())
return base::TimeDelta(); return base::TimeDelta();
// Rely on MaybeFastForwardToNextTask to be called to advance
// virtual time.
return base::nullopt;
}
bool AutoAdvancingVirtualTimeDomain::MaybeFastForwardToNextTask() {
if (!can_advance_virtual_time_) if (!can_advance_virtual_time_)
return base::nullopt; return false;
base::Optional<base::TimeTicks> run_time = NextScheduledRunTime();
if (!run_time)
return false;
if (MaybeAdvanceVirtualTime(*run_time)) { if (MaybeAdvanceVirtualTime(*run_time)) {
task_starvation_count_ = 0; task_starvation_count_ = 0;
return base::TimeDelta(); // Makes DoWork post an immediate continuation. return true;
} }
return base::nullopt; return false;
} }
void AutoAdvancingVirtualTimeDomain::SetNextDelayedDoWork( void AutoAdvancingVirtualTimeDomain::SetNextDelayedDoWork(
......
...@@ -77,6 +77,7 @@ class PLATFORM_EXPORT AutoAdvancingVirtualTimeDomain ...@@ -77,6 +77,7 @@ class PLATFORM_EXPORT AutoAdvancingVirtualTimeDomain
base::TimeTicks Now() const override; base::TimeTicks Now() const override;
base::Optional<base::TimeDelta> DelayTillNextTask( base::Optional<base::TimeDelta> DelayTillNextTask(
base::sequence_manager::LazyNow* lazy_now) override; base::sequence_manager::LazyNow* lazy_now) override;
bool MaybeFastForwardToNextTask() override;
protected: protected:
const char* GetName() const override; const char* GetName() const override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment