85 lines
3.0 KiB
Plaintext
85 lines
3.0 KiB
Plaintext
[/
|
|
Copyright Oliver Kowalke, Nat Goodspeed 2015.
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file LICENSE_1_0.txt or copy at
|
|
http://www.boost.org/LICENSE_1_0.txt
|
|
]
|
|
|
|
[/ import path is relative to this .qbk file]
|
|
[import ../examples/adapt_nonblocking.cpp]
|
|
|
|
[#nonblocking]
|
|
[section:nonblocking Integrating Fibers with Nonblocking I/O]
|
|
|
|
[heading Overview]
|
|
|
|
['Nonblocking] I/O is distinct from ['asynchronous] I/O. A true async I/O
|
|
operation promises to initiate the operation and notify the caller on
|
|
completion, usually via some sort of callback (as described in [link callbacks
|
|
Integrating Fibers with Asynchronous Callbacks]).
|
|
|
|
In contrast, a nonblocking I/O operation refuses to start at all if it would
|
|
be necessary to block, returning an error code such as
|
|
[@http://man7.org/linux/man-pages/man3/errno.3.html `EWOULDBLOCK`]. The
|
|
operation is performed only when it can complete immediately. In effect, the
|
|
caller must repeatedly retry the operation until it stops returning
|
|
`EWOULDBLOCK`.
|
|
|
|
In a classic event-driven program, it can be something of a headache to use
|
|
nonblocking I/O. At the point where the nonblocking I/O is attempted, a return
|
|
value of `EWOULDBLOCK` requires the caller to pass control back to the main
|
|
event loop, arranging to retry again on the next iteration.
|
|
|
|
Worse, a nonblocking I/O operation might ['partially] succeed. That means that
|
|
the relevant business logic must continue receiving control on every main loop
|
|
iteration until all required data have been processed: a doubly-nested loop,
|
|
implemented as a callback-driven state machine.
|
|
|
|
__boost_fiber__ can simplify this problem immensely. Once you have integrated
|
|
with the application's main loop as described in [link integration Sharing a
|
|
Thread with Another Main Loop], waiting for the next main-loop iteration is as
|
|
simple as calling [ns_function_link this_fiber..yield].
|
|
|
|
[heading Example Nonblocking API]
|
|
|
|
For purposes of illustration, consider this API:
|
|
|
|
[NonblockingAPI]
|
|
|
|
[heading Polling for Completion]
|
|
|
|
We can build a low-level wrapper around `NonblockingAPI::read()` that
|
|
shields its caller from ever having to deal with `EWOULDBLOCK`:
|
|
|
|
[nonblocking_read_chunk]
|
|
|
|
[heading Filling All Desired Data]
|
|
|
|
Given `read_chunk()`, we can straightforwardly iterate until we have all
|
|
desired data:
|
|
|
|
[nonblocking_read_desired]
|
|
|
|
(Of ['course] there are more efficient ways to accumulate string data. That's
|
|
not the point of this example.)
|
|
|
|
[heading Wrapping it Up]
|
|
|
|
Finally, we can define a relevant exception:
|
|
|
|
[nonblocking_IncompleteRead]
|
|
|
|
and write a simple `read()` function that either returns all desired data or
|
|
throws `IncompleteRead`:
|
|
|
|
[nonblocking_read]
|
|
|
|
Once we can transparently wait for the next main-loop iteration using
|
|
[ns_function_link this_fiber..yield], ordinary encapsulation Just Works.
|
|
|
|
[/ @path link is relative to (eventual) doc/html/index.html, hence ../..]
|
|
The source code above is found in
|
|
[@../../examples/adapt_nonblocking.cpp adapt_nonblocking.cpp].
|
|
|
|
[endsect]
|