Merge team/russell/frame_caching

There are some situations in Asterisk where ast_frame and/or iax_frame
structures are rapidly allocatted and freed (at least 50 times per second
for one call).

This code significantly improves the performance of ast_frame_header_new(), 
ast_frdup(), ast_frfree(), iax_frame_new(), and iax_frame_free() by keeping
a thread-local cache of these structures and using frames from the cache 
whenever possible instead of calling malloc/free every time.

This commit also converts the ast_frame and iax_frame structures to use the
linked list macros.


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@41278 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.4
Russell Bryant 19 years ago
parent d22476348a
commit f7e7161607

@ -66,10 +66,16 @@ static void milliwatt_release(struct ast_channel *chan, void *data)
static int milliwatt_generate(struct ast_channel *chan, void *data, int len, int samples) static int milliwatt_generate(struct ast_channel *chan, void *data, int len, int samples)
{ {
struct ast_frame wf;
unsigned char buf[AST_FRIENDLY_OFFSET + 640]; unsigned char buf[AST_FRIENDLY_OFFSET + 640];
const int maxsamples = sizeof (buf) / sizeof (buf[0]); const int maxsamples = sizeof (buf) / sizeof (buf[0]);
int i, *indexp = (int *) data; int i, *indexp = (int *) data;
struct ast_frame wf = {
.frametype = AST_FRAME_VOICE,
.subclass = AST_FORMAT_ULAW,
.offset = AST_FRIENDLY_OFFSET,
.data = buf + AST_FRIENDLY_OFFSET,
.src = __FUNCTION__,
};
/* Instead of len, use samples, because channel.c generator_force /* Instead of len, use samples, because channel.c generator_force
* generate(chan, tmp, 0, 160) ignores len. In any case, len is * generate(chan, tmp, 0, 160) ignores len. In any case, len is
@ -82,17 +88,8 @@ static int milliwatt_generate(struct ast_channel *chan, void *data, int len, int
samples = maxsamples; samples = maxsamples;
} }
len = samples * sizeof (buf[0]); len = samples * sizeof (buf[0]);
wf.frametype = AST_FRAME_VOICE;
wf.subclass = AST_FORMAT_ULAW;
wf.offset = AST_FRIENDLY_OFFSET;
wf.mallocd = 0;
wf.data = buf + AST_FRIENDLY_OFFSET;
wf.datalen = len; wf.datalen = len;
wf.samples = samples; wf.samples = samples;
wf.src = "app_milliwatt";
wf.delivery.tv_sec = 0;
wf.delivery.tv_usec = 0;
wf.prev = wf.next = NULL;
/* create a buffer containing the digital milliwatt pattern */ /* create a buffer containing the digital milliwatt pattern */
for(i = 0; i < len; i++) for(i = 0; i < len; i++)
{ {

@ -191,7 +191,7 @@ static void *mixmonitor_thread(void *obj)
of frames if a queue flush was necessary, so process them of frames if a queue flush was necessary, so process them
*/ */
for (; f; f = next) { for (; f; f = next) {
next = f->next; next = AST_LIST_NEXT(f, frame_list);
if (write) if (write)
ast_writestream(mixmonitor->fs, f); ast_writestream(mixmonitor->fs, f);
ast_frfree(f); ast_frfree(f);

@ -621,11 +621,11 @@ struct chan_iax2_pvt {
}; };
static struct ast_iax2_queue { static struct ast_iax2_queue {
struct iax_frame *head; AST_LIST_HEAD(, iax_frame) queue;
struct iax_frame *tail;
int count; int count;
ast_mutex_t lock; } iaxq = {
} iaxq; .queue = AST_LIST_HEAD_INIT_VALUE
};
static AST_LIST_HEAD_STATIC(users, iax2_user); static AST_LIST_HEAD_STATIC(users, iax2_user);
@ -1071,11 +1071,12 @@ static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, cons
static struct iax_frame *iaxfrdup2(struct iax_frame *fr) static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
{ {
/* Malloc() a copy of a frame */
struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen); struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen);
if (new) { if (new) {
memcpy(new, fr, sizeof(struct iax_frame)); size_t mallocd_datalen = new->mallocd_datalen;
memcpy(new, fr, sizeof(*new));
iax_frame_wrap(new, &fr->af); iax_frame_wrap(new, &fr->af);
new->mallocd_datalen = mallocd_datalen;
new->data = NULL; new->data = NULL;
new->datalen = 0; new->datalen = 0;
new->direction = DIRECTION_INGRESS; new->direction = DIRECTION_INGRESS;
@ -1754,7 +1755,7 @@ retry:
ast_queue_hangup(owner); ast_queue_hangup(owner);
} }
for (cur = iaxq.head; cur ; cur = cur->next) { AST_LIST_TRAVERSE(&iaxq.queue, cur, list) {
/* Cancel any pending transmissions */ /* Cancel any pending transmissions */
if (cur->callno == pvt->callno) if (cur->callno == pvt->callno)
cur->retries = -1; cur->retries = -1;
@ -1875,17 +1876,10 @@ static void __attempt_transmit(void *data)
/* Do not try again */ /* Do not try again */
if (freeme) { if (freeme) {
/* Don't attempt delivery, just remove it from the queue */ /* Don't attempt delivery, just remove it from the queue */
ast_mutex_lock(&iaxq.lock); AST_LIST_LOCK(&iaxq.queue);
if (f->prev) AST_LIST_REMOVE(&iaxq.queue, f, list);
f->prev->next = f->next;
else
iaxq.head = f->next;
if (f->next)
f->next->prev = f->prev;
else
iaxq.tail = f->prev;
iaxq.count--; iaxq.count--;
ast_mutex_unlock(&iaxq.lock); AST_LIST_UNLOCK(&iaxq.queue);
f->retrans = -1; f->retrans = -1;
/* Free the IAX frame */ /* Free the IAX frame */
iax2_frame_free(f); iax2_frame_free(f);
@ -2082,7 +2076,7 @@ static int iax2_show_stats(int fd, int argc, char *argv[])
int cnt = 0, dead=0, final=0; int cnt = 0, dead=0, final=0;
if (argc != 3) if (argc != 3)
return RESULT_SHOWUSAGE; return RESULT_SHOWUSAGE;
for (cur = iaxq.head; cur ; cur = cur->next) { AST_LIST_TRAVERSE(&iaxq.queue, cur, list) {
if (cur->retries < 0) if (cur->retries < 0)
dead++; dead++;
if (cur->final) if (cur->final)
@ -2092,7 +2086,8 @@ static int iax2_show_stats(int fd, int argc, char *argv[])
ast_cli(fd, " IAX Statistics\n"); ast_cli(fd, " IAX Statistics\n");
ast_cli(fd, "---------------------\n"); ast_cli(fd, "---------------------\n");
ast_cli(fd, "Outstanding frames: %d (%d ingress, %d egress)\n", iax_get_frames(), iax_get_iframes(), iax_get_oframes()); ast_cli(fd, "Outstanding frames: %d (%d ingress, %d egress)\n", iax_get_frames(), iax_get_iframes(), iax_get_oframes());
ast_cli(fd, "Packets in transmit queue: %d dead, %d final, %d total\n", dead, final, cnt); ast_cli(fd, "Packets in transmit queue: %d dead, %d final, %d total\n\n", dead, final, cnt);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -2369,24 +2364,13 @@ static int schedule_delivery(struct iax_frame *fr, int updatehistory, int fromtr
static int iax2_transmit(struct iax_frame *fr) static int iax2_transmit(struct iax_frame *fr)
{ {
/* Lock the queue and place this packet at the end */ /* Lock the queue and place this packet at the end */
fr->next = NULL;
fr->prev = NULL;
/* By setting this to 0, the network thread will send it for us, and /* By setting this to 0, the network thread will send it for us, and
queue retransmission if necessary */ queue retransmission if necessary */
fr->sentyet = 0; fr->sentyet = 0;
ast_mutex_lock(&iaxq.lock); AST_LIST_LOCK(&iaxq.queue);
if (!iaxq.head) { AST_LIST_INSERT_TAIL(&iaxq.queue, fr, list);
/* Empty queue */
iaxq.head = fr;
iaxq.tail = fr;
} else {
/* Double link */
iaxq.tail->next = fr;
fr->prev = iaxq.tail;
iaxq.tail = fr;
}
iaxq.count++; iaxq.count++;
ast_mutex_unlock(&iaxq.lock); AST_LIST_UNLOCK(&iaxq.queue);
/* Wake up the network and scheduler thread */ /* Wake up the network and scheduler thread */
pthread_kill(netthreadid, SIGURG); pthread_kill(netthreadid, SIGURG);
signal_condition(&sched_lock, &sched_cond); signal_condition(&sched_lock, &sched_cond);
@ -5330,15 +5314,15 @@ static int complete_transfer(int callno, struct iax_ies *ies)
pvt->lastsent = 0; pvt->lastsent = 0;
pvt->nextpred = 0; pvt->nextpred = 0;
pvt->pingtime = DEFAULT_RETRY_TIME; pvt->pingtime = DEFAULT_RETRY_TIME;
ast_mutex_lock(&iaxq.lock); AST_LIST_LOCK(&iaxq.queue);
for (cur = iaxq.head; cur ; cur = cur->next) { AST_LIST_TRAVERSE(&iaxq.queue, cur, list) {
/* We must cancel any packets that would have been transmitted /* We must cancel any packets that would have been transmitted
because now we're talking to someone new. It's okay, they because now we're talking to someone new. It's okay, they
were transmitted to someone that didn't care anyway. */ were transmitted to someone that didn't care anyway. */
if (callno == cur->callno) if (callno == cur->callno)
cur->retries = -1; cur->retries = -1;
} }
ast_mutex_unlock(&iaxq.lock); AST_LIST_UNLOCK(&iaxq.queue);
return 0; return 0;
} }
@ -5835,17 +5819,16 @@ static int iax2_vnak(int callno)
static void vnak_retransmit(int callno, int last) static void vnak_retransmit(int callno, int last)
{ {
struct iax_frame *f; struct iax_frame *f;
ast_mutex_lock(&iaxq.lock);
f = iaxq.head; AST_LIST_LOCK(&iaxq.queue);
while(f) { AST_LIST_TRAVERSE(&iaxq.queue, f, list) {
/* Send a copy immediately */ /* Send a copy immediately */
if ((f->callno == callno) && iaxs[f->callno] && if ((f->callno == callno) && iaxs[f->callno] &&
(f->oseqno >= last)) { (f->oseqno >= last)) {
send_packet(f); send_packet(f);
} }
f = f->next;
} }
ast_mutex_unlock(&iaxq.lock); AST_LIST_UNLOCK(&iaxq.queue);
} }
static void __iax2_poke_peer_s(void *data) static void __iax2_poke_peer_s(void *data)
@ -6570,8 +6553,8 @@ static int socket_process(struct iax2_thread *thread)
/* Ack the packet with the given timestamp */ /* Ack the packet with the given timestamp */
if (option_debug && iaxdebug) if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "Cancelling transmission of packet %d\n", x); ast_log(LOG_DEBUG, "Cancelling transmission of packet %d\n", x);
ast_mutex_lock(&iaxq.lock); AST_LIST_LOCK(&iaxq.queue);
for (cur = iaxq.head; cur ; cur = cur->next) { AST_LIST_TRAVERSE(&iaxq.queue, cur, list) {
/* If it's our call, and our timestamp, mark -1 retries */ /* If it's our call, and our timestamp, mark -1 retries */
if ((fr->callno == cur->callno) && (x == cur->oseqno)) { if ((fr->callno == cur->callno) && (x == cur->oseqno)) {
cur->retries = -1; cur->retries = -1;
@ -6583,7 +6566,7 @@ static int socket_process(struct iax2_thread *thread)
} }
} }
} }
ast_mutex_unlock(&iaxq.lock); AST_LIST_UNLOCK(&iaxq.queue);
} }
/* Note how much we've received acknowledgement for */ /* Note how much we've received acknowledgement for */
if (iaxs[fr->callno]) if (iaxs[fr->callno])
@ -6723,13 +6706,13 @@ retryowner:
case IAX_COMMAND_TXACC: case IAX_COMMAND_TXACC:
if (iaxs[fr->callno]->transferring == TRANSFER_BEGIN) { if (iaxs[fr->callno]->transferring == TRANSFER_BEGIN) {
/* Ack the packet with the given timestamp */ /* Ack the packet with the given timestamp */
ast_mutex_lock(&iaxq.lock); AST_LIST_LOCK(&iaxq.queue);
for (cur = iaxq.head; cur ; cur = cur->next) { AST_LIST_TRAVERSE(&iaxq.queue, cur, list) {
/* Cancel any outstanding txcnt's */ /* Cancel any outstanding txcnt's */
if ((fr->callno == cur->callno) && (cur->transfer)) if ((fr->callno == cur->callno) && (cur->transfer))
cur->retries = -1; cur->retries = -1;
} }
ast_mutex_unlock(&iaxq.lock); AST_LIST_UNLOCK(&iaxq.queue);
memset(&ied1, 0, sizeof(ied1)); memset(&ied1, 0, sizeof(ied1));
iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->callno); iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->callno);
send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied1.buf, ied1.pos, -1); send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied1.buf, ied1.pos, -1);
@ -8031,49 +8014,42 @@ static void *network_thread(void *ignore)
/* Our job is simple: Send queued messages, retrying if necessary. Read frames /* Our job is simple: Send queued messages, retrying if necessary. Read frames
from the network, and queue them for delivery to the channels */ from the network, and queue them for delivery to the channels */
int res, count; int res, count;
struct iax_frame *f, *freeme; struct iax_frame *f;
if (timingfd > -1) if (timingfd > -1)
ast_io_add(io, timingfd, timing_read, AST_IO_IN | AST_IO_PRI, NULL); ast_io_add(io, timingfd, timing_read, AST_IO_IN | AST_IO_PRI, NULL);
for(;;) { for(;;) {
/* Go through the queue, sending messages which have not yet been /* Go through the queue, sending messages which have not yet been
sent, and scheduling retransmissions if appropriate */ sent, and scheduling retransmissions if appropriate */
ast_mutex_lock(&iaxq.lock); AST_LIST_LOCK(&iaxq.queue);
f = iaxq.head;
count = 0; count = 0;
while(f) { AST_LIST_TRAVERSE_SAFE_BEGIN(&iaxq.queue, f, list) {
freeme = NULL; if (f->sentyet)
if (!f->sentyet) { continue;
f->sentyet++;
/* Send a copy immediately -- errors here are ok, so don't bother locking */ f->sentyet++;
if (iaxs[f->callno]) { /* Send a copy immediately -- errors here are ok, so don't bother locking */
send_packet(f); if (iaxs[f->callno]) {
count++; send_packet(f);
} count++;
if (f->retries < 0) { }
/* This is not supposed to be retransmitted */ if (f->retries < 0) {
if (f->prev) /* This is not supposed to be retransmitted */
f->prev->next = f->next; AST_LIST_REMOVE(&iaxq.queue, f, list);
else iaxq.count--;
iaxq.head = f->next; /* Free the iax frame */
if (f->next) iax_frame_free(f);
f->next->prev = f->prev; } else {
else /* We need reliable delivery. Schedule a retransmission */
iaxq.tail = f->prev; f->retries++;
iaxq.count--; f->retrans = ast_sched_add(sched, f->retrytime, attempt_transmit, f);
/* Free the iax frame */ signal_condition(&sched_lock, &sched_cond);
freeme = f;
} else {
/* We need reliable delivery. Schedule a retransmission */
f->retries++;
f->retrans = ast_sched_add(sched, f->retrytime, attempt_transmit, f);
signal_condition(&sched_lock, &sched_cond);
}
} }
f = f->next;
if (freeme)
iax_frame_free(freeme);
} }
ast_mutex_unlock(&iaxq.lock); AST_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK(&iaxq.queue);
if (count >= 20) if (count >= 20)
ast_log(LOG_DEBUG, "chan_iax2: Sent %d queued outbound frames all at once\n", count); ast_log(LOG_DEBUG, "chan_iax2: Sent %d queued outbound frames all at once\n", count);
@ -9751,7 +9727,6 @@ static int __unload_module(void)
static int unload_module(void) static int unload_module(void)
{ {
ast_mutex_destroy(&iaxq.lock);
ast_mutex_destroy(&waresl.lock); ast_mutex_destroy(&waresl.lock);
ast_custom_function_unregister(&iaxpeer_function); ast_custom_function_unregister(&iaxpeer_function);
return __unload_module(); return __unload_module();
@ -9803,7 +9778,6 @@ static int load_module(void)
} }
ast_netsock_init(netsock); ast_netsock_init(netsock);
ast_mutex_init(&iaxq.lock);
ast_mutex_init(&waresl.lock); ast_mutex_init(&waresl.lock);
ast_cli_register_multiple(iax2_cli, sizeof(iax2_cli) / sizeof(iax2_cli[0])); ast_cli_register_multiple(iax2_cli, sizeof(iax2_cli) / sizeof(iax2_cli[0]));

@ -226,7 +226,7 @@ static void check_bridge(struct local_pvt *p, int isoutbound)
frames on the owner channel (because they would be transferred to the frames on the owner channel (because they would be transferred to the
outbound channel during the masquerade) outbound channel during the masquerade)
*/ */
if (isoutbound && p->chan->_bridge /* Not ast_bridged_channel! Only go one step! */ && !p->owner->readq) { if (isoutbound && p->chan->_bridge /* Not ast_bridged_channel! Only go one step! */ && AST_LIST_EMPTY(&p->owner->readq)) {
/* Masquerade bridged channel into owner */ /* Masquerade bridged channel into owner */
/* Lock everything we need, one by one, and give up if /* Lock everything we need, one by one, and give up if
we can't get everything. Remember, we'll get another we can't get everything. Remember, we'll get another
@ -248,7 +248,7 @@ static void check_bridge(struct local_pvt *p, int isoutbound)
when the local channels go away. when the local channels go away.
*/ */
#if 0 #if 0
} else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && !p->chan->readq) { } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) {
/* Masquerade bridged channel into chan */ /* Masquerade bridged channel into chan */
if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) { if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
if (!p->owner->_bridge->_softhangup) { if (!p->owner->_bridge->_softhangup) {

@ -3516,7 +3516,6 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
pthread_t threadid; pthread_t threadid;
pthread_attr_t attr; pthread_attr_t attr;
struct ast_channel *chan; struct ast_channel *chan;
struct ast_frame dtmf_frame = { .frametype = AST_FRAME_DTMF };
pthread_attr_init(&attr); pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@ -3560,8 +3559,6 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
*/ */
p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN; p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN;
p->subs[index].f.subclass = res & 0xff; p->subs[index].f.subclass = res & 0xff;
dtmf_frame.subclass = res & 0xff;
p->subs[index].f.next = ast_frdup(&dtmf_frame);
#ifdef HAVE_PRI #ifdef HAVE_PRI
} }
#endif #endif

@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h" #include "asterisk/utils.h"
#include "asterisk/unaligned.h" #include "asterisk/unaligned.h"
#include "asterisk/lock.h" #include "asterisk/lock.h"
#include "asterisk/threadstorage.h"
#include "iax2.h" #include "iax2.h"
#include "iax2-parser.h" #include "iax2-parser.h"
@ -49,6 +50,15 @@ static int frames = 0;
static int iframes = 0; static int iframes = 0;
static int oframes = 0; static int oframes = 0;
static void frame_cache_cleanup(void *data);
/*! \brief A per-thread cache of iax_frame structures */
AST_THREADSTORAGE_CUSTOM(frame_cache, frame_cache_init, frame_cache_cleanup);
/*! \brief This is just so iax_frames, a list head struct for holding a list of
* iax_frame structures, is defined. */
AST_LIST_HEAD_NOLOCK(iax_frames, iax_frame);
static void internaloutput(const char *str) static void internaloutput(const char *str)
{ {
fputs(str, stdout); fputs(str, stdout);
@ -926,22 +936,44 @@ void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f)
struct iax_frame *iax_frame_new(int direction, int datalen) struct iax_frame *iax_frame_new(int direction, int datalen)
{ {
struct iax_frame *fr; struct iax_frame *fr = NULL;
fr = malloc((int)sizeof(struct iax_frame) + datalen); struct iax_frames *iax_frames;
if (fr) {
fr->direction = direction; /* Attempt to get a frame from this thread's cache */
fr->retrans = -1; if ((iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
ast_atomic_fetchadd_int(&frames, 1); AST_LIST_TRAVERSE_SAFE_BEGIN(iax_frames, fr, list) {
if (fr->direction == DIRECTION_INGRESS) if (fr->mallocd_datalen >= datalen) {
ast_atomic_fetchadd_int(&iframes, 1); size_t mallocd_datalen = fr->mallocd_datalen;
else AST_LIST_REMOVE_CURRENT(iax_frames, list);
ast_atomic_fetchadd_int(&oframes, 1); memset(fr, 0, sizeof(*fr));
fr->mallocd_datalen = mallocd_datalen;
break;
}
}
AST_LIST_TRAVERSE_SAFE_END
}
if (!fr) {
if (!(fr = ast_calloc(1, sizeof(*fr) + datalen)))
return NULL;
fr->mallocd_datalen = datalen;
} }
fr->direction = direction;
fr->retrans = -1;
if (fr->direction == DIRECTION_INGRESS)
ast_atomic_fetchadd_int(&iframes, 1);
else
ast_atomic_fetchadd_int(&oframes, 1);
return fr; return fr;
} }
void iax_frame_free(struct iax_frame *fr) static void __iax_frame_free(struct iax_frame *fr, int cache)
{ {
struct iax_frames *iax_frames;
/* Note: does not remove from scheduler! */ /* Note: does not remove from scheduler! */
if (fr->direction == DIRECTION_INGRESS) if (fr->direction == DIRECTION_INGRESS)
ast_atomic_fetchadd_int(&iframes, -1); ast_atomic_fetchadd_int(&iframes, -1);
@ -952,8 +984,34 @@ void iax_frame_free(struct iax_frame *fr)
return; return;
} }
fr->direction = 0; fr->direction = 0;
free(fr);
ast_atomic_fetchadd_int(&frames, -1); ast_atomic_fetchadd_int(&frames, -1);
if (!cache) {
free(fr);
return;
}
if (!(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
free(fr);
return;
}
AST_LIST_INSERT_HEAD(iax_frames, fr, list);
}
static void frame_cache_cleanup(void *data)
{
struct iax_frames *frames = data;
struct iax_frame *cur;
while ((cur = AST_LIST_REMOVE_HEAD(frames, list)))
__iax_frame_free(cur, 0);
free(frames);
}
void iax_frame_free(struct iax_frame *fr)
{
__iax_frame_free(fr, 1);
} }
int iax_get_frames(void) { return frames; } int iax_get_frames(void) { return frames; }

@ -18,6 +18,8 @@
#ifndef _IAX2_PARSER_H #ifndef _IAX2_PARSER_H
#define _IAX2_PARSER_H #define _IAX2_PARSER_H
#include "asterisk/linkedlists.h"
struct iax_ies { struct iax_ies {
char *called_number; char *called_number;
char *calling_number; char *calling_number;
@ -115,10 +117,11 @@ struct iax_frame {
/* Retransmission ID */ /* Retransmission ID */
int retrans; int retrans;
/* Easy linking */ /* Easy linking */
struct iax_frame *next; AST_LIST_ENTRY(iax_frame) list;
struct iax_frame *prev;
/* Actual, isolated frame header */ /* Actual, isolated frame header */
struct ast_frame af; struct ast_frame af;
/*! Amount of space _allocated_ for data */
size_t mallocd_datalen;
unsigned char unused[AST_FRIENDLY_OFFSET]; unsigned char unused[AST_FRIENDLY_OFFSET];
unsigned char afdata[0]; /* Data for frame */ unsigned char afdata[0]; /* Data for frame */
}; };

@ -410,7 +410,7 @@ struct ast_channel {
unsigned int pickupgroup; /*!< Pickup group - which calls groups can be picked up? */ unsigned int pickupgroup; /*!< Pickup group - which calls groups can be picked up? */
unsigned int flags; /*!< channel flags of AST_FLAG_ type */ unsigned int flags; /*!< channel flags of AST_FLAG_ type */
unsigned short transfercapability; /*!< ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */ unsigned short transfercapability; /*!< ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */
struct ast_frame *readq; AST_LIST_HEAD_NOLOCK(, ast_frame) readq;
int alertpipe[2]; int alertpipe[2];
int nativeformats; /*!< Kinds of data this channel can natively handle */ int nativeformats; /*!< Kinds of data this channel can natively handle */

@ -49,7 +49,7 @@ enum chanspy_flags {
}; };
struct ast_channel_spy_queue { struct ast_channel_spy_queue {
struct ast_frame *head; AST_LIST_HEAD_NOLOCK(, ast_frame) list;
unsigned int samples; unsigned int samples;
unsigned int format; unsigned int format;
}; };

@ -32,6 +32,7 @@ extern "C" {
#include <sys/types.h> #include <sys/types.h>
#include <sys/time.h> #include <sys/time.h>
#include "asterisk/endian.h" #include "asterisk/endian.h"
#include "asterisk/linkedlists.h"
struct ast_codec_pref { struct ast_codec_pref {
char order[32]; char order[32];
@ -97,6 +98,8 @@ struct ast_frame {
int samples; int samples;
/*! Was the data malloc'd? i.e. should we free it when we discard the frame? */ /*! Was the data malloc'd? i.e. should we free it when we discard the frame? */
int mallocd; int mallocd;
/*! The number of bytes allocated for a malloc'd frame header */
size_t mallocd_hdr_len;
/*! How many bytes exist _before_ "data" that can be used if needed */ /*! How many bytes exist _before_ "data" that can be used if needed */
int offset; int offset;
/*! Optional source of frame for debugging */ /*! Optional source of frame for debugging */
@ -105,10 +108,8 @@ struct ast_frame {
void *data; void *data;
/*! Global delivery time */ /*! Global delivery time */
struct timeval delivery; struct timeval delivery;
/*! Next/Prev for linking stand alone frames */ /*! For placing in a linked list */
struct ast_frame *prev; AST_LIST_ENTRY(ast_frame) frame_list;
/*! Next/Prev for linking stand alone frames */
struct ast_frame *next;
/*! Timing data flag */ /*! Timing data flag */
int has_timing_info; int has_timing_info;
/*! Timestamp in milliseconds */ /*! Timestamp in milliseconds */

@ -32,7 +32,7 @@ extern "C" {
#endif #endif
struct ast_slinfactory { struct ast_slinfactory {
struct ast_frame *queue; AST_LIST_HEAD_NOLOCK(, ast_frame) queue;
struct ast_trans_pvt *trans; struct ast_trans_pvt *trans;
short hold[1280]; short hold[1280];
short *offset; short *offset;

@ -324,10 +324,7 @@ int ast_jb_put(struct ast_channel *chan, struct ast_frame *f)
return -1; return -1;
} }
if (f->mallocd & AST_MALLOCD_HDR) frr = ast_frdup(f);
frr = ast_frdup(f);
else
frr = ast_frisolate(f);
if (!frr) { if (!frr) {
ast_log(LOG_ERROR, "Failed to isolate frame for the jitterbuffer on channel '%s'\n", chan->name); ast_log(LOG_ERROR, "Failed to isolate frame for the jitterbuffer on channel '%s'\n", chan->name);

@ -706,7 +706,7 @@ struct ast_channel *ast_channel_alloc(int needqueue)
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin) int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
{ {
struct ast_frame *f; struct ast_frame *f;
struct ast_frame *prev, *cur; struct ast_frame *cur;
int blah = 1; int blah = 1;
int qlen = 0; int qlen = 0;
@ -716,17 +716,19 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
return -1; return -1;
} }
ast_channel_lock(chan); ast_channel_lock(chan);
prev = NULL;
for (cur = chan->readq; cur; cur = cur->next) { /* See if the last frame on the queue is a hangup, if so don't queue anything */
if ((cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) { if ((cur = AST_LIST_LAST(&chan->readq)) && (cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
/* Don't bother actually queueing anything after a hangup */ ast_frfree(f);
ast_frfree(f); ast_channel_unlock(chan);
ast_channel_unlock(chan); return 0;
return 0; }
}
prev = cur; /* Count how many frames exist on the queue */
AST_LIST_TRAVERSE(&chan->readq, cur, frame_list) {
qlen++; qlen++;
} }
/* Allow up to 96 voice frames outstanding, and up to 128 total frames */ /* 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) && (qlen > 96)) || (qlen > 128)) {
if (fin->frametype != AST_FRAME_VOICE) { if (fin->frametype != AST_FRAME_VOICE) {
@ -739,10 +741,7 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
return 0; return 0;
} }
} }
if (prev) AST_LIST_INSERT_TAIL(&chan->readq, f, frame_list);
prev->next = f;
else
chan->readq = f;
if (chan->alertpipe[1] > -1) { if (chan->alertpipe[1] > -1) {
if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah)) 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", ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
@ -973,7 +972,7 @@ void ast_channel_free(struct ast_channel *chan)
{ {
int fd; int fd;
struct ast_var_t *vardata; struct ast_var_t *vardata;
struct ast_frame *f, *fp; struct ast_frame *f;
struct varshead *headp; struct varshead *headp;
struct ast_datastore *datastore = NULL; struct ast_datastore *datastore = NULL;
char name[AST_CHANNEL_NAME]; char name[AST_CHANNEL_NAME];
@ -1024,13 +1023,8 @@ void ast_channel_free(struct ast_channel *chan)
close(fd); close(fd);
if ((fd = chan->timingfd) > -1) if ((fd = chan->timingfd) > -1)
close(fd); close(fd);
f = chan->readq; while ((f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list)))
chan->readq = NULL; ast_frfree(f);
while(f) {
fp = f;
f = f->next;
ast_frfree(fp);
}
/* Get rid of each of the data stores on the channel */ /* Get rid of each of the data stores on the channel */
while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry))) while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry)))
@ -1247,14 +1241,11 @@ void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *sp
spy->chan = NULL; spy->chan = NULL;
for (f = spy->read_queue.head; f; f = spy->read_queue.head) { while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
spy->read_queue.head = f->next;
ast_frfree(f); ast_frfree(f);
}
for (f = spy->write_queue.head; f; f = spy->write_queue.head) { while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list)))
spy->write_queue.head = f->next;
ast_frfree(f); ast_frfree(f);
}
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_destroy(&spy->trigger); ast_cond_destroy(&spy->trigger);
@ -1337,8 +1328,6 @@ static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f,
trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator; trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) { AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
struct ast_frame *last;
struct ast_frame *f1; /* the frame to append */
struct ast_channel_spy_queue *queue; struct ast_channel_spy_queue *queue;
ast_mutex_lock(&spy->lock); ast_mutex_lock(&spy->lock);
@ -1370,7 +1359,7 @@ static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f,
break; break;
} }
} }
f1 = translated_frame; AST_LIST_INSERT_TAIL(&queue->list, ast_frdup(f), frame_list);
} else { } else {
if (f->subclass != queue->format) { if (f->subclass != queue->format) {
ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n", ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n",
@ -1379,17 +1368,8 @@ static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f,
ast_mutex_unlock(&spy->lock); ast_mutex_unlock(&spy->lock);
continue; continue;
} }
f1 = f; AST_LIST_INSERT_TAIL(&queue->list, ast_frdup(f), frame_list);
} }
/* duplicate and append f1 to the tail */
f1 = ast_frdup(f1);
for (last = queue->head; last && last->next; last = last->next)
;
if (last)
last->next = f1;
else
queue->head = f1;
queue->samples += f->samples; queue->samples += f->samples;
@ -1425,10 +1405,8 @@ static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f,
ast_log(LOG_DEBUG, "Spy '%s' on channel '%s' %s queue too long, dropping frames\n", ast_log(LOG_DEBUG, "Spy '%s' on channel '%s' %s queue too long, dropping frames\n",
spy->type, chan->name, (dir == SPY_READ) ? "read" : "write"); spy->type, chan->name, (dir == SPY_READ) ? "read" : "write");
while (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) { while (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
struct ast_frame *drop = queue->head; struct ast_frame *drop = AST_LIST_REMOVE_HEAD(&queue->list, frame_list);
queue->samples -= drop->samples; queue->samples -= drop->samples;
queue->head = drop->next;
ast_frfree(drop); ast_frfree(drop);
} }
} }
@ -1946,7 +1924,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
blah = ZT_EVENT_TIMER_EXPIRED; blah = ZT_EVENT_TIMER_EXPIRED;
if (blah == ZT_EVENT_TIMER_PING) { if (blah == ZT_EVENT_TIMER_PING) {
if (!chan->readq || !chan->readq->next) { if (AST_LIST_EMPTY(&chan->readq) || !AST_LIST_NEXT(AST_LIST_FIRST(&chan->readq), frame_list)) {
/* Acknowledge PONG unless we need it again */ /* Acknowledge PONG unless we need it again */
if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) { if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) {
ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno)); ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
@ -1985,10 +1963,8 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
} }
/* Check for pending read queue */ /* Check for pending read queue */
if (chan->readq) { if (!AST_LIST_EMPTY(&chan->readq)) {
f = chan->readq; f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list);
chan->readq = f->next;
f->next = NULL;
/* Interpret hangup and return NULL */ /* Interpret hangup and return NULL */
/* XXX why not the same for frames from the channel ? */ /* XXX why not the same for frames from the channel ? */
if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) { if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
@ -2014,11 +1990,13 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
if (f) { if (f) {
/* if the channel driver returned more than one frame, stuff the excess /* if the channel driver returned more than one frame, stuff the excess
into the readq for the next ast_read call into the readq for the next ast_read call (note that we can safely assume
that the readq is empty, because otherwise we would not have called into
the channel driver and f would be only a single frame)
*/ */
if (f->next) { if (AST_LIST_NEXT(f, frame_list)) {
chan->readq = f->next; AST_LIST_HEAD_SET_NOLOCK(&chan->readq, AST_LIST_NEXT(f, frame_list));
f->next = NULL; AST_LIST_NEXT(f, frame_list) = NULL;
} }
switch (f->frametype) { switch (f->frametype) {
@ -3068,7 +3046,7 @@ int ast_do_masquerade(struct ast_channel *original)
int x,i; int x,i;
int res=0; int res=0;
int origstate; int origstate;
struct ast_frame *cur, *prev; struct ast_frame *cur;
const struct ast_channel_tech *t; const struct ast_channel_tech *t;
void *t_pvt; void *t_pvt;
struct ast_callerid tmpcid; struct ast_callerid tmpcid;
@ -3132,9 +3110,9 @@ int ast_do_masquerade(struct ast_channel *original)
clone->tech_pvt = t_pvt; clone->tech_pvt = t_pvt;
/* Swap the readq's */ /* Swap the readq's */
cur = original->readq; cur = AST_LIST_FIRST(&original->readq);
original->readq = clone->readq; AST_LIST_HEAD_SET_NOLOCK(&original->readq, AST_LIST_FIRST(&clone->readq));
clone->readq = cur; AST_LIST_HEAD_SET_NOLOCK(&clone->readq, cur);
/* Swap the alertpipes */ /* Swap the alertpipes */
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
@ -3153,23 +3131,22 @@ int ast_do_masquerade(struct ast_channel *original)
/* Save any pending frames on both sides. Start by counting /* Save any pending frames on both sides. Start by counting
* how many we're going to need... */ * how many we're going to need... */
prev = NULL;
x = 0; x = 0;
for (cur = clone->readq; cur; cur = cur->next) { if (original->alertpipe[1] > -1) {
x++; AST_LIST_TRAVERSE(&clone->readq, cur, frame_list)
prev = cur; x++;
} }
/* If we had any, prepend them to the ones already in the queue, and
/* If we had any, prepend them to the ones already in the queue, and
* load up the alertpipe */ * load up the alertpipe */
if (prev) { if (AST_LIST_FIRST(&clone->readq)) {
prev->next = original->readq; AST_LIST_INSERT_TAIL(&clone->readq, AST_LIST_FIRST(&original->readq), frame_list);
original->readq = clone->readq; AST_LIST_HEAD_SET_NOLOCK(&original->readq, AST_LIST_FIRST(&clone->readq));
clone->readq = NULL; AST_LIST_HEAD_SET_NOLOCK(&clone->readq, NULL);
if (original->alertpipe[1] > -1) { for (i = 0; i < x; i++)
for (i = 0; i < x; i++) write(original->alertpipe[1], &x, sizeof(x));
write(original->alertpipe[1], &x, sizeof(x));
}
} }
clone->_softhangup = AST_SOFTHANGUP_DEV; clone->_softhangup = AST_SOFTHANGUP_DEV;
@ -4086,9 +4063,7 @@ static void copy_data_from_queue(struct ast_channel_spy_queue *queue, short *buf
int bytestocopy; int bytestocopy;
while (samples) { while (samples) {
f = queue->head; if (!(f = AST_LIST_FIRST(&queue->list))) {
if (!f) {
ast_log(LOG_ERROR, "Ran out of frames before buffer filled!\n"); ast_log(LOG_ERROR, "Ran out of frames before buffer filled!\n");
break; break;
} }
@ -4103,10 +4078,9 @@ static void copy_data_from_queue(struct ast_channel_spy_queue *queue, short *buf
f->datalen -= bytestocopy; f->datalen -= bytestocopy;
f->offset += bytestocopy; f->offset += bytestocopy;
queue->samples -= tocopy; queue->samples -= tocopy;
if (!f->samples) {
queue->head = f->next; if (!f->samples)
ast_frfree(f); ast_frfree(AST_LIST_REMOVE_HEAD(&queue->list, frame_list));
}
} }
} }
@ -4136,19 +4110,19 @@ struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsign
if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) { if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
if (spy->read_queue.samples > spy->write_queue.samples) { if (spy->read_queue.samples > spy->write_queue.samples) {
if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) { if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
for (result = spy->read_queue.head; result; result = result->next) AST_LIST_TRAVERSE(&spy->read_queue.list, result, frame_list)
ast_frame_adjust_volume(result, spy->read_vol_adjustment); ast_frame_adjust_volume(result, spy->read_vol_adjustment);
} }
result = spy->read_queue.head; result = AST_LIST_FIRST(&spy->read_queue.list);
spy->read_queue.head = NULL; AST_LIST_HEAD_SET_NOLOCK(&spy->read_queue.list, NULL);
spy->read_queue.samples = 0; spy->read_queue.samples = 0;
} else { } else {
if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) { if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
for (result = spy->write_queue.head; result; result = result->next) AST_LIST_TRAVERSE(&spy->write_queue.list, result, frame_list)
ast_frame_adjust_volume(result, spy->write_vol_adjustment); ast_frame_adjust_volume(result, spy->write_vol_adjustment);
} }
result = spy->write_queue.head; result = AST_LIST_FIRST(&spy->write_queue.list);
spy->write_queue.head = NULL; AST_LIST_HEAD_SET_NOLOCK(&spy->write_queue.list, NULL);
spy->write_queue.samples = 0; spy->write_queue.samples = 0;
} }
ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH); ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
@ -4159,15 +4133,10 @@ struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsign
return NULL; return NULL;
/* short-circuit if both head frames have exactly what we want */ /* short-circuit if both head frames have exactly what we want */
if ((spy->read_queue.head->samples == samples) && if ((AST_LIST_FIRST(&spy->read_queue.list)->samples == samples) &&
(spy->write_queue.head->samples == samples)) { (AST_LIST_FIRST(&spy->write_queue.list)->samples == samples)) {
read_frame = spy->read_queue.head; read_frame = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list);
spy->read_queue.head = read_frame->next; write_frame = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list);
read_frame->next = NULL;
write_frame = spy->write_queue.head;
spy->write_queue.head = write_frame->next;
write_frame->next = NULL;
spy->read_queue.samples -= samples; spy->read_queue.samples -= samples;
spy->write_queue.samples -= samples; spy->write_queue.samples -= samples;
@ -4200,10 +4169,10 @@ struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsign
} else { } else {
if (need_dup) { if (need_dup) {
result = ast_frdup(read_frame); result = ast_frdup(read_frame);
result->next = ast_frdup(write_frame); AST_LIST_NEXT(result, frame_list) = ast_frdup(write_frame);
} else { } else {
result = read_frame; result = read_frame;
result->next = write_frame; AST_LIST_NEXT(result, frame_list) = write_frame;
} }
} }

