pw_async: Define RunUntilIdle() behavior and tweak helper methods

Comment that Dispatcher::RunUntilIdle() executes all runnable
tasks and returns without waiting. Comment that
FakeDispatcher::RunUntilIdle() executes all runnable tasks and
returns without advancing simulated time.

Split BasicDispatcher::RunLoopOnce() into MaybeWait() and
ExecuteDueTasks().

Remove comments redundant with lock annotations.

Change-Id: I0d705dff0eac3daa2e6ea5566273cfa08b7a4e1c
diff --git a/pw_async/fake_dispatcher_test.cc b/pw_async/fake_dispatcher_test.cc
index 73dc090..6e21d63 100644
--- a/pw_async/fake_dispatcher_test.cc
+++ b/pw_async/fake_dispatcher_test.cc
@@ -45,6 +45,10 @@
   Task task3([&tp]([[maybe_unused]] Context& c) { ++tp.count; });
   dispatcher.PostTask(task3);
 
+  // Should not run; RunUntilIdle() does not advance time.
+  Task task4([&tp]([[maybe_unused]] Context& c) { ++tp.count; });
+  dispatcher.PostDelayedTask(task4, 1ms);
+
   dispatcher.RunUntilIdle();
   dispatcher.RequestStop();
 
@@ -81,7 +85,7 @@
   tp.task_b.set_function(
       [&tp]([[maybe_unused]] Context& c) { tp.count = tp.count * 10 + 2; });
 
-  dispatcher.RunUntilIdle();
+  dispatcher.RunFor(200ms);
   dispatcher.RequestStop();
 
   ASSERT_TRUE(tp.count == 1234);
@@ -149,7 +153,7 @@
       [&periodic_task](Context& c) { c.dispatcher->Cancel(periodic_task); });
   dispatcher.PostDelayedTask(cancel_task, 100ms);
 
-  dispatcher.RunUntilIdle();
+  dispatcher.RunFor(300ms);
   dispatcher.RequestStop();
 
   ASSERT_TRUE(tp.count == 3);
diff --git a/pw_async/public/pw_async/dispatcher.h b/pw_async/public/pw_async/dispatcher.h
index 98185d9..33d50e8 100644
--- a/pw_async/public/pw_async/dispatcher.h
+++ b/pw_async/public/pw_async/dispatcher.h
@@ -20,7 +20,7 @@
 class Task;
 
 /// Asynchronous Dispatcher abstract class. A default implementation is provided
-/// in dispatcher_basic.h.
+/// in pw_async_basic.
 ///
 /// Dispatcher implements VirtualSystemClock so the Dispatcher's time can be
 /// injected into other modules under test. This is useful for consistently
@@ -60,7 +60,7 @@
   /// Periodic tasks may be posted once more after they are canceled.
   virtual bool Cancel(Task& task) = 0;
 
-  /// Execute tasks until the Dispatcher enters a state where none are queued.
+  /// Execute all runnable tasks and return without waiting.
   virtual void RunUntilIdle() = 0;
 
   /// Run the Dispatcher until Now() has reached `end_time`, executing all tasks
diff --git a/pw_async/public/pw_async/fake_dispatcher.h b/pw_async/public/pw_async/fake_dispatcher.h
index 0497637..f6cae7b 100644
--- a/pw_async/public/pw_async/fake_dispatcher.h
+++ b/pw_async/public/pw_async/fake_dispatcher.h
@@ -66,7 +66,7 @@
   // Periodic tasks may run once more after they are canceled.
   bool Cancel(Task& task) override { return native_dispatcher_.Cancel(task); }
 
-  // Execute tasks until the Dispatcher enters a state where none are queued.
+  // Execute all runnable tasks and return without advancing simulated time.
   void RunUntilIdle() override { native_dispatcher_.RunUntilIdle(); }
 
   // Run the Dispatcher until Now() has reached `end_time`, executing all tasks
diff --git a/pw_async_basic/dispatcher.cc b/pw_async_basic/dispatcher.cc
index 3ab4813..017d119 100644
--- a/pw_async_basic/dispatcher.cc
+++ b/pw_async_basic/dispatcher.cc
@@ -26,23 +26,23 @@
 void BasicDispatcher::Run() {
   lock_.lock();
   while (!stop_requested_) {
-    RunLoopOnce();
+    MaybeSleep();
+    ExecuteDueTasks();
   }
   lock_.unlock();
 }
 
 void BasicDispatcher::RunUntilIdle() {
   lock_.lock();
-  while (!task_queue_.empty()) {
-    RunLoopOnce();
-  }
+  ExecuteDueTasks();
   lock_.unlock();
 }
 
 void BasicDispatcher::RunUntil(chrono::SystemClock::time_point end_time) {
   lock_.lock();
   while (end_time < now()) {
-    RunLoopOnce();
+    MaybeSleep();
+    ExecuteDueTasks();
   }
   lock_.unlock();
 }
