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