• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_async2:
2
3=============
4pw_async2
5=============
6.. pigweed-module::
7   :name: pw_async2
8
9   - **Simple Ownership**: Say goodbye to that jumble of callbacks and shared
10     state! Complex tasks with many concurrent elements can be expressed by
11     simply combining smaller tasks.
12   - **Efficient**: No dynamic memory allocation required.
13   - **Pluggable**: Your existing event loop, work queue, or task scheduler
14     can run the ``Dispatcher`` without any extra threads.
15   - **Coroutine-capable**: C++20 coroutines and Rust ``async fn`` work just
16     like other tasks, and can easily plug into an existing ``pw_async2``
17     systems.
18
19:cpp:class:`pw::async2::Task` is Pigweed's async primitive. ``Task`` objects
20are cooperatively-scheduled "threads" which yield to the
21:cpp:class:`pw::async2::Dispatcher` when waiting. When the ``Task`` is able to make
22progress, the ``Dispatcher`` will run it again. For example:
23
24.. code-block:: cpp
25
26   #include "pw_async2/dispatcher.h"
27   #include "pw_async2/poll.h"
28
29   #include "pw_result/result.h"
30
31   using ::pw::async2::Context;
32   using ::pw::async2::Poll;
33   using ::pw::async2::Ready;
34   using ::pw::async2::Pending;
35   using ::pw::async2::Task;
36
37   class ReceiveAndSend: public Task {
38    public:
39     ReceiveAndSend(Receiver receiver, Sender sender):
40       receiver_(receiver), sender_(sender) {}
41
42     Poll<> Pend(Context& cx) {
43       if (!send_future_) {
44         // ``PendReceive`` checks for available data or errors.
45         //
46         // If no data is available, it will grab a ``Waker`` from
47         // ``cx.Waker()`` and return ``Pending``. When data arrives,
48         // it will call ``waker.Wake()`` which tells the ``Dispatcher`` to
49         // ``Pend`` this ``Task`` again.
50         Poll<pw::Result<Data>> new_data = receiver_.PendReceive(cx);
51         if (new_data.is_pending()) {
52           // The ``Task`` is still waiting on data. Return ``Pending``,
53           // yielding to the dispatcher. ``Pend`` will be called again when
54           // data becomes available.
55           return Pending();
56         }
57         if (!new_data->ok()) {
58           PW_LOG_ERROR("Receiving failed: %s", data->status().str());
59           // The ``Task`` completed;
60           return Ready();
61         }
62         Data& data = **new_data;
63         send_future_ = sender_.Send(std::move(data));
64       }
65       // ``PendSend`` attempts to send ``data_``, returning ``Pending`` if
66       // ``sender_`` was not yet able to accept ``data_``.
67       Poll<pw::Status> sent = send_future_.Pend(cx);
68       if (sent.is_pending()) {
69         return Pending();
70       }
71       if (!sent->ok()) {
72         PW_LOG_ERROR("Sending failed: %s", sent->str());
73       }
74       return Ready();
75     }
76    private:
77     Receiver receiver_;
78     Sender sender_;
79
80     // ``SendFuture`` is some type returned by `Sender::Send` that offers a
81     // ``Pend`` method similar to the one on ``Task``.
82     std::optional<SendFuture> send_future_ = std::nullopt;
83   };
84
85Tasks can then be run on a :cpp:class:`pw::async2::Dispatcher` using the
86:cpp:func:`pw::async2::Dispatcher::Post` method:
87
88.. code-block:: cpp
89
90   #include "pw_async2/dispatcher.h"
91
92   int main() {
93     ReceiveAndSendTask task(SomeMakeReceiverFn(), SomeMakeSenderFn());
94     Dispatcher dispatcher;
95     dispatcher.Post(task);
96     dispatcher.RunUntilComplete(task);
97     return 0;
98   }
99
100.. _module-pw_async2-coroutines:
101
102----------
103Coroutines
104----------
105C++20 users can also define tasks using coroutines!
106
107.. literalinclude:: examples/coro.cc
108   :language: cpp
109   :linenos:
110   :start-after: [pw_async2-examples-coro-injection]
111   :end-before: [pw_async2-examples-coro-injection]
112
113Any value with a ``Poll<T> Pend(Context&)`` method can be passed to
114``co_await``, which will return with a ``T`` when the result is ready.
115
116To return from a coroutine, ``co_return <expression>`` must be used instead of
117the usual ``return <expression>`` syntax. Because of this, the
118:c:macro:`PW_TRY` and :c:macro:`PW_TRY_ASSIGN` macros are not usable within
119coroutines. :c:macro:`PW_CO_TRY` and :c:macro:`PW_CO_TRY_ASSIGN` should be
120used instead.
121
122For a more detailed explanation of Pigweed's coroutine support, see the
123documentation on the :cpp:class:`pw::async2::Coro<T>` type.
124
125-----------------
126C++ API reference
127-----------------
128.. doxygenclass:: pw::async2::Task
129  :members:
130
131.. doxygenclass:: pw::async2::Poll
132  :members:
133
134.. doxygenfunction:: pw::async2::Ready()
135
136.. doxygenfunction:: pw::async2::Ready(std::in_place_t, Args&&... args)
137
138.. doxygenfunction:: pw::async2::Ready(T&& value)
139
140.. doxygenfunction:: pw::async2::Pending()
141
142.. doxygenclass:: pw::async2::Context
143  :members:
144
145.. doxygenclass:: pw::async2::Waker
146  :members:
147
148.. doxygenclass:: pw::async2::Dispatcher
149  :members:
150
151.. doxygenclass:: pw::async2::Coro
152  :members:
153
154.. doxygenclass:: pw::async2::CoroContext
155  :members:
156
157-------------
158C++ Utilities
159-------------
160.. doxygenfunction:: pw::async2::AllocateTask(pw::allocator::Allocator& allocator, Pendable&& pendable)
161
162.. doxygenfunction:: pw::async2::AllocateTask(pw::allocator::Allocator& allocator, Args&&... args)
163
164.. doxygenclass:: pw::async2::PendFuncTask
165  :members:
166
167.. doxygenclass:: pw::async2::PendableAsTask
168  :members:
169
170.. toctree::
171   :hidden:
172   :maxdepth: 1
173
174   Backends <backends>
175