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