blob: be1d610cb67083e3a569e3b3340b21180a550713 [file] [log] [blame] [view] [edit]
# zxio
The zxio client library provides an object oriented abstraction on top of basic
I/O primitives such as files, pipes, directories, and sockets. This library is
intended for use in low-level system libraries such as fdio and language
runtimes to provide a minimal abstraction over different representations and
protocols used for common primitives.
Internally, this library uses the FIDL fuchsia.io* family of protocols and
Zircon syscalls.
# Objects
The zxio library is based on objects of type zxio_t. An object represents a
single logical entity and a set of operations that can be performed on that
entity. Internally an object may contain one or more Zircon handles to kernel
objects.
## Storage and lifetime of objects
The caller of the zxio library is responsible for providing storage for zxio
objects and is responsible for their lifetime. A zxio object lives within an
instance of the zxio_storage_t type.
## Kernel objects
A zxio object may allocate and retain ownership of one or more kernel objects
during the course of operation. Destroying an object will close all handles
owned by a zxio object. Calling `zxio_release()` will close all handles except for
the extracted primary object handle.
Functions with `zx_handle_t` parameters take ownership of the provided handles in
all cases including errors unless the function documentation specifically states
otherwise.
Functions with `zx_handle_t*` out parameters will do one of the following unless
the function documentation specifically states otherwise:
- Store a valid handle value into the provided address and return success. The
caller has ownership of the handle in this case.
- Store the value `ZX_HANDLE_INVALID` into the provided address and return an
error code.
- Do not store anything into the provided address and return an error code.
## Object types
The zxio library has an extensible set of object types. The library provides
many object types internally and allows users of the library to define their own
types by implementing the zxio ops table defined in zxio/ops.h. Custom object
implementers should adhere to the threading model for those objects.
## Threading model
Operations on zxio objects are thread-safe unless otherwise noted below. The
zxio library will internally synchronize any local state changes.
### Creation and destruction
A zxio object must be fully created with one of the `zxio_create..()` functions
before it can be used.
A zxio object is destroyed with the `zxio_close()` call. The caller is responsible
for ensuring that the zxio object is not in use on any thread before calling
`zxio_close()`. The `zxio_close()` operations always destroys the object and frees
any resources associated with the object even in error cases. The close
operation cannot be retried.
The storage underlying the zxio object should not be modified except by the zxio
library or custom object implementations and must remain valid until after
`zxio_close()` returns.
### Intrusive and compound operations
Some zxio operations provide access to or rely on the internal state of the
object and have additional considerations.
- `zxio_release()` extracts a handle from the zxio object and is partially
destructive on the object. The caller is responsible for ensuring that the zxio
object is not in use on any thread before calling `zxio_release()`. After
calling `zxio_release()`, the only operation that is safe to perform on the zxio
object is `zxio_close()`. `zxio_close()` must be called before the storage
underlying the object is deallocated.
- `zxio_borrow()` returns a handle to the zxio object's primary object if it
exists. The caller must take care to ensure that they do not close this handle
and that any operations they perform are safe with concurrent operations on the
object.
- `zxio_dirent_iterator_init()` creates an iterator object tied to a directory
object. The directory object must outlive the iterator object.
- `zxio_watch_directory()` initiates a watch operation tied to a directory object.
The operation must conclude before the directory can be destroyed.
## Blocking and cancellation
Many zxio operations are synchronized with a kernel object and possibly remote
server before returning. These operations will return the error
ZX_ERR_WOULD_BLOCK if the operation is unable to make progress in the object's
current state. Such operations may block if the remote server is unresponsive.
They should not block based on the object's internal state.
Operations which will never synchronize with a remote server are
documented as such. Some operations also expose a variant with the suffix
`_async` to indicate that they do not block and that the caller is responsible
for completing the remainder of the operation. `zxio_open_async()` is an
example of such an operation. It solicits an event from the server backing the
opened object into a provided channel object. The caller can wait for the
channel to become readable using an asynchronous waiting facility such as a
`zx_port_wait()` and then call `zxio_open_with_on_open()` to process the event
when it is ready, or simply call `zxio_open_with_on_open()` to block until an
event is ready.
The zxio library does not currently provide any mechanism for canceling pending
or concurrent blocking operations on an object.
## Asynchronously waiting for state changes
The zxio library supports asynchronously waiting for objects to change state.
This can be used to defer operations until they are likely to make progress. To
use this facility:
1. Call `zxio_wait_begin()` with a set of states of interest. This will produce
a `zx_handle_t` value and a `zx_signals_t` value. Note that this operation does
not transfer ownership of the handle.
2. Register a wait on the handle + signals tuple with `zx_object_async_wait()`
or another Zircon waiting operation.
3. When the wait completes, call `zxio_wait_end()` with the values from kernel.
This will produce a `zxio_signals_t` value reflecting the object's new state.
4. Make calls based on the new state.
As the handle value produced by `zxio_wait_begin()` is borrowed from the zxio
object, it is unsafe to destroy the zxio object or release and close the
object's primary handle and then register and async wait.
This can be used to implement blocking calls by attempting the operation and
then blocking with `zx_object_wait_one()` if the operation returns
`ZX_ERR_WOULD_BLOCK`.
# Allocations, buffers, and strings
## Heap allocations
The zxio library currently allocates internal heap buffers for some operations.
These allocations are internal to the library and not exposed to callers. Custom
object implementations should avoid heap allocations where possible and maintain
ownership internal to the object when not.
TODO(https://fxbug.com/91172): Remove all of the heap allocations from inside
zxio and update this text.
## Buffers
Many zxio operations read or write to caller allocated buffers. The caller must
take care to ensure that these buffers are of the required size and alignment
and that they remain valid for the duration of the call.
## Stack usage
The zxio library attempts to use a limited amount of stack space to be usable in
as many situations as possible. Zxio does not perform any dynamically sized
stack allocations. There is currently no concrete upper bound on the stack usage
of the zxio library.
## Strings
The zxio library represents path components as strings with a pointer to a
buffer and an length. Path components cannot contain embedded nulls. The zxio library
does not require or set a null terminator.
When interfacing with C-style strings, compute a string length explicitly for
input parameters and allocate space for a null terminator for output parameters.
# Linkage
The zxio library does not maintain internal static state and can be used as a
static library. It internally uses the C++ standard library.
## zxio_standalone
libzxio_standalone.so is provided as a standalone shared library version of the
zxio with most dependencies statically linked in (including the C++ standard
library). This library depends dynamically on a subset of the C standard
library, many vVDSO calls, and the __zx_panic symbol. The exact list of symbol
dependencies is listed in zxio_standalone.imported_symbols.allowlist.