@ -41,13 +41,39 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h" #include "asterisk/cli.h"
#include "asterisk/term.h" #include "asterisk/term.h"
#include "asterisk/utils.h" #include "asterisk/utils.h"
#include "asterisk/threadstorage.h"
#include "asterisk/linkedlists.h"
#ifdef TRACE_FRAMES #ifdef TRACE_FRAMES
static int headers = 0; static int headers = 0;
static struct ast_frame *headerlist = NULL; static AST_LIST_HEAD_STATIC(headerlist, ast_frame);
AST_MUTEX_DEFINE_STATIC(framelock);
#endif #endif
static void frame_cache_cleanup(void *data);
/*! \brief A per-thread cache of frame headers */
AST_THREADSTORAGE_CUSTOM(frame_cache, frame_cache_init, frame_cache_cleanup);
/*!
* \brief Maximum ast_frame cache size
*
* In most cases where the frame header cache will be useful, the size
* of the cache will stay very small. However, it is not always the case that
* the same thread that allocates the frame will be the one freeing them, so
* sometimes a thread will never have any frames in its cache, or the cache
* will never be pulled from. For the latter case, we limit the maximum size.
*/
#define FRAME_CACHE_MAX_SIZE 10
/*! \brief This is just so ast_frames, a list head struct for holding a list of
* ast_frame structures, is defined. */
AST_LIST_HEAD_NOLOCK(ast_frames, ast_frame);
struct ast_frame_cache {
struct ast_frames list;
size_t size;
};
#define SMOOTHER_SIZE 8000 #define SMOOTHER_SIZE 8000
enum frame_type { enum frame_type {
@ -252,28 +278,63 @@ void ast_smoother_free(struct ast_smoother *s)
static struct ast_frame *ast_frame_header_new(void) static struct ast_frame *ast_frame_header_new(void)
{ {
struct ast_frame *f = ast_calloc(1, sizeof(*f)); struct ast_frame *f;
#ifdef TRACE_FRAMES struct ast_frame_cache *frames;
if (f) {
f->prev = NULL; if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))) {
ast_mutex_lock(&framelock); if ((f = AST_LIST_REMOVE_HEAD(&frames->list, frame_list))) {
headers++; size_t mallocd_len = f->mallocd_hdr_len;
f->next = headerlist; memset(f, 0, sizeof(*f));
if (headerlist) f->mallocd_hdr_len = mallocd_len;
headerlist->prev = f; f->mallocd = AST_MALLOCD_HDR;
headerlist = f; frames->size--;
ast_mutex_unlock(&framelock); return f;
}
} }
if (!(f = ast_calloc(1, sizeof(*f))))
return NULL;
f->mallocd_hdr_len = sizeof(*f);
#ifdef TRACE_FRAMES
AST_LIST_LOCK(&headerlist);
headers++;
AST_LIST_INSERT_HEAD(&headerlist, f, frame_list);
AST_LIST_UNLOCK(&headerlist);
#endif #endif
return f; return f;
} }
/*! static void frame_cache_cleanup(void *data)
* \todo Important: I should be made more efficient. Frame headers should {
* most definitely be cached struct ast_frame_cache *frames = data;
*/ struct ast_frame *f;
while ((f = AST_LIST_REMOVE_HEAD(&frames->list, frame_list)))
free(f);
free(frames);
}
void ast_frfree(struct ast_frame *fr) void ast_frfree(struct ast_frame *fr)
{ {
if (!fr->mallocd)
return;
if (fr->mallocd == AST_MALLOCD_HDR) {
/* Cool, only the header is malloc'd, let's just cache those for now
* to keep things simple... */
struct ast_frame_cache *frames;
if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))
&& frames->size < FRAME_CACHE_MAX_SIZE) {
AST_LIST_INSERT_HEAD(&frames->list, fr, frame_list);
frames->size++;
return;
}
}
if (fr->mallocd & AST_MALLOCD_DATA) { if (fr->mallocd & AST_MALLOCD_DATA) {
if (fr->data) if (fr->data)
free(fr->data - fr->offset); free(fr->data - fr->offset);
@ -284,15 +345,10 @@ void ast_frfree(struct ast_frame *fr)
} }
if (fr->mallocd & AST_MALLOCD_HDR) { if (fr->mallocd & AST_MALLOCD_HDR) {
#ifdef TRACE_FRAMES #ifdef TRACE_FRAMES
ast_mutex_lock(&framelock); AST_LIST_LOCK(&headerlist);
headers--; headers--;
if (fr->next) AST_LIST_REMOVE(&headerlist, fr, frame_list);
fr->next->prev = fr->prev; AST_LIST_UNLOCK(&headerlist);
if (fr->prev)
fr->prev->next = fr->next;
else
headerlist = fr->next;
ast_mutex_unlock(&framelock);
#endif #endif
free(fr); free(fr);
} }
@ -361,9 +417,11 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
struct ast_frame *ast_frdup(const struct ast_frame *f) struct ast_frame *ast_frdup(const struct ast_frame *f)
{ {
struct ast_frame_cache *frames;
struct ast_frame *out; struct ast_frame *out;
int len, srclen = 0; int len, srclen = 0;
void *buf; void *buf = NULL;
/* Start with standard stuff */ /* Start with standard stuff */
len = sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen; len = sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
/* If we have a source, add space for it */ /* If we have a source, add space for it */
@ -375,16 +433,35 @@ struct ast_frame *ast_frdup(const struct ast_frame *f)
srclen = strlen(f->src); srclen = strlen(f->src);
if (srclen > 0) if (srclen > 0)
len += srclen + 1; len += srclen + 1;
if (!(buf = ast_calloc(1, len)))
return NULL; if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))) {
out = buf; AST_LIST_TRAVERSE_SAFE_BEGIN(&frames->list, out, frame_list) {
/* Set us as having malloc'd header only, so it will eventually if (out->mallocd_hdr_len >= len) {
get freed. */ size_t mallocd_len = out->mallocd_hdr_len;
AST_LIST_REMOVE_CURRENT(&frames->list, frame_list);
memset(out, 0, sizeof(*out));
out->mallocd_hdr_len = mallocd_len;
buf = out;
frames->size--;
break;
}
}
AST_LIST_TRAVERSE_SAFE_END
}
if (!buf) {
if (!(buf = ast_calloc(1, len)))
return NULL;
out = buf;
out->mallocd_hdr_len = len;
}
out->frametype = f->frametype; out->frametype = f->frametype;
out->subclass = f->subclass; out->subclass = f->subclass;
out->datalen = f->datalen; out->datalen = f->datalen;
out->samples = f->samples; out->samples = f->samples;
out->delivery = f->delivery; out->delivery = f->delivery;
/* Set us as having malloc'd header only, so it will eventually
get freed. */
out->mallocd = AST_MALLOCD_HDR; out->mallocd = AST_MALLOCD_HDR;
out->offset = AST_FRIENDLY_OFFSET; out->offset = AST_FRIENDLY_OFFSET;
if (out->datalen) { if (out->datalen) {
@ -860,15 +937,14 @@ static int show_frame_stats(int fd, int argc, char *argv[])
int x=1; int x=1;
if (argc != 3) if (argc != 3)
return RESULT_SHOWUSAGE; return RESULT_SHOWUSAGE;
ast_mutex_lock(&framelock); AST_LIST_LOCK(&headerlist);
ast_cli(fd, " Framer Statistics \n"); ast_cli(fd, " Framer Statistics \n");
ast_cli(fd, "---------------------------\n"); ast_cli(fd, "---------------------------\n");
ast_cli(fd, "Total allocated headers: %d\n", headers); ast_cli(fd, "Total allocated headers: %d\n", headers);
ast_cli(fd, "Queue Dump:\n"); ast_cli(fd, "Queue Dump:\n");
for (f=headerlist; f; f = f->next) { AST_LIST_TRAVERSE(&headerlist, f, frame_list)
ast_cli(fd, "%d. Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>"); ast_cli(fd, "%d. Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
} AST_LIST_UNLOCK(&headerlist);
ast_mutex_unlock(&framelock);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -889,6 +965,7 @@ static struct ast_cli_entry my_clis[] = {
#endif #endif
}; };
int init_framer(void) int init_framer(void)
{ {
ast_cli_register_multiple(my_clis, sizeof(my_clis)/sizeof(my_clis[0]) ); ast_cli_register_multiple(my_clis, sizeof(my_clis)/sizeof(my_clis[0]) );
@ -1342,28 +1419,3 @@ int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2)
return 0; return 0;
} }
struct ast_frame *ast_frame_enqueue(struct ast_frame *head, struct ast_frame *f, int maxlen, int dupe)
{
struct ast_frame *cur, *oldhead;
int len=0;
if (f && dupe)
f = ast_frdup(f);
if (!f)
return head;
f->next = NULL;
if (!head)
return f;
cur = head;
while(cur->next) {
cur = cur->next;
len++;
if (len >= maxlen) {
oldhead = head;
head = head->next;
ast_frfree(oldhead);
}
}
return head;
}

@ -39,7 +39,6 @@ void ast_slinfactory_init(struct ast_slinfactory *sf)
{ {
memset(sf, 0, sizeof(*sf)); memset(sf, 0, sizeof(*sf));
sf->offset = sf->hold; sf->offset = sf->hold;
sf->queue = NULL;
} }
void ast_slinfactory_destroy(struct ast_slinfactory *sf) void ast_slinfactory_destroy(struct ast_slinfactory *sf)
@ -51,10 +50,8 @@ void ast_slinfactory_destroy(struct ast_slinfactory *sf)
sf->trans = NULL; sf->trans = NULL;
} }
while ((f = sf->queue)) { while ((f = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list)))
sf->queue = f->next;
ast_frfree(f); ast_frfree(f);
}
} }
int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f)
@ -85,15 +82,12 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f)
if (!frame) if (!frame)
return 0; return 0;
for (x = 0, frame_ptr = sf->queue; frame_ptr && frame_ptr->next; frame_ptr = frame_ptr->next) x = 0;
AST_LIST_TRAVERSE(&sf->queue, frame_ptr, frame_list)
x++; x++;
if (frame_ptr) AST_LIST_INSERT_TAIL(&sf->queue, frame, frame_list);
frame_ptr->next = frame;
else
sf->queue = frame;
frame->next = NULL;
sf->size += frame->samples; sf->size += frame->samples;
return x; return x;
@ -125,8 +119,7 @@ int ast_slinfactory_read(struct ast_slinfactory *sf, short *buf, size_t samples)
continue; continue;
} }
if ((frame_ptr = sf->queue)) { if ((frame_ptr = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list))) {
sf->queue = frame_ptr->next;
frame_data = frame_ptr->data; frame_data = frame_ptr->data;
if ((sofar + frame_ptr->samples) <= ineed) { if ((sofar + frame_ptr->samples) <= ineed) {

@ -296,8 +296,7 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
ptr = 0; ptr = 0;
ifp_no = 0; ifp_no = 0;
s->f[0].prev = NULL; memset(&s->f[0], 0, sizeof(s->f[0]));
s->f[0].next = NULL;
/* Decode seq_number */ /* Decode seq_number */
if (ptr + 2 > len) if (ptr + 2 > len)
@ -342,11 +341,9 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
s->f[ifp_no].data = (uint8_t *) bufs[i - 1]; s->f[ifp_no].data = (uint8_t *) bufs[i - 1];
s->f[ifp_no].offset = 0; s->f[ifp_no].offset = 0;
s->f[ifp_no].src = "UDPTL"; s->f[ifp_no].src = "UDPTL";
if (ifp_no > 0) { if (ifp_no > 0)
s->f[ifp_no].prev = &s->f[ifp_no - 1]; AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
s->f[ifp_no - 1].next = &s->f[ifp_no]; AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
}
s->f[ifp_no].next = NULL;
ifp_no++; ifp_no++;
} }
} }
@ -364,11 +361,9 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
s->f[ifp_no].data = (uint8_t *) ifp; s->f[ifp_no].data = (uint8_t *) ifp;
s->f[ifp_no].offset = 0; s->f[ifp_no].offset = 0;
s->f[ifp_no].src = "UDPTL"; s->f[ifp_no].src = "UDPTL";
if (ifp_no > 0) { if (ifp_no > 0)
s->f[ifp_no].prev = &s->f[ifp_no - 1]; AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
s->f[ifp_no - 1].next = &s->f[ifp_no]; AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
}
s->f[ifp_no].next = NULL;
} }
} }
else else
@ -465,11 +460,9 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
s->f[ifp_no].data = s->rx[l].buf; s->f[ifp_no].data = s->rx[l].buf;
s->f[ifp_no].offset = 0; s->f[ifp_no].offset = 0;
s->f[ifp_no].src = "UDPTL"; s->f[ifp_no].src = "UDPTL";
if (ifp_no > 0) { if (ifp_no > 0)
s->f[ifp_no].prev = &s->f[ifp_no - 1]; AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
s->f[ifp_no - 1].next = &s->f[ifp_no]; AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
}
s->f[ifp_no].next = NULL;
ifp_no++; ifp_no++;
} }
} }
@ -483,11 +476,9 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
s->f[ifp_no].data = (uint8_t *) ifp; s->f[ifp_no].data = (uint8_t *) ifp;
s->f[ifp_no].offset = 0; s->f[ifp_no].offset = 0;
s->f[ifp_no].src = "UDPTL"; s->f[ifp_no].src = "UDPTL";
if (ifp_no > 0) { if (ifp_no > 0)
s->f[ifp_no].prev = &s->f[ifp_no - 1]; AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
s->f[ifp_no - 1].next = &s->f[ifp_no]; AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
}
s->f[ifp_no].next = NULL;
} }
s->rx_seq_no = seq_no + 1; s->rx_seq_no = seq_no + 1;

Loading…
Cancel
Save