Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(613)

Side by Side Diff: net/docs/code-patterns.md

Issue 1320933003: Writeup summary of common netstack coding patterns. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Incorporate comments, discuss single threading in various forms. Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | net/net.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
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 pointers must be kept
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 DoLoop pattern is a pattern used in the network stack to construct
40 simple state machines. It is used for cases in which processing is
41 basically single threaded and could be written in a single function,
42 if that function could block waiting for input. Generally initiation
43 of a state machine is triggerred by some method invocation by a class
44 consumer, and that state machine is driven (possibly across
45 asynchronous IO initiated by the class) until the operation requested
46 by the method invocation completes, at which point the state machine
47 is reset of completed and the consumer notified.
mmenke 2015/09/02 16:41:17 reset if completed?
Randy Smith (Not in Mondays) 2015/09/02 20:25:54 Yep, oops, done.
48
49 Cases which do not fit into this single-threaded, single consumer
50 operation model are generally adapted in some way to fit the model,
51 either by multiple state machines (e.g. independent state machines for
52 reading and writing, if each can be initiated while the other is
53 outstanding) or by storing information across consumer invocations and
54 returns that can be used to restart the state machine in the proper
55 state.
56
57 Any class using this pattern will contain an enum listing all states
58 of that machine, and define a function, |DoLoop|, to drive that state
59 machine. If a class has multiple state machines (as above) it will
60 have multiple methods (e.g. |DoReadLoop| and |DoWriteLoop|) to drive
61 those different machines.
62
63 The characteristics of the DoLoop pattern are:
64
65 * Each state has a corresponding function which is called by DoLoop
66 for handling when the state machine is in that state. Generally the
67 states are named STATE`_<`STATENAME`>` (upper case separated by
68 underscores), and the routine is named Do`<`StateName`>` (CamelCase).
69 Those functions both take and return values that are either
70 net::Errors or the above combined error and byte count value.
71 * If a given state may complete synchronously or asynchronously (for example,
72 writing to an underlying transport socket), then there will often
73 be pairs of related states, such as `STATE_WRITE` and
74 `STATE_WRITE_COMPLETE`. The first state is responsible for
75 starting/continuing the original operation, while the second state
76 is responsible for handling completion (e.g. success vs error,
77 complete vs. incomplete writes), and determining the next state to
78 transition to.
79 * Each state handling function has two basic responsibilities in
80 addition to state specific handling: Setting the data member
81 (named |`next_state_`| or something similar)
82 to specify the next state, and returning a net::Error (or combined
83 error and byte count, as above).
84 * On each DoLoop iteration, it saves the next state to a local
85 variable and resets to the default/terminal state, and then calls
86 the appropriate state handling based on the original value of the
87 next state. This pattern is followed primarily to ensure that in
88 the event of a bug where the next state isn't set, the loop
89 terminates rather than loops infinitely. It's not a perfect
90 mitigation, but works well as a defensive measure.
91 * If the return value from the state handling function is
92 `net::ERR_IO_PENDING`, that indicates that the function has arranged
93 for DoLoop() to be called at some point in the future, when further
94 progress can be made on the state transitions. The `next_state_` variable
95 will have been set to the value proper for handling that incoming
96 call. In this case, DoLoop() will exit.
97 * A class state machine is generally invoked in response to a consumer
98 calling one of its methods. While the operation that method
mmenke 2015/09/02 16:41:17 My god! Two spaces after a period.
mmenke 2015/09/02 16:43:05 Worth noting: 1) I'm trying to be silly, not cri
Randy Smith (Not in Mondays) 2015/09/02 20:25:54 Indeed. I will arrange to commit ritual hari-kari
Randy Smith (Not in Mondays) 2015/09/02 20:25:54 No worries on my end; I've basically accepted that
99 requested is occuring, the state machine stays active, possibly
100 over multiple asynchronous operations and state transitions. When
101 that operation is complete, the state machine transitions to
102 `STATE_NONE` (by a DoLoop callee not setting `next_state_`) or
103 `STATE_DONE` (by explicitly setting next_state_ to `STATE_DONE`
104 indicating that the operation is complete *and* the state machine is
105 not amenable to further driving). At this point the consumer is
106 notified of the completion of the operation (by synchronous return
107 or asynchronous callback).
108
109 Note that this implies that when DoLoop() returns, one of two
110 things will be true:
111
112 * The return value will be `net::ERR_IO_PENDING`, indicating that the
113 caller should take no action and instead wait for asynchronous
114 notification.
115 * The state of the machine will be either `STATE_DONE` or `STATE_NONE`,
116 indicating that the operation that first initiated the DoLoop() has
117 completed.
118
119 This invariant reflects and enforces the single-threaded (though
120 possibly asynchronous) nature of the driven state machine--the
121 machine is always executing one requested operation.
122 * DoLoop is called from two places: a) methods exposed to the consumer
123 for specific operations (e.g. |ReadHeaders|), and b) an IO completion
124 callbacks called asynchronously by spawned IO operations.
125
126 In the first case, the return value from DoLoop is returned directly
127 to the caller; if the operation completed synchronously, that will
128 contain the operation result, and if it completed asynchronously, it
129 will be `net::ERR_IO_PENDING`.
130
131 In the second case, the IO completion callback will examine the
132 return value from DoLoop(). If it is `net::ERR_IO_PENDING`, no
133 further action will be taken, and the IO completion callback will be
134 called again at some future point. If it is not
135 `net::ERR_IO_PENDING`, that is a signal that the operation has
136 completed, and the IO completion callback will call the appropriate
137 consumer callback to notify the consumer that the operation has
138 completed. Note that it is important that this callback be done
139 from the IO completion callback and not DoLoop or a DoLoop callee,
140 both to support the sync/async error return (DoLoop and its callees
141 don't know the difference) and to avoid consumer callbacks deleting
142 the object out from under DoLoop().
143 * The DoLoop pattern has no concept of events; each state, if
mmenke 2015/09/02 16:41:17 This contradicts itself - you say no concept of ev
Randy Smith (Not in Mondays) 2015/09/02 20:25:54 So what I meant was that when the state machine is
mmenke 2015/09/02 20:31:29 Sorry, I mean HttpNetworkTransaction. Notice it's
144 waiting, is waiting for one particular event, and when DoLoop is
145 invoked when the machine is in that state, it will handle that
146 event. This reflects the single-threaded model for operations
147 spawned by the state machine.
148
149 Public class methods should have as little processing as possible,
150 often simply making copies of arguments into data members, setting the
151 `next_state_` variable to indicate the section of the state diagram to
152 process, and calling DoLoop().
153
154 This idiom allows synchronous and asynchronous logic to be written in
155 the same fashion; it's all just state transition handling. For mostly
156 linear state diagrams, the handling code can be very easy to
157 comprehend, as such code is usually written linearly (in different
158 handling functions) in the order it's executed. If there can be
159 multiple different events that complete outstanding IO, the framework
160 doesn't handle that explicitly; the state handling code for the
161 receiving state must explicitly distinguish between those events and
162 do the appropriate state transition.
163
164 For examples of this idiom, see
165
166 * [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).
167 * [HttpNetworkTransaction::DoLoop](https://code.google.com/p/chromium/codesearch #chromium/src/net/http/http_network_transaction.cc&q=HttpNetworkTransaction::DoL oop&sq=package:chromium)
168
OLDNEW
« no previous file with comments | « no previous file | net/net.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698