]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | Copyright Oliver Kowalke, Nat Goodspeed 2015. | |
3 | Distributed under the Boost Software License, Version 1.0. | |
4 | (See accompanying file LICENSE_1_0.txt or copy at | |
5 | http://www.boost.org/LICENSE_1_0.txt | |
6 | ] | |
7 | ||
8 | [/ import path is relative to this .qbk file] | |
9 | [import ../examples/adapt_nonblocking.cpp] | |
10 | ||
11 | [#nonblocking] | |
12 | [section:nonblocking Integrating Fibers with Nonblocking I/O] | |
13 | ||
14 | [heading Overview] | |
15 | ||
16 | ['Nonblocking] I/O is distinct from ['asynchronous] I/O. A true async I/O | |
17 | operation promises to initiate the operation and notify the caller on | |
18 | completion, usually via some sort of callback (as described in [link callbacks | |
19 | Integrating Fibers with Asynchronous Callbacks]). | |
20 | ||
21 | In contrast, a nonblocking I/O operation refuses to start at all if it would | |
22 | be necessary to block, returning an error code such as | |
23 | [@http://man7.org/linux/man-pages/man3/errno.3.html `EWOULDBLOCK`]. The | |
24 | operation is performed only when it can complete immediately. In effect, the | |
25 | caller must repeatedly retry the operation until it stops returning | |
26 | `EWOULDBLOCK`. | |
27 | ||
28 | In a classic event-driven program, it can be something of a headache to use | |
29 | nonblocking I/O. At the point where the nonblocking I/O is attempted, a return | |
30 | value of `EWOULDBLOCK` requires the caller to pass control back to the main | |
31 | event loop, arranging to retry again on the next iteration. | |
32 | ||
33 | Worse, a nonblocking I/O operation might ['partially] succeed. That means that | |
34 | the relevant business logic must continue receiving control on every main loop | |
35 | iteration until all required data have been processed: a doubly-nested loop, | |
36 | implemented as a callback-driven state machine. | |
37 | ||
38 | __boost_fiber__ can simplify this problem immensely. Once you have integrated | |
39 | with the application's main loop as described in [link integration Sharing a | |
40 | Thread with Another Main Loop], waiting for the next main-loop iteration is as | |
41 | simple as calling [ns_function_link this_fiber..yield]. | |
42 | ||
43 | [heading Example Nonblocking API] | |
44 | ||
45 | For purposes of illustration, consider this API: | |
46 | ||
47 | [NonblockingAPI] | |
48 | ||
49 | [heading Polling for Completion] | |
50 | ||
51 | We can build a low-level wrapper around `NonblockingAPI::read()` that | |
52 | shields its caller from ever having to deal with `EWOULDBLOCK`: | |
53 | ||
54 | [nonblocking_read_chunk] | |
55 | ||
56 | [heading Filling All Desired Data] | |
57 | ||
58 | Given `read_chunk()`, we can straightforwardly iterate until we have all | |
59 | desired data: | |
60 | ||
61 | [nonblocking_read_desired] | |
62 | ||
63 | (Of ['course] there are more efficient ways to accumulate string data. That's | |
64 | not the point of this example.) | |
65 | ||
66 | [heading Wrapping it Up] | |
67 | ||
68 | Finally, we can define a relevant exception: | |
69 | ||
70 | [nonblocking_IncompleteRead] | |
71 | ||
72 | and write a simple `read()` function that either returns all desired data or | |
73 | throws `IncompleteRead`: | |
74 | ||
75 | [nonblocking_read] | |
76 | ||
77 | Once we can transparently wait for the next main-loop iteration using | |
78 | [ns_function_link this_fiber..yield], ordinary encapsulation Just Works. | |
79 | ||
80 | [/ @path link is relative to (eventual) doc/html/index.html, hence ../..] | |
81 | The source code above is found in | |
82 | [@../../examples/adapt_nonblocking.cpp adapt_nonblocking.cpp]. | |
83 | ||
84 | [endsect] |