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