Skip to content

Commit

Permalink
pw_async: Add module with Task and basic & test Dispatchers
Browse files Browse the repository at this point in the history
Land module with experimental flag limiting visibility.

Change-Id: I5652fcb5dbebfc03dbc0bcafb20b2462e3dfea5b
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/110454
Reviewed-by: Erik Gilling <[email protected]>
Commit-Queue: Ali Saeed <[email protected]>
  • Loading branch information
acsaeed authored and CQ Bot Account committed Jan 13, 2023
1 parent 723880e commit 7767406
Show file tree
Hide file tree
Showing 15 changed files with 1,056 additions and 0 deletions.
1 change: 1 addition & 0 deletions PIGWEED_MODULES
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pw_assert_basic
pw_assert_log
pw_assert_tokenized
pw_assert_zephyr
pw_async
pw_base64
pw_bloat
pw_blob_store
Expand Down
27 changes: 27 additions & 0 deletions pw_async/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2023 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

filegroup(
name = "pw_async_files",
srcs = [
"dispatcher_basic.cc",
"dispatcher_basic_test.cc",
"dispatcher_test.cc",
"public/pw_async/dispatcher.h",
"public/pw_async/dispatcher_basic.h",
"public/pw_async/task.h",
"public_test/pw_async/test_dispatcher.h",
"test_dispatcher.cc",
],
)
99 changes: 99 additions & 0 deletions pw_async/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

import("//build_overrides/pigweed.gni")

import("$dir_pw_async/async.gni")
import("$dir_pw_chrono/backend.gni")
import("$dir_pw_docgen/docs.gni")
import("$dir_pw_thread/backend.gni")
import("$dir_pw_unit_test/test.gni")

config("public_include_path") {
include_dirs = [ "public" ]
}

config("public_test_include_path") {
include_dirs = [ "public_test" ]
}

pw_source_set("task") {
public_configs = [ ":public_include_path" ]
public_deps = [
"$dir_pw_chrono:system_clock",
"$dir_pw_containers:intrusive_list",
dir_pw_function,
]
public = [ "public/pw_async/task.h" ]
visibility = [ ":*" ] + pw_async_EXPERIMENTAL_MODULE_VISIBILITY
}

pw_source_set("dispatcher") {
public_configs = [ ":public_include_path" ]
public_deps = [ ":task" ]
public = [ "public/pw_async/dispatcher.h" ]
visibility = [ ":*" ] + pw_async_EXPERIMENTAL_MODULE_VISIBILITY
}

pw_source_set("test_dispatcher") {
public_configs = [ ":public_test_include_path" ]
public_deps = [
":pw_dispatcher_basic",
dir_pw_log,
]
public = [ "public_test/pw_async/test_dispatcher.h" ]
sources = [ "test_dispatcher.cc" ]
visibility = [ ":*" ] + pw_async_EXPERIMENTAL_MODULE_VISIBILITY
}

pw_source_set("pw_dispatcher_basic") {
public_configs = [ ":public_include_path" ]
public = [ "public/pw_async/dispatcher_basic.h" ]
public_deps = [
":dispatcher",
"$dir_pw_sync:interrupt_spin_lock",
"$dir_pw_sync:timed_thread_notification",
"$dir_pw_thread:thread",
dir_pw_log,
]
sources = [ "dispatcher_basic.cc" ]
visibility = [ ":*" ] + pw_async_EXPERIMENTAL_MODULE_VISIBILITY
}

pw_test("dispatcher_test") {
enable_if = pw_chrono_SYSTEM_CLOCK_BACKEND != ""
deps = [ ":test_dispatcher" ]
sources = [ "dispatcher_test.cc" ]
}

pw_test("dispatcher_basic_test") {
enable_if = pw_chrono_SYSTEM_CLOCK_BACKEND != "" &&
pw_thread_THREAD_BACKEND == "$dir_pw_thread_stl:thread"
public_deps = [
":pw_dispatcher_basic",
dir_pw_log,
]
sources = [ "dispatcher_basic_test.cc" ]
}

