blob: 460440e1ab70e45c1324e11f9509ec16409da78d [file]
// Copyright 2026 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 <utility>
#include "pw_allocator/synchronized_allocator.h"
#include "pw_allocator/testing.h"
#include "pw_assert/check.h"
#include "pw_async2/dispatcher_for_test.h"
#include "pw_async2/func_task.h"
#include "pw_async2/future_task.h"
#include "pw_async2/try.h"
#include "pw_async2/value_future.h"
#include "pw_log/log.h"
#include "pw_sync/mutex.h"
#include "pw_thread/test_thread_context.h"
#include "pw_thread/thread.h"
#include "pw_unit_test/framework.h"
namespace {
pw::async2::Poll<int> ReadSensor(pw::async2::Context& cx) {
static bool read_value = false;
if (read_value) {
return cx.Unschedule(); // For test use, only produce one value
}
read_value = true;
return pw::async2::Ready(2468);
}
float ConvertSensorReadingToValue(int reading) {
return static_cast<float>(reading);
}
// DOCSTAG: [pw_async2-examples-future-task-owned]
int FutureTaskOwnsTheFuture(pw::async2::Dispatcher& dispatcher,
pw::async2::ValueFuture<int>&& future) {
pw::async2::FutureTask task(std::move(future));
dispatcher.Post(task);
return task.Wait(); // Join the FutureTask and return its value
}
// DOCSTAG: [pw_async2-examples-future-task-owned]
// DOCSTAG: [pw_async2-examples-future-task-ref]
int FutureTaskReferencesTheFuture(pw::async2::Dispatcher& dispatcher,
pw::async2::ValueFuture<int>& future) {
pw::async2::FutureTask task(future);
dispatcher.Post(task);
return task.Wait(); // Join the FutureTask and return its value
}
// DOCSTAG: [pw_async2-examples-future-task-ref]
} // namespace
class TaskHelpersExampleTest : public ::testing::Test {
public:
TaskHelpersExampleTest()
: allocator_(allocator_for_test_),
thread_(pw::Thread(context_.options(), [this] {
dispatcher_.RunToCompletionUntilReleased();
})) {}
~TaskHelpersExampleTest() override {
dispatcher_.Release();
thread_.join();
}
pw::allocator::test::AllocatorForTest<1024> allocator_for_test_;
pw::allocator::SynchronizedAllocator<pw::sync::Mutex> allocator_;
pw::async2::DispatcherForTest dispatcher_;
private:
pw::thread::test::TestThreadContext context_;
pw::Thread thread_;
};
class ExampleClass {
public:
pw::async2::Poll<> RunCalibration(pw::async2::Context&) {
return pw::async2::Ready();
}
void RunCalibrationTask(pw::async2::Dispatcher& dispatcher) {
// DOCSTAG: [pw_async2-examples-func-task-class]
pw::async2::FuncTask task(
[this](pw::async2::Context& cx) { return RunCalibration(cx); });
dispatcher.Post(task);
// DOCSTAG: [pw_async2-examples-func-task-class]
task.Join();
}
};
TEST_F(TaskHelpersExampleTest, FuncTaskLambda) {
pw::async2::ValueProvider<float> float_provider;
pw::async2::FutureTask future_task(float_provider.Get());
dispatcher_.Post(future_task);
// DOCSTAG: [pw_async2-examples-func-task]
pw::async2::FuncTask task(
[&float_provider](pw::async2::Context& cx) -> pw::async2::Poll<> {
while (true) {
// Read a raw sensor sample when one is available.
PW_TRY_READY_ASSIGN(int raw_sample, ReadSensor(cx));
// Convert the raw values to standard units.
float sensor_value = ConvertSensorReadingToValue(raw_sample);
float_provider.Resolve(sensor_value);
}
});
dispatcher_.Post(task);
// DOCSTAG: [pw_async2-examples-func-task]
EXPECT_EQ(future_task.Wait(), 2468.f);
}
TEST_F(TaskHelpersExampleTest, FuncTaskMemberFunction) {
ExampleClass object;
object.RunCalibrationTask(dispatcher_);
}
TEST_F(TaskHelpersExampleTest, FutureTaskOwnsTheFuture) {
EXPECT_EQ(FutureTaskOwnsTheFuture(
dispatcher_, pw::async2::ValueFuture<int>::Resolved(1234)),
1234);
}
TEST_F(TaskHelpersExampleTest, FutureTaskReferencesTheFuture) {
auto future = pw::async2::ValueFuture<int>::Resolved(1234);
EXPECT_EQ(FutureTaskReferencesTheFuture(dispatcher_, future), 1234);
}
TEST_F(TaskHelpersExampleTest, RunOnceTask) {
// DOCSTAG: [pw_async2-examples-run-once]
// Calculate the 8th fibonacci number on the dispatcher thread.
pw::async2::RunOnceTask task([] {
int a = 0, b = 1;
for (int i = 3; i <= 8; i++) {
a = std::exchange(b, a + b);
}
return b;
});
dispatcher_.Post(task);
int result = task.Wait(); // returns 13
// DOCSTAG: [pw_async2-examples-run-once]
EXPECT_EQ(result, 13);
}
TEST_F(TaskHelpersExampleTest, PostSharedTask) {
// DOCSTAG: [pw_async2-examples-allocate-custom-task]
class MyTask : public pw::async2::Task {
public:
MyTask(int value) : value_(value) {}
private:
pw::async2::Poll<> DoPend(pw::async2::Context&) override {
for (int i = 0; i < value_; ++i) {
// Do some work.
}
return pw::async2::Ready();
}
int value_;
};
// Allocate MyTask(123) and posts it to the dispatcher.
PW_CHECK(dispatcher_.Post<MyTask>(allocator_, 123) != nullptr);
// DOCSTAG: [pw_async2-examples-allocate-custom-task]
// DOCSTAG: [pw_async2-examples-allocate-lambda]
PW_CHECK(dispatcher_.Post(allocator_, [](pw::async2::Context&) {
return pw::async2::Ready();
}) != nullptr);
// DOCSTAG: [pw_async2-examples-allocate-lambda]
}
TEST_F(TaskHelpersExampleTest, PostSharedFuture) {
// DOCSTAG: [pw_async2-examples-allocate-future-task]
pw::async2::ValueProvider<void> provider;
PW_CHECK(dispatcher_.PostFuture(allocator_, provider.Get()) != nullptr);
// DOCSTAG: [pw_async2-examples-allocate-future-task]
provider.Resolve();
}
TEST_F(TaskHelpersExampleTest, AllocateAndRunOnce) {
// DOCSTAG: [pw_async2-examples-allocate-run-once]
PW_CHECK(dispatcher_.RunOnce(
allocator_, [] { PW_LOG_DEBUG("Running once!"); }) != nullptr);
// DOCSTAG: [pw_async2-examples-allocate-run-once]
}