Skip to content

Commit

Permalink
pw_async: Document and test Cancel and re-Post
Browse files Browse the repository at this point in the history
Change-Id: If735d76fd4673f4ff13bcf08312aab7bda7aaccb
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/146599
Presubmit-Verified: CQ Bot Account <[email protected]>
Reviewed-by: Ali Saeed <[email protected]>
Reviewed-by: Keir Mierle <[email protected]>
Commit-Queue: Taylor Cramer <[email protected]>
  • Loading branch information
cramertj authored and CQ Bot Account committed May 12, 2023
1 parent a9706f7 commit 157fcd9
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 6 deletions.
99 changes: 99 additions & 0 deletions pw_async/fake_dispatcher_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,105 @@ TEST(FakeDispatcher, PostAfterRunsTasksInSequence) {
EXPECT_EQ(task_run_order, expected_run_order);
}

TEST(FakeDispatcher, PostAfterWithEarlierTimeRunsSooner) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.PostAfter(task, 100ms);
dispatcher.PostAfter(task, 50ms);
dispatcher.RunFor(60ms);
EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
}

TEST(FakeDispatcher, PostAfterWithLaterTimeRunsSooner) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.PostAfter(task, 50ms);
dispatcher.PostAfter(task, 100ms);
dispatcher.RunFor(60ms);
EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
}

TEST(FakeDispatcher, PostThenPostAfterRunsImmediately) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.Post(task);
dispatcher.PostAfter(task, 50ms);
dispatcher.RunUntilIdle();
EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
}

TEST(FakeDispatcher, PostAfterThenPostRunsImmediately) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.PostAfter(task, 50ms);
dispatcher.Post(task);
dispatcher.RunUntilIdle();
EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
}

TEST(FakeDispatcher, CancelAfterPostStopsTaskFromRunning) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.Post(task);
EXPECT_TRUE(dispatcher.Cancel(task));
dispatcher.RunUntilIdle();
EXPECT_EQ(counter.counts, CallCounts{});
}

TEST(FakeDispatcher, CancelAfterPostAfterStopsTaskFromRunning) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.PostAfter(task, 50ms);
EXPECT_TRUE(dispatcher.Cancel(task));
dispatcher.RunFor(60ms);
EXPECT_EQ(counter.counts, CallCounts{});
}

TEST(FakeDispatcher, CancelAfterPostAndPostAfterStopsTaskFromRunning) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.Post(task);
dispatcher.PostAfter(task, 50ms);
EXPECT_TRUE(dispatcher.Cancel(task));
dispatcher.RunFor(60ms);
EXPECT_EQ(counter.counts, CallCounts{});
}

TEST(FakeDispatcher, PostAgainAfterCancelRuns) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.Post(task);
EXPECT_TRUE(dispatcher.Cancel(task));
dispatcher.Post(task);
dispatcher.RunUntilIdle();
EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
}

TEST(FakeDispatcher, CancelWithoutPostReturnsFalse) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
EXPECT_FALSE(dispatcher.Cancel(task));
}

TEST(FakeDispatcher, CancelAfterRunningReturnsFalse) {
FakeDispatcher dispatcher;
CallCounter counter;
Task task(counter.fn());
dispatcher.Post(task);
dispatcher.RunUntilIdle();
EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
EXPECT_FALSE(dispatcher.Cancel(task));
}

TEST(FakeDispatcher, CancelInsideOtherTaskCancelsTaskWithoutRunningIt) {
FakeDispatcher dispatcher;

Expand Down
23 changes: 17 additions & 6 deletions pw_async/public/pw_async/dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,34 @@ class Dispatcher : public chrono::VirtualSystemClock {
/// Posted tasks execute in the order they are posted. This ensures that
/// tasks can re-post themselves and yield in order to allow other tasks the
/// opportunity to execute.
///
/// A given |task| must only be posted to a single `Dispatcher`.
virtual void Post(Task& task) { PostAt(task, now()); }

/// Post caller owned |task| to be run after |delay|.
///
/// If |task| was already posted to run at an earlier time (before |delay|
/// would expire), |task| must be run at the earlier time, and |task|
/// *may* also be run at the later time.
virtual void PostAfter(Task& task, chrono::SystemClock::duration delay) {
PostAt(task, now() + delay);
}

/// Post caller owned |task| to be run at |time|.
///
/// If |task| was already posted to run before |time|,
/// |task| must be run at the earlier time, and |task| *may* also be run at
/// the later time.
virtual void PostAt(Task& task, chrono::SystemClock::time_point time) = 0;

/// Request that a task not be invoked again.
/// Prevent a `Post`ed task from starting.
///
/// Periodic tasks may be posted once more after they are canceled. Tasks may
/// be canceled from within a `TaskFunction` by calling
/// `context.dispatcher.Cancel(context.task)`.
/// @return true if `task` successfully canceled, false otherwise. If
/// cancelation fails, the task may be running or completed.
/// Returns:
/// true: the task was successfully canceled and will not be run by the
/// dispatcher until `Post`ed again.
/// false: the task could not be cancelled because it either was not
/// posted, already ran, or is currently running on the `Dispatcher`
/// thread.
virtual bool Cancel(Task& task) = 0;
};

Expand Down

0 comments on commit 157fcd9

Please sign in to comment.