[M94 Dev][Tizen] Fix for compiler and linker errors
[platform/framework/web/chromium-efl.git] / net / docs / code-patterns.md
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][]).
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` or `rv`) containing
14 such a 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 indicating either zero
25   bytes/EOF or 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 failure.
28 * If the return value is the special value `net::ERR_IO_PENDING`, it
29   indicates that the routine will complete asynchronously. A reference to
30   any provided IOBuffer will be retained by the called entity until
31   completion, to be written into or read from as required. 
32   If there is a callback argument, that callback will be called upon
33   completion with the return value; if there is no callback argument, it
34   usually means that some known callback mechanism will be employed.
35
36 ## DoLoop
37
38 The DoLoop pattern is used in the network stack to construct simple
39 state machines. It is used for cases in which processing is basically
40 single-threaded and could be written in a single function, if that
41 function could block waiting for input. Generally, initiation of a
42 state machine is triggered by some method invocation by a class
43 consumer, and that state machine is driven (possibly across
44 asynchronous IO initiated by the class) until the operation requested
45 by the method invocation completes, at which point the state variable is
46 set to `STATE_NONE` and the consumer notified.  
47
48 Cases which do not fit into this single-threaded, single consumer
49 operation model are generally adapted in some way to fit the model,
50 either by multiple state machines (e.g. independent state machines for
51 reading and writing, if each can be initiated while the other is
52 outstanding) or by storing information across consumer invocations and
53 returns that can be used to restart the state machine in the proper
54 state. 
55
56 Any class using this pattern will contain an enum listing all states
57 of that machine, and define a function, `DoLoop()`, to drive that state
58 machine. If a class has multiple state machines (as above) it will
59 have multiple methods (e.g. `DoReadLoop()` and `DoWriteLoop()`) to drive
60 those different machines.
61
62 The characteristics of the DoLoop pattern are:
63
64 *   Each state has a corresponding function which is called by `DoLoop()`
65     for handling when the state machine is in that state. Generally the
66     states are named STATE`_<`STATE_NAME`>` (upper case separated by
67     underscores), and the routine is named Do`<`StateName`>` (CamelCase).
68     For example:
69
70          enum State {
71              STATE_NONE, 
72              STATE_INIT,
73              STATE_FOO,
74              STATE_FOO_COMPLETE,
75          };
76          int DoInit();
77          int DoFoo();
78          int DoFooComplete(int result);
79
80 *   Each state handling function has two basic responsibilities in
81     addition to state specific handling: Setting the data member
82     (named `next_state_` or something similar)
83     to specify the next state, and returning a `net::Error` (or combined
84     error and byte count, as above). 
85     
86 *   On each `DoLoop()` iteration, the function saves the next state to a local
87     variable and resets to a default state (`STATE_NONE`),
88     and then calls the appropriate state handling based on the
89     original value of the next state. This looks like:
90
91            do {
92              State state = io_state_;
93              next_state_ = STATE_NONE;
94              switch (state) {
95                case STATE_INIT:
96                  result = DoInit();
97                  break;
98                ...
99
100     This pattern is followed primarily to ensure that in the event of
101     a bug where the next state isn't set, the loop terminates rather
102     than loops infinitely. It's not a perfect mitigation, but works
103     well as a defensive measure.
104     
105 *   If a given state may complete asynchronously (for example,
106     writing to an underlying transport socket), then there will often
107     be split states, such as `STATE_WRITE` and
108     `STATE_WRITE_COMPLETE`. The first state is responsible for
109     starting/continuing the original operation, while the second state
110     is responsible for handling completion (e.g. success vs error,
111     complete vs. incomplete writes), and determining the next state to
112     transition to. 
113     
114 *   While the return value from each call is propagated through the loop
115     to the next state, it is expected that for most state transitions the
116     return value will be `net::OK`, and that an error return will also
117     set the state to `STATE_NONE` or fail to override the default
118     assignment to `STATE_DONE` to exit the loop and return that 
119     error to the caller. This is often asserted with a DCHECK, e.g.
120
121             case STATE_FOO:
122                 DCHECK_EQ(result, OK);
123                 result = DoFoo();
124                 break;
125
126     The exception to this pattern is split states, where an IO
127     operation has been dispatched, and the second state is handling
128     the result. In that case, the second state's function takes the
129     result code:
130     
131             case STATE_FOO_COMPLETE:
132                 result = DoFooComplete(result);
133                 break;
134     
135 *   If the return value from the state handling function is
136     `net::ERR_IO_PENDING`, that indicates that the function has arranged
137     for `DoLoop()` to be called at some point in the future, when further
138     progress can be made on the state transitions. The `next_state_` variable
139     will have been set to the proper value for handling that incoming
140     call. In this case, `DoLoop()` will exit. This often occurs between
141     split states, as described above. 
142     
143 *   The DoLoop mechanism is generally invoked in response to a consumer
144     calling one of its methods. While the operation that method
145     requested is occuring, the state machine stays active, possibly
146     over multiple asynchronous operations and state transitions. When
147     that operation is complete, the state machine transitions to
148     `STATE_NONE` (by a `DoLoop()` callee not setting `next_state_`) or
149     explicitly to `STATE_DONE` (indicating that the operation is
150     complete *and* the state machine is not amenable to further
151     driving). At this point the consumer is notified of the completion
152     of the operation (by synchronous return or asynchronous callback).
153  
154     Note that this implies that when `DoLoop()` returns, one of two
155     things will be true:
156  
157     * The return value will be `net::ERR_IO_PENDING`, indicating that the
158       caller should take no action and instead wait for asynchronous
159       notification. 
160     * The state of the machine will be either `STATE_DONE` or `STATE_NONE`,
161       indicating that the operation that first initiated the `DoLoop()` has
162       completed. 
163  
164     This invariant reflects and enforces the single-threaded (though
165     possibly asynchronous) nature of the driven state machine--the
166     machine is always executing one requested operation.
167     
168 *   `DoLoop()` is called from two places: a) methods exposed to the consumer
169     for specific operations (e.g. `ReadHeaders()`), and b) an IO completion
170     callbacks called asynchronously by spawned IO operations.
171
172     In the first case, the return value from `DoLoop()` is returned directly
173     to the caller; if the operation completed synchronously, that will
174     contain the operation result, and if it completed asynchronously, it
175     will be `net::ERR_IO_PENDING`. For example (from 
176     `HttpStreamParser`, abridged for clarity): 
177
178              int HttpStreamParser::ReadResponseHeaders(
179                  CompletionOnceCallback callback) {
180                DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE);
181                DCHECK(callback_.is_null());
182                DCHECK(!callback.is_null());
183
184                int result = OK;
185                io_state_ = STATE_READ_HEADERS;
186
187                result = DoLoop(result);
188
189                if (result == ERR_IO_PENDING)
190                  callback_ = std::move(callback);
191
192                return result > 0 ? OK : result;
193              }
194
195     In the second case, the IO completion callback will examine the
196     return value from `DoLoop()`. If it is `net::ERR_IO_PENDING`, no
197     further action will be taken, and the IO completion callback will be
198     called again at some future point. If it is not
199     `net::ERR_IO_PENDING`, that is a signal that the operation has
200     completed, and the IO completion callback will call the appropriate
201     consumer callback to notify the consumer that the operation has
202     completed. Note that it is important that this callback be done
203     from the IO completion callback and not from `DoLoop()` or a
204     `DoLoop()` callee, both to support the sync/async error return
205     (DoLoop and its callees don't know the difference) and to avoid
206     consumer callbacks deleting the object out from under `DoLoop()`.
207     Example: 
208
209              void HttpStreamParser::OnIOComplete(int result) {
210                result = DoLoop(result);
211
212                if (result != ERR_IO_PENDING && !callback_.is_null())
213                  std::move(callback_).Run(result);
214              }
215     
216 *   The DoLoop pattern has no concept of different events arriving for
217     a single state; each state, if waiting, is waiting for one
218     particular event, and when `DoLoop()` is invoked when the machine is
219     in that state, it will handle that event. This reflects the
220     single-threaded model for operations spawned by the state machine.
221
222 Public class methods generally have very little processing, primarily wrapping 
223 `DoLoop()`. For `DoLoop()` entry this involves setting the `next_state_`
224 variable, and possibly making copies of arguments into class members. For
225 `DoLoop()` exit, it involves inspecting the return and passing it back to
226 the caller, and in the asynchronous case, saving any passed completion callback
227 for executing by a future subsidiary IO completion (see above example). 
228
229 This idiom allows synchronous and asynchronous logic to be written in
230 the same fashion; it's all just state transition handling. For mostly
231 linear state diagrams, the handling code can be very easy to
232 comprehend, as such code is usually written linearly (in different
233 handling functions) in the order it's executed. 
234
235 For examples of this idiom, see
236
237 * [HttpStreamParser::DoLoop](https://source.chromium.org/chromium/chromium/src/+/HEAD:net/http/http_stream_parser.cc).
238 * [HttpNetworkTransaction::DoLoop](https://source.chromium.org/chromium/chromium/src/+/HEAD:net/http/http_network_transaction.cc)
239
240 [net_error_list.h]: https://chromium.googlesource.com/chromium/src/+/main/net/base/net_error_list.h#1