xref: /aosp_15_r20/external/pigweed/pw_async2/examples/basic.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_allocator/allocator.h"
16 #include "pw_async2/dispatcher.h"
17 #include "pw_status/status.h"
18 
19 namespace {
20 
21 using ::pw::OkStatus;
22 using ::pw::Result;
23 using ::pw::Status;
24 using ::pw::async2::Context;
25 using ::pw::async2::Poll;
26 
27 class MyData {
28  public:
29 };
30 
31 class ReceiveFuture {
32  public:
Pend(Context &)33   Poll<Result<MyData>> Pend(Context&) { return MyData(); }
34 };
35 
36 class MyReceiver {
37  public:
Receive()38   ReceiveFuture Receive() { return ReceiveFuture(); }
PendReceive(Context &)39   Poll<Result<MyData>> PendReceive(Context&) { return MyData(); }
40 };
41 
42 class SendFuture {
43  public:
Pend(Context &)44   Poll<Status> Pend(Context&) { return OkStatus(); }
45 };
46 
47 class MySender {
48  public:
Send(MyData &&)49   SendFuture Send(MyData&&) { return SendFuture(); }
50 };
51 
52 }  // namespace
53 
54 // NOTE: we double-include so that the example shows the `#includes`, but we
55 // can still define types beforehand.
56 
57 // DOCSTAG: [pw_async2-examples-basic-manual]
58 #include "pw_async2/dispatcher.h"
59 #include "pw_async2/poll.h"
60 #include "pw_async2/try.h"
61 #include "pw_log/log.h"
62 #include "pw_result/result.h"
63 
64 namespace {
65 
66 using ::pw::async2::Context;
67 using ::pw::async2::Pending;
68 using ::pw::async2::Poll;
69 using ::pw::async2::Ready;
70 using ::pw::async2::Task;
71 
72 class MyReceiver;
73 class MySender;
74 
75 // Receive then send that data asynchronously. If the reader or writer aren't
76 // ready, the task suspends when TRY_READY invokes ``return Pending()``.
77 class ReceiveAndSend final : public Task {
78  public:
ReceiveAndSend(MyReceiver receiver,MySender sender)79   ReceiveAndSend(MyReceiver receiver, MySender sender)
80       : receiver_(receiver), sender_(sender), state_(kReceiving) {}
81 
DoPend(Context & cx)82   Poll<> DoPend(Context& cx) final {
83     switch (state_) {
84       case kReceiving: {
85         PW_TRY_READY_ASSIGN(auto new_data, receiver_.PendReceive(cx));
86         if (!new_data.ok()) {
87           PW_LOG_ERROR("Receiving failed: %s", new_data.status().str());
88           return Ready();  // Completes the task.
89         }
90         // Start transmitting and switch to transmitting state.
91         send_future_ = sender_.Send(std::move(*new_data));
92         state_ = kTransmitting;
93       }
94         [[fallthrough]];
95       case kTransmitting: {
96         PW_TRY_READY_ASSIGN(auto sent, send_future_->Pend(cx));
97         if (!sent.ok()) {
98           PW_LOG_ERROR("Sending failed: %s", sent.str());
99         }
100         return Ready();  // Completes the task.
101       }
102     }
103   }
104 
105  private:
106   MyReceiver receiver_;  // Can receive data async.
107   MySender sender_;      // Can send data async.
108   std::optional<SendFuture> send_future_ = std::nullopt;
109 
110   enum State { kTransmitting, kReceiving };
111   State state_;
112 };
113 
114 }  // namespace
115 // DOCSTAG: [pw_async2-examples-basic-manual]
116 
117 // DOCSTAG: [pw_async2-examples-basic-coro]
118 #include "pw_allocator/allocator.h"
119 #include "pw_async2/coro.h"
120 #include "pw_log/log.h"
121 #include "pw_result/result.h"
122 
123 namespace {
124 
125 using ::pw::OkStatus;
126 using ::pw::Result;
127 using ::pw::Status;
128 using ::pw::async2::Coro;
129 using ::pw::async2::CoroContext;
130 
131 class MyReceiver;
132 class MySender;
133 
134 /// Create a coroutine which asynchronously receives a value from
135 /// ``receiver`` and forwards it to ``sender``.
136 ///
137 /// Note: the ``CoroContext`` argument is used by the ``Coro<T>`` internals to
138 /// allocate the coroutine state. If this allocation fails, ``Coro<Status>``
139 /// will return ``Status::Internal()``.
ReceiveAndSendCoro(CoroContext &,MyReceiver receiver,MySender sender)140 Coro<Status> ReceiveAndSendCoro(CoroContext&,
141                                 MyReceiver receiver,
142                                 MySender sender) {
143   Result<MyData> data = co_await receiver.Receive();
144   if (!data.ok()) {
145     PW_LOG_ERROR("Receiving failed: %s", data.status().str());
146     co_return Status::Unavailable();
147   }
148   Status sent = co_await sender.Send(std::move(*data));
149   if (!sent.ok()) {
150     PW_LOG_ERROR("Sending failed: %s", sent.str());
151     co_return Status::Unavailable();
152   }
153   co_return OkStatus();
154 }
155 
156 }  // namespace
157 // DOCSTAG: [pw_async2-examples-basic-coro]
158 
159 #include "pw_allocator/testing.h"
160 
161 namespace {
162 
163 using ::pw::OkStatus;
164 using ::pw::allocator::test::AllocatorForTest;
165 using ::pw::async2::Context;
166 using ::pw::async2::Coro;
167 using ::pw::async2::CoroContext;
168 using ::pw::async2::Dispatcher;
169 using ::pw::async2::Pending;
170 using ::pw::async2::Poll;
171 using ::pw::async2::Ready;
172 using ::pw::async2::Task;
173 
TEST(ManualExample,ReturnsOk)174 TEST(ManualExample, ReturnsOk) {
175   auto task = ReceiveAndSend(MyReceiver(), MySender());
176   Dispatcher dispatcher;
177   dispatcher.Post(task);
178   EXPECT_TRUE(dispatcher.RunUntilStalled().IsReady());
179 }
180 
TEST(ManualExample,Runs)181 TEST(ManualExample, Runs) {
182   auto receiver = MyReceiver();
183   auto sender = MySender();
184   // DOCSTAG: [pw_async2-examples-basic-dispatcher]
185   auto task = ReceiveAndSend(std::move(receiver), std::move(sender));
186   Dispatcher dispatcher;
187   // Registers `task` to run on the dispatcher.
188   dispatcher.Post(task);
189   // Sets the dispatcher to run until all `Post`ed tasks have completed.
190   dispatcher.RunToCompletion();
191   // DOCSTAG: [pw_async2-examples-basic-dispatcher]
192 }
193 
TEST(CoroExample,ReturnsOk)194 TEST(CoroExample, ReturnsOk) {
195   AllocatorForTest<256> alloc;
196   CoroContext coro_cx(alloc);
197   auto coro = ReceiveAndSendCoro(coro_cx, MyReceiver(), MySender());
198   Dispatcher dispatcher;
199   EXPECT_EQ(dispatcher.RunPendableUntilStalled(coro), Ready(OkStatus()));
200 }
201 
202 }  // namespace
203