@@ -51,7 +51,7 @@
   RunUntil(now() + duration);
 }
 
-void BasicDispatcher::RunLoopOnce() {
+void BasicDispatcher::MaybeSleep() {
   if (task_queue_.empty() || task_queue_.front().due_time_ > 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
@@ -64,10 +64,10 @@
     PW_LOG_DEBUG("no task due; waiting for signal");
     timed_notification_.try_acquire_until(wake_time);
     lock_.lock();
-
-    return;
   }
+}
 
+void BasicDispatcher::ExecuteDueTasks() {
   while (!task_queue_.empty() && task_queue_.front().due_time_ <= now()) {
     backend::NativeTask& task = task_queue_.front();
     task_queue_.pop_front();
@@ -126,7 +126,6 @@
   return task_queue_.remove(task.native_type());
 }
 
-// Ensure lock_ is held when invoking this function.
 void BasicDispatcher::PostTaskInternal(
     backend::NativeTask& task, chrono::SystemClock::time_point time_due) {
   task.due_time_ = time_due;
diff --git a/pw_async_basic/fake_dispatcher.cc b/pw_async_basic/fake_dispatcher.cc
index ac999fd..9576c56 100644
--- a/pw_async_basic/fake_dispatcher.cc
+++ b/pw_async_basic/fake_dispatcher.cc
@@ -26,19 +26,12 @@
 
 NativeFakeDispatcher::~NativeFakeDispatcher() { RequestStop(); }
 
-void NativeFakeDispatcher::RunUntilIdle() {
-  while (!task_queue_.empty()) {
-    // Only advance to the due time of the next task because new tasks can be
-    // scheduled in the next task.
-    now_ = task_queue_.front().due_time();
-    RunLoopOnce();
-  }
-}
+void NativeFakeDispatcher::RunUntilIdle() { ExecuteDueTasks(); }
 
 void NativeFakeDispatcher::RunUntil(chrono::SystemClock::time_point end_time) {
   while (!task_queue_.empty() && task_queue_.front().due_time() <= end_time) {
     now_ = task_queue_.front().due_time();
-    RunLoopOnce();
+    ExecuteDueTasks();
   }
 
   if (now_ < end_time) {
@@ -50,7 +43,7 @@
   RunUntil(now() + duration);
 }
 
-void NativeFakeDispatcher::RunLoopOnce() {
+void NativeFakeDispatcher::ExecuteDueTasks() {
   while (!task_queue_.empty() && task_queue_.front().due_time() <= now()) {
     ::pw::async::backend::NativeTask& task = task_queue_.front();
     task_queue_.pop_front();
diff --git a/pw_async_basic/public/pw_async_basic/dispatcher.h b/pw_async_basic/public/pw_async_basic/dispatcher.h
index 2e31cde..ec97edb 100644
--- a/pw_async_basic/public/pw_async_basic/dispatcher.h
+++ b/pw_async_basic/public/pw_async_basic/dispatcher.h
@@ -55,18 +55,17 @@
 
  private:
   // Insert |task| into task_queue_ maintaining its min-heap property, keyed by
-  // |time_due|. Must be holding lock_ when calling this function.
+  // |time_due|.
   void PostTaskInternal(backend::NativeTask& task,
                         chrono::SystemClock::time_point time_due)
       PW_EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
-  // If no tasks are due, sleeps until a notification is received or until the
-  // due time of the next task.
-  //
-  // If at least one task is due, dequeues and runs each task that is due.
-  //
-  // Must be holding lock_ when calling this function.
-  void RunLoopOnce() PW_EXCLUSIVE_LOCKS_REQUIRED(lock_);
+  // If no tasks are due, sleep until a notification is received, the next task
+  // comes due, or a timeout elapses; whichever occurs first.
+  void MaybeSleep() PW_EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
+  // Dequeue and run each task that is due.
+  void ExecuteDueTasks() PW_EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   sync::InterruptSpinLock lock_;
   sync::TimedThreadNotification timed_notification_;
diff --git a/pw_async_basic/public/pw_async_basic/fake_dispatcher.h b/pw_async_basic/public/pw_async_basic/fake_dispatcher.h
index 49938fb..7cf4b00 100644
--- a/pw_async_basic/public/pw_async_basic/fake_dispatcher.h
+++ b/pw_async_basic/public/pw_async_basic/fake_dispatcher.h
@@ -53,8 +53,8 @@
   void PostTaskInternal(::pw::async::backend::NativeTask& task,
                         chrono::SystemClock::time_point time_due);
 
-  // Executes all pending tasks with a due time <= now().
-  void RunLoopOnce();
+  // Dequeue and run each task that is due.
+  void ExecuteDueTasks();
 
   Dispatcher& dispatcher_;