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_;