paulo@0: /* paulo@0: * $Id: rx_stack.c,v 1.8 2004/02/01 08:17:12 hipnod Exp $ paulo@0: * paulo@0: * Copyright (C) 2003 giFT project (gift.sourceforge.net) paulo@0: * paulo@0: * This program is free software; you can redistribute it and/or modify it paulo@0: * under the terms of the GNU General Public License as published by the paulo@0: * Free Software Foundation; either version 2, or (at your option) any paulo@0: * later version. paulo@0: * paulo@0: * This program is distributed in the hope that it will be useful, but paulo@0: * WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU paulo@0: * General Public License for more details. paulo@0: */ paulo@0: paulo@0: #include "gt_gnutella.h" paulo@0: #include "gt_node.h" paulo@0: paulo@0: #include "rx_stack.h" paulo@0: #include "rx_layer.h" paulo@0: #include "rx_link.h" paulo@0: #include "rx_inflate.h" paulo@0: #include "rx_packet.h" paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: struct gt_rx_stack paulo@0: { paulo@0: TCPC *c; paulo@0: BOOL inflated; paulo@0: paulo@0: int depth; /* how deep in the stack we have paulo@0: currently called into the stack */ paulo@0: BOOL aborted; /* one of our layers bailed out */ paulo@0: BOOL free_delayed; /* somebody called free during paulo@0: message emission, and we delayed paulo@0: it for later */ paulo@0: void *udata; paulo@0: struct rx_layer *layers; paulo@0: paulo@0: GtRxStackHandler handler; paulo@0: GtRxStackCleanup cleanup; paulo@0: }; paulo@0: paulo@0: static struct use_layer paulo@0: { paulo@0: const char *name; paulo@0: struct rx_layer_ops *ops; paulo@0: } layers[] = paulo@0: { paulo@0: { "rx_link", >_rx_link_ops }, paulo@0: { "rx_inflate", >_rx_inflate_ops }, paulo@0: { "rx_packet", >_rx_packet_ops }, paulo@0: }; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void foreach_child (struct rx_layer *rx, paulo@0: void (*exec)(struct rx_layer *rx, void *udata), paulo@0: void *udata) paulo@0: { paulo@0: struct rx_layer *next; paulo@0: paulo@0: while (rx != NULL) paulo@0: { paulo@0: /* grab the next element first so the callback can call free */ paulo@0: next = rx->lower; paulo@0: paulo@0: exec (rx, udata); paulo@0: paulo@0: rx = next; paulo@0: } paulo@0: } paulo@0: paulo@0: static void disable_layer (struct rx_layer *rx, void *udata) paulo@0: { paulo@0: gt_rx_layer_disable (rx); paulo@0: } paulo@0: paulo@0: static void destroy_foreach (struct rx_layer *rx, void *udata) paulo@0: { paulo@0: gt_rx_layer_free (rx); paulo@0: } paulo@0: paulo@0: static void disable_all (GtRxStack *stack) paulo@0: { paulo@0: struct rx_layer *layers = stack->layers; paulo@0: paulo@0: /* we must be at the top layer already */ paulo@0: assert (layers->upper == NULL); paulo@0: paulo@0: foreach_child (layers, disable_layer, NULL); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static struct rx_layer *push_layer (struct rx_layer *below, paulo@0: struct rx_layer *above) paulo@0: { paulo@0: if (above) paulo@0: above->lower = below; paulo@0: paulo@0: if (below) paulo@0: below->upper = above; paulo@0: paulo@0: return above; paulo@0: } paulo@0: paulo@0: static void free_all_layers (GtRxStack *stack) paulo@0: { paulo@0: struct rx_layer *layers; paulo@0: paulo@0: if (!stack) paulo@0: return; paulo@0: paulo@0: layers = stack->layers; paulo@0: paulo@0: if (!layers) paulo@0: return; paulo@0: paulo@0: /* make sure we've stopped all processing on this layer */ paulo@0: disable_all (stack); paulo@0: paulo@0: /* call each layer's destroy method */ paulo@0: foreach_child (layers, destroy_foreach, NULL); paulo@0: } paulo@0: paulo@0: static struct rx_layer *alloc_layers (GtRxStack *stack, TCPC *c, paulo@0: BOOL rx_inflated) paulo@0: { paulo@0: struct rx_layer *layer = NULL; paulo@0: struct rx_layer *new_layer = NULL; paulo@0: void *udata = NULL; paulo@0: int i; paulo@0: paulo@0: for (i = 0; i < sizeof(layers) / sizeof(layers[0]); i++) paulo@0: { paulo@0: /* XXX */ paulo@0: if (!strcmp (layers[i].name, "rx_link")) paulo@0: udata = c; paulo@0: paulo@0: if (!strcmp (layers[i].name, "rx_inflate") && !rx_inflated) paulo@0: continue; paulo@0: paulo@0: if (!(new_layer = gt_rx_layer_new (stack, layers[i].name, paulo@0: layers[i].ops, udata))) paulo@0: { paulo@0: foreach_child (layer, destroy_foreach, NULL); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: layer = push_layer (layer, new_layer); paulo@0: udata = NULL; paulo@0: } paulo@0: paulo@0: return layer; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void enable_layer (struct rx_layer *rx, void *udata) paulo@0: { paulo@0: gt_rx_layer_enable (rx); paulo@0: } paulo@0: paulo@0: GtRxStack *gt_rx_stack_new (GtNode *node, TCPC *c, BOOL rx_inflated) paulo@0: { paulo@0: GtRxStack *stack; paulo@0: int size; paulo@0: paulo@0: if (!(stack = NEW (GtRxStack))) paulo@0: return NULL; paulo@0: paulo@0: stack->c = c; paulo@0: stack->inflated = rx_inflated; paulo@0: paulo@0: if (!(stack->layers = alloc_layers (stack, c, rx_inflated))) paulo@0: { paulo@0: free (stack); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: /* set the receive buf to a not large value */ paulo@0: size = 4096; paulo@0: paulo@0: if (setsockopt (c->fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) != 0) paulo@0: GT->DBGSOCK (GT, c, "Error setting rcvbuf size: %s", GIFT_NETERROR()); paulo@0: paulo@0: /* enable each layer */ paulo@0: foreach_child (stack->layers, enable_layer, NULL); paulo@0: paulo@0: return stack; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * Cleanup handling. This is a bit tricky, because both the owner of the paulo@0: * stack and the stack itself may want to free the stack, and they both have paulo@0: * to coordinate so only one of them does the free(). paulo@0: * paulo@0: * Also, the stack has to worry about the free happening whilst a message paulo@0: * emission is taking place, and so has to unwind the stack to the top level paulo@0: * to make sure it doesn't reference freed memory. paulo@0: */ paulo@0: paulo@0: static void free_stack (GtRxStack *stack) paulo@0: { paulo@0: /* paulo@0: * ->cleanup gets called from the lower layers, and only paulo@0: * if something bad happened (socket close, etc). paulo@0: */ paulo@0: free_all_layers (stack); paulo@0: FREE (stack); paulo@0: } paulo@0: paulo@0: void gt_rx_stack_free (GtRxStack *stack) paulo@0: { paulo@0: if (!stack) paulo@0: return; paulo@0: paulo@0: /* paulo@0: * If we in the middle of a reception when someone calls this function paulo@0: * [say by calling gt_node_disconenct()], we can't free right now paulo@0: * because the data structures are still in use in the reception stack. paulo@0: * paulo@0: * So we queue the removal in that case, by setting free_delayed and paulo@0: * freeing when the stack unwinds, just like when waiting to notify the paulo@0: * stack's listener about gt_rx_stack_abort(). paulo@0: */ paulo@0: if (stack->depth > 0) paulo@0: { paulo@0: /* we'll defer the real free until later */ paulo@0: stack->free_delayed = TRUE; paulo@0: paulo@0: /* we must stop processing */ paulo@0: gt_rx_stack_abort (stack); paulo@0: paulo@0: return; paulo@0: } paulo@0: paulo@0: free_stack (stack); paulo@0: } paulo@0: paulo@0: /* notify the user of the stack it's time to clean up this stack */ paulo@0: static void cleanup_notify (GtRxStack *stack) paulo@0: { paulo@0: /* paulo@0: * First, we check if our owner tried to call ->free on us already. paulo@0: * If so, we don't notify them because they already are well aware we are paulo@0: * on our way to oblivion. paulo@0: */ paulo@0: if (stack->free_delayed) paulo@0: { paulo@0: free_stack (stack); paulo@0: return; paulo@0: } paulo@0: paulo@0: if (stack->aborted) paulo@0: stack->cleanup (stack->udata); paulo@0: } paulo@0: paulo@0: void gt_rx_stack_recv_start (GtRxStack *stack) paulo@0: { paulo@0: assert (stack->depth >= 0); paulo@0: stack->depth++; paulo@0: } paulo@0: paulo@0: void gt_rx_stack_recv_end (GtRxStack *stack) paulo@0: { paulo@0: assert (stack->depth > 0); paulo@0: paulo@0: if (--stack->depth == 0) paulo@0: cleanup_notify (stack); paulo@0: } paulo@0: paulo@0: /* paulo@0: * RX layers call this function when something bad happens and they need paulo@0: * to abort the stack processing. paulo@0: * paulo@0: * In other words, this is the "oh shit" function. paulo@0: */ paulo@0: void gt_rx_stack_abort (GtRxStack *stack) paulo@0: { paulo@0: disable_all (stack); paulo@0: paulo@0: /* set the flag indicated this stack has been aborted while processing */ paulo@0: stack->aborted = TRUE; paulo@0: paulo@0: /* paulo@0: * If we are in the middle of receiving some data, set stack->aborted paulo@0: * so when the reception unwinds, we'll notify the owner of the paulo@0: * stack's cleanup function. paulo@0: */ paulo@0: if (stack->depth > 0) paulo@0: return; paulo@0: paulo@0: /* paulo@0: * This can happen from the bottom layer, if it hasn't passed any data to paulo@0: * upper layers yet. TODO: if the bottom layer was driven by the RX stack paulo@0: * instead of rx_link, this wouldn't need to be here, i think.. paulo@0: */ paulo@0: cleanup_notify (stack); paulo@0: } paulo@0: paulo@0: void gt_rx_stack_set_handler (GtRxStack *stack, GtRxStackHandler handler, paulo@0: GtRxStackCleanup cleanup, void *udata) paulo@0: { paulo@0: stack->udata = udata; paulo@0: stack->handler = handler; paulo@0: stack->cleanup = cleanup; paulo@0: paulo@0: /* paulo@0: * The topmost layer is rx_packet, so we can simply set the data. paulo@0: */ paulo@0: gt_rx_packet_set_handler (stack->layers, handler, udata); paulo@0: }