• Home
  • Annotate
  • current directory
Name Date Size #Lines LOC

..21-Aug-2025-

include/lib/async/21-Aug-2025-

MAINTAINERS A D21-Aug-202522 21

README.md A D21-Aug-202510.3 KiB262199

default.c A D21-Aug-2025468 189

exception.cpp A D21-Aug-20252.4 KiB7651

ops.c A D21-Aug-20252.6 KiB6348

receiver.cpp A D21-Aug-20251 KiB3218

rules.mk A D21-Aug-20252.1 KiB10363

task.cpp A D21-Aug-20254.1 KiB12184

trap.cpp A D21-Aug-20251.1 KiB3319

wait.cpp A D21-Aug-20252 KiB6339

README.md

1# libasync and friends
2
3This set of libraries defines a C and C++ language interface for initiating
4asynchronous operations and dispatching their results to callback functions.
5
6The purpose of these libraries is to decouple clients which want to perform
7asynchronous operations from the message loop implementations which dispatch
8the results of those operations.  This makes it an important building block
9for other abstractions such as the FIDL bindings.
10
11## Libraries
12
13The async package consists of three libraries:
14
15- `libasync.a` provides the C client API which includes all of the function
16and structures declared in the following headers:
17    - [async/dispatcher.h](include/lib/async/dispatcher.h)
18    - [async/exception.h](include/lib/async/exception.h)
19    - [async/receiver.h](include/lib/async/receiver.h)
20    - [async/task.h](include/lib/async/task.h)
21    - [async/time.h](include/lib/async/time.h)
22    - [async/trap.h](include/lib/async/trap.h)
23    - [async/wait.h](include/lib/async/wait.h)
24
25- `libasync-cpp.a` provides C++ wrappers:
26    - [async/cpp/exception.h](include/lib/async/cpp/exception.h)
27    - [async/cpp/receiver.h](include/lib/async/cpp/receiver.h)
28    - [async/cpp/task.h](include/lib/async/cpp/task.h)
29    - [async/cpp/time.h](include/lib/async/cpp/time.h)
30    - [async/cpp/trap.h](include/lib/async/cpp/trap.h)
31    - [async/cpp/wait.h](include/lib/async/cpp/wait.h)
32
33- `libasync-default.so` provides functions for getting or setting a thread-local
34default asynchronous dispatcher as declared in [async/default.h](include/lib/async/default.h).
35
36See also [libasync-loop.a](../async-loop/README.md) which provides a general-purpose
37implementation of `async_dispatcher_t`.
38
39## Using the asynchronous dispatcher
40
41### Waiting for signals
42
43To asynchronously wait for signals, the client prepares an `async_wait_t`
44structure then calls `async_begin_wait()` to register it with the dispatcher.
45When the wait completes, the dispatcher invokes the handler.
46
47The client can register handlers from any thread but dispatch will occur
48on a thread of the dispatcher's choosing depending on its implementation.
49
50The client is responsible for ensuring that the wait structure remains in
51memory until the wait's handler runs or the wait is successfully canceled using
52`async_cancel_wait()`.
53
54See [async/wait.h](include/lib/async/wait.h) for details.
55
56```c
57#include <lib/async/wait.h>     // for async_begin_wait()
58#include <lib/async/default.h>  // for async_get_default_dispatcher()
59
60void handler(async_dispatcher_t* async, async_wait_t* wait,
61             zx_status_t status, const zx_packet_signal_t* signal) {
62    printf("signal received: status=%d, observed=%d", status, signal ? signal->observed : 0);
63    free(wait);
64}
65
66zx_status_t await(zx_handle_t object, zx_signals_t trigger, void* data) {
67    async_dispatcher_t* async = async_get_default_dispatcher();
68    async_wait_t* wait = calloc(1, sizeof(async_wait_t));
69    wait->handler = handler;
70    wait->object = object;
71    wait->trigger = trigger;
72    return async_begin_wait(async, wait);
73}
74```
75
76### Waiting for exceptions
77
78To asynchronously wait for exceptions, the client prepares an
79`async_exception_t` structure then calls `async_bind_exception_port()`
80to register it with the dispatcher. When an exception happens, the dispatcher
81invokes the handler.
82
83The client can register handlers from any thread but dispatch will occur
84on a thread of the dispatcher's choosing depending on its implementation.
85
86The client is responsible for ensuring that the exception structure remains in
87memory, and the task handle contained therein remains valid, until the port is
88successfully unbound using `async_unbind_exception_port()`.
89
90See [async/exception.h](include/lib/async/exception.h) for details.
91
92```c
93#include <lib/async/wait.h>     // for async_begin_wait()
94#include <lib/async/default.h>  // for async_get_default_dispatcher()
95
96void handler(async_dispatcher_t* async, async_exception_t* exception,
97             zx_status_t status, const zx_port_packet_t* exception_packet) {
98    printf("signal received: status=%d, kind=0x%x",
99           status, exception ? exception->type : 0);
100    if (status == ZX_OK) {
101        // ... process exception ...
102        // N.B. If the port is attached to a process or job then we will
103        // get exceptions for more than just |exception->task|.
104        zx_handle_t task = handle_of(exception_packet->exception.tid);
105        uint32_t options = exception_handled ? 0u : ZX_RESUME_TRY_NEXT;
106        status = async_resume_from_exception(async, exception, task, options);
107        // ... examine status ...
108    }
109}
110
111zx_status_t ebind(async_exception_handler_t* handler,
112                  zx_handle_t task, uint32_t options,
113                  zx_async_exception_t* out_exception) {
114    async_dispatcher_t* async = async_get_default_dispatcher();
115    async_exception_t* exception = calloc(1, sizeof(async_exception_t));
116    exception->handler = handler;
117    exception->task = task;
118    exception->options = options;
119    zx_status_t status = async_bind_exception_port(async, exception);
120    if (status == ZX_OK) {
121        *out_exception = exception;
122    } else }
123        free(exception);
124    }
125    return status;
126}
127
128zx_status_t eunbind(async_exception_t* exception) {
129    async_dispatcher_t* async = async_get_default_dispatcher();
130    zx_status_t status = async_unbind_exception_port(dispatcher, exception);
131    free(exception);
132    return status;
133}
134```
135
136### Getting the current time
137
138The dispatcher represents time in the form of a `zx_time_t`.  In normal
139operation, values of this type represent a moment in the `ZX_CLOCK_MONOTONIC`
140time base.  However for unit testing purposes, dispatchers may use a synthetic
141time base instead.
142
143To make unit testing easier, prefer using `async_now()` to get the current
144time according the dispatcher's time base.
145
146See [async/time.h](include/lib/async/time.h) for details.
147
148### Posting tasks and getting the current time
149
150To schedule asynchronous tasks, the client prepares an `async_task_t`
151structure then calls `async_post_task()` to register it with the dispatcher.
152When the task comes due, the dispatcher invokes the handler.
153
154The client can post tasks from any thread but dispatch will occur
155on a thread of the dispatcher's choosing depending on its implementation.
156
157The client is responsible for ensuring that the task structure remains in
158memory until the task's handler runs or the task is successfully canceled using
159`async_cancel_task()`.
160
161See [async/task.h](include/lib/async/task.h) for details.
162
163```c
164#include <lib/async/task.h>     // for async_post_task()
165#include <lib/async/time.h>     // for async_now()
166#include <lib/async/default.h>  // for async_get_default_dispatcher()
167
168typedef struct {
169    async_task_t task;
170    void* data;
171} task_data_t;
172
173void handler(async_dispatcher_t* async, async_task_t* task, zx_status_t status) {
174    task_data_t* task_data = (task_data_t*)task;
175    printf("task deadline elapsed: status=%d, data=%p", status, task_data->data);
176    free(task_data);
177}
178
179zx_status_t schedule_work(void* data) {
180    async_dispatcher_t* async = async_get_default_dispatcher();
181    task_data_t* task_data = calloc(1, sizeof(task_data_t));
182    task_data->task.handler = handler;
183    task_data->task.deadline = async_now(async) + ZX_SEC(2);
184    task_data->data = data;
185    return async_post_task(async, &task_data->task);
186}
187```
188
189### Delivering packets to a receiver
190
191Occasionally it may be useful to register a receiver which will be the
192recipient of multiple data packets instead of allocating a separate task
193structure for each one.  The Zircon port takes care of storing the queued
194packet data contents until it is delivered.
195
196The client can queue packets from any thread but dispatch will occur
197on a thread of the dispatcher's choosing depending on its implementation.
198
199The client is responsible for ensuring that the receiver structure remains in
200memory until all queued packets have been delivered.
201
202See [async/receiver.h](include/lib/async/receiver.h) for details.
203
204```c
205#include <lib/async/receiver.h>  // for async_queue_packet()
206#include <lib/async/default.h>   // for async_get_default_dispatcher()
207
208void handler(async_dispatcher_t* async, async_receiver_t* receiver, zx_status_t status,
209             const zx_packet_user_t* data) {
210    printf("packet received: status=%d, data.u32[0]=%d", status, data ? data.u32[0] : 0);
211}
212
213const async_receiver_t receiver = {
214    .state = ASYNC_STATE_INIT,
215    .handler = handler;
216}
217
218zx_status_t send(const zx_packet_user_t* data) {
219    async_dispatcher_t* async = async_get_default_dispatcher();
220    return async_queue_packet(async, &receiver, data);
221}
222```
223
224## The default async dispatcher
225
226As a client of the async dispatcher, where should you get your `async_dispatcher_t*` from?
227
228The ideal answer is for the `async_dispatcher_t*` to be passed into your code when it is
229initialized.  However sometimes this becomes burdensome or isn't practical.
230
231For this reason, the `libasync-default.so` shared library provides functions
232for getting or setting a thread-local default `async_dispatcher_t*` using
233`async_get_default_dispatcher()` or `async_set_default_dispatcher()`.
234
235This makes it easy to retrieve the `async_dispatcher_t*` from the ambient environment
236by calling `async_get_default_dispatcher()`, which is used by many libraries.
237
238Message loop implementations should register themselves as the default
239dispatcher any threads they service.
240
241See [async/default.h](include/lib/async/default.h) for details.
242
243## Using the C++ helpers
244
245`libasync-cpp.a` includes helper classes such as `Wait`, `Task`, and `Receiver`
246which wrap the C API with a more convenient type safe interface for use
247in C++.
248
249Note that the C API can of course be used directly from C++ for special
250situations which may not be well addressed by the wrappers.
251
252## Implementing a dispatcher
253
254The `async_ops_t` interface is a low-level abstraction for asynchronous
255dispatchers.  You can make custom implementations of this interface to
256integrate clients of this library with your own dispatcher.
257
258It is possible to implement only some of the operations but this may cause
259incompatibilities with certain clients.
260
261See [async/dispatcher.h](include/lib/async/dispatcher.h) for details.
262