Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
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", >_rx_link_ops }, | |
53 { "rx_inflate", >_rx_inflate_ops }, | |
54 { "rx_packet", >_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 } |