|  | # zx_channel_call | 
|  |  | 
|  | ## NAME | 
|  |  | 
|  | <!-- Updated by update-docs-from-fidl, do not edit. --> | 
|  |  | 
|  | Send a message to a channel and await a reply. | 
|  |  | 
|  | ## SYNOPSIS | 
|  |  | 
|  | <!-- Updated by update-docs-from-fidl, do not edit. --> | 
|  |  | 
|  | ```c | 
|  | #include <zircon/syscalls.h> | 
|  |  | 
|  | zx_status_t zx_channel_call(zx_handle_t handle, | 
|  | uint32_t options, | 
|  | zx_time_t deadline, | 
|  | const zx_channel_call_args_t* args, | 
|  | uint32_t* actual_bytes, | 
|  | uint32_t* actual_handles); | 
|  | ``` | 
|  |  | 
|  | ## DESCRIPTION | 
|  |  | 
|  | `zx_channel_call()` is like a combined [`zx_channel_write()`], [`zx_object_wait_one()`], | 
|  | and [`zx_channel_read()`], with the addition of a feature where a transaction id at | 
|  | the front of the message payload *bytes* is used to match reply messages with send | 
|  | messages, enabling multiple calling threads to share a channel without any additional | 
|  | userspace bookkeeping. | 
|  |  | 
|  | The write and read phases of this operation behave like [`zx_channel_write()`] and | 
|  | [`zx_channel_read()`] with the difference that their parameters are provided via the | 
|  | `zx_channel_call_args_t` structure. | 
|  |  | 
|  | The first four bytes of the written and read back messages are treated as a | 
|  | transaction ID of type `zx_txid_t`.  The kernel generates a txid for the | 
|  | written message, replacing that part of the message as read from userspace. | 
|  | The kernel generated txid will be between 0x80000000 and 0xFFFFFFFF, and will | 
|  | not collide with any txid from any other `zx_channel_call()` in progress against | 
|  | this channel endpoint.  If the written message has a length of fewer than four | 
|  | bytes, an error is reported. | 
|  |  | 
|  | When the outbound message is written, simultaneously an interest is registered | 
|  | for inbound messages of the matching txid. | 
|  |  | 
|  | *deadline* may be automatically adjusted according to the job's [timer slack] | 
|  | policy. | 
|  |  | 
|  | While the slack-adjusted *deadline* has not passed, if an inbound message | 
|  | arrives with a matching txid, instead of being added to the tail of the general | 
|  | inbound message queue, it is delivered directly to the thread waiting in | 
|  | `zx_channel_call()`. | 
|  |  | 
|  | If such a reply arrives after the slack-adjusted *deadline* has passed, it will | 
|  | arrive in the general inbound message queue, cause **ZX_CHANNEL_READABLE** to be | 
|  | signaled, etc. | 
|  |  | 
|  | Inbound messages that are too large to fit in *rd_num_bytes* and *rd_num_handles* | 
|  | are discarded and **ZX_ERR_BUFFER_TOO_SMALL** is returned in that case. | 
|  |  | 
|  | As with [`zx_channel_write()`], the handles in *handles* are always consumed by | 
|  | `zx_channel_call()` and no longer exist in the calling process. | 
|  |  | 
|  | ## RIGHTS | 
|  |  | 
|  | <!-- Updated by update-docs-from-fidl, do not edit. --> | 
|  |  | 
|  | *handle* must be of type **ZX_OBJ_TYPE_CHANNEL** and have **ZX_RIGHT_READ** and have **ZX_RIGHT_WRITE**. | 
|  |  | 
|  | All wr_handles of *args* must have **ZX_RIGHT_TRANSFER**. | 
|  |  | 
|  | ## RETURN VALUE | 
|  |  | 
|  | `zx_channel_call()` returns **ZX_OK** on success and the number of bytes and | 
|  | count of handles in the reply message are returned via *actual_bytes* and | 
|  | *actual_handles*, respectively. | 
|  |  | 
|  | ## ERRORS | 
|  |  | 
|  | **ZX_ERR_BAD_HANDLE**  *handle* is not a valid handle, any element in | 
|  | *handles* is not a valid handle, or there are duplicates among the handles | 
|  | in the *handles* array. | 
|  |  | 
|  | **ZX_ERR_WRONG_TYPE**  *handle* is not a channel handle. | 
|  |  | 
|  | **ZX_ERR_INVALID_ARGS**  any of the provided pointers are invalid or null, | 
|  | or *wr_num_bytes* is less than four, or *options* is nonzero. | 
|  |  | 
|  | **ZX_ERR_ACCESS_DENIED**  *handle* does not have **ZX_RIGHT_WRITE** or | 
|  | any element in *handles* does not have **ZX_RIGHT_TRANSFER**. | 
|  |  | 
|  | **ZX_ERR_PEER_CLOSED**  The other side of the channel was closed or became | 
|  | closed while waiting for the reply. | 
|  |  | 
|  | **ZX_ERR_CANCELED**  *handle* was closed while waiting for a reply. TODO(fxbug.dev/34013): | 
|  | Transferring a channel with pending calls currently leads to undefined behavior. With | 
|  | the current implementation, transferring such a channel does not interrupt the | 
|  | pending calls, as it does not close the underlying channel endpoint. Programs should | 
|  | be aware of this behavior, but they **must not** rely on it. | 
|  |  | 
|  | **ZX_ERR_NO_MEMORY**  Failure due to lack of memory. | 
|  | There is no good way for userspace to handle this (unlikely) error. | 
|  | In a future build this error will no longer occur. | 
|  |  | 
|  | **ZX_ERR_OUT_OF_RANGE**  *wr_num_bytes* or *wr_num_handles* are larger than the | 
|  | largest allowable size for channel messages. | 
|  |  | 
|  | **ZX_ERR_BUFFER_TOO_SMALL**  *rd_num_bytes* or *rd_num_handles* are too small | 
|  | to contain the reply message. | 
|  |  | 
|  | **ZX_ERR_NOT_SUPPORTED**  one of the handles in *handles* was *handle* | 
|  | (the handle to the channel being written to). | 
|  |  | 
|  | ## NOTES | 
|  |  | 
|  | The facilities provided by `zx_channel_call()` can interoperate with message dispatchers | 
|  | using [`zx_channel_read()`] and [`zx_channel_write()`] directly, provided the following rules | 
|  | are observed: | 
|  |  | 
|  | 1. A server receiving synchronous messages via [`zx_channel_read()`] should ensure that the | 
|  | txid of incoming messages is reflected back in outgoing responses via [`zx_channel_write()`] | 
|  | so that clients using `zx_channel_call()` can correctly route the replies. | 
|  |  | 
|  | 2. A client sending messages via [`zx_channel_write()`] that will be replied to should ensure | 
|  | that it uses txids between 0 and 0x7FFFFFFF only, to avoid colliding with other threads | 
|  | communicating via `zx_channel_call()`. | 
|  |  | 
|  | If a `zx_channel_call()` returns due to **ZX_ERR_TIMED_OUT**, if the server eventually replies, | 
|  | at some point in the future, the reply *could* match another outbound request (provided about | 
|  | 2^31 `zx_channel_call()`s have happened since the original request.  This syscall is designed | 
|  | around the expectation that timeouts are generally fatal and clients do not expect to continue | 
|  | communications on a channel that is timing out. | 
|  |  | 
|  | ## SEE ALSO | 
|  |  | 
|  | - [timer slack](/docs/concepts/kernel/timer_slack.md) | 
|  | - [`zx_channel_create()`] | 
|  | - [`zx_channel_read()`] | 
|  | - [`zx_channel_write()`] | 
|  | - [`zx_handle_close()`] | 
|  | - [`zx_handle_duplicate()`] | 
|  | - [`zx_handle_replace()`] | 
|  | - [`zx_object_wait_async()`] | 
|  | - [`zx_object_wait_many()`] | 
|  | - [`zx_object_wait_one()`] | 
|  |  | 
|  | <!-- References updated by update-docs-from-fidl, do not edit. --> | 
|  |  | 
|  | [`zx_channel_create()`]: channel_create.md | 
|  | [`zx_channel_read()`]: channel_read.md | 
|  | [`zx_channel_write()`]: channel_write.md | 
|  | [`zx_handle_close()`]: handle_close.md | 
|  | [`zx_handle_duplicate()`]: handle_duplicate.md | 
|  | [`zx_handle_replace()`]: handle_replace.md | 
|  | [`zx_object_wait_async()`]: object_wait_async.md | 
|  | [`zx_object_wait_many()`]: object_wait_many.md | 
|  | [`zx_object_wait_one()`]: object_wait_one.md |