OLD | NEW |
---|---|
(Empty) | |
1 # Chrome Network Stack Common Coding Patterns | |
2 | |
3 ## Combined error and byte count into a single value | |
4 | |
5 At many places in the network stack, functions return a value that, if | |
6 positive, indicate a count of bytes that the the function read or | |
7 wrote, and if negative, indicates a network stack error code (see | |
8 [net_error_list.h](https://chromium.googlesource.com/chromium/src/+/master/net/b ase/net_error_list.h#1)). | |
9 Zero indicates either net::OK or zero bytes read (usually EOF) | |
10 depending on the context. This pattern is generally specified by | |
11 an |int| return type. | |
12 | |
Ryan Sleevi
2015/08/31 22:17:43
Worth discussing the net::CompletionCallback that
Randy Smith (Not in Mondays)
2015/09/02 02:32:28
I detest net::CompletionCallback and consider it a
Ryan Sleevi
2015/09/02 19:14:56
Maybe, but I'm happy to save that discussion for a
| |
13 Many functions also have variables (often named |result|) containing | |
14 such value; this is especially common in the [DoLoop](#DoLoop) pattern | |
15 described below. | |
16 | |
17 ## Sync/Async Return | |
18 | |
19 Many network stack routines may return synchronously or | |
20 asynchronously. These functions generally return an int as described | |
21 above. There are three cases: | |
22 | |
23 * If the value is positive or zero, that indicates a synchronous | |
24 successful return, with a zero return value possibly indicating zero | |
25 bytes/EOF and possibly indicating net::OK, depending on context. | |
26 * If the value is negative and != net::ERR_IO_PENDING, it is an error | |
27 code specifying a synchronous failing return. | |
28 * If the return value is the special value net::ERR_IO_PENDING, it | |
29 indicates that the routine will complete asynchronously. An IOBuffer | |
30 provided will be retained by the called entity until completion, to | |
31 be written into or read from as required. Other buffers must be kept | |
Ryan Sleevi
2015/08/31 22:17:43
s/buffers/pointers/ (or objects)
Randy Smith (Not in Mondays)
2015/09/02 02:32:29
Done.
| |
32 alive manually until asynchronous completion is signaled. | |
33 If a callback was provided, that callback will be called upon | |
34 completion with the return value; if a callback is not provided, it | |
35 usually means that some known callback mechanism will be employed. | |
36 | |
37 ## DoLoop | |
38 | |
39 The pattern usually used to construct state machines in the Chrome | |
40 network stack is the DoLoop function. Any class that must drive a | |
41 state machine will contain an enum listing all states of that machine, | |
42 and define a function, |DoLoop|, to drive that state | |
43 machine. Sometimes a single class may have multiple state machines, | |
44 driven by multiple methods (e.g. DoFooLoop and DoBarLoop), and | |
45 sometimes a larger state machine may be broken into smaller state | |
46 machines, e.g. |DoHandshakeLoop|. | |
47 | |
48 The characteristics of the DoLoop pattern are: | |
49 | |
50 * Each state has a corresponding function which is called by DoLoop | |
Ryan Sleevi
2015/08/31 22:17:43
STYLE PEDANTRY: Google style is three spaces, not
Randy Smith (Not in Mondays)
2015/09/02 02:32:28
Huh. The markdown documentation that I found said
| |
51 for handling when the state machine is in that state. Generally the | |
52 states are named STATE_<state name> (upper case separated by | |
53 underscores), and the routine is named Do<StateName> (CamelCase). | |
54 Those functions both take and return values that are either | |
55 net::Errors or the above combined error and byte count value. | |
56 * There are often pairs of related states, such as STATE_SEND_HEADERS | |
57 and STATE_SEND_HEADERS_COMPLETE. The routine associated with the | |
Ryan Sleevi
2015/08/31 22:17:43
pedantry: You introduced another double space :P [
Randy Smith (Not in Mondays)
2015/09/02 02:32:29
I *did* warn you :-}. Done.
| |
58 second state handles completion processing (e.g. success vs. error) | |
59 and determining the next state to transition to (e.g. back to | |
60 STATE_SEND_HEADERS if the headers haven't actually all been sent). | |
Ryan Sleevi
2015/08/31 22:17:43
I would suggest rewording this; we don't do FOO/FO
Randy Smith (Not in Mondays)
2015/09/02 02:32:28
SGTM; this is exactly the kind of thing I'm relyin
| |
61 * Each state handling function has two basic responsibilities in | |
62 addition to state specific handling: Setting the data member | |
63 (named |next_state_| or something similar) | |
64 to specify the next state, and returning a net::Error (or combined | |
65 error and byte count, as above). | |
66 * DoLoop loops, saving next_state_ to a local variable and resetting | |
67 it to STATE_NONE, and then calling the appropriate state handling | |
68 based the original value of next_state_. | |
Ryan Sleevi
2015/08/31 22:17:43
Suggestion: Add a sentence explaining why
* On
Randy Smith (Not in Mondays)
2015/09/02 02:32:28
Thanks for the explanation; done.
| |
69 * If the return value from the state handling function is | |
70 net::ERR_IO_PENDING, that indicates that the function has arranged | |
71 for DoLoop() to be called at some point in the future, when further | |
72 progress can be made on the state transitions. The next_state_ variable | |
73 will have been set to the value proper for handling that incoming | |
74 call. In this case, DoLoop() will exit. | |
75 * A class state machine is generally invoked in response to a consumer | |
76 calling one of its methods. While the operation that method | |
77 requested is occuring, the state machine stays actively, possibly | |
78 over multiple asynchronous operations and state transitions. When | |
79 that operation is complete, the state machine transitions to | |
80 STATE_NONE (by a DoLoop callee not setting next_state_) or | |
81 STATE_DONE (by explicitly setting next_state_ to STATE_DONE | |
82 indicating that the operation is complete *and* the state machine is | |
83 not amenable to further driving). At this point the consumer is | |
84 notified of the completion of the operation (by synchronous return | |
85 or asynchronous callback). | |
86 | |
87 Note that this implies that when DoLoop() returns, one of two | |
88 things will be true: | |
89 | |
90 * The return value will be net::ERR_IO_PENDING, indicating that the | |
91 caller should take no action and instead wait for asynchronous | |
92 notification. | |
93 * The state of the machine will be either STATE_DONE or STATE_NONE, | |
94 indicating that the operation that first initiated the DoLoop() has | |
95 completed. | |
96 | |
97 * DoLoop is called from two places: a) methods exposed to the consumer | |
98 for specific operations (e.g. |ReadHeaders|), and b) IO completion | |
99 callbacks called asynchronously by spawned IO operations. | |
100 | |
101 In the first case, the return value from DoLoop is returned directly | |
102 to the caller; if the operation completed synchronously, that will | |
103 contain the operation result, and if it completed asynchronously, it | |
104 will be net::ERR_IO_PENDING. | |
105 | |
106 In the second case, the IO completion callback will examine the | |
107 return value from DoLoop(). If it is net::ERR_IO_PENDING, no | |
108 further action will be taken, and the IO completion callback will be | |
109 called again at some future point. If it is not | |
110 net::ERR_IO_PENDING, that is a signal that the operation has | |
111 completed, and the IO completion callback will call the appropriate | |
112 consumer callback to notify the consumer that the operation has | |
113 completed. Note that it is important that this callback be done | |
114 from the IO completion callback and not DoLoop or a DoLoop callee, | |
115 both to support the sync/async error return (DoLoop and its callees | |
116 don't konw the difference) and to avoid consumer callbacks deleting | |
Ryan Sleevi
2015/08/31 22:17:43
typo: know
Randy Smith (Not in Mondays)
2015/09/02 02:32:28
Done.
| |
117 the object out from under DoLoop(). | |
118 | |
119 Public class methods should have as little processing as possible, | |
120 often simply making copies of arguments into data members, setting the | |
121 next_state_ variable to indicate the section of the state diagram to | |
122 process, and calling DoLoop(). | |
123 | |
124 This idiom allows synchronous and asynchronous logic to be written in | |
125 the same fashion; it's all just state transition handling. For mostly | |
126 linear state diagrams, the handling code can be very easy to | |
127 comprehend, as such code is usually written linearly (in different | |
128 handling functions) in the order it's executed. If there can be | |
129 multiple different events that complete outstanding IO, the framework | |
130 doesn't handle that explicitly; the state handling code for the | |
131 receiving state must explicitly distinguish between those events and | |
132 do the appropriate state transition. | |
133 | |
134 For examples of this idiom, see | |
135 | |
136 * [HttpStreamParser::DoLoop](https://code.google.com/p/chromium/codesearch#chrom ium/src/net/http/http_stream_parser.cc&q=HttpStreamParser::DoLoop&sq=package:chr omium). | |
137 * [HttpNetworkTransaction::DoLoop](https://code.google.com/p/chromium/codesearch #chromium/src/net/http/http_network_transaction.cc&q=HttpNetworkTransaction::DoL oop&sq=package:chromium) | |
138 | |
OLD | NEW |