blob: b954677e86ec53606e2afde5a8b9d9dc45e1b8ef [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_allocator/testing.h"
#include "pw_async2/dispatcher.h"
#include "pw_status/status.h"
// DOCSTAG: [pw_async2-examples-basic-manual]
#include "pw_async2/await.h"
#include "pw_async2/channel.h"
#include "pw_async2/dispatcher_for_test.h"
#include "pw_async2/poll.h"
#include "pw_log/log.h"
namespace {
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;
// Receive then send that data asynchronously. If the receiver or sender
// isn't ready, the task suspends when `PW_AWAIT` returns
// `Pending()`.
class ForwardingTask final : public Task {
public:
ForwardingTask(Receiver<int> receiver, Sender<int> sender)
: receiver_(std::move(receiver)),
sender_(std::move(sender)),
state_(kReceiving) {}
Poll<> DoPend(Context& cx) final {
receive_future_ = receiver_.Receive();
switch (state_) {
case kReceiving: {
PW_AWAIT(std::optional<int> new_data, receive_future_, cx);
if (!new_data.has_value()) {
PW_LOG_ERROR("Receive failed: channel has closed");
return Ready(); // Completes the task.
}
// Start transmitting and switch to transmitting state.
send_future_ = sender_.Send(*new_data);
state_ = kTransmitting;
}
[[fallthrough]];
case kTransmitting: {
PW_AWAIT(bool sent, send_future_, cx);
if (!sent) {
PW_LOG_ERROR("Send failed: channel has closed");
}
return Ready(); // Completes the task.
}
}
}
private:
// Can receive data async, tracking state in a future.
Receiver<int> receiver_;
ReceiveFuture<int> receive_future_;
// Can send data async, tracking state in a future.
Sender<int> sender_;
SendFuture<int> send_future_;
enum State { kReceiving, kTransmitting };
State state_;
};
} // namespace
// DOCSTAG: [pw_async2-examples-basic-manual]
namespace {
using ::pw::async2::ChannelStorage;
using ::pw::async2::CreateSpscChannel;
using ::pw::async2::DispatcherForTest;
TEST(ManualExample, Runs) {
// DOCSTAG: [pw_async2-examples-basic-dispatcher]
ChannelStorage<int, 1> storage_a;
auto [handle_a, sender_a, receiver_a] = CreateSpscChannel(storage_a);
ChannelStorage<int, 1> storage_b;
auto [handle_b, sender_b, receiver_b] = CreateSpscChannel(storage_b);
ForwardingTask task(std::move(receiver_a), std::move(sender_b));
DispatcherForTest dispatcher;
// Registers `task` to run on the dispatcher.
dispatcher.Post(task);
// Runs the dispatcher until all `Post`ed tasks are blocked.
dispatcher.RunUntilStalled();
// DOCSTAG: [pw_async2-examples-basic-dispatcher]
EXPECT_EQ(sender_a.TrySend(42), pw::OkStatus());
dispatcher.AllowBlocking();
dispatcher.RunToCompletion();
handle_a.Release();
handle_b.Release();
EXPECT_EQ(receiver_b.TryReceive().value(), 42);
}
} // namespace