view libfaim/aim_txqueue.c @ 270:cfa39d39dec6

[gaim-migrate @ 280] Fixed the 100% bug, but in doing so, broke permit/deny lists, so that got commented out (yet again). Gaim/Faim is now usable. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Fri, 26 May 2000 23:10:21 +0000
parents 6ced2f1c8b24
children 501e09c51cbc
line wrap: on
line source

/*
 *  aim_txqueue.c
 *
 * Herein lies all the mangement routines for the transmit (Tx) queue.
 *
 * Changes by EWarmenhoven Fri May 26 22:52:46 UTC 2000:
 * - added aim_tx_flushqueue() to the end of aim_tx_enqueue() so that I don't
 * have to worry about it any more. mid tells me that doing so will solve the
 * 100% bug. Thanks mid!
 *
 */

#include <aim.h>

/*
 * Allocate a new tx frame.
 *
 * This is more for looks than anything else.
 *
 * Right now, that is.  If/when we implement a pool of transmit
 * frames, this will become the request-an-unused-frame part.
 */
struct command_tx_struct *aim_tx_new(int chan, struct aim_conn_t *conn, int datalen)
{
  struct command_tx_struct *new;

  if (!conn)
    return NULL;

  new = (struct command_tx_struct *)malloc(sizeof(struct command_tx_struct));
  if (!new)
    return NULL;
  memset(new, 0, sizeof(struct command_tx_struct));

  new->conn = conn; 
  new->type = chan;

  if(datalen) {
    new->data = (u_char *)malloc(datalen);
    new->commandlen = datalen;
  }

  return new;
}

/*
 * aim_tx_enqeue()
 *
 * The overall purpose here is to enqueue the passed in command struct
 * into the outgoing (tx) queue.  Basically...
 *   1) Make a scope-irrelevent copy of the struct
 *   2) Lock the struct
 *   3) Mark as not-sent-yet
 *   4) Enqueue the struct into the list
 *   5) Unlock the struct once it's linked in
 *   6) Return
 *
 */
int aim_tx_enqueue(struct aim_session_t *sess,
		   struct command_tx_struct *newpacket)
{
  struct command_tx_struct *cur;

