From 2ac729f333395659c527a21fbcda128b46af7352 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Thu, 24 Oct 2024 22:58:47 -0700 Subject: docs: AIO chapter (consumer only) converted to mdbook. --- docs/ref/SUMMARY.md | 5 +- docs/ref/api/aio.md | 287 ++++++++++++++++++++++++++++++++++++++++++++ docs/ref/api/aio/index.md | 1 - docs/ref/api/aio/nng_aio.md | 65 ---------- docs/ref/api/synch.md | 6 + docs/ref/xref.md | 20 ++- 6 files changed, 312 insertions(+), 72 deletions(-) create mode 100644 docs/ref/api/aio.md delete mode 100644 docs/ref/api/aio/index.md delete mode 100644 docs/ref/api/aio/nng_aio.md (limited to 'docs/ref') diff --git a/docs/ref/SUMMARY.md b/docs/ref/SUMMARY.md index 5b79644a..ed3e1233 100644 --- a/docs/ref/SUMMARY.md +++ b/docs/ref/SUMMARY.md @@ -14,10 +14,7 @@ - [URLs](./api/url.md) - - [Asynchronous Operations](./api/aio/index.md) - - - [nng_aio](./api/aio/nng_aio.md) - - [aio_cancel](./api/aio/aio_cancel.md) + - [Asynchronous I/O](./api/aio.md) - [Synchronization](./api/synch.md) diff --git a/docs/ref/api/aio.md b/docs/ref/api/aio.md new file mode 100644 index 00000000..230744e8 --- /dev/null +++ b/docs/ref/api/aio.md @@ -0,0 +1,287 @@ +# Asynchronous Operations + +In order to obtain significant scalability, with low-latency, and minimal +overheads, _NNG_ supports performing operations asynchronously. + +One way that applications can perform work asynchronously and concurrently +is by using [threads][thread], but threads carry significant resource overheads +and frequently there are limits on the number that can easily be created. + +Additionally, with most network applications, the flow of execution will spend +the bulk of its time waiting for traffic from a peer. + +For these kinds of applications, it is far more efficient to use asynchronous operations +using the mechanisms described in this chapter. + +> [!TIP] +> To get the highest performance with the least overhead, applications should use +> asynchronous operations described in this chapter whenever possible. + +## Asynchronous I/O Handle + +```c +typedef struct nng_aio nng_aio; +``` + +An {{i:`nng_aio`}}{{hi:aio}} is an opaque structure used in conjunction with +{{i:asynchronous I/O}} operations. +Every asynchronous operation uses one of these structures, each of which +can only be used with a single operation at a time. + +Asynchronous operations are performed without blocking calling application threads. +Instead the application registers a callback function to be executed +when the operation is complete (whether successfully or not). +This callback will be executed exactly once. + +The asynchronous I/O framework also supports [cancellation][`nng_aio_cancel`] of +operations that are already in progress as well setting a maximum +[timeout][`nng_aio_set_timeout`] for them to complete. + +It is also possible to initiate an asynchronous operation, and [wait][`nng_aio_wait`] for it to +complete, creating a synchronous flow from an asynchronous one. + +## Create Handle + +```c +int nng_aio_alloc(nng_aio **aiop, void (*callb)(void *), void *arg); +``` + +The {{i:`nng_aio_alloc`}} function creates an [`nng_aio`] object, with the +{{i:callback}} _callb_ taking the argument _arg_, and returns it in _aiop_. + +If this succeeds, the function returns zero. Otherwise it may return [`NNG_ENOMEM`]. + +> [!TIP] +> The _arg_ should normally be a structure that contains a pointer to the _aiop_, +> or from which it can be located. This allows _callb_ to check the handle for +> success using [`nng_aio_result`], as well access other properties of _aiop_. + +> [!TIP] +> The _callb_ may be executed on another [thread], so it may be necessary to use +> [synchronization] methods in _callb_ to avoid data races. + +## Destroy Handle + +```c +void nng_aio_free(nng_aio *aio); +void nng_aio_reap(nng_aio *aio); +``` + +The {{i:`nng_aio_free`}} handle destroys the handle _aio_, waiting for any operations +and associated callbacks to complete before doing so. + +The {{i:`nng_aio_reap`}} handle destroys the handle _aio_ asynchronously, using a _reaper_ +[thread] to do so. It does not wait for the object to be destroyed. Thus this function +is safe to call from _aio_'s own callback. + +> [!NOTE] +> The `nng_aio_free` function must never be called from an _aio_ callback. +> Use `nng_aio_reap` instead if an object must be destroyed from a callback. + +## Cancellation + +```c +void nng_aio_abort(nng_aio *aio, int err); +void nng_aio_cancel(nng_aio *aio); +void nng_aio_stop(nng_aio *aio); +``` + +These functions are used to stop a previously submitted asynchronous +I/O operation. The operation may be canceled, or may continue to +completion. If no operation is in progress (perhaps because it has +already completed), then these operations have no effect. +If the operation is successfully canceled or aborted, then the callback +will still be called. + +The {{i:`nng_aio_abort`}} function aborts the operation associated with _aio_ +and returns immediately without waiting. If cancellation was successful, +then [`nng_aio_result`] will return _err_. + +The {{i:`nng_aio_cancel`}} function acts like `nng_aio_abort`, but uses the error code +[`NNG_ECANCELED`]{{hi:`NNG_ECANCELED`}}. + +The {{i:`nng_aio_stop`}} function aborts the _aio_ operation with [`NNG_ECANCELED`], +and then waits the operation and any associated callback to complete. +This function also marks _aio_ itself permanently stopped, so that any +new operations scheduled by I/O providers using [`nng_aio_begin`] +return false. Thus this function should be used to teardown operations. + +> [!TIP] +> When multiple asynchronous I/O handles are in use and need to be +> deallocated, it is safest to stop all of them using `nng_aio_stop`, +> before deallocating any of them with [`nng_aio_free`], +> particularly if the callbacks might attempt to reschedule further operations. + +## Set Timeout + +```c +void nng_aio_set_timeout(nng_aio *aio, nng_duration timeout); +void nng_aio_set_expire(nng_aio *aio, nng_time expiration); +``` + +The `nng_aio_set_timeout` function sets a {{ii:timeout}} +for the asynchronous operation associated with _aio_. +This causes a timer to be started when the operation is actually started. +If the timer expires before the operation is completed, then it is +[aborted][`nng_aio_abort`] with an error of `NNG_ETIMEDOUT`. +The _timeout_ [duration][time] is specified as a relative number of milliseconds. + +If the timeout is [`NNG_DURATION_INFINITE`], then no timeout is used. +If the timeout is [`NNG_DURATION_DEFAULT`], then a "default" or socket-specific +timeout is used. +(This is frequently the same as [`NNG_DURATION_INFINITE`].) + +The {{i:`nng_aio_set_expire`}} function is similar to `nng_aio_set_timeout`, but sets +an expiration time based on the system clock. The _expiration_ +[time] is a clock timestamp, such as would be returned by [`nng_clock`]. + +## Wait for Completion + +```c +void nng_aio_wait(nng_aio *aio); +``` + +The {{i:`nng_aio_wait`}} function waits for an asynchronous I/O operation to complete. +If the operation has not been started, or has already completed, then it returns immediately. + +If a callback was set with _aio_ when it was allocated, then this +function will not be called until the callback has completed. + +> [!IMPORTANT] +> The `nng_aio_wait` function should never be called from a function that itself +> is a callback of an [`nng_aio`], either this one or any other. +> Doing so may result in a deadlock. + +## Test for Completion + +```c +bool nng_aio_busy(nng_aio *aio); +``` + +The {{i:`nng_aio_busy`}} function returns `true` if the _aio_ is currently busy performing an +operation or is executing a completion callback. Otherwise it return `false`. +This is the same test used internally by [`nng_aio_wait`]. + +> [!IMPORTANT] +> The caller is responsible for coordinating any use of this with any reuse of the _aio_. +> Because the _aio_ can be reused use of this function can be racy. + +## Result of Operation + +```c +int nng_aio_result(nng_aio *aio); +size_t nng_aio_count(nng_aio *aio); +``` + +The {{i:`nng_aio_result`}} function returns the result of the operation associated +with the handle _aio_. +If the operation was successful, then 0 is returned. +Otherwise a non-zero [error] code, such as [`NNG_ECANCELED`] or [`NNG_ETIMEDOUT`], is returned. + +For operations that transfer data, {{i:`nng_aio_count`}} returns the +number of bytes transferred by the operation associated with the handle _aio_. +Operations that do not transfer data, or do not keep a count, may return zero for this function. + +> [!NOTE] +> The return value from these functions is undefined if the operation has not completed yet. +> Either call these from the handle's completion callback, or after waiting for the +> operation to complete with [`nng_aio_wait`]. + +## Messages + +```c +nng_msg *nng_aio_get_msg(nng_aio *aio); +void nng_aio_set_msg(nng_aio *aio, nng_msg *msg); +``` + +The {{i:`nng_aio_get_msg`}} and {{i:`nng_aio_set_msg`}} functions retrieve and store a [message] +in _aio_. +For example, if a function to receive data is called, that function can generally be expected +to store a message on the asssociated _aio_, for the application to retrieve with +`nng_aio_get_msg`. +Conversely an application desiring to send a message _msg_ will store it in the _aio_ using +`nng_aio_set_msg`. The function implementing the send operation will retrieve the message +and arrange for it to be sent. + +### Message Ownership + +For send or transmit operations, the rule of thumb is that implementation of the operation +is responsible for taking ownership of the message (and releasing resources when it is complete), +if it will return success. If the operation will end in error, then the message will be +retained and it is the consuming application's responsibility to dispose of the message. +This allows an application the opportunity to reuse the message to try again, if it so desires. + +For receive operations, the implementation of the operation will set the message on the _aio_ +on success, and the consuming application hasa responsibility to retrieve and dispose of the +message. Failure to do so will leak the message. If the operation does not complete successfully, +then no message is stored on the _aio_. + +## I/O Vector + +```c +typedef struct nng_iov { + void * iov_buf; + size_t iov_len; +}; + +int nng_aio_set_iov(nng_aio *aio, unsigned int niov, nng_iov *iov); +``` + +For some operations, the unit of data transferred is not a [message], but +rather a stream of bytes. + +For these operations, an array of _niov_ {{i:`nng_iov`}} structures can be passed to +the {{i:`nng_aio_set_iov`}} function to provide a scatter/gather array of +elements describing the location (`iov_buf`) and length (`iov_len`) of data, +to transfer. + +The _iov_ vector is copied into storage in the _aio_ itself, so that callers may use stack allocated `nng_iov` structures. +The values pointed to by the `iov_buf` members are _not_ copied by this function though. + +A maximum of four (4) `nng_iov` members may be supplied. + +> [!TIP] +> Most functions using `nng_iov` do not guarantee to transfer all of the data that they +> are requested to. To be sure that correct amount of data is transferred, as well as to +> start an attempt to complete any partial transfer, check the amount of data transferred by +> calling [`nng_aio_count`]. + +## Inputs and Outputs + +```c +void nng_aio_set_input(nng_aio *aio, unsigned int index, void *param); +void *nng_aio_get_output(nng_aio *aio, unsigned int index); +``` + +Asynchronous operations can take additional input parameters, and +provide additional result outputs besides the [result][`nng_aio_result`] code. + +The `nng_aio_set_input` function sets the input parameter at _index_ +to _param_ for the operation associated with _aio_. + +The `nng_aio_get_output` function returns the output result at _index_ +for the operation associated with _aio_. + +The type and semantics of input parameters and output results are determined by specific +operations. The documentation for the operation should provide details. + +The valid values of _index_ range from zero (0) to three (3), as no operation +currently defined can accept more than four parameters or return more than four additional +results. + +> [!NOTE] +> If the _index_ does not correspond to a defined input for the operation, +> then `nng_aio_set_input` will have no effect, and `nng_aio_get_output` will +> return `NULL`. + +> [!IMPORTANT] +> It is an error to call this function while the _aio_ is currently +> in use by an active asynchronous operation. + +## See Also + +[Synchronization][synchronization], +[Threads][thread], +[Time][time] + +{{#include ../xref.md}} diff --git a/docs/ref/api/aio/index.md b/docs/ref/api/aio/index.md deleted file mode 100644 index 1e9872e9..00000000 --- a/docs/ref/api/aio/index.md +++ /dev/null @@ -1 +0,0 @@ -# Asynchronous I/O Operations diff --git a/docs/ref/api/aio/nng_aio.md b/docs/ref/api/aio/nng_aio.md deleted file mode 100644 index 0fdb6ca1..00000000 --- a/docs/ref/api/aio/nng_aio.md +++ /dev/null @@ -1,65 +0,0 @@ -# nng_aio - -## NAME - -nng_aio - asynchronous I/O handle - -## SYNOPSIS - -```c -#include - -typedef struct nng_aio nng_aio; -``` - -## DESCRIPTION - -An {{i:`nng_aio`}}{{hi:aio}} is an opaque structure used in conjunction with -{{i:asynchronous I/O}} operations. -Every asynchronous operation uses one of these structures, each of which -can only be used with a single operation at a time. - -Asynchronous operations are performed without blocking calling application threads. -Instead the application registers a callback function to be executed -when the operation is complete (whether successfully or not). -This callback will be executed exactly once. - -The asynchronous I/O framework also supports cancellation of -operations that are already in progress -(see [`nng_aio_cancel`][aio_cancel]), as well setting a maximum -timeout for them to complete within -(see [`nng_aio_set_timeout`][nng_aio_set_timeout]). - -It is also possible to initiate an asynchronous operation, and wait for it to -complete using [`nng_aio_wait`][nng_aio_wait]. - -These structures are created using [`nng_aio_alloc`][nng_aio_alloc], -and destroyed using [`nng_aio_free`][nng_aio_free]. - -## SEE ALSO - -[nng_aio_cancel][aio_cancel], -[nng_aio_alloc][nng_aio_alloc], -[nng_aio_free][nng_aio_free], -[nng_aio_set_timeout][nng_aio_set_timeout] - - - -[aio_cancel]: nng_aio_cancel.md -[nng_aio_alloc]: nng_aio_alloc.md -[nng_aio_free]: nng_aio_free.md -[nng_aio_set_timeout]: nng_aio_set_timeout.md -[nng_aio_wait]: TODO.md diff --git a/docs/ref/api/synch.md b/docs/ref/api/synch.md index 82eee869..d56fb600 100644 --- a/docs/ref/api/synch.md +++ b/docs/ref/api/synch.md @@ -172,3 +172,9 @@ one thread (which may be chosen randomly). ``` {{#include ../xref.md}} + +## See Also + +[Threads][thread], +[Time][time], +[Asynchronous I/O][aio] diff --git a/docs/ref/xref.md b/docs/ref/xref.md index d103a73b..3abf999c 100644 --- a/docs/ref/xref.md +++ b/docs/ref/xref.md @@ -56,8 +56,20 @@ [`nng_send`]: /TODO.md [`nng_recv`]: /TODO.md [`nng_aio`]: /TODO.md +[`nng_aio_alloc`]: /api/aio.md#create-handle +[`nng_aio_free`]: /api/aio.md#destroy-handle [`nng_sleep_aio`]: /TODO.md -[`nng_aio_cancel`]: /TODO.md +[`nng_aio`]: /api/aio.md#asynchronous-i-o-handle +[`nng_aio_cancel`]: /api/aio.md#cancellation +[`nng_aio_abort`]: /api/aio.md#cancellation +[`nng_aio_stop`]: /api/aio.md#cancellation +[`nng_aio_wait`]: /api/api.md#wait-for-completion +[`nng_aio_busy`]: /api/api.md#test-for-completion +[`nng_aio_result`]: /api/api.md#result-of-operation +[`nng_aio_count`]: /api/api.md#result-of-operation +[`nng_aio_begin`]: /TODO.md +[`nng_aio_finish`]: /TODO.md +[`nng_aio_set_timeout`]: /TODO.md [`nng_opts_parse`]: /TODO.md @@ -131,12 +143,13 @@ -[aio]: /TODO.md +[aio]: /api/aio.md [raw]: /TODO.md [pipe]: /TODO.md [socket]: /TODO.md [dialer]: /TODO.md [listener]: /TODO.md +[message]: /api/msg.md [message-body]: /api/msg.md#message-body [message-header]: /api/msg.md#message-header [synchronization]: /api/synch.md @@ -144,3 +157,6 @@ [condvar]: /api/synch.md#condition-variable [thread]: /api/thread.md [statistic]: /api/stats.md +[time]: /api/time.md +[error]: /api/errors.md +[duration]: /api/time.md#duration-type -- cgit v1.2.3-70-g09d2