comparison src/io/rx_stack.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:def74424dcdd
1 /*
2 * $Id: rx_stack.c,v 1.8 2004/02/01 08:17:12 hipnod Exp $
3 *
4 * Copyright (C) 2003 giFT project (gift.sourceforge.net)
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
16
17 #include "gt_gnutella.h"
18 #include "gt_node.h"
19
20 #include "rx_stack.h"
21 #include "rx_layer.h"
22 #include "rx_link.h"
23 #include "rx_inflate.h"
24 #include "rx_packet.h"
25
26 /*****************************************************************************/
27
28 struct gt_rx_stack
29 {
30 TCPC *c;
31 BOOL inflated;
32
33 int depth; /* how deep in the stack we have
34 currently called into the stack */
35 BOOL aborted; /* one of our layers bailed out */
36 BOOL free_delayed; /* somebody called free during
37 message emission, and we delayed
38 it for later */
39 void *udata;
40 struct rx_layer *layers;
41
42 GtRxStackHandler handler;
43 GtRxStackCleanup cleanup;
44 };
45
46 static struct use_layer
47 {
48 const char *name;
49 struct rx_layer_ops *ops;
50 } layers[] =
51 {
52 { "rx_link", &gt_rx_link_ops },
53 { "rx_inflate", &gt_rx_inflate_ops },
54 { "rx_packet", &gt_rx_packet_ops },
55 };
56
57 /*****************************************************************************/
58
59 static void foreach_child (struct rx_layer *rx,
60 void (*exec)(struct rx_layer *rx, void *udata),
61 void *udata)
62 {
63 struct rx_layer *next;
64
65 while (rx != NULL)
66 {
67 /* grab the next element first so the callback can call free */
68 next = rx->lower;
69
70 exec (rx, udata);
71
72 rx = next;
73 }
74 }
75
76 static void disable_layer (struct rx_layer *rx, void *udata)
77 {
78 gt_rx_layer_disable (rx);
79 }
80
81 static void destroy_foreach (struct rx_layer *rx, void *udata)
82 {
83 gt_rx_layer_free (rx);
84 }
85
86 static void disable_all (GtRxStack *stack)
87 {
88 struct rx_layer *layers = stack->layers;
89
90 /* we must be at the top layer already */
91 assert (layers->upper == NULL);
92
93 foreach_child (layers, disable_layer, NULL);
94 }
95
96 /*****************************************************************************/
97
98 static struct rx_layer *push_layer (struct rx_layer *below,
99 struct rx_layer *above)
100 {
101 if (above)
102 above->lower = below;
103
104 if (below)
105 below->upper = above;
106
107 return above;
108 }
109
110 static void free_all_layers (GtRxStack *stack)
111 {
112 struct rx_layer *layers;
113
114 if (!stack)
115 return;
116
117 layers = stack->layers;
118
119 if (!layers)
120 return;
121
122 /* make sure we've stopped all processing on this layer */
123 disable_all (stack);
124
125 /* call each layer's destroy method */
126 foreach_child (layers, destroy_foreach, NULL);
127 }
128
129 static struct rx_layer *alloc_layers (GtRxStack *stack, TCPC *c,
130 BOOL rx_inflated)
131 {
132 struct rx_layer *layer = NULL;
133 struct rx_layer *new_layer = NULL;
134 void *udata = NULL;
135 int i;
136
137 for (i = 0; i < sizeof(layers) / sizeof(layers[0]); i++)
138 {
139 /* XXX */
140 if (!strcmp (layers[i].name, "rx_link"))
141 udata = c;
142
143 if (!strcmp (layers[i].name, "rx_inflate") && !rx_inflated)
144 continue;
145
146 if (!(new_layer = gt_rx_layer_new (stack, layers[i].name,
147 layers[i].ops, udata)))
148 {
149 foreach_child (layer, destroy_foreach, NULL);
150 return NULL;
151 }
152
153 layer = push_layer (layer, new_layer);
154 udata = NULL;
155 }
156
157 return layer;
158 }
159
160 /*****************************************************************************/
161
162 static void enable_layer (struct rx_layer *rx, void *udata)
163 {
164 gt_rx_layer_enable (rx);
165 }
166
167 GtRxStack *gt_rx_stack_new (GtNode *node, TCPC *c, BOOL rx_inflated)
168 {
169 GtRxStack *stack;
170 int size;
171
172 if (!(stack = NEW (GtRxStack)))
173 return NULL;
174
175 stack->c = c;
176 stack->inflated = rx_inflated;
177
178 if (!(stack->layers = alloc_layers (stack, c, rx_inflated)))
179 {
180 free (stack);
181 return NULL;
182 }
183
184 /* set the receive buf to a not large value */
185 size = 4096;
186
187 if (setsockopt (c->fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) != 0)
188 GT->DBGSOCK (GT, c, "Error setting rcvbuf size: %s", GIFT_NETERROR());
189
190 /* enable each layer */
191 foreach_child (stack->layers, enable_layer, NULL);
192
193 return stack;
194 }
195
196 /*****************************************************************************/
197
198 /*
199 * Cleanup handling. This is a bit tricky, because both the owner of the
200 * stack and the stack itself may want to free the stack, and they both have
201 * to coordinate so only one of them does the free().
202 *
203 * Also, the stack has to worry about the free happening whilst a message
204 * emission is taking place, and so has to unwind the stack to the top level
205 * to make sure it doesn't reference freed memory.
206 */
207
208 static void free_stack (GtRxStack *stack)
209 {
210 /*
211 * ->cleanup gets called from the lower layers, and only
212 * if something bad happened (socket close, etc).
213 */
214 free_all_layers (stack);
215 FREE (stack);
216 }
217
218 void gt_rx_stack_free (GtRxStack *stack)
219 {
220 if (!stack)
221 return;
222
223 /*
224 * If we in the middle of a reception when someone calls this function
225 * [say by calling gt_node_disconenct()], we can't free right now
226 * because the data structures are still in use in the reception stack.
227 *
228 * So we queue the removal in that case, by setting free_delayed and
229 * freeing when the stack unwinds, just like when waiting to notify the
230 * stack's listener about gt_rx_stack_abort().
231 */
232 if (stack->depth > 0)
233 {
234 /* we'll defer the real free until later */
235 stack->free_delayed = TRUE;
236
237 /* we must stop processing */
238 gt_rx_stack_abort (stack);
239
240 return;
241 }
242
243 free_stack (stack);
244 }
245
246 /* notify the user of the stack it's time to clean up this stack */
247 static void cleanup_notify (GtRxStack *stack)
248 {
249 /*
250 * First, we check if our owner tried to call ->free on us already.
251 * If so, we don't notify them because they already are well aware we are
252 * on our way to oblivion.
253 */
254 if (stack->free_delayed)
255 {
256 free_stack (stack);
257 return;
258 }
259
260 if (stack->aborted)
261 stack->cleanup (stack->udata);
262 }
263
264 void gt_rx_stack_recv_start (GtRxStack *stack)
265 {
266 assert (stack->depth >= 0);
267 stack->depth++;
268 }
269
270 void gt_rx_stack_recv_end (GtRxStack *stack)
271 {
272 assert (stack->depth > 0);
273
274 if (--stack->depth == 0)
275 cleanup_notify (stack);
276 }
277
278 /*
279 * RX layers call this function when something bad happens and they need
280 * to abort the stack processing.
281 *
282 * In other words, this is the "oh shit" function.
283 */
284 void gt_rx_stack_abort (GtRxStack *stack)
285 {
286 disable_all (stack);
287
288 /* set the flag indicated this stack has been aborted while processing */
289 stack->aborted = TRUE;
290
291 /*
292 * If we are in the middle of receiving some data, set stack->aborted
293 * so when the reception unwinds, we'll notify the owner of the
294 * stack's cleanup function.
295 */
296 if (stack->depth > 0)
297 return;
298
299 /*
300 * This can happen from the bottom layer, if it hasn't passed any data to
301 * upper layers yet. TODO: if the bottom layer was driven by the RX stack
302 * instead of rx_link, this wouldn't need to be here, i think..
303 */
304 cleanup_notify (stack);
305 }
306
307 void gt_rx_stack_set_handler (GtRxStack *stack, GtRxStackHandler handler,
308 GtRxStackCleanup cleanup, void *udata)
309 {
310 stack->udata = udata;
311 stack->handler = handler;
312 stack->cleanup = cleanup;
313
314 /*
315 * The topmost layer is rx_packet, so we can simply set the data.
316 */
317 gt_rx_packet_set_handler (stack->layers, handler, udata);
318 }