Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
diff 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 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/io/rx_stack.c Sat Feb 20 21:18:28 2010 -0800 1.3 @@ -0,0 +1,318 @@ 1.4 +/* 1.5 + * $Id: rx_stack.c,v 1.8 2004/02/01 08:17:12 hipnod Exp $ 1.6 + * 1.7 + * Copyright (C) 2003 giFT project (gift.sourceforge.net) 1.8 + * 1.9 + * This program is free software; you can redistribute it and/or modify it 1.10 + * under the terms of the GNU General Public License as published by the 1.11 + * Free Software Foundation; either version 2, or (at your option) any 1.12 + * later version. 1.13 + * 1.14 + * This program is distributed in the hope that it will be useful, but 1.15 + * WITHOUT ANY WARRANTY; without even the implied warranty of 1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.17 + * General Public License for more details. 1.18 + */ 1.19 + 1.20 +#include "gt_gnutella.h" 1.21 +#include "gt_node.h" 1.22 + 1.23 +#include "rx_stack.h" 1.24 +#include "rx_layer.h" 1.25 +#include "rx_link.h" 1.26 +#include "rx_inflate.h" 1.27 +#include "rx_packet.h" 1.28 + 1.29 +/*****************************************************************************/ 1.30 + 1.31 +struct gt_rx_stack 1.32 +{ 1.33 + TCPC *c; 1.34 + BOOL inflated; 1.35 + 1.36 + int depth; /* how deep in the stack we have 1.37 + currently called into the stack */ 1.38 + BOOL aborted; /* one of our layers bailed out */ 1.39 + BOOL free_delayed; /* somebody called free during 1.40 + message emission, and we delayed 1.41 + it for later */ 1.42 + void *udata; 1.43 + struct rx_layer *layers; 1.44 + 1.45 + GtRxStackHandler handler; 1.46 + GtRxStackCleanup cleanup; 1.47 +}; 1.48 + 1.49 +static struct use_layer 1.50 +{ 1.51 + const char *name; 1.52 + struct rx_layer_ops *ops; 1.53 +} layers[] = 1.54 +{ 1.55 + { "rx_link", >_rx_link_ops }, 1.56 + { "rx_inflate", >_rx_inflate_ops }, 1.57 + { "rx_packet", >_rx_packet_ops }, 1.58 +}; 1.59 + 1.60 +/*****************************************************************************/ 1.61 + 1.62 +static void foreach_child (struct rx_layer *rx, 1.63 + void (*exec)(struct rx_layer *rx, void *udata), 1.64 + void *udata) 1.65 +{ 1.66 + struct rx_layer *next; 1.67 + 1.68 + while (rx != NULL) 1.69 + { 1.70 + /* grab the next element first so the callback can call free */ 1.71 + next = rx->lower; 1.72 + 1.73 + exec (rx, udata); 1.74 + 1.75 + rx = next; 1.76 + } 1.77 +} 1.78 + 1.79 +static void disable_layer (struct rx_layer *rx, void *udata) 1.80 +{ 1.81 + gt_rx_layer_disable (rx); 1.82 +} 1.83 + 1.84 +static void destroy_foreach (struct rx_layer *rx, void *udata) 1.85 +{ 1.86 + gt_rx_layer_free (rx); 1.87 +} 1.88 + 1.89 +static void disable_all (GtRxStack *stack) 1.90 +{ 1.91 + struct rx_layer *layers = stack->layers; 1.92 + 1.93 + /* we must be at the top layer already */ 1.94 + assert (layers->upper == NULL); 1.95 + 1.96 + foreach_child (layers, disable_layer, NULL); 1.97 +} 1.98 + 1.99 +/*****************************************************************************/ 1.100 + 1.101 +static struct rx_layer *push_layer (struct rx_layer *below, 1.102 + struct rx_layer *above) 1.103 +{ 1.104 + if (above) 1.105 + above->lower = below; 1.106 + 1.107 + if (below) 1.108 + below->upper = above; 1.109 + 1.110 + return above; 1.111 +} 1.112 + 1.113 +static void free_all_layers (GtRxStack *stack) 1.114 +{ 1.115 + struct rx_layer *layers; 1.116 + 1.117 + if (!stack) 1.118 + return; 1.119 + 1.120 + layers = stack->layers; 1.121 + 1.122 + if (!layers) 1.123 + return; 1.124 + 1.125 + /* make sure we've stopped all processing on this layer */ 1.126 + disable_all (stack); 1.127 + 1.128 + /* call each layer's destroy method */ 1.129 + foreach_child (layers, destroy_foreach, NULL); 1.130 +} 1.131 + 1.132 +static struct rx_layer *alloc_layers (GtRxStack *stack, TCPC *c, 1.133 + BOOL rx_inflated) 1.134 +{ 1.135 + struct rx_layer *layer = NULL; 1.136 + struct rx_layer *new_layer = NULL; 1.137 + void *udata = NULL; 1.138 + int i; 1.139 + 1.140 + for (i = 0; i < sizeof(layers) / sizeof(layers[0]); i++) 1.141 + { 1.142 + /* XXX */ 1.143 + if (!strcmp (layers[i].name, "rx_link")) 1.144 + udata = c; 1.145 + 1.146 + if (!strcmp (layers[i].name, "rx_inflate") && !rx_inflated) 1.147 + continue; 1.148 + 1.149 + if (!(new_layer = gt_rx_layer_new (stack, layers[i].name, 1.150 + layers[i].ops, udata))) 1.151 + { 1.152 + foreach_child (layer, destroy_foreach, NULL); 1.153 + return NULL; 1.154 + } 1.155 + 1.156 + layer = push_layer (layer, new_layer); 1.157 + udata = NULL; 1.158 + } 1.159 + 1.160 + return layer; 1.161 +} 1.162 + 1.163 +/*****************************************************************************/ 1.164 + 1.165 +static void enable_layer (struct rx_layer *rx, void *udata) 1.166 +{ 1.167 + gt_rx_layer_enable (rx); 1.168 +} 1.169 + 1.170 +GtRxStack *gt_rx_stack_new (GtNode *node, TCPC *c, BOOL rx_inflated) 1.171 +{ 1.172 + GtRxStack *stack; 1.173 + int size; 1.174 + 1.175 + if (!(stack = NEW (GtRxStack))) 1.176 + return NULL; 1.177 + 1.178 + stack->c = c; 1.179 + stack->inflated = rx_inflated; 1.180 + 1.181 + if (!(stack->layers = alloc_layers (stack, c, rx_inflated))) 1.182 + { 1.183 + free (stack); 1.184 + return NULL; 1.185 + } 1.186 + 1.187 + /* set the receive buf to a not large value */ 1.188 + size = 4096; 1.189 + 1.190 + if (setsockopt (c->fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) != 0) 1.191 + GT->DBGSOCK (GT, c, "Error setting rcvbuf size: %s", GIFT_NETERROR()); 1.192 + 1.193 + /* enable each layer */ 1.194 + foreach_child (stack->layers, enable_layer, NULL); 1.195 + 1.196 + return stack; 1.197 +} 1.198 + 1.199 +/*****************************************************************************/ 1.200 + 1.201 +/* 1.202 + * Cleanup handling. This is a bit tricky, because both the owner of the 1.203 + * stack and the stack itself may want to free the stack, and they both have 1.204 + * to coordinate so only one of them does the free(). 1.205 + * 1.206 + * Also, the stack has to worry about the free happening whilst a message 1.207 + * emission is taking place, and so has to unwind the stack to the top level 1.208 + * to make sure it doesn't reference freed memory. 1.209 + */ 1.210 + 1.211 +static void free_stack (GtRxStack *stack) 1.212 +{ 1.213 + /* 1.214 + * ->cleanup gets called from the lower layers, and only 1.215 + * if something bad happened (socket close, etc). 1.216 + */ 1.217 + free_all_layers (stack); 1.218 + FREE (stack); 1.219 +} 1.220 + 1.221 +void gt_rx_stack_free (GtRxStack *stack) 1.222 +{ 1.223 + if (!stack) 1.224 + return; 1.225 + 1.226 + /* 1.227 + * If we in the middle of a reception when someone calls this function 1.228 + * [say by calling gt_node_disconenct()], we can't free right now 1.229 + * because the data structures are still in use in the reception stack. 1.230 + * 1.231 + * So we queue the removal in that case, by setting free_delayed and 1.232 + * freeing when the stack unwinds, just like when waiting to notify the 1.233 + * stack's listener about gt_rx_stack_abort(). 1.234 + */ 1.235 + if (stack->depth > 0) 1.236 + { 1.237 + /* we'll defer the real free until later */ 1.238 + stack->free_delayed = TRUE; 1.239 + 1.240 + /* we must stop processing */ 1.241 + gt_rx_stack_abort (stack); 1.242 + 1.243 + return; 1.244 + } 1.245 + 1.246 + free_stack (stack); 1.247 +} 1.248 + 1.249 +/* notify the user of the stack it's time to clean up this stack */ 1.250 +static void cleanup_notify (GtRxStack *stack) 1.251 +{ 1.252 + /* 1.253 + * First, we check if our owner tried to call ->free on us already. 1.254 + * If so, we don't notify them because they already are well aware we are 1.255 + * on our way to oblivion. 1.256 + */ 1.257 + if (stack->free_delayed) 1.258 + { 1.259 + free_stack (stack); 1.260 + return; 1.261 + } 1.262 + 1.263 + if (stack->aborted) 1.264 + stack->cleanup (stack->udata); 1.265 +} 1.266 + 1.267 +void gt_rx_stack_recv_start (GtRxStack *stack) 1.268 +{ 1.269 + assert (stack->depth >= 0); 1.270 + stack->depth++; 1.271 +} 1.272 + 1.273 +void gt_rx_stack_recv_end (GtRxStack *stack) 1.274 +{ 1.275 + assert (stack->depth > 0); 1.276 + 1.277 + if (--stack->depth == 0) 1.278 + cleanup_notify (stack); 1.279 +} 1.280 + 1.281 +/* 1.282 + * RX layers call this function when something bad happens and they need 1.283 + * to abort the stack processing. 1.284 + * 1.285 + * In other words, this is the "oh shit" function. 1.286 + */ 1.287 +void gt_rx_stack_abort (GtRxStack *stack) 1.288 +{ 1.289 + disable_all (stack); 1.290 + 1.291 + /* set the flag indicated this stack has been aborted while processing */ 1.292 + stack->aborted = TRUE; 1.293 + 1.294 + /* 1.295 + * If we are in the middle of receiving some data, set stack->aborted 1.296 + * so when the reception unwinds, we'll notify the owner of the 1.297 + * stack's cleanup function. 1.298 + */ 1.299 + if (stack->depth > 0) 1.300 + return; 1.301 + 1.302 + /* 1.303 + * This can happen from the bottom layer, if it hasn't passed any data to 1.304 + * upper layers yet. TODO: if the bottom layer was driven by the RX stack 1.305 + * instead of rx_link, this wouldn't need to be here, i think.. 1.306 + */ 1.307 + cleanup_notify (stack); 1.308 +} 1.309 + 1.310 +void gt_rx_stack_set_handler (GtRxStack *stack, GtRxStackHandler handler, 1.311 + GtRxStackCleanup cleanup, void *udata) 1.312 +{ 1.313 + stack->udata = udata; 1.314 + stack->handler = handler; 1.315 + stack->cleanup = cleanup; 1.316 + 1.317 + /* 1.318 + * The topmost layer is rx_packet, so we can simply set the data. 1.319 + */ 1.320 + gt_rx_packet_set_handler (stack->layers, handler, udata); 1.321 +}