pw_async: Implement VirtualSystemClock for Dispatchers

Implement VirtualSystemClock in BasicDispatcher and TestDispatcher.

Bug: b/254532947
Change-Id: I50861ea67d833d01b7af377b2e739526397ec53c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/126930
Reviewed-by: Erik Gilling <konkers@google.com>
Commit-Queue: Ben Lawson <benlawson@google.com>
diff --git a/pw_async/BUILD.gn b/pw_async/BUILD.gn
index 42f1dc3..ebed69f 100644
--- a/pw_async/BUILD.gn
+++ b/pw_async/BUILD.gn
@@ -41,7 +41,10 @@
 
 pw_source_set("dispatcher") {
   public_configs = [ ":public_include_path" ]
-  public_deps = [ ":task" ]
+  public_deps = [
+    ":task",
+    "$dir_pw_chrono:system_clock",
+  ]
   public = [ "public/pw_async/dispatcher.h" ]
   visibility = [ ":*" ] + pw_async_EXPERIMENTAL_MODULE_VISIBILITY
 }
diff --git a/pw_async/dispatcher_basic.cc b/pw_async/dispatcher_basic.cc
index 03394fe..2425b11 100644
--- a/pw_async/dispatcher_basic.cc
+++ b/pw_async/dispatcher_basic.cc
@@ -39,23 +39,23 @@
 
 void BasicDispatcher::RunUntil(chrono::SystemClock::time_point end_time) {
   lock_.lock();
-  while (end_time < Now()) {
+  while (end_time < now()) {
     RunLoopOnce();
   }
   lock_.unlock();
 }
 
 void BasicDispatcher::RunFor(chrono::SystemClock::duration duration) {
-  RunUntil(Now() + duration);
+  RunUntil(now() + duration);
 }
 
 void BasicDispatcher::RunLoopOnce() {
-  if (task_queue_.empty() || DueTime(task_queue_.front()) > Now()) {
+  if (task_queue_.empty() || DueTime(task_queue_.front()) > now()) {
     // Sleep until a notification is received or until the due time of the
     // next task. Notifications are sent when tasks are posted or 'stop' is
     // requested.
     chrono::SystemClock::time_point wake_time =
-        task_queue_.empty() ? Now() + SLEEP_DURATION
+        task_queue_.empty() ? now() + SLEEP_DURATION
                             : DueTime(task_queue_.front());
 
     lock_.unlock();
@@ -66,7 +66,7 @@
     return;
   }
 
-  while (!task_queue_.empty() && DueTime(task_queue_.front()) <= Now()) {
+  while (!task_queue_.empty() && DueTime(task_queue_.front()) <= now()) {
     Task& task = task_queue_.front();
     task_queue_.pop_front();
 
@@ -90,11 +90,11 @@
   timed_notification_.release();
 }
 
-void BasicDispatcher::PostTask(Task& task) { PostTaskForTime(task, Now()); }
+void BasicDispatcher::PostTask(Task& task) { PostTaskForTime(task, now()); }
 
 void BasicDispatcher::PostDelayedTask(Task& task,
                                       chrono::SystemClock::duration delay) {
-  PostTaskForTime(task, Now() + delay);
+  PostTaskForTime(task, now() + delay);
 }
 
 void BasicDispatcher::PostTaskForTime(Task& task,
@@ -107,7 +107,7 @@
 
 void BasicDispatcher::SchedulePeriodicTask(
     Task& task, chrono::SystemClock::duration interval) {
-  SchedulePeriodicTask(task, interval, Now());
+  SchedulePeriodicTask(task, interval, now());
 }
 
 void BasicDispatcher::SchedulePeriodicTask(
diff --git a/pw_async/dispatcher_test.cc b/pw_async/dispatcher_test.cc
index b37a3ae..dba0723 100644
--- a/pw_async/dispatcher_test.cc
+++ b/pw_async/dispatcher_test.cc
@@ -141,7 +141,7 @@
   TestPrimitives tp;
 
   Task periodic_task([&tp]([[maybe_unused]] Context& c) { ++tp.count; });
-  dispatcher.SchedulePeriodicTask(periodic_task, 20ms, dispatcher.Now() + 50ms);
+  dispatcher.SchedulePeriodicTask(periodic_task, 20ms, dispatcher.now() + 50ms);
 
   // Cancel periodic task after it has run thrice, at +50ms, +70ms, and +90ms.
   Task cancel_task(
diff --git a/pw_async/public/pw_async/dispatcher.h b/pw_async/public/pw_async/dispatcher.h
index df44920..bdd6e19 100644
--- a/pw_async/public/pw_async/dispatcher.h
+++ b/pw_async/public/pw_async/dispatcher.h
@@ -14,20 +14,23 @@
 #pragma once
 
 #include "pw_async/task.h"
+#include "pw_chrono/system_clock.h"
 
 namespace pw::async {
 
 // Asynchronous Dispatcher abstract class. A default implementation is provided
 // in dispatcher_basic.h.
-class Dispatcher {
+//
+// Dispatcher implements VirtualSystemClock so the Dispatcher's time can be
+// injected into other modules under test. This is useful for consistently
+// simulating time when using TestDispatcher (rather than using
+// chrono::SimulatedSystemClock separately).
+class Dispatcher : public chrono::VirtualSystemClock {
  public:
-  virtual ~Dispatcher() = default;
+  ~Dispatcher() override = default;
 
   virtual void RequestStop() = 0;
 
-  // Returns the current time as viewed by the Dispatcher.
-  virtual chrono::SystemClock::time_point Now() = 0;
-
   // Post caller owned |task|.
   virtual void PostTask(Task& task) = 0;
 
diff --git a/pw_async/public/pw_async/dispatcher_basic.h b/pw_async/public/pw_async/dispatcher_basic.h
index 482990c..51efc91 100644
--- a/pw_async/public/pw_async/dispatcher_basic.h
+++ b/pw_async/public/pw_async/dispatcher_basic.h
@@ -28,11 +28,6 @@
 
   void RequestStop() override PW_LOCKS_EXCLUDED(lock_);
 
-  // Returns the current time as viewed by the BasicDispatcher.
-  chrono::SystemClock::time_point Now() override {
-    return chrono::SystemClock::now();
-  }
-
   // Post caller owned |task|.
   void PostTask(Task& task) override;
 
@@ -68,6 +63,13 @@
   void RunUntil(chrono::SystemClock::time_point end_time) override;
   void RunFor(chrono::SystemClock::duration duration) override;
 
+  // VirtualSystemClock overrides:
+
+  // Returns the current time as viewed by the BasicDispatcher.
+  chrono::SystemClock::time_point now() override {
+    return chrono::SystemClock::now();
+  }
+
  private:
   // TestDispatcher uses BasicDispatcher methods operating on Task state.
   friend class TestDispatcher;
diff --git a/pw_async/public_test/pw_async/test_dispatcher.h b/pw_async/public_test/pw_async/test_dispatcher.h
index 0c86026..2a396cb 100644
--- a/pw_async/public_test/pw_async/test_dispatcher.h
+++ b/pw_async/public_test/pw_async/test_dispatcher.h
@@ -22,8 +22,6 @@
   explicit TestDispatcher() {}
   ~TestDispatcher() override { RequestStop(); }
 
-  chrono::SystemClock::time_point Now() override { return now_; }
-
   void RequestStop() override;
 
   // Post caller owned |task|.
@@ -64,6 +62,10 @@
   // come due in that period.
   void RunFor(chrono::SystemClock::duration duration) override;
 
+  // VirtualSystemClock overrides:
+
+  chrono::SystemClock::time_point now() override { return now_; }
+
  private:
   // Insert |task| into task_queue_ maintaining its min-heap property, keyed by
   // |time_due|.
diff --git a/pw_async/test_dispatcher.cc b/pw_async/test_dispatcher.cc
index 66da26a..64b484a 100644
--- a/pw_async/test_dispatcher.cc
+++ b/pw_async/test_dispatcher.cc
@@ -42,12 +42,12 @@
 }
 
 void TestDispatcher::RunFor(chrono::SystemClock::duration duration) {
-  RunUntil(Now() + duration);
+  RunUntil(now() + duration);
 }
 
 void TestDispatcher::RunLoopOnce() {
   while (!task_queue_.empty() &&
-         BasicDispatcher::DueTime(task_queue_.front()) <= Now()) {
+         BasicDispatcher::DueTime(task_queue_.front()) <= now()) {
     Task& task = task_queue_.front();
     task_queue_.pop_front();
 
@@ -67,11 +67,11 @@
   task_queue_.clear();
 }
 
-void TestDispatcher::PostTask(Task& task) { PostTaskForTime(task, Now()); }
+void TestDispatcher::PostTask(Task& task) { PostTaskForTime(task, now()); }
 
 void TestDispatcher::PostDelayedTask(Task& task,
                                      chrono::SystemClock::duration delay) {
-  PostTaskForTime(task, Now() + delay);
+  PostTaskForTime(task, now() + delay);
 }
 
 void TestDispatcher::PostTaskForTime(Task& task,
@@ -82,7 +82,7 @@
 
 void TestDispatcher::SchedulePeriodicTask(
     Task& task, chrono::SystemClock::duration interval) {
-  SchedulePeriodicTask(task, interval, Now());
+  SchedulePeriodicTask(task, interval, now());
 }
 
 void TestDispatcher::SchedulePeriodicTask(