Index: channel.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channel.c,v
retrieving revision 1.193
diff -u -r1.193 channel.c
--- channel.c	29 Apr 2005 17:00:33 -0000	1.193
+++ channel.c	4 May 2005 13:49:59 -0000
@@ -405,49 +405,46 @@
 int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
 {
 	struct ast_frame *f;
-	struct ast_frame *prev, *cur;
 	int blah = 1;
-	int qlen = 0;
-	/* Build us a copy and free the original one */
+
+	/* Build us a copy so we do not depend on the original one */
 	f = ast_frdup(fin);
 	if (!f) {
 		ast_log(LOG_WARNING, "Unable to duplicate frame\n");
 		return -1;
 	}
 	ast_mutex_lock(&chan->lock);
-	prev = NULL;
-	cur = chan->readq;
-	while(cur) {
-		if ((cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
-			/* Don't bother actually queueing anything after a hangup */
-			ast_frfree(f);
-			ast_mutex_unlock(&chan->lock);
-			return 0;
-		}
-		prev = cur;
-		cur = cur->next;
-		qlen++;
+	if (chan->readq.len > 0 && chan->readq.tail->frametype == AST_FRAME_CONTROL &&
+			chan->readq.tail->subclass == AST_CONTROL_HANGUP) {
+		/* Don't bother actually queueing anything after a hangup */
+		goto done;
 	}
 	/* Allow up to 96 voice frames outstanding, and up to 128 total frames */
-	if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen  > 128)) {
-		if (fin->frametype != AST_FRAME_VOICE) {
-			ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
-			CRASH;
-		} else {
-			ast_log(LOG_DEBUG, "Dropping voice to exceptionally long queue on %s\n", chan->name);
-			ast_frfree(f);
-			ast_mutex_unlock(&chan->lock);
-			return 0;
-		}
-	}
-	if (prev)
-		prev->next = f;
+	if (chan->readq.len > 128) {
+		ast_log(LOG_WARNING,
+			"Exceptionally long queue length queuing to %s\n",
+			chan->name);
+		goto done;
+	}
+	if (fin->frametype == AST_FRAME_VOICE && chan->readq.len_voice > 96) {
+		ast_log(LOG_DEBUG,
+			"Dropping voice to exceptionally long queue on %s\n",
+				chan->name);
+		goto done;
+	}
+	/* actually queue frame */
+	if (chan->readq.head == NULL)
+		chan->readq.head = f;
 	else
-		chan->readq = f;
+		chan->readq.tail->next = f;
+	chan->readq.tail = f;
+	chan->readq.len++;
+	if (fin->frametype == AST_FRAME_VOICE)
+		chan->readq.len_voice++;
 	if (chan->alertpipe[1] > -1) {
 		if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
 			ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
-				chan->name, f->frametype, f->subclass, qlen, strerror(errno));
+				chan->name, f->frametype, f->subclass, chan->readq.len, strerror(errno));
 #ifdef ZAPTEL_OPTIMIZATIONS
 	} else if (chan->timingfd > -1) {
 		ioctl(chan->timingfd, ZT_TIMERPING, &blah);
@@ -455,7 +452,12 @@
 	} else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
 		pthread_kill(chan->blocker, SIGURG);
 	}
+	f = NULL;	/* record we consumed the frame */
+
+done:	/* common exit point */
 	ast_mutex_unlock(&chan->lock);
+	if (f)
+		ast_frfree(f);
 	return 0;
 }
 
@@ -671,8 +673,9 @@
 		close(fd);
 	if ((fd = chan->timingfd) > -1)
 		close(fd);
