xref: /aosp_15_r20/external/pigweed/pw_async/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_async:
2
3========
4pw_async
5========
6.. pigweed-module::
7   :name: pw_async
8
9--------
10Overview
11--------
12Pigweed's async module provides portable APIs and utilities for writing
13asynchronous code. Currently, it provides:
14
15- Message loop APIs
16
17.. attention::
18  This module is still under construction. The API is not yet stable.
19
20----------
21Dispatcher
22----------
23Dispatcher is an API for a message loop that schedules and executes Tasks. See
24:bdg-ref-primary-line:`module-pw_async_basic` for an example implementation.
25
26Dispatcher is a pure virtual interface that is implemented by backends and
27FakeDispatcher. A virtual interface is used instead of a facade to allow
28substituting a FakeDispatcher for a Dispatcher backend in tests.
29
30Dispatcher API
31==============
32.. doxygenclass:: pw::async::Dispatcher
33   :members:
34
35
36Task API
37==============
38.. doxygenstruct:: pw::async::Context
39   :members:
40
41.. doxygentypedef:: pw::async::TaskFunction
42
43.. doxygenclass:: pw::async::Task
44   :members:
45
46Facade API
47==========
48
49Task
50----
51The ``Task`` type represents a work item that can be submitted to and executed
52by a ``Dispatcher``.
53
54To run work on a ``Dispatcher`` event loop, a ``Task`` can be constructed from
55a function or lambda (see ``pw::async::TaskFunction``) and submitted to run
56using the ``pw::async::Dispatcher::Post`` method (and its siblings, ``PostAt``
57etc.).
58
59The ``Task`` facade enables backends to provide custom storage containers for
60``Task`` s, as well as to keep per- ``Task`` data alongside the ``TaskFunction``
61(such as ``next`` pointers for intrusive linked-lists of ``Task``).
62
63The active Task backend is configured with the GN variable
64``pw_async_TASK_BACKEND``. The specified target must define a class
65``pw::async::backend::NativeTask`` in the header ``pw_async_backend/task.h``
66that meets the interface requirements in ``public/pw_async/task.h``. Task will
67then trivially wrap ``NativeTask``.
68
69The bazel build provides the ``pw_async_task_backend`` label flag to configure
70the active Task backend.
71
72FakeDispatcher
73--------------
74The FakeDispatcher facade is a utility for simulating a real Dispatcher
75in tests. FakeDispatcher simulates time to allow for reliable, fast testing of
76code that uses Dispatcher. FakeDispatcher is a facade instead of a concrete
77implementation because it depends on Task state for processing tasks, which
78varies across Task backends.
79
80The active FakeDispatcher backend is configured with the GN variable
81``pw_async_FAKE_DISPATCHER_BACKEND``. The specified target must define a class
82``pw::async::test::backend::NativeFakeDispatcher`` in the header
83``pw_async_backend/fake_dispatcher.h`` that meets the interface requirements in
84``public/pw_async/task.h``. FakeDispatcher will then trivially wrap
85``NativeFakeDispatcher``.
86
87The bazel build provides the ``pw_async_fake_dispatcher_backend`` label flag to
88configure the FakeDispatcher backend.
89
90Testing FakeDispatcher
91^^^^^^^^^^^^^^^^^^^^^^
92The GN template ``fake_dispatcher_tests`` in ``fake_dispatcher_tests.gni``
93creates a test target that tests a FakeDispatcher backend. This enables
94one test suite to be shared across FakeDispatcher backends and ensures
95conformance.
96
97FunctionDispatcher
98------------------
99.. doxygenclass:: pw::async::FunctionDispatcher
100   :members:
101
102HeapDispatcher
103--------------
104.. doxygenclass:: pw::async::HeapDispatcher
105   :members:
106
107Design
108======
109
110Task Ownership
111--------------
112Tasks are owned by clients rather than the Dispatcher. This avoids either
113memory allocation or queue size limits in Dispatcher implementations. However,
114care must be taken that clients do not destroy Tasks before they have been
115executed or canceled.
116
117Getting Started
118===============
119First, configure the Task backend for the Dispatcher backend you will be using:
120
121.. code-block::
122
123   pw_async_TASK_BACKEND = "$dir_pw_async_basic:task"
124
125
126Next, create an executable target that depends on the Dispatcher backend you
127want to use:
128
129.. code-block::
130
131   pw_executable("hello_world") {
132     sources = [ "main.cc" ]
133     deps = [ "$dir_pw_async_basic:dispatcher" ]
134   }
135
136Next, instantiate the Dispatcher and post a task:
137
138.. code-block:: cpp
139
140   #include "pw_async_basic/dispatcher.h"
141
142   int main() {
143     BasicDispatcher dispatcher;
144
145     // Spawn a thread for the dispatcher to run on.
146     Thread work_thread(thread::stl::Options(), dispatcher);
147
148     Task task([](pw::async::Context& ctx){
149       printf("hello world\n");
150       ctx.dispatcher->RequestStop();
151     });
152
153     // Execute `task` in 5 seconds.
154     dispatcher.PostAfter(task, 5s);
155
156     // Blocks until `task` runs.
157     work_thread.join();
158     return 0;
159   }
160
161The above example runs the dispatcher on a new thread, but it can also run on
162the current/main thread:
163
164.. code-block:: cpp
165
166   #include "pw_async_basic/dispatcher.h"
167
168   int main() {
169     BasicDispatcher dispatcher;
170
171     Task task([](pw::async::Context& ctx){
172       printf("hello world\n");
173     });
174
175     // Execute `task` in 5 seconds.
176     dispatcher.PostAfter(task, 5s);
177
178     dispatcher.Run();
179     return 0;
180   }
181
182Fake Dispatcher
183===============
184To test async code, FakeDispatcher should be dependency injected in place of
185Dispatcher. Then, time should be driven in unit tests using the ``Run*()``
186methods. For convenience, you can use the test fixture
187FakeDispatcherFixture.
188
189.. doxygenclass:: pw::async::test::FakeDispatcherFixture
190   :members:
191
192.. attention::
193
194   ``FakeDispatcher::now()`` will return the simulated time.
195   ``Dispatcher::now()`` should therefore be used to get the current time in
196   async code instead of other sources of time to ensure consistent time values
197   and reliable tests.
198
199-------
200Roadmap
201-------
202- Stabilize Task cancellation API
203- Utility for dynamically allocated Tasks
204- CMake support
205- Support for C++20 coroutines
206
207.. toctree::
208   :hidden:
209   :maxdepth: 1
210
211   Backends <backends>
212