  if (newpacket->conn == NULL) {
      faimdprintf(1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n");
      newpacket->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
  }
 
  /* assign seqnum */
  newpacket->seqnum = aim_get_next_txseqnum(newpacket->conn);
  /* set some more fields */
  newpacket->lock = 1; /* lock */
  newpacket->sent = 0; /* not sent yet */
  newpacket->next = NULL; /* always last */

  /* see overhead note in aim_rxqueue counterpart */
  if (sess->queue_outgoing == NULL) {
    sess->queue_outgoing = newpacket;
  } else {
    for (cur = sess->queue_outgoing;
	 cur->next;
	 cur = cur->next)
      ;
    cur->next = newpacket;
  }

  newpacket->lock = 0; /* unlock so it can be sent */

#if debug == 2
  faimdprintf(2, "calling aim_tx_printqueue()\n");
  aim_tx_printqueue(sess);
  faimdprintf(2, "back from aim_tx_printqueue()\n");
#endif

  /* mid tells me this should solve a lot of my problems */
  aim_tx_flushqueue(sess);

  return 0;
}

/* 
 *  aim_get_next_txseqnum()
 *
 *   This increments the tx command count, and returns the seqnum
 *   that should be stamped on the next FLAP packet sent.  This is
 *   normally called during the final step of packet preparation
 *   before enqueuement (in aim_tx_enqueue()).
 *
 */
u_int aim_get_next_txseqnum(struct aim_conn_t *conn)
{
  return ( ++conn->seqnum );
}

/*
 *  aim_tx_printqueue()
 *
 *  This is basically for debuging purposes only.  It dumps all the
 *  records in the tx queue and their current status.  Very helpful
 *  if the queue isn't working quite right.
 *
 */
#if debug == 2
int aim_tx_printqueue(struct aim_session_t *sess)
{
  struct command_tx_struct *cur;

  faimdprintf(2, "\ncurrent aim_queue_outgoing...\n");
  faimdprintf(2, "\ttype seqnum  len  lock sent\n");  

  if (sess->queue_outgoing == NULL)
    faimdprintf(2, "aim_tx_flushqueue(): queue empty");
  else {
      for (cur = sess->queue_outgoing; cur; cur = cur->next) {
	  faimdprintf(2, "\t  %2x   %4x %4x   %1d    %1d\n", 
		      cur->type, cur->seqnum, 
		      cur->commandlen, cur->lock, 
		      cur->sent);
      }
  }

  faimdprintf(2, "\n(done printing queue)\n");
  
  return 0;
}
#endif

/*
 *  aim_tx_flushqueue()
 *
 *  This the function is responsable for putting the queued commands
 *  onto the wire.  This function is critical to the operation of 
 *  the queue and therefore is the most prone to brokenness.  It
 *  seems to be working quite well at this point.
 *
 *  Procedure:
 *    1) Traverse the list, only operate on commands that are unlocked
 *       and haven't been sent yet.
 *    2) Lock the struct
 *    3) Allocate a temporary buffer to store the finished, fully
 *       processed packet in.
 *    4) Build the packet from the command_tx_struct data.
 *    5) Write the packet to the socket.
 *    6) If success, mark the packet sent, if fail report failure, do NOT
 *       mark the packet sent (so it will not get purged and therefore
 *       be attempted again on next call).
 *    7) Unlock the struct.
 *    8) Free the temp buffer
 *    9) Step to next struct in list and go back to 1.
 *
 */
int aim_tx_flushqueue(struct aim_session_t *sess)
{
  struct command_tx_struct *cur;
  u_char *curPacket = NULL;
#if debug > 1
  int i = 0;
#endif

  if (sess->queue_outgoing == NULL)
    return 0;

  faimdprintf(2, "beginning txflush...\n");
  for (cur = sess->queue_outgoing; cur; cur = cur->next) {
    /* only process if its unlocked and unsent */
    if (!cur->lock && !cur->sent) {

      /*
       * And now for the meager attempt to force transmit
       * latency and avoid missed messages.
       */
      if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
	/* FIXME FIXME -- should be a break! we dont want to block the upper layers */
	sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
      }
      
      cur->lock = 1; /* lock the struct */
      
      /* allocate full-packet buffer */
      curPacket = (char *) malloc(cur->commandlen + 6);
      
      /* command byte */
      curPacket[0] = 0x2a;
      
      /* type/family byte */
      curPacket[1] = cur->type;
      
      /* bytes 3+4: word: FLAP sequence number */
      aimutil_put16(curPacket+2, cur->seqnum);

      /* bytes 5+6: word: SNAC len */
      aimutil_put16(curPacket+4, cur->commandlen);
      
      /* bytes 7 and on: raw: SNAC data */
      memcpy(&(curPacket[6]), cur->data, cur->commandlen);
      
      /* full image of raw packet data now in curPacket */
      if ( (u_int)write(cur->conn->fd, curPacket, (cur->commandlen + 6)) != (cur->commandlen + 6)) {
	printf("\nWARNING: Error in sending packet 0x%4x -- will try again next time\n\n", cur->seqnum);
	cur->sent = 0; /* mark it unsent */
	continue; /* bail out */
      } else {
	faimdprintf(2, "\nSENT 0x%4x\n\n", cur->seqnum);

	cur->sent = 1; /* mark the struct as sent */
	cur->conn->lastactivity = time(NULL);
      }
#if debug > 2
      faimdprintf(2, "\nPacket:");
      for (i = 0; i < (cur->commandlen + 6); i++) {
	if ((i % 8) == 0) {
	  faimdprintf(2, "\n\t");
	}
	if (curPacket[i] >= ' ' && curPacket[i]<127) {
	  faimdprintf(2, "%c=%02x ", curPacket[i], curPacket[i]);
	} else {
	  faimdprintf(2, "0x%2x ", curPacket[i]);
	}
      }
      faimdprintf(2, "\n");
#endif
      cur->lock = 0; /* unlock the struct */
      free(curPacket); /* free up full-packet buffer */
    }
  }

  /* purge sent commands from queue */
  aim_tx_purgequeue(sess);

  return 0;
}

/*
 *  aim_tx_purgequeue()
 *  
 *  This is responsable for removing sent commands from the transmit 
 *  queue. This is not a required operation, but it of course helps
 *  reduce memory footprint at run time!  
 *
 */
void aim_tx_purgequeue(struct aim_session_t *sess)
{
  struct command_tx_struct *cur = NULL;
  struct command_tx_struct *tmp;

  if (sess->queue_outgoing == NULL)
    return;
  
  if (sess->queue_outgoing->next == NULL) {
    if (!sess->queue_outgoing->lock && sess->queue_outgoing->sent) {
      tmp = sess->queue_outgoing;
      sess->queue_outgoing = NULL;
      free(tmp->data);
      free(tmp);
    }
    return;
  }

  for(cur = sess->queue_outgoing; cur->next != NULL; ) {
    if (!cur->next->lock && cur->next->sent) {
      tmp = cur->next;
      cur->next = tmp->next;
      free(tmp->data);
      free(tmp);
    }	
    cur = cur->next;

    /* 
     * Be careful here.  Because of the way we just
     * manipulated the pointer, cur may be NULL and 
     * the for() will segfault doing the check unless
     * we find this case first.
     */
    if (cur == NULL)	
      break;
  }
  return;
}