pw_test_group("tests") {
tests = [
":dispatcher_test",
":dispatcher_basic_test",
]
}

pw_doc_group("docs") {
sources = [ "docs.rst" ]
}
Empty file added pw_async/README.md
Empty file.
20 changes: 20 additions & 0 deletions pw_async/async.gni
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

declare_args() {
# To depend on pw_async, add targets to this list.
#
# WARNING: This is experimental and *not* guaranteed to work.
pw_async_EXPERIMENTAL_MODULE_VISIBILITY = []
}
140 changes: 140 additions & 0 deletions pw_async/dispatcher_basic.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_async/dispatcher_basic.h"

#include "pw_log/log.h"

using namespace std::chrono_literals;

namespace pw::async {

const chrono::SystemClock::duration SLEEP_DURATION = 5s;

void BasicDispatcher::Run() {
lock_.lock();
while (!stop_requested_) {
RunLoopOnce();
}
lock_.unlock();
}

void BasicDispatcher::RunUntilIdle() {
lock_.lock();
while (!task_queue_.empty()) {
RunLoopOnce();
}
lock_.unlock();
}

void BasicDispatcher::RunUntil(chrono::SystemClock::time_point end_time) {
lock_.lock();
while (end_time < Now()) {
RunLoopOnce();
}
lock_.unlock();
}

void BasicDispatcher::RunFor(chrono::SystemClock::duration duration) {
RunUntil(Now() + duration);
}

void BasicDispatcher::RunLoopOnce() {
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
: DueTime(task_queue_.front());

lock_.unlock();
PW_LOG_DEBUG("no task due; waiting for signal");
timed_notification_.try_acquire_until(wake_time);
lock_.lock();

return;
}

while (!task_queue_.empty() && DueTime(task_queue_.front()) <= Now()) {
Task& task = task_queue_.front();
task_queue_.pop_front();

if (IsPeriodic(task)) {
PostTaskInternal(task, DueTime(task) + SetInterval(task));
}

lock_.unlock();
PW_LOG_DEBUG("running task");
Context ctx{this, &task};
task(ctx);
lock_.lock();
}
}

void BasicDispatcher::RequestStop() {
std::lock_guard lock(lock_);
PW_LOG_DEBUG("stop requested");
stop_requested_ = true;
task_queue_.clear();
timed_notification_.release();
}

void BasicDispatcher::PostTask(Task& task) { PostTaskForTime(task, Now()); }

void BasicDispatcher::PostDelayedTask(Task& task,
chrono::SystemClock::duration delay) {
PostTaskForTime(task, Now() + delay);
}

void BasicDispatcher::PostTaskForTime(Task& task,
chrono::SystemClock::time_point time) {
lock_.lock();
PW_LOG_DEBUG("posting task");
PostTaskInternal(task, time);
lock_.unlock();
}

void BasicDispatcher::SchedulePeriodicTask(
Task& task, chrono::SystemClock::duration interval) {
SchedulePeriodicTask(task, interval, Now());
}

void BasicDispatcher::SchedulePeriodicTask(
Task& task,
chrono::SystemClock::duration interval,
chrono::SystemClock::time_point start_time) {
SetInterval(task, interval);
PostTaskForTime(task, start_time);
}

bool BasicDispatcher::Cancel(Task& task) {
std::lock_guard lock(lock_);
return task_queue_.remove(task);
}

// Ensure lock_ is held when invoking this function.
void BasicDispatcher::PostTaskInternal(
Task& task, chrono::SystemClock::time_point time_due) {
SetDueTime(task, time_due);
auto it_front = task_queue_.begin();
auto it_behind = task_queue_.before_begin();
while (it_front != task_queue_.end() && time_due > DueTime(*it_front)) {
++it_front;
++it_behind;
}
task_queue_.insert_after(it_behind, task);
timed_notification_.release();
}

} // namespace pw::async
Loading

0 comments on commit 7767406

Please sign in to comment.