channel - Bidirectional interprocess communication


A channel is a bidirectional transport of messages consisting of some amount of byte data and some number of handles.


Channels have two endpoints. Each endpoint, logically, maintains an ordered queue of messages to be read. Writing to an endpoint enqueues a message in the other endpoint‘s queue. When the last handle to an endpoint is closed the unread messages in that endpoint’s queue are destroyed. Because destroying a message closes any handles contained by the message, closing a channel endpoint may have a recursive effect (e.g. channel contains a message, which contains a channel, which contains a message, and so on).

Closing the last handle to a channel has no impact on the lifetime of messages previously written to that channel. This gives channels “fire and forget” semantics.

A message consists of some amount of data and some number of handles. A call to zx_channel_write() enqueues one message, and a call to zx_channel_read() dequeues one message (if any are queued). A thread can block until messages are pending via zx_object_wait_one() or other waiting mechanisms.

Alternatively, a call to zx_channel_call() enqueues a message in one direction of the channel, waits for a corresponding response, and dequeues the response message. In call mode, corresponding responses are identified via the first 4 bytes of the message, called the transaction ID. The kernel supplies distinct transaction IDs (always with the high bit set) for messages written with zx_channel_call().

The process of sending a message via a channel has two steps. The first is to atomically write the data into the channel and move ownership of all handles in the message into this channel. This operation always consumes the handles: at the end of the call, all handles either are all in the channel or are all discarded. The second operation, channel read, is similar: on success all the handles in the next message are atomically moved into the receiving process' handle table. On failure, the channel retains ownership unless the ZX_CHANNEL_READ_MAY_DISCARD option is specified, then they are dropped.

Unlike many other kernel object types, channels are not duplicatable. Thus, there is only ever one handle associated with a channel endpoint, and the process holding that handle is considered the owner. Only the owner can read or write messages or send the channel endpoint to another process.

When ownership of a channel endpoint moves from one process to another, messages will not be reordered or truncated, even if a write is in progress. Messages before the transfer event belong to the previous owner and messages after the transfer belong to the new owner. The same applies if a read is in progress when the endpoint is transferred.

The above sequential guarantee is not provided for other kernel objects, even if the last remaining handle is stripped of the ZX_RIGHT_DUPLICATE right.