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

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: Cleanup pass. 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)).
asanka 2015/09/04 14:51:03 Minor nit: I'd +1 Helen's suggestion to break this
Randy Smith (Not in Mondays) 2015/09/18 22:06:32 Completely missed Helen's suggestion until you cal
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.
asanka 2015/09/04 14:51:03 Code is indicated using backticks. I.e. `int` inst
Randy Smith (Not in Mondays) 2015/09/18 22:06:33 Done.
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.
asanka 2015/09/04 14:51:03 Nit: the problem with having to code format one id
Randy Smith (Not in Mondays) 2015/09/18 22:06:33 Too true :-}. Done (I hope).
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
asanka 2015/09/04 14:51:03 The DoLoop pattern is used in the network stack to
Randy Smith (Not in Mondays) 2015/09/18 22:06:33 Done.
40 simple state machines. It is used for cases in which processing is
asanka 2015/09/04 14:51:03 "simple" haha
Randy Smith (Not in Mondays) 2015/09/18 22:06:33 I'm meta amused. These state machines are *dirt*
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 if completed and the consumer notified.
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
asanka 2015/09/04 14:51:03 Minor nit: STATE_<STATE_NAME> or STATE_<NAME_OF_ST
Randy Smith (Not in Mondays) 2015/09/18 22:06:33 http://www.guntheranderson.com/v/data/yourstat.htm
68 underscores), and the routine is named Do`<`StateName`>` (CamelCase).
69 Those functions both take and return values that are either
asanka 2015/09/04 14:51:03 This deviates from what I've seen. Notably, indivi
Randy Smith (Not in Mondays) 2015/09/18 22:06:33 Huh. Right you are, at least based on the two can
70 net::Errors or the above combined error and byte count value.
71
72 * If a given state may complete synchronously or asynchronously (for example,
73 writing to an underlying transport socket), then there will often
74 be pairs of related states, such as `STATE_WRITE` and
75 `STATE_WRITE_COMPLETE`. The first state is responsible for
76 starting/continuing the original operation, while the second state
77 is responsible for handling completion (e.g. success vs error,
78 complete vs. incomplete writes), and determining the next state to
79 transition to.
80
81 * Each state handling function has two basic responsibilities in
82 addition to state specific handling: Setting the data member
83 (named `next_state_` or something similar)
84 to specify the next state, and returning a net::Error (or combined
85 error and byte count, as above).
86
87 * On each DoLoop iteration, it saves the next state to a local
88 variable and resets to the default/terminal state, and then calls
89 the appropriate state handling based on the original value of the
90 next state. This pattern is followed primarily to ensure that in
91 the event of a bug where the next state isn't set, the loop
92 terminates rather than loops infinitely. It's not a perfect
93 mitigation, but works well as a defensive measure.
94
95 * If the return value from the state handling function is
96 `net::ERR_IO_PENDING`, that indicates that the function has arranged
97 for `DoLoop()` to be called at some point in the future, when further
98 progress can be made on the state transitions. The `next_state_` variable
99 will have been set to the value proper for handling that incoming
100 call. In this case, `DoLoop()` will exit.
101
102 * A class state machine is generally invoked in response to a consumer
103 calling one of its methods. While the operation that method
104 requested is occuring, the state machine stays active, possibly
105 over multiple asynchronous operations and state transitions. When
106 that operation is complete, the state machine transitions to
107 `STATE_NONE` (by a `DoLoop()` callee not setting `next_state_`) or
108 `STATE_DONE` (by explicitly setting next_state_ to `STATE_DONE`
asanka 2015/09/04 14:51:03 I don't see any consistent use of an explicit DONE
Randy Smith (Not in Mondays) 2015/09/18 22:06:33 Good point; oops. Writing documentation CLs is a
109 indicating that the operation is complete *and* the state machine is
110 not amenable to further driving). At this point the consumer is
111 notified of the completion of the operation (by synchronous return
112 or asynchronous callback).
113
114 Note that this implies that when `DoLoop()` returns, one of two
115 things will be true:
116
117 * The return value will be `net::ERR_IO_PENDING`, indicating that the
118 caller should take no action and instead wait for asynchronous
119 notification.
120 * The state of the machine will be either `STATE_DONE` or `STATE_NONE`,
121 indicating that the operation that first initiated the `DoLoop()` has
122 completed.
123
124 This invariant reflects and enforces the single-threaded (though
125 possibly asynchronous) nature of the driven state machine--the
126 machine is always executing one requested operation.
127
128 * `DoLoop()` is called from two places: a) methods exposed to the consumer
129 for specific operations (e.g. |ReadHeaders|), and b) an IO completion
asanka 2015/09/04 14:51:03 |o_O| -> `o_O`
Randy Smith (Not in Mondays) 2015/09/18 22:06:33 Done.
130 callbacks called asynchronously by spawned IO operations.
131
132 In the first case, the return value from `DoLoop()` is returned directly
133 to the caller; if the operation completed synchronously, that will
134 contain the operation result, and if it completed asynchronously, it
135 will be `net::ERR_IO_PENDING`.
136
137 In the second case, the IO completion callback will examine the
138 return value from `DoLoop()`. If it is `net::ERR_IO_PENDING`, no
139 further action will be taken, and the IO completion callback will be
140 called again at some future point. If it is not
141 `net::ERR_IO_PENDING`, that is a signal that the operation has
142 completed, and the IO completion callback will call the appropriate
143 consumer callback to notify the consumer that the operation has
144 completed. Note that it is important that this callback be done
145 from the IO completion callback and not from `DoLoop()` or a
146 `DoLoop()` callee, both to support the sync/async error return
147 (DoLoop and its callees don't know the difference) and to avoid
148 consumer callbacks deleting the object out from under `DoLoop()`.
149
150 * The DoLoop pattern has no concept of different events arriving for
151 a single state; each state, if waiting, is waiting for one
152 particular event, and when `DoLoop()` is invoked when the machine is
153 in that state, it will handle that event. This reflects the
154 single-threaded model for operations spawned by the state machine.
155
156 Public class methods have very little processing as possible,
157 often simply making copies of arguments into data members, setting the
158 `next_state_` variable to indicate the section of the state diagram to
159 process, and calling `DoLoop()`.
asanka 2015/09/04 14:51:03 Also, inspecting the return value of DoLoop and se
Randy Smith (Not in Mondays) 2015/09/18 22:06:32 Done.
160
161 This idiom allows synchronous and asynchronous logic to be written in
162 the same fashion; it's all just state transition handling. For mostly
163 linear state diagrams, the handling code can be very easy to
164 comprehend, as such code is usually written linearly (in different
165 handling functions) in the order it's executed.
166
167 For examples of this idiom, see
168
169 * [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).
170 * [HttpNetworkTransaction::DoLoop](https://code.google.com/p/chromium/codesearch #chromium/src/net/http/http_network_transaction.cc&q=HttpNetworkTransaction::DoL oop&sq=package:chromium)
171
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