-	f = chan->readq;
-	chan->readq = NULL;
+	f = chan->readq.head;
+	chan->readq.head = chan->readq.tail = NULL;
+	chan->readq.len = chan->readq.len_voice = 0;
 	while(f) {
 		fp = f;
 		f = f->next;
@@ -1367,7 +1370,7 @@
 #if 0
 			ast_log(LOG_NOTICE, "Oooh, there's a PING!\n");
 #endif			
-			if (!chan->readq || !chan->readq->next) {
+			if (chan->readq.len < 2) {	/* 0 or 1 frame queued XXX */
 				/* Acknowledge PONG unless we need it again */
 #if 0
 				ast_log(LOG_NOTICE, "Sending a PONG!\n");
@@ -1400,9 +1403,15 @@
 	}
 #endif
 	/* Check for pending read queue */
-	if (chan->readq) {
-		f = chan->readq;
-		chan->readq = f->next;
+	if (chan->readq.head) {
+		/* XXX dequeue */
+		f = chan->readq.head;
+		chan->readq.head = f->next;
+		if (chan->readq.len-- == 1)
+			chan->readq.tail = NULL;
+		if (f->frametype == AST_FRAME_VOICE)
+			chan->readq.len_voice--;
+
 		/* Interpret hangup and return NULL */
 		if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
 			ast_frfree(f);
@@ -2421,7 +2430,6 @@
 	int x,i;
 	int res=0;
 	int origstate;
-	struct ast_frame *cur, *prev;
 	const struct ast_channel_tech *t;
 	void *t_pvt;
 	struct ast_callerid tmpcid;
@@ -2484,9 +2492,12 @@
 	clone->tech_pvt = t_pvt;
 
 	/* Swap the readq's */
-	cur = original->readq;
-	original->readq = clone->readq;
-	clone->readq = cur;
+	/* XXX what was in the clone ? */
+	{ struct ast_frame_queue tmp;
+		tmp = original->readq;
+		original->readq = clone->readq;
+		clone->readq = tmp;
+	}
 
 	/* Swap the alertpipes */
 	for (i = 0; i < 2; i++) {
@@ -2505,20 +2516,17 @@
 
 	/* Save any pending frames on both sides.  Start by counting
 	 * how many we're going to need... */
-	prev = NULL;
-	cur = clone->readq;
-	x = 0;
-	while(cur) {
-		x++;
-		prev = cur;
-		cur = cur->next;
-	}
+	x = clone->readq.len;
 	/* If we had any, prepend them to the ones already in the queue, and 
 	 * load up the alertpipe */
-	if (prev) {
-		prev->next = original->readq;
-		original->readq = clone->readq;
-		clone->readq = NULL;
+	if (clone->readq.len > 0) {
+		clone->readq.tail->next = original->readq.head;
+		original->readq.head = clone->readq.head;
+		original->readq.len += clone->readq.len;
+		original->readq.len_voice += clone->readq.len_voice;
+		/* XXX zero queue */
+		clone->readq.head = clone->readq.tail = NULL;
+		clone->readq.len = clone->readq.len_voice = 0;
 		if (original->alertpipe[1] > -1) {
 			for (i=0;i<x;i++)
 				write(original->alertpipe[1], &x, sizeof(x));
Index: channels/chan_local.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_local.c,v
retrieving revision 1.46
diff -u -r1.46 chan_local.c
--- channels/chan_local.c	21 Apr 2005 06:02:43 -0000	1.46
+++ channels/chan_local.c	4 May 2005 13:50:00 -0000
@@ -164,7 +164,7 @@
 {
 	if (p->alreadymasqed || p->nooptimization)
 		return;
-	if (isoutbound && p->chan && p->chan->_bridge /* Not ast_bridged_channel!  Only go one step! */ && p->owner && !p->owner->readq) {
+	if (isoutbound && p->chan && p->chan->_bridge /* Not ast_bridged_channel!  Only go one step! */ && p->owner && !p->owner->readq.head) {
 		/* Masquerade bridged channel into owner */
 		/* Lock everything we need, one by one, and give up if
 		   we can't get everything.  Remember, we'll get another
@@ -177,7 +177,7 @@
 			}
 			ast_mutex_unlock(&(p->chan->_bridge)->lock);
 		}
-	} else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && !p->chan->readq) {
+	} else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && !p->chan->readq.head) {
 		/* Masquerade bridged channel into chan */
 		if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
 			if (!ast_mutex_trylock(&p->chan->lock)) {
Index: include/asterisk/channel.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/channel.h,v
retrieving revision 1.83
diff -u -r1.83 channel.h
--- include/asterisk/channel.h	29 Apr 2005 15:04:26 -0000	1.83
+++ include/asterisk/channel.h	4 May 2005 13:50:01 -0000
@@ -318,7 +318,7 @@
 	/* ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */
 	unsigned short transfercapability;
 
-	struct ast_frame *readq;
+	struct ast_frame_queue readq;
 	int alertpipe[2];
 	/*! Write translation path */
 	struct ast_trans_pvt *writetrans;
Index: include/asterisk/frame.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/frame.h,v
retrieving revision 1.49
diff -u -r1.49 frame.h
--- include/asterisk/frame.h	21 Apr 2005 06:02:44 -0000	1.49
+++ include/asterisk/frame.h	4 May 2005 13:50:01 -0000
@@ -66,6 +66,17 @@
 	struct ast_frame_chain *next;
 };
 
+/*
+ * Used to queue frames in a channel.
+ * if queue is empty, head == NULL, len = 0 and tail unspecified.
+ * len_voice counts the number of VOICE frames.
+ */
+struct ast_frame_queue {
+	struct ast_frame *head, *tail;
+	int len;
+	int len_voice;	/* number of special frames */
+};
+
 #define AST_FRIENDLY_OFFSET 	64		/*! It's polite for a a new frame to
 						    				have this number of bytes for additional
 											headers.  */
