From 22114b509dc93cd3f19610362bf3757db2c04787 Mon Sep 17 00:00:00 2001 From: Joshua Colp Date: Wed, 8 Aug 2007 21:44:58 +0000 Subject: Add support for using epoll instead of poll. This should increase scalability and is done in such a way that we should be able to add support for other poll() replacements. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@78683 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_dial.c | 14 +- channels/chan_agent.c | 4 +- channels/chan_alsa.c | 2 +- channels/chan_features.c | 8 +- channels/chan_gtalk.c | 8 +- channels/chan_h323.c | 18 +- channels/chan_jingle.c | 8 +- channels/chan_mgcp.c | 4 +- channels/chan_misdn.c | 2 +- channels/chan_nbs.c | 2 +- channels/chan_oss.c | 4 +- channels/chan_phone.c | 2 +- channels/chan_sip.c | 12 +- channels/chan_skinny.c | 10 +- channels/chan_zap.c | 10 +- configure | 56 ++++++- configure.ac | 8 + include/asterisk/autoconfig.h.in | 3 + include/asterisk/channel.h | 16 ++ main/channel.c | 351 ++++++++++++++++++++++++++++++++++++--- main/rtp.c | 12 ++ 21 files changed, 482 insertions(+), 72 deletions(-) diff --git a/apps/app_dial.c b/apps/app_dial.c index 758426f6d..f118b7cb2 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -560,6 +560,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct ast_channel *peer = NULL; /* single is set if only one destination is enabled */ int single = outgoing && !outgoing->next && !ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK); +#ifdef HAVE_EPOLL + struct chanlist *epollo; +#endif if (single) { /* Turn off hold music, etc */ @@ -567,7 +570,11 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, /* If we are calling a single channel, make them compatible for in-band tone purpose */ ast_channel_make_compatible(outgoing->chan, in); } - + +#ifdef HAVE_EPOLL + for (epollo = outgoing; epollo; epollo = epollo->next) + ast_poll_channel_add(in, epollo->chan); +#endif while (*to && !peer) { struct chanlist *o; @@ -814,6 +821,11 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, } +#ifdef HAVE_EPOLL + for (epollo = outgoing; epollo; epollo = epollo->next) + ast_poll_channel_del(in, epollo->chan); +#endif + return peer; } diff --git a/channels/chan_agent.c b/channels/chan_agent.c index 21b82b8de..880501ac9 100644 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -207,9 +207,9 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (l if (p->chan) { \ for (x=0;xfds[x] = p->chan->fds[x]; \ + ast_channel_set_fd(ast, x, p->chan->fds[x]); \ } \ - ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \ + ast_channel_set_fd(ast, AST_AGENT_FD, p->chan->fds[AST_TIMING_FD]); \ } \ } while(0) diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c index d3decea05..a2a10e85f 100644 --- a/channels/chan_alsa.c +++ b/channels/chan_alsa.c @@ -789,7 +789,7 @@ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state) return NULL; tmp->tech = &alsa_tech; - tmp->fds[0] = readdev; + ast_channel_set_fd(tmp, 0, readdev); tmp->nativeformats = AST_FORMAT_SLINEAR; tmp->readformat = AST_FORMAT_SLINEAR; tmp->writeformat = AST_FORMAT_SLINEAR; diff --git a/channels/chan_features.c b/channels/chan_features.c index 3d199c36f..aa893de38 100644 --- a/channels/chan_features.c +++ b/channels/chan_features.c @@ -171,8 +171,8 @@ static void restore_channel(struct feature_pvt *p, int index) p->subs[index].owner->timingfd = p->subs[index].timingfdbackup; p->subs[index].owner->alertpipe[0] = p->subs[index].alertpipebackup[0]; p->subs[index].owner->alertpipe[1] = p->subs[index].alertpipebackup[1]; - p->subs[index].owner->fds[AST_ALERT_FD] = p->subs[index].alertpipebackup[0]; - p->subs[index].owner->fds[AST_TIMING_FD] = p->subs[index].timingfdbackup; + ast_channel_set_fd(p->subs[index].owner, AST_ALERT_FD, p->subs[index].alertpipebackup[0]); + ast_channel_set_fd(p->subs[index].owner, AST_TIMING_FD, p->subs[index].timingfdbackup); } static void update_features(struct feature_pvt *p, int index) @@ -181,9 +181,9 @@ static void update_features(struct feature_pvt *p, int index) if (p->subs[index].owner) { for (x=0; xsubs[index].owner->fds[x] = -1; + ast_channel_set_fd(p->subs[index].owner, x, -1); else - p->subs[index].owner->fds[x] = p->subchan->fds[x]; + ast_channel_set_fd(p->subs[index].owner, x, p->subchan->fds[x]); } if (!index) { /* Copy timings from master channel */ diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c index bd53d7ae0..cbdaa076f 100644 --- a/channels/chan_gtalk.c +++ b/channels/chan_gtalk.c @@ -934,13 +934,13 @@ static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, if (i->rtp) { ast_rtp_setstun(i->rtp, 1); - tmp->fds[0] = ast_rtp_fd(i->rtp); - tmp->fds[1] = ast_rtcp_fd(i->rtp); + ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); + ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); } if (i->vrtp) { ast_rtp_setstun(i->rtp, 1); - tmp->fds[2] = ast_rtp_fd(i->vrtp); - tmp->fds[3] = ast_rtcp_fd(i->vrtp); + ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); + ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); } if (state == AST_STATE_RING) tmp->rings = 1; diff --git a/channels/chan_h323.c b/channels/chan_h323.c index 4e38a4eb5..434d210d2 100644 --- a/channels/chan_h323.c +++ b/channels/chan_h323.c @@ -396,8 +396,8 @@ static void __oh323_update_info(struct ast_channel *c, struct oh323_pvt *pvt) if (pvt->update_rtp_info > 0) { if (pvt->rtp) { ast_jb_configure(c, &global_jbconf); - c->fds[0] = ast_rtp_fd(pvt->rtp); - c->fds[1] = ast_rtcp_fd(pvt->rtp); + ast_channel_set_fd(c, 0, ast_rtp_fd(pvt->rtp)); + ast_channel_set_fd(c, 1, ast_rtcp_fd(pvt->rtp)); ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ } pvt->update_rtp_info = -1; @@ -995,8 +995,8 @@ static int __oh323_rtp_create(struct oh323_pvt *pvt) if (pvt->owner && !ast_channel_trylock(pvt->owner)) { ast_jb_configure(pvt->owner, &global_jbconf); - pvt->owner->fds[0] = ast_rtp_fd(pvt->rtp); - pvt->owner->fds[1] = ast_rtcp_fd(pvt->rtp); + ast_channel_set_fd(pvt->owner, 0, ast_rtp_fd(pvt->rtp)); + ast_channel_set_fd(pvt->owner, 1, ast_rtcp_fd(pvt->rtp)); ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ ast_channel_unlock(pvt->owner); } else @@ -1040,18 +1040,18 @@ static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const c ch->readformat = fmt; ch->rawreadformat = fmt; #if 0 - ch->fds[0] = ast_rtp_fd(pvt->rtp); - ch->fds[1] = ast_rtcp_fd(pvt->rtp); + ast_channel_set_fd(ch, 0, ast_rtp_fd(pvt->rtp)); + ast_channel_set_fd(ch, 1, ast_rtcp_fd(pvt->rtp)); #endif #ifdef VIDEO_SUPPORT if (pvt->vrtp) { - ch->fds[2] = ast_rtp_fd(pvt->vrtp); - ch->fds[3] = ast_rtcp_fd(pvt->vrtp); + ast_channel_set_fd(ch, 2, ast_rtp_fd(pvt->vrtp)); + ast_channel_set_fd(ch, 3, ast_rtcp_fd(pvt->vrtp)); } #endif #ifdef T38_SUPPORT if (pvt->udptl) { - ch->fds[4] = ast_udptl_fd(pvt->udptl); + ast_channel_set_fd(ch, 4, ast_udptl_fd(pvt->udptl)); } #endif if (state == AST_STATE_RING) { diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c index 089797315..e45724f04 100644 --- a/channels/chan_jingle.c +++ b/channels/chan_jingle.c @@ -795,12 +795,12 @@ static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt * fmt = ast_best_codec(tmp->nativeformats); if (i->rtp) { - tmp->fds[0] = ast_rtp_fd(i->rtp); - tmp->fds[1] = ast_rtcp_fd(i->rtp); + ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); + ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); } if (i->vrtp) { - tmp->fds[2] = ast_rtp_fd(i->vrtp); - tmp->fds[3] = ast_rtcp_fd(i->vrtp); + ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); + ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); } if (state == AST_STATE_RING) tmp->rings = 1; diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index ddb811ed3..198c964ad 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -1456,7 +1456,7 @@ static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state) fmt = ast_best_codec(tmp->nativeformats); ast_string_field_build(tmp, name, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id); if (sub->rtp) - tmp->fds[0] = ast_rtp_fd(sub->rtp); + ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp)); if (i->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)) { i->dsp = ast_dsp_new(); ast_dsp_set_features(i->dsp,DSP_FEATURE_DTMF_DETECT); @@ -2588,7 +2588,7 @@ static void start_rtp(struct mgcp_subchannel *sub) /* Allocate the RTP now */ sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (sub->rtp && sub->owner) - sub->owner->fds[0] = ast_rtp_fd(sub->rtp); + ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp)); if (sub->rtp) ast_rtp_setnat(sub->rtp, sub->nat); #if 0 diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index 61a4198b1..ad4d5a5a9 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -3220,7 +3220,7 @@ static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char if (pipe(chlist->pipe) < 0) ast_log(LOG_ERROR, "Pipe failed\n"); - tmp->fds[0] = chlist->pipe[0]; + ast_channel_set_fd(tmp, 0, chlist->pipe[0]); if (state == AST_STATE_RING) tmp->rings = 1; diff --git a/channels/chan_nbs.c b/channels/chan_nbs.c index 1c342c6fd..d2718b3de 100644 --- a/channels/chan_nbs.c +++ b/channels/chan_nbs.c @@ -232,7 +232,7 @@ static struct ast_channel *nbs_new(struct nbs_pvt *i, int state) tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "NBS/%s", i->stream); if (tmp) { tmp->tech = &nbs_tech; - tmp->fds[0] = nbs_fd(i->nbs); + ast_channel_set_fd(tmp, 0, nbs_fd(i->nbs)); tmp->nativeformats = prefformat; tmp->rawreadformat = prefformat; tmp->rawwriteformat = prefformat; diff --git a/channels/chan_oss.c b/channels/chan_oss.c index 9dc2fca86..317873759 100644 --- a/channels/chan_oss.c +++ b/channels/chan_oss.c @@ -692,7 +692,7 @@ static int setformat(struct chan_oss_pvt *o, int mode) return -1; } if (o->owner) - o->owner->fds[0] = fd; + ast_channel_set_fd(o->owner, 0, fd); #if __BYTE_ORDER == __LITTLE_ENDIAN fmt = AFMT_S16_LE; @@ -1026,7 +1026,7 @@ static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, c->tech = &oss_tech; if (o->sounddev < 0) setformat(o, O_RDWR); - c->fds[0] = o->sounddev; /* -1 if device closed, override later */ + ast_channel_set_fd(c, 0, o->sounddev); /* -1 if device closed, override later */ c->nativeformats = AST_FORMAT_SLINEAR; c->readformat = AST_FORMAT_SLINEAR; c->writeformat = AST_FORMAT_SLINEAR; diff --git a/channels/chan_phone.c b/channels/chan_phone.c index a3f82bb5b..6a51b6dd6 100644 --- a/channels/chan_phone.c +++ b/channels/chan_phone.c @@ -855,7 +855,7 @@ static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *conte tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, 0, "Phone/%s", i->dev + 5); if (tmp) { tmp->tech = cur_tech; - tmp->fds[0] = i->fd; + ast_channel_set_fd(tmp, 0, i->fd); /* XXX Switching formats silently causes kernel panics XXX */ if (i->mode == MODE_FXS && ioctl(i->fd, PHONE_QUERY_CODEC, &codec) == 0) { diff --git a/channels/chan_sip.c b/channels/chan_sip.c index f6681709e..8800a60ba 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -4553,18 +4553,18 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit ast_dsp_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); } if (i->rtp) { - tmp->fds[0] = ast_rtp_fd(i->rtp); - tmp->fds[1] = ast_rtcp_fd(i->rtp); + ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); + ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); } if (needvideo && i->vrtp) { - tmp->fds[2] = ast_rtp_fd(i->vrtp); - tmp->fds[3] = ast_rtcp_fd(i->vrtp); + ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); + ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); } if (needtext && i->trtp) { - tmp->fds[4] = ast_rtp_fd(i->trtp); + ast_channel_set_fd(tmp, 4, ast_rtp_fd(i->trtp)); } if (i->udptl) { - tmp->fds[5] = ast_udptl_fd(i->udptl); + ast_channel_set_fd(tmp, 5, ast_udptl_fd(i->udptl)); } if (state == AST_STATE_RING) tmp->rings = 1; diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index bc1e73322..c11643805 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -2515,12 +2515,12 @@ static void start_rtp(struct skinny_subchannel *sub) sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (sub->rtp && sub->owner) { - sub->owner->fds[0] = ast_rtp_fd(sub->rtp); - sub->owner->fds[1] = ast_rtcp_fd(sub->rtp); + ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp)); + ast_channel_set_fd(sub->owner, 1, ast_rtcp_fd(sub->rtp)); } if (hasvideo && sub->vrtp && sub->owner) { - sub->owner->fds[2] = ast_rtp_fd(sub->vrtp); - sub->owner->fds[3] = ast_rtcp_fd(sub->vrtp); + ast_channel_set_fd(sub->owner, 2, ast_rtp_fd(sub->vrtp)); + ast_channel_set_fd(sub->owner, 3, ast_rtcp_fd(sub->vrtp)); } if (sub->rtp) { ast_rtp_setnat(sub->rtp, l->nat); @@ -3070,7 +3070,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state) if (skinnydebug) ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt); if (sub->rtp) { - tmp->fds[0] = ast_rtp_fd(sub->rtp); + ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp)); } if (state == AST_STATE_RING) { tmp->rings = 1; diff --git a/channels/chan_zap.c b/channels/chan_zap.c index fbad1ac90..1426f456a 100644 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -1013,9 +1013,9 @@ static void swap_subs(struct zt_pvt *p, int a, int b) p->subs[b].inthreeway = tinthreeway; if (p->subs[a].owner) - p->subs[a].owner->fds[0] = p->subs[a].zfd; + ast_channel_set_fd(p->subs[a].owner, 0, p->subs[a].zfd); if (p->subs[b].owner) - p->subs[b].owner->fds[0] = p->subs[b].zfd; + ast_channel_set_fd(p->subs[b].owner, 0, p->subs[b].zfd); wakeup_sub(p, a, NULL); wakeup_sub(p, b, NULL); } @@ -2595,7 +2595,7 @@ static int pri_assign_bearer(struct zt_pvt *crv, struct zt_pri *pri, struct zt_p bearer->realcall = crv; crv->subs[SUB_REAL].zfd = bearer->subs[SUB_REAL].zfd; if (crv->subs[SUB_REAL].owner) - crv->subs[SUB_REAL].owner->fds[0] = crv->subs[SUB_REAL].zfd; + ast_channel_set_fd(crv->subs[SUB_REAL].owner, 0, crv->subs[SUB_REAL].zfd); crv->bearer = bearer; crv->call = bearer->call; crv->pri = pri; @@ -5515,7 +5515,7 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int else deflaw = AST_FORMAT_ULAW; } - tmp->fds[0] = i->subs[index].zfd; + ast_channel_set_fd(tmp, 0, i->subs[index].zfd); tmp->nativeformats = AST_FORMAT_SLINEAR | deflaw; /* Start out assuming ulaw since it's smaller :) */ tmp->rawreadformat = deflaw; @@ -8977,7 +8977,7 @@ static int pri_fixup_principle(struct zt_pri *pri, int principle, q931_call *c) "Zap/%d:%d-%d", pri->trunkgroup, pri->pvts[principle]->channel, 1); pri->pvts[principle]->owner->tech_pvt = pri->pvts[principle]; - pri->pvts[principle]->owner->fds[0] = pri->pvts[principle]->subs[SUB_REAL].zfd; + ast_channel_set_fd(pri->pvts[principle]->owner, 0, pri->pvts[principle]->subs[SUB_REAL].zfd); pri->pvts[principle]->subs[SUB_REAL].owner = pri->pvts[x]->subs[SUB_REAL].owner; } else ast_log(LOG_WARNING, "Whoa, there's no owner, and we're having to fix up channel %d to channel %d\n", pri->pvts[x]->channel, pri->pvts[principle]->channel); diff --git a/configure b/configure index 2a6cf3a59..41c9ec77d 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac Revision: 77878 . +# From configure.ac Revision: 60612 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61. # @@ -15624,6 +15624,60 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi +{ echo "$as_me:$LINENO: checking for working epoll support" >&5 +echo $ECHO_N "checking for working epoll support... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +epoll_create(10); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_EPOLL 1 +_ACEOF + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + { echo "$as_me:$LINENO: checking for compiler atomic operations" >&5 echo $ECHO_N "checking for compiler atomic operations... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF diff --git a/configure.ac b/configure.ac index c4093ff56..40ee8c11f 100644 --- a/configure.ac +++ b/configure.ac @@ -332,6 +332,14 @@ fi AST_C_DEFINE_CHECK([PTHREAD_RWLOCK_INITIALIZER], [PTHREAD_RWLOCK_INITIALIZER], [pthread.h]) AST_C_DEFINE_CHECK([PTHREAD_RWLOCK_PREFER_WRITER_NP], [PTHREAD_RWLOCK_PREFER_WRITER_NP], [pthread.h]) +AC_MSG_CHECKING(for working epoll support) +AC_LINK_IFELSE( +AC_LANG_PROGRAM([#include ], [epoll_create(10);]), +AC_MSG_RESULT(yes) +AC_DEFINE([HAVE_EPOLL], 1, [Define to 1 if your system has working epoll support.]), +AC_MSG_RESULT(no) +) + AC_MSG_CHECKING(for compiler atomic operations) AC_LINK_IFELSE( AC_LANG_PROGRAM([], [int foo1; int foo2 = __sync_fetch_and_add(&foo1, 1);]), diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index 1031ee8ab..0facfe2be 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -114,6 +114,9 @@ /* Define to 1 if you have the `endpwent' function. */ #undef HAVE_ENDPWENT +/* Define to 1 if your system has working epoll support. */ +#undef HAVE_EPOLL + /* Define this to indicate the ${EXP10_DESCRIP} library */ #undef HAVE_EXP10 diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 39f2c636d..b58880853 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -316,6 +316,8 @@ struct ast_channel_tech { int (* func_channel_write)(struct ast_channel *chan, const char *function, char *data, const char *value); }; +struct ast_epoll_data; + /*! * The high bit of the frame count is used as a debug marker, so * increments of the counters must be done with care. @@ -490,6 +492,11 @@ struct ast_channel { /*! \brief Data stores on the channel */ AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores; + +#ifdef HAVE_EPOLL + int epfd; + struct ast_epoll_data *epfd_data[AST_MAX_FDS]; +#endif }; /*! \brief ast_channel_tech Properties */ @@ -1184,6 +1191,15 @@ void ast_deactivate_generator(struct ast_channel *chan); void ast_set_callerid(struct ast_channel *chan, const char *cidnum, const char *cidname, const char *ani); +/*! Set the file descriptor on the channel */ +void ast_channel_set_fd(struct ast_channel *chan, int which, int fd); + +/*! Add a channel to an optimized waitfor */ +void ast_poll_channel_add(struct ast_channel *chan0, struct ast_channel *chan1); + +/*! Delete a channel from an optimized waitfor */ +void ast_poll_channel_del(struct ast_channel *chan0, struct ast_channel *chan1); + /*! Start a tone going */ int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol); /*! Stop a tone from playing */ diff --git a/main/channel.c b/main/channel.c index 3f3ce9466..26f9da5f7 100644 --- a/main/channel.c +++ b/main/channel.c @@ -67,6 +67,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/slinfactory.h" #include "asterisk/audiohook.h" +#ifdef HAVE_EPOLL +#include +#endif + +struct ast_epoll_data { + struct ast_channel *chan; + int which; +}; + /* uncomment if you have problems with 'monitoring' synchronized files */ #if 0 #define MONITOR_CONSTANT_DELAY @@ -631,11 +640,16 @@ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_ return NULL; } - /* Don't bother initializing the last two FD here, because they - will *always* be set just a few lines down (AST_TIMING_FD, - AST_ALERT_FD). */ - for (x = 0; x < AST_MAX_FDS - 2; x++) +#ifdef HAVE_EPOLL + tmp->epfd = epoll_create(25); +#endif + + for (x = 0; x < AST_MAX_FDS; x++) { tmp->fds[x] = -1; +#ifdef HAVE_EPOLL + tmp->epfd_data[x] = NULL; +#endif + } #ifdef HAVE_ZAPTEL tmp->timingfd = open("/dev/zap/timer", O_RDWR); @@ -666,9 +680,9 @@ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_ tmp->alertpipe[0] = tmp->alertpipe[1] = -1; /* Always watch the alertpipe */ - tmp->fds[AST_ALERT_FD] = tmp->alertpipe[0]; + ast_channel_set_fd(tmp, AST_ALERT_FD, tmp->alertpipe[0]); /* And timing pipe */ - tmp->fds[AST_TIMING_FD] = tmp->timingfd; + ast_channel_set_fd(tmp, AST_TIMING_FD, tmp->timingfd); ast_string_field_set(tmp, name, "**Unknown**"); /* Initial state */ @@ -1065,6 +1079,9 @@ static void free_cid(struct ast_callerid *cid) void ast_channel_free(struct ast_channel *chan) { int fd; +#ifdef HAVE_EPOLL + int i; +#endif struct ast_var_t *vardata; struct ast_frame *f; struct varshead *headp; @@ -1116,6 +1133,13 @@ void ast_channel_free(struct ast_channel *chan) close(fd); if ((fd = chan->timingfd) > -1) close(fd); +#ifdef HAVE_EPOLL + for (i = 0; i < AST_MAX_FDS; i++) { + if (chan->epfd_data[i]) + free(chan->epfd_data[i]); + } + close(chan->epfd); +#endif while ((f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list))) ast_frfree(f); @@ -1256,6 +1280,83 @@ struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const return datastore; } +/*! Set the file descriptor on the channel */ +void ast_channel_set_fd(struct ast_channel *chan, int which, int fd) +{ +#ifdef HAVE_EPOLL + struct epoll_event ev; + struct ast_epoll_data *aed = NULL; + + if (chan->fds[which] > -1) { + epoll_ctl(chan->epfd, EPOLL_CTL_DEL, chan->fds[which], &ev); + aed = chan->epfd_data[which]; + } + + /* If this new fd is valid, add it to the epoll */ + if (fd > -1) { + if (!aed && (!(aed = ast_calloc(1, sizeof(*aed))))) + return; + + chan->epfd_data[which] = aed; + aed->chan = chan; + aed->which = which; + + ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP; + ev.data.ptr = aed; + epoll_ctl(chan->epfd, EPOLL_CTL_ADD, fd, &ev); + } else if (aed) { + /* We don't have to keep around this epoll data structure now */ + free(aed); + chan->epfd_data[which] = NULL; + } +#endif + chan->fds[which] = fd; + return; +} + +/*! Add a channel to an optimized waitfor */ +void ast_poll_channel_add(struct ast_channel *chan0, struct ast_channel *chan1) +{ +#ifdef HAVE_EPOLL + struct epoll_event ev; + int i = 0; + + if (chan0->epfd == -1) + return; + + /* Iterate through the file descriptors on chan1, adding them to chan0 */ + for (i = 0; i < AST_MAX_FDS; i++) { + if (chan1->fds[i] == -1) + continue; + ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP; + ev.data.ptr = chan1->epfd_data[i]; + epoll_ctl(chan0->epfd, EPOLL_CTL_ADD, chan1->fds[i], &ev); + } + +#endif + return; +} + +/*! Delete a channel from an optimized waitfor */ +void ast_poll_channel_del(struct ast_channel *chan0, struct ast_channel *chan1) +{ +#ifdef HAVE_EPOLL + struct epoll_event ev; + int i = 0; + + if (chan0->epfd == -1) + return; + + for (i = 0; i < AST_MAX_FDS; i++) { + if (chan1->fds[i] == -1) + continue; + epoll_ctl(chan0->epfd, EPOLL_CTL_DEL, chan1->fds[i], &ev); + } + +#endif + return; +} + /*! \brief Softly hangup a channel, don't lock */ int ast_softhangup_nolock(struct ast_channel *chan, int cause) { @@ -1437,7 +1538,7 @@ void ast_deactivate_generator(struct ast_channel *chan) chan->generator->release(chan, chan->generatordata); chan->generatordata = NULL; chan->generator = NULL; - chan->fds[AST_GENERATOR_FD] = -1; + ast_channel_set_fd(chan, AST_GENERATOR_FD, -1); ast_clear_flag(chan, AST_FLAG_WRITE_INT); ast_settimeout(chan, 0, NULL, NULL); } @@ -1499,8 +1600,13 @@ int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception) } /*! \brief Wait for x amount of time on a file descriptor to have input. */ +#ifdef HAVE_EPOLL +static struct ast_channel *ast_waitfor_nandfds_classic(struct ast_channel **c, int n, int *fds, int nfds, + int *exception, int *outfd, int *ms) +#else struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, - int *exception, int *outfd, int *ms) + int *exception, int *outfd, int *ms) +#endif { struct timeval start = { 0 , 0 }; struct pollfd *pfds; @@ -1526,15 +1632,13 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, *exception = 0; /* Perform any pending masquerades */ - for (x=0; x < n; x++) { + for (x = 0; x < n; x++) { ast_channel_lock(c[x]); - if (c[x]->masq) { - if (ast_do_masquerade(c[x])) { - ast_log(LOG_WARNING, "Masquerade failed\n"); - *ms = -1; - ast_channel_unlock(c[x]); - return NULL; - } + if (c[x]->masq && ast_do_masquerade(c[x])) { + ast_log(LOG_WARNING, "Masquerade failed\n"); + *ms = -1; + ast_channel_unlock(c[x]); + return NULL; } if (c[x]->whentohangup) { if (!whentohangup) @@ -1564,8 +1668,8 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, * individual fd's must have priority over channel fds. */ max = 0; - for (x=0; xfds[y]); @@ -1573,7 +1677,7 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, CHECK_BLOCKING(c[x]); } /* Add the individual fds */ - for (x=0; xwhentohangup && now >= c[x]->whentohangup) { c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT; if (winner == NULL) @@ -1646,6 +1750,200 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, return winner; } +#ifdef HAVE_EPOLL +static struct ast_channel *ast_waitfor_nandfds_simple(struct ast_channel *chan, int *ms) +{ + struct timeval start = { 0 , 0 }; + int res = 0; + struct epoll_event ev[1]; + long whentohangup = 0, rms = *ms; + time_t now; + struct ast_channel *winner = NULL; + struct ast_epoll_data *aed = NULL; + + ast_channel_lock(chan); + + /* See if this channel needs to be masqueraded */ + if (chan->masq && ast_do_masquerade(chan)) { + ast_log(LOG_WARNING, "Failed to perform masquerade on %s\n", chan->name); + *ms = -1; + ast_channel_unlock(chan); + return NULL; + } + + /* Figure out their timeout */ + if (chan->whentohangup) { + time(&now); + if ((whentohangup = chan->whentohangup - now) < 1) { + /* They should already be hungup! */ + chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT; + ast_channel_unlock(chan); + return NULL; + } + /* If this value is smaller then the current one... make it priority */ + whentohangup *= 1000; + if (rms > whentohangup) + rms = whentohangup; + } + + ast_channel_unlock(chan); + + /* Time to make this channel block... */ + CHECK_BLOCKING(chan); + + if (*ms > 0) + start = ast_tvnow(); + + /* We don't have to add any file descriptors... they are already added, we just have to wait! */ + res = epoll_wait(chan->epfd, ev, 1, rms); + + /* Stop blocking */ + ast_clear_flag(chan, AST_FLAG_BLOCKING); + + /* Simulate a timeout if we were interrupted */ + if (res < 0) { + if (errno != EINTR) + *ms = -1; + return NULL; + } + + /* If this channel has a timeout see if it expired */ + if (chan->whentohangup) { + time(&now); + if (now >= chan->whentohangup) { + chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT; + winner = chan; + } + } + + /* No fd ready, reset timeout and be done for now */ + if (!res) { + *ms = 0; + return winner; + } + + /* See what events are pending */ + aed = ev[0].data.ptr; + chan->fdno = aed->which; + if (ev[0].events & EPOLLPRI) + ast_set_flag(chan, AST_FLAG_EXCEPTION); + else + ast_clear_flag(chan, AST_FLAG_EXCEPTION); + + if (*ms > 0) { + *ms -= ast_tvdiff_ms(ast_tvnow(), start); + if (*ms < 0) + *ms = 0; + } + + return chan; +} + +static struct ast_channel *ast_waitfor_nandfds_complex(struct ast_channel **c, int n, int *ms) +{ + struct timeval start = { 0 , 0 }; + int res = 0, i; + struct epoll_event ev[25] = { { 0, } }; + long whentohangup = 0, diff, rms = *ms; + time_t now; + struct ast_channel *winner = NULL; + + for (i = 0; i < n; i++) { + ast_channel_lock(c[i]); + if (c[i]->masq && ast_do_masquerade(c[i])) { + ast_log(LOG_WARNING, "Masquerade failed\n"); + *ms = -1; + ast_channel_unlock(c[i]); + return NULL; + } + if (c[i]->whentohangup) { + if (!whentohangup) + time(&now); + if ((diff = c[i]->whentohangup - now) < 1) { + c[i]->_softhangup |= AST_SOFTHANGUP_TIMEOUT; + ast_channel_unlock(c[i]); + return c[i]; + } + if (!whentohangup || (diff < whentohangup)) + whentohangup = diff; + } + ast_channel_unlock(c[i]); + CHECK_BLOCKING(c[i]); + } + + rms = *ms; + if (whentohangup) { + rms = whentohangup * 1000; + if (*ms >= 0 && *ms < rms) + rms = *ms; + } + + if (*ms > 0) + start = ast_tvnow(); + + res = epoll_wait(c[0]->epfd, ev, 25, rms); + + for (i = 0; i < n; i++) + ast_clear_flag(c[i], AST_FLAG_BLOCKING); + + if (res < 0) { + if (errno != EINTR) + *ms = -1; + return NULL; + } + + if (whentohangup) { + time(&now); + for (i = 0; i < n; i++) { + if (c[i]->whentohangup && now >= c[i]->whentohangup) { + c[i]->_softhangup |= AST_SOFTHANGUP_TIMEOUT; + if (!winner) + winner = c[i]; + } + } + } + + if (!res) { + *ms = 0; + return winner; + } + + for (i = 0; i < 25; i++) { + struct ast_epoll_data *aed = ev[i].data.ptr; + + if (!ev[i].events || !aed) + continue; + + winner = aed->chan; + if (ev[i].events & EPOLLPRI) + ast_set_flag(winner, AST_FLAG_EXCEPTION); + else + ast_clear_flag(winner, AST_FLAG_EXCEPTION); + winner->fdno = aed->which; + } + + if (*ms > 0) { + *ms -= ast_tvdiff_ms(ast_tvnow(), start); + if (*ms < 0) + *ms = 0; + } + + return winner; +} + +struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, + int *exception, int *outfd, int *ms) +{ + /* If no epoll file descriptor is available resort to classic nandfds */ + if (!n || nfds || c[0]->epfd == -1) + return ast_waitfor_nandfds_classic(c, n, fds, nfds, exception, outfd, ms); + else if (!nfds && n == 1) + return ast_waitfor_nandfds_simple(c[0], ms); + else + return ast_waitfor_nandfds_complex(c, n, ms); +} +#endif + struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms) { return ast_waitfor_nandfds(c, n, NULL, 0, NULL, NULL, ms); @@ -3259,7 +3557,7 @@ int ast_do_masquerade(struct ast_channel *original) /* Copy the FD's other than the generator fd */ for (x = 0; x < AST_MAX_FDS; x++) { if (x != AST_GENERATOR_FD) - original->fds[x] = clone->fds[x]; + ast_channel_set_fd(original, x, clone->fds[x]); } ast_app_group_update(clone, original); @@ -3290,7 +3588,7 @@ int ast_do_masquerade(struct ast_channel *original) clone->cid = tmpcid; /* Restore original timing file descriptor */ - original->fds[AST_TIMING_FD] = original->timingfd; + ast_channel_set_fd(original, AST_TIMING_FD, original->timingfd); /* Our native formats are different now */ original->nativeformats = clone->nativeformats; @@ -3487,6 +3785,8 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct /* Check the need of a jitterbuffer for each channel */ jb_in_use = ast_jb_do_usecheck(c0, c1); + ast_poll_channel_add(c0, c1); + for (;;) { struct ast_channel *who, *other; @@ -3591,11 +3891,16 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct /* XXX do we want to pass on also frames not matched above ? */ ast_frfree(f); +#ifndef HAVE_EPOLL /* Swap who gets priority */ cs[2] = cs[0]; cs[0] = cs[1]; cs[1] = cs[2]; +#endif } + + ast_poll_channel_del(c0, c1); + return res; } diff --git a/main/rtp.c b/main/rtp.c index d8d99012f..465755afe 100644 --- a/main/rtp.c +++ b/main/rtp.c @@ -3212,6 +3212,8 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel_unlock(c0); ast_channel_unlock(c1); + ast_poll_channel_add(c0, c1); + /* Throw our channels into the structure and enter the loop */ cs[0] = c0; cs[1] = c1; @@ -3228,6 +3230,7 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct if (c1->tech_pvt == pvt1) if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); + ast_poll_channel_del(c0, c1); return AST_BRIDGE_RETRY; } @@ -3313,6 +3316,7 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct if (c1->tech_pvt == pvt1) if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); + ast_poll_channel_del(c0, c1); return AST_BRIDGE_COMPLETE; } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { if ((fr->subclass == AST_CONTROL_HOLD) || @@ -3353,11 +3357,15 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_frfree(fr); } /* Swap priority */ +#ifndef HAVE_EPOLL cs[2] = cs[0]; cs[0] = cs[1]; cs[1] = cs[2]; +#endif } + ast_poll_channel_del(c0, c1); + if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) @@ -3488,6 +3496,8 @@ static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast ast_channel_unlock(c0); ast_channel_unlock(c1); + ast_poll_channel_add(c0, c1); + /* Go into a loop forwarding frames until we don't need to anymore */ cs[0] = c0; cs[1] = c1; @@ -3591,6 +3601,8 @@ static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast p2p_set_bridge(p0, NULL); p2p_set_bridge(p1, NULL); + ast_poll_channel_del(c0, c1); + return res; } -- cgit v1.2.3