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