blob: f85122d2e252db4acc53b4c69c0cfc57bcf41624 [file]
// Copyright 2025 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_async2/channel.h"
#include "pw_allocator/testing.h"
#include "pw_async2/await.h"
#include "pw_async2/coro.h"
#include "pw_async2/coro_task.h"
#include "pw_async2/dispatcher_for_test.h"
#include "pw_containers/vector.h"
#include "pw_unit_test/framework.h"
namespace {
// DOCSTAG: [pw_async2-examples-channel-manual]
using pw::async2::Context;
using pw::async2::Poll;
using pw::async2::Ready;
using pw::async2::ReceiveFuture;
using pw::async2::Receiver;
using pw::async2::Sender;
using pw::async2::SendFuture;
using pw::async2::Task;
class Producer : public Task {
public:
explicit Producer(Sender<int>&& sender)
: Task(PW_ASYNC_TASK_NAME("Producer")), sender_(std::move(sender)) {}
private:
Poll<> DoPend(Context& cx) override {
while (data_ < 3) {
if (!send_future_.is_pendable()) {
send_future_ = sender_.Send(data_);
}
PW_AWAIT(send_future_, cx);
send_future_ = {};
++data_;
}
sender_.Disconnect();
return Ready();
}
int data_ = 0;
Sender<int> sender_;
SendFuture<int> send_future_;
};
class Consumer : public Task {
public:
explicit Consumer(Receiver<int>&& receiver)
: Task(PW_ASYNC_TASK_NAME("Consumer")), receiver_(std::move(receiver)) {}
const pw::Vector<int>& values() const { return values_; }
private:
Poll<> DoPend(Context& cx) override {
while (true) {
if (!receive_future_.is_pendable()) {
receive_future_ = receiver_.Receive();
}
PW_AWAIT(std::optional<int> result, receive_future_, cx);
if (!result.has_value()) {
break;
}
values_.push_back(*result);
}
receiver_.Disconnect();
return Ready();
}
Receiver<int> receiver_;
ReceiveFuture<int> receive_future_;
pw::Vector<int, 3> values_;
};
// DOCSTAG: [pw_async2-examples-channel-manual]
TEST(Channel, Manual) {
pw::async2::ChannelStorage<int, 1> storage;
auto [channel, sender, receiver] = CreateSpscChannel<int>(storage);
// The returned channel handle is used to create senders and receivers.
// Since this is a single producer single consumer channel, that isn't
// possible, so its only other use is to manually close the channel.
// We don't need that as we rely on automatic closing when the sender
// completes.
//
// It is important to call `Release` once you are done with the handle to
// prevent keeping the channel alive longer than needed.
channel.Release();
Producer producer(std::move(sender));
Consumer consumer(std::move(receiver));
pw::async2::DispatcherForTest dispatcher;
dispatcher.Post(producer);
dispatcher.Post(consumer);
dispatcher.RunToCompletion();
EXPECT_EQ(consumer.values().size(), 3u);
}
} // namespace
namespace {
// DOCSTAG: [pw_async2-examples-channel-coro]
using pw::async2::Coro;
using pw::async2::CoroContext;
using pw::async2::Receiver;
using pw::async2::Sender;
// Returns the number of ints that were successfully sent.
Coro<int> CoroProducer(CoroContext, Sender<int> sender) {
int data = 0;
for (; data < 3; ++data) {
if (!co_await sender.Send(data)) {
break;
}
}
co_return data;
}
// Reads values from the receiver and stores them in `values` until the channel
// closes.
Coro<void> CoroConsumer(CoroContext,
Receiver<int> receiver,
pw::Vector<int>& values) {
while (true) {
std::optional<int> result = co_await receiver.Receive();
if (!result.has_value()) {
break;
}
values.push_back(*result);
}
}
// DOCSTAG: [pw_async2-examples-channel-coro]
TEST(Channel, Coro) {
pw::allocator::test::AllocatorForTest<1024> alloc;
pw::async2::DispatcherForTest dispatcher;
pw::async2::ChannelStorage<int, 1> storage;
auto [channel, sender, receiver] = CreateSpscChannel<int>(storage);
channel.Release();
auto producer = pw::async2::CoroTask(CoroProducer(alloc, std::move(sender)));
pw::Vector<int, 3> values;
auto consumer =
pw::async2::CoroTask(CoroConsumer(alloc, std::move(receiver), values));
dispatcher.Post(producer);
dispatcher.Post(consumer);
dispatcher.RunToCompletion();
EXPECT_EQ(producer.Wait(), 3);
consumer.Join();
EXPECT_EQ(values.size(), 3u);
}
} // namespace