]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Fix misuses of timeouts throughout the code.
authorMark Michelson <mmichelson@digium.com>
Wed, 7 Nov 2012 17:01:13 +0000 (17:01 +0000)
committerMark Michelson <mmichelson@digium.com>
Wed, 7 Nov 2012 17:01:13 +0000 (17:01 +0000)
Prior to this change, a common method for determining if a timeout
was reached was to call a function such as ast_waitfor_n() and inspect
the out parameter that told how many milliseconds were left, then use
that as the input to ast_waitfor_n() on the next go-around.

The problem with this is that in some cases, submillisecond timeouts
can occur, resulting in the out parameter not decreasing any. When this
happens thousands of times, the result is that the timeout takes much
longer than intended to be reached. As an example, I had a situation where
a 3 second timeout took multiple days to finally end since most wakeups
from ast_waitfor_n() were under a millisecond.

This patch seeks to fix this pattern throughout the code. Now we log the
time when an operation began and find the difference in wall clock time
between now and when the event started. This means that sub-millisecond timeouts
now cannot play havoc when trying to determine if something has timed out.

Part of this fix also includes changing the function ast_waitfor() so that it
is possible for it to return less than zero when a negative timeout is given
to it. This makes it actually possible to detect errors in ast_waitfor() when
there is no timeout.

(closes issue ASTERISK-20414)
reported by David M. Lee

Review: https://reviewboard.asterisk.org/r/2135/

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8@375993 65c4cc65-6c06-0410-ace0-fbb531ad65f3

19 files changed:
apps/app_dial.c
apps/app_jack.c
apps/app_meetme.c
apps/app_queue.c
apps/app_record.c
apps/app_waitforring.c
channels/chan_agent.c
channels/chan_dahdi.c
channels/chan_iax2.c
channels/chan_sip.c
channels/sig_analog.c
channels/sig_pri.c
include/asterisk/channel.h
include/asterisk/time.h
main/channel.c
main/pbx.c
main/rtp_engine.c
main/utils.c
res/res_fax.c

index 247b4466a8518493babd14dbdc3fe874835afcaf..ac0574cb31eef297378e49bf91fe066c8c3cafb0 100644 (file)
@@ -1055,6 +1055,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
        int is_cc_recall;
        int cc_frame_received = 0;
        int num_ringing = 0;
+       struct timeval start = ast_tvnow();
 
        ast_party_connected_line_init(&connected_caller);
        if (single) {
@@ -1095,7 +1096,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                ast_poll_channel_add(in, epollo->chan);
 #endif
 
-       while (*to && !peer) {
+       while ((*to = ast_remaining_ms(start, orig)) && !peer) {
                struct chanlist *o;
                int pos = 0; /* how many channels do we handle */
                int numlines = prestart;
@@ -1626,10 +1627,13 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 skip_frame:;
                        ast_frfree(f);
                }
-               if (!*to)
-                       ast_verb(3, "Nobody picked up in %d ms\n", orig);
-               if (!*to || ast_check_hangup(in))
-                       ast_cdr_noanswer(in->cdr);
+       }
+
+       if (!*to) {
+               ast_verb(3, "Nobody picked up in %d ms\n", orig);
+       }
+       if (!*to || ast_check_hangup(in)) {
+               ast_cdr_noanswer(in->cdr);
        }
 
 #ifdef HAVE_EPOLL
index 5ba5d9ac005fe4bfee513e7487a4dd6ce020dbf9..8b93956faa66b6db08258e19cebf89291fca2c26 100644 (file)
@@ -768,7 +768,9 @@ static int jack_exec(struct ast_channel *chan, const char *data)
        while (!jack_data->stop) {
                struct ast_frame *f;
 
-               ast_waitfor(chan, -1);
+               if (ast_waitfor(chan, -1) < 0) {
+                       break;
+               }
 
                f = ast_read(chan);
                if (!f) {
index e1f0ade5cea92de1dc4e66b6227f2377b6fb75c3..bfa83985a5dfd47c14ab1c68356a6917c72fe07b 100644 (file)
@@ -1869,7 +1869,7 @@ static void conf_flush(int fd, struct ast_channel *chan)
                /* when no frames are available, this will wait
                   for 1 millisecond maximum
                */
-               while (ast_waitfor(chan, 1)) {
+               while (ast_waitfor(chan, 1) > 0) {
                        f = ast_read(chan);
                        if (f)
                                ast_frfree(f);
index 0874bd488d964e3e3d9ccdafd9b31fdd7cab86f4..02e93f3cd9fd1021dfa7c663cf18a6abbd71aa1e 100644 (file)
@@ -3556,6 +3556,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
 #endif
        struct ast_party_connected_line connected_caller;
        char *inchan_name;
+       struct timeval start_time_tv = ast_tvnow();
 
        ast_party_connected_line_init(&connected_caller);
 
@@ -3570,8 +3571,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                        ast_poll_channel_add(in, epollo->chan);
        }
 #endif
-       
-       while (*to && !peer) {
+
+       while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
                int numlines, retry, pos = 1;
                struct ast_channel *watchers[AST_MAX_WATCHERS];
                watchers[0] = in;
@@ -3827,10 +3828,11 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                                        endtime -= starttime;
                                                        rna(endtime * 1000, qe, on, membername, 0);
                                                        if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
-                                                               if (qe->parent->timeoutrestart)
-                                                                       *to = orig;
+                                                               if (qe->parent->timeoutrestart) {
+                                                                       start_time_tv = ast_tvnow();
+                                                               }
                                                                /* Have enough time for a queue member to answer? */
-                                                               if (*to > 500) {
+                                                               if (ast_remaining_ms(start_time_tv, orig) > 500) {
                                                                        ring_one(qe, outgoing, &numbusies);
                                                                        starttime = (long) time(NULL);
                                                                }
@@ -3846,9 +3848,10 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                                        rna(endtime * 1000, qe, on, membername, 0);
                                                        do_hang(o);
                                                        if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
-                                                               if (qe->parent->timeoutrestart)
-                                                                       *to = orig;
-                                                               if (*to > 500) {
+                                                               if (qe->parent->timeoutrestart) {
+                                                                       start_time_tv = ast_tvnow();
+                                                               }
+                                                               if (ast_remaining_ms(start_time_tv, orig) > 500) {
                                                                        ring_one(qe, outgoing, &numbusies);
                                                                        starttime = (long) time(NULL);
                                                                }
@@ -3935,9 +3938,10 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                        rna(endtime * 1000, qe, on, membername, 1);
                                        do_hang(o);
                                        if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
-                                               if (qe->parent->timeoutrestart)
-                                                       *to = orig;
-                                               if (*to > 500) {
+                                               if (qe->parent->timeoutrestart) {
+                                                       start_time_tv = ast_tvnow();
+                                               }
+                                               if (ast_remaining_ms(start_time_tv, orig) > 500) {
                                                        ring_one(qe, outgoing, &numbusies);
                                                        starttime = (long) time(NULL);
                                                }
@@ -4008,9 +4012,11 @@ skip_frame:;
 
                        ast_frfree(f);
                }
-               if (!*to) {
-                       for (o = start; o; o = o->call_next)
-                               rna(orig, qe, o->interface, o->member->membername, 1);
+       }
+
+       if (!*to) {
+               for (o = start; o; o = o->call_next) {
+                       rna(orig, qe, o->interface, o->member->membername, 1);
                }
        }
 
index 20aff2d48f98231464d735460860111065274b24..2e2b732332687b3e2c0d49837fd07fab1c1c1f22 100644 (file)
@@ -160,7 +160,6 @@ static int record_exec(struct ast_channel *chan, const char *data)
        int terminator = '#';
        int rfmt = 0;
        int ioflags;
-       int waitres;
        struct ast_silence_generator *silgen = NULL;
        struct ast_flags flags = { 0, };
        AST_DECLARE_APP_ARGS(args,
@@ -169,7 +168,9 @@ static int record_exec(struct ast_channel *chan, const char *data)
                AST_APP_ARG(maxduration);
                AST_APP_ARG(options);
        );
-       
+       int ms;
+       struct timeval start;
+
        /* The next few lines of code parse out the filename and header from the input string */
        if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
                ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
@@ -330,14 +331,15 @@ static int record_exec(struct ast_channel *chan, const char *data)
        if (maxduration <= 0)
                maxduration = -1;
 
-       while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
-               if (maxduration > 0) {
-                       if (waitres == 0) {
-                               gottimeout = 1;
-                               pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "TIMEOUT");
-                               break;
-                       }
-                       maxduration = waitres;
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, maxduration))) {
+               ms = ast_waitfor(chan, ms);
+               if (ms < 0) {
+                       break;
+               }
+
+               if (maxduration > 0 && ms == 0) {
+                       break;
                }
 
                f = ast_read(chan);
@@ -389,6 +391,12 @@ static int record_exec(struct ast_channel *chan, const char *data)
                }
                ast_frfree(f);
        }
+
+       if (maxduration > 0 && !ms) {
+               gottimeout = 1;
+               pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "TIMEOUT");
+       }
+
        if (!f) {
                ast_debug(1, "Got hangup\n");
                res = -1;
index bd0353b074d15da07e0ea47ccd1f9e57b49f7b39..fc02de303ede0b752c252497a748d4d524862ba1 100644 (file)
@@ -63,22 +63,29 @@ static int waitforring_exec(struct ast_channel *chan, const char *data)
        struct ast_silence_generator *silgen = NULL;
        int res = 0;
        double s;
+       int timeout_ms;
        int ms;
+       struct timeval start = ast_tvnow();
 
        if (!data || (sscanf(data, "%30lg", &s) != 1)) {
                ast_log(LOG_WARNING, "WaitForRing requires an argument (minimum seconds)\n");
                return 0;
        }
 
+       if (s < 0.0) {
+               ast_log(LOG_WARNING, "Invalid timeout provided for WaitForRing (%lg)\n", s);
+               return 0;
+       }
+
        if (ast_opt_transmit_silence) {
                silgen = ast_channel_start_silence_generator(chan);
        }
 
-       ms = s * 1000.0;
-       while (ms > 0) {
+       timeout_ms = s * 1000.0;
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
                ms = ast_waitfor(chan, ms);
                if (ms < 0) {
-                       res = ms;
+                       res = -1;
                        break;
                }
                if (ms > 0) {
@@ -95,14 +102,12 @@ static int waitforring_exec(struct ast_channel *chan, const char *data)
        }
        /* Now we're really ready for the ring */
        if (!res) {
-               ms = 99999999;
-               while(ms > 0) {
-                       ms = ast_waitfor(chan, ms);
-                       if (ms < 0) {
-                               res = ms;
+               for (;;) {
+                       int wait_res = ast_waitfor(chan, -1);
+                       if (wait_res < 0) {
+                               res = -1;
                                break;
-                       }
-                       if (ms > 0) {
+                       } else {
                                f = ast_read(chan);
                                if (!f) {
                                        res = -1;
index eba07c8ad516558d5934fa9da47e3caffa567c7e..06c64b26faccb68599a41446748215eaaf847136 100644 (file)
@@ -1045,6 +1045,8 @@ static int agent_ack_sleep(void *data)
        int res=0;
        int to = 1000;
        struct ast_frame *f;
+       struct timeval start = ast_tvnow();
+       int ms;
 
        /* Wait a second and look for something */
 
@@ -1052,12 +1054,14 @@ static int agent_ack_sleep(void *data)
        if (!p->chan) 
                return -1;
 
-       for(;;) {
-               to = ast_waitfor(p->chan, to);
-               if (to < 0) 
+       while ((ms = ast_remaining_ms(start, to))) {
+               ms = ast_waitfor(p->chan, ms);
+               if (ms < 0) {
                        return -1;
-               if (!to) 
+               }
+               if (ms == 0) {
                        return 0;
+               }
                f = ast_read(p->chan);
                if (!f) 
                        return -1;
@@ -1077,7 +1081,7 @@ static int agent_ack_sleep(void *data)
                ast_mutex_unlock(&p->lock);
                res = 0;
        }
-       return res;
+       return 0;
 }
 
 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
index 889b92c75c5596c48ff20352ed8e49fd21b4d283..ccd8f3b1d23483387e23f6570c8ae2b8d1b003f8 100644 (file)
@@ -6062,6 +6062,7 @@ static int dahdi_accept_r2_call_exec(struct ast_channel *chan, const char *data)
                if (res == 0) {
                        continue;
                }
+               res = 0;
                f = ast_read(chan);
                if (!f) {
                        ast_log(LOG_DEBUG, "No frame read on channel %s, going out ...\n", chan->name);
@@ -7233,6 +7234,7 @@ static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_ch
        int priority = 0;
        struct ast_channel *oc0, *oc1;
        enum ast_bridge_result res;
+       struct timeval start = ast_tvnow();
 #ifdef PRI_2BCT
        int triedtopribridge = 0;
        q931_call *q931c0;
@@ -7454,6 +7456,7 @@ static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_ch
        for (;;) {
                struct ast_channel *c0_priority[2] = {c0, c1};
                struct ast_channel *c1_priority[2] = {c1, c0};
+               int ms;
 
                /* Here's our main loop...  Start by locking things, looking for private parts,
                   and then balking if anything is wrong */
@@ -7473,8 +7476,8 @@ static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_ch
 
                ast_channel_unlock(c0);
                ast_channel_unlock(c1);
-
-               if (!timeoutms ||
+               ms = ast_remaining_ms(start, timeoutms);
+               if (!ms ||
                        (op0 != p0) ||
                        (op1 != p1) ||
                        (ofd0 != c0->fds[0]) ||
@@ -7522,7 +7525,7 @@ static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_ch
                }
 #endif
 
-               who = ast_waitfor_n(priority ? c0_priority : c1_priority, 2, &timeoutms);
+               who = ast_waitfor_n(priority ? c0_priority : c1_priority, 2, &ms);
                if (!who) {
                        ast_debug(1, "Ooh, empty read...\n");
                        continue;
@@ -10509,6 +10512,9 @@ static void *analog_ss_thread(void *data)
                        /* If set to use DTMF CID signalling, listen for DTMF */
                        if (p->cid_signalling == CID_SIG_DTMF) {
                                int k = 0;
+                               int off_ms;
+                               struct timeval start = ast_tvnow();
+                               int ms;
                                cs = NULL;
                                ast_debug(1, "Receiving DTMF cid on channel %s\n", chan->name);
                                dahdi_setlinear(p->subs[idx].dfd, 0);
@@ -10519,10 +10525,12 @@ static void *analog_ss_thread(void *data)
                                 * can drop some of them.
                                 */
                                ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
-                               res = 4000;/* This is a typical OFF time between rings. */
+                               off_ms = 4000;/* This is a typical OFF time between rings. */
                                for (;;) {
                                        struct ast_frame *f;
-                                       res = ast_waitfor(chan, res);
+
+                                       ms = ast_remaining_ms(start, off_ms);
+                                       res = ast_waitfor(chan, ms);
                                        if (res <= 0) {
                                                /*
                                                 * We do not need to restore the dahdi_setlinear()
@@ -10542,7 +10550,7 @@ static void *analog_ss_thread(void *data)
                                                        dtmfbuf[k++] = f->subclass.integer;
                                                }
                                                ast_debug(1, "CID got digit '%c'\n", f->subclass.integer);
-                                               res = 4000;/* This is a typical OFF time between rings. */
+                                               start = ast_tvnow();
                                        }
                                        ast_frfree(f);
                                        if (chan->_state == AST_STATE_RING ||
@@ -10565,6 +10573,9 @@ static void *analog_ss_thread(void *data)
                        } else if ((p->cid_signalling == CID_SIG_V23) || (p->cid_signalling == CID_SIG_V23_JP)) {
                                cs = callerid_new(p->cid_signalling);
                                if (cs) {
+                                       int off_ms;
+                                       struct timeval start;
+                                       int ms;
                                        samples = 0;
 #if 1
                                        bump_gains(p);
@@ -10641,10 +10652,13 @@ static void *analog_ss_thread(void *data)
                                        }
 
                                        /* Finished with Caller*ID, now wait for a ring to make sure there really is a call coming */
-                                       res = 4000;/* This is a typical OFF time between rings. */
+                                       start = ast_tvnow();
+                                       off_ms = 4000;/* This is a typical OFF time between rings. */
                                        for (;;) {
                                                struct ast_frame *f;
-                                               res = ast_waitfor(chan, res);
+
+                                               ms = ast_remaining_ms(start, off_ms);
+                                               res = ast_waitfor(chan, ms);
                                                if (res <= 0) {
                                                        ast_log(LOG_WARNING, "CID timed out waiting for ring. "
                                                                "Exiting simple switch\n");
@@ -10772,12 +10786,18 @@ static void *analog_ss_thread(void *data)
                } else if (p->use_callerid && p->cid_start == CID_START_RING) {
                        if (p->cid_signalling == CID_SIG_DTMF) {
                                int k = 0;
+                               int off_ms;
+                               struct timeval start;
+                               int ms;
                                cs = NULL;
                                dahdi_setlinear(p->subs[idx].dfd, 0);
-                               res = 2000;
+                               off_ms = 2000;
+                               start = ast_tvnow();
                                for (;;) {
                                        struct ast_frame *f;
-                                       res = ast_waitfor(chan, res);
+
+                                       ms = ast_remaining_ms(start, off_ms);
+                                       res = ast_waitfor(chan, ms);
                                        if (res <= 0) {
                                                ast_log(LOG_WARNING, "DTMFCID timed out waiting for ring. "
                                                        "Exiting simple switch\n");
@@ -10793,7 +10813,7 @@ static void *analog_ss_thread(void *data)
                                        if (f->frametype == AST_FRAME_DTMF) {
                                                dtmfbuf[k++] = f->subclass.integer;
                                                ast_log(LOG_DEBUG, "CID got digit '%c'\n", f->subclass.integer);
-                                               res = 2000;
+                                               start = ast_tvnow();
                                        }
                                        ast_frfree(f);
 
index eaae961f54c1bff03012a629a9ef995a215c7899..d7eeb245a3188bb8069eaef6a6e6a69ce10f4a2d 100644 (file)
@@ -5558,6 +5558,11 @@ static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_cha
                }
                to = 1000;
                who = ast_waitfor_n(cs, 2, &to);
+               /* XXX This will need to be updated to calculate
+                * timeout correctly once timeoutms is allowed to be
+                * > 0. Right now, this can go badly if the waitfor
+                * times out in less than a millisecond
+                */
                if (timeoutms > -1) {
                        timeoutms -= (1000 - to);
                        if (timeoutms < 0)
@@ -13708,6 +13713,8 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
 
        /* By here we must have a dp */
        if (dp->flags & CACHE_FLAG_PENDING) {
+               struct timeval start;
+               int ms;
                /* Okay, here it starts to get nasty.  We need a pipe now to wait
                   for a reply to come back so long as it's pending */
                for (x = 0; x < ARRAY_LEN(dp->waiters); x++) {
@@ -13732,8 +13739,9 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
                if (chan)
                        old = ast_channel_defer_dtmf(chan);
                doabort = 0;
-               while(timeout) {
-                       c = ast_waitfor_nandfds(&chan, chan ? 1 : 0, &com[0], 1, NULL, &outfd, &timeout);
+               start = ast_tvnow();
+               while ((ms = ast_remaining_ms(start, timeout))) {
+                       c = ast_waitfor_nandfds(&chan, chan ? 1 : 0, &com[0], 1, NULL, &outfd, &ms);
                        if (outfd > -1)
                                break;
                        if (!c)
@@ -13744,7 +13752,7 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
                        }
                        ast_frfree(f);
                }
-               if (!timeout) {
+               if (!ms) {
                        ast_log(LOG_WARNING, "Timeout waiting for %s exten %s\n", data, exten);
                }
                AST_LIST_LOCK(&dpcache);
index 5f68ff2d163481639dadacaf6528bfc29298aae3..1a2073f5cd84f25a89affe0328fe175be6eb2ba9 100644 (file)
@@ -5859,6 +5859,7 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
        const char *referer = NULL;   /* SIP referrer */
        int cc_core_id;
        char uri[SIPBUFSIZE] = "";
+       char capabilities[SIPBUFSIZE];
 
        if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
                ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
@@ -5966,6 +5967,8 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
        p->jointcapability = ast_rtp_instance_available_formats(p->rtp, p->capability, p->prefcodec);
        p->jointnoncodeccapability = p->noncodeccapability;
 
+       ast_log(LOG_NOTICE, "jointcapability is %s\n", ast_getformatname_multiple(capabilities, SIPBUFSIZE, p->jointcapability));
+
        /* If there are no audio formats left to offer, punt */
        if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
                ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username);
index 2831b5662ee9fde0fdd1ff77dba58e42a31a1915..6d11a3d73ef5495f4043f4cb81a79de3c6515093 100644 (file)
@@ -2386,6 +2386,9 @@ static void *__analog_ss_thread(void *data)
                        if (p->cid_signalling == CID_SIG_DTMF) {
                                int k = 0;
                                int oldlinearity; 
+                               int timeout_ms;
+                               int ms;
+                               struct timeval start = ast_tvnow();
                                cs = NULL;
                                ast_debug(1, "Receiving DTMF cid on channel %s\n", chan->name);
 
@@ -2398,10 +2401,12 @@ static void *__analog_ss_thread(void *data)
                                 * can drop some of them.
                                 */
                                ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
-                               res = 4000;/* This is a typical OFF time between rings. */
+                               timeout_ms = 4000;/* This is a typical OFF time between rings. */
                                for (;;) {
                                        struct ast_frame *f;
-                                       res = ast_waitfor(chan, res);
+
+                                       ms = ast_remaining_ms(start, timeout_ms);
+                                       res = ast_waitfor(chan, ms);
                                        if (res <= 0) {
                                                /*
                                                 * We do not need to restore the analog_set_linear_mode()
@@ -2422,7 +2427,7 @@ static void *__analog_ss_thread(void *data)
                                                        dtmfbuf[k++] = f->subclass.integer;
                                                }
                                                ast_debug(1, "CID got digit '%c'\n", f->subclass.integer);
-                                               res = 4000;/* This is a typical OFF time between rings. */
+                                               start = ast_tvnow();
                                        }
                                        ast_frfree(f);
                                        if (chan->_state == AST_STATE_RING ||
@@ -2456,6 +2461,9 @@ static void *__analog_ss_thread(void *data)
                                numbuf[0] = 0;
 
                                if (!analog_start_cid_detect(p, p->cid_signalling)) {
+                                       int off_ms;
+                                       int ms;
+                                       struct timeval off_start;
                                        while (1) {
                                                res = analog_get_callerid(p, namebuf, numbuf, &ev, timeout - ast_tvdiff_ms(ast_tvnow(), start));
 
@@ -2493,10 +2501,12 @@ static void *__analog_ss_thread(void *data)
                                        }
 
                                        /* Finished with Caller*ID, now wait for a ring to make sure there really is a call coming */
-                                       res = 4000;/* This is a typical OFF time between rings. */
-                                       for (;;) {
+                                       off_start = ast_tvnow();
+                                       off_ms = 4000;/* This is a typical OFF time between rings. */
+                                       while ((ms = ast_remaining_ms(off_start, off_ms))) {
                                                struct ast_frame *f;
-                                               res = ast_waitfor(chan, res);
+
+                                               res = ast_waitfor(chan, ms);
                                                if (res <= 0) {
                                                        ast_log(LOG_WARNING, "CID timed out waiting for ring. "
                                                                "Exiting simple switch\n");
index 50a7be5589e4a761f70831de0dbb9651edf01e94..e9536e63917836b80fc20898482b547c1880349a 100644 (file)
@@ -1831,7 +1831,9 @@ static void *do_idle_thread(void *v_pvt)
        struct ast_frame *f;
        char ex[80];
        /* Wait up to 30 seconds for an answer */
-       int newms, ms = 30000;
+       int timeout_ms = 30000;
+       int ms;
+       struct timeval start;
 
        ast_verb(3, "Initiating idle call on channel %s\n", chan->name);
        snprintf(ex, sizeof(ex), "%d/%s", pvt->channel, pvt->pri->idledial);
@@ -1840,7 +1842,12 @@ static void *do_idle_thread(void *v_pvt)
                ast_hangup(chan);
                return NULL;
        }
-       while ((newms = ast_waitfor(chan, ms)) > 0) {
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
+               if (ast_waitfor(chan, ms) <= 0) {
+                       break;
+               }
+
                f = ast_read(chan);
                if (!f) {
                        /* Got hangup */
@@ -1866,7 +1873,6 @@ static void *do_idle_thread(void *v_pvt)
                        };
                }
                ast_frfree(f);
-               ms = newms;
        }
        /* Hangup the channel since nothing happend */
        ast_hangup(chan);
index 52e0238107c747ff3ff7fb7f4cf0049f8b9d5ba8..d5aa046af37dd496edad599d1d1c5e5cec752f3d 100644 (file)
@@ -1688,7 +1688,7 @@ int ast_is_deferrable_frame(const struct ast_frame *frame);
 /*!
  * \brief Wait for a specified amount of time, looking for hangups
  * \param chan channel to wait for
- * \param ms length of time in milliseconds to sleep
+ * \param ms length of time in milliseconds to sleep. This should never be less than zero.
  * \details
  * Waits for a specified amount of time, servicing the channel as required.
  * \return returns -1 on hangup, otherwise 0.
@@ -1698,7 +1698,7 @@ int ast_safe_sleep(struct ast_channel *chan, int ms);
 /*!
  * \brief Wait for a specified amount of time, looking for hangups and a condition argument
  * \param chan channel to wait for
- * \param ms length of time in milliseconds to sleep
+ * \param ms length of time in milliseconds to sleep.
  * \param cond a function pointer for testing continue condition
  * \param data argument to be passed to the condition test function
  * \return returns -1 on hangup, otherwise 0.
index 2c3b1253340573de10e276ac3b2d258cf66d4839..fe446113b19c4e6f05694c92c93ba93791869d03 100644 (file)
@@ -151,6 +151,20 @@ struct timeval ast_tvadd(struct timeval a, struct timeval b);
  */
 struct timeval ast_tvsub(struct timeval a, struct timeval b);
 
+/*!
+ * \brief Calculate remaining milliseconds given a starting timestamp
+ * and upper bound
+ *
+ * If the upper bound is negative, then this indicates that there is no
+ * upper bound on the amount of time to wait. This will result in a
+ * negative return.
+ *
+ * \param start When timing started being calculated
+ * \param max_ms The maximum number of milliseconds to wait from start. May be negative.
+ * \return The number of milliseconds left to wait for. May be negative.
+ */
+int ast_remaining_ms(struct timeval start, int max_ms);
+
 /*!
  * \brief Returns a timeval from sec, usec
  */
index 44cf01c59e15e80af4883526a3665e715fbb4a55..a6ec4ce4a810ff4c02b40c5d56c2ee810a91d430 100644 (file)
@@ -1837,11 +1837,13 @@ int ast_is_deferrable_frame(const struct ast_frame *frame)
 }
 
 /*! \brief Wait, look for hangups and condition arg */
-int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(void*), void *data)
+int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*cond)(void*), void *data)
 {
        struct ast_frame *f;
        struct ast_silence_generator *silgen = NULL;
        int res = 0;
+       struct timeval start;
+       int ms;
        AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
 
        AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
@@ -1851,8 +1853,10 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(voi
                silgen = ast_channel_start_silence_generator(chan);
        }
 
-       while (ms > 0) {
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
                struct ast_frame *dup_f = NULL;
+
                if (cond && ((*cond)(data) == 0)) {
                        break;
                }
@@ -2989,12 +2993,15 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay, int cdr_answer)
                do {
                        AST_LIST_HEAD_NOLOCK(, ast_frame) frames;
                        struct ast_frame *cur, *new;
-                       int ms = MAX(delay, 500);
+                       int timeout_ms = MAX(delay, 500);
                        unsigned int done = 0;
+                       struct timeval start;
 
                        AST_LIST_HEAD_INIT_NOLOCK(&frames);
 
+                       start = ast_tvnow();
                        for (;;) {
+                               int ms = ast_remaining_ms(start, timeout_ms);
                                ms = ast_waitfor(chan, ms);
                                if (ms < 0) {
                                        ast_log(LOG_WARNING, "Error condition occurred when polling channel %s for a voice frame: %s\n", chan->name, strerror(errno));
@@ -3509,11 +3516,14 @@ struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
 
 int ast_waitfor(struct ast_channel *c, int ms)
 {
-       int oldms = ms; /* -1 if no timeout */
-
-       ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
-       if ((ms < 0) && (oldms < 0))
-               ms = 0;
+       if (ms < 0) {
+               do {
+                       ms = 100000;
+                       ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
+               } while (!ms);
+       } else {
+               ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
+       }
        return ms;
 }
 
@@ -3568,6 +3578,7 @@ int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const v
 int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, int cmdfd)
 {
        struct timeval start = ast_tvnow();
+       int ms;
 
        /* Stop if we're a zombie or need a soft hangup */
        if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
@@ -3579,19 +3590,9 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in
        /* Wait for a digit, no more than timeout_ms milliseconds total.
         * Or, wait indefinitely if timeout_ms is <0.
         */
-       while (ast_tvdiff_ms(ast_tvnow(), start) < timeout_ms || timeout_ms < 0) {
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
                struct ast_channel *rchan;
-               int outfd=-1;
-               int ms;
-
-               if (timeout_ms < 0) {
-                       ms = timeout_ms;
-               } else {
-                       ms = timeout_ms - ast_tvdiff_ms(ast_tvnow(), start);
-                       if (ms < 0) {
-                               ms = 0;
-                       }
-               }
+               int outfd = -1;
 
                errno = 0;
                /* While ast_waitfor_nandfds tries to help by reducing the timeout by how much was waited,
@@ -4597,25 +4598,32 @@ int ast_recvchar(struct ast_channel *chan, int timeout)
 
 char *ast_recvtext(struct ast_channel *chan, int timeout)
 {
-       int res, done = 0;
+       int res;
        char *buf = NULL;
-       
-       while (!done) {
+       struct timeval start = ast_tvnow();
+       int ms;
+
+       while ((ms = ast_remaining_ms(start, timeout))) {
                struct ast_frame *f;
-               if (ast_check_hangup(chan))
+
+               if (ast_check_hangup(chan)) {
                        break;
-               res = ast_waitfor(chan, timeout);
-               if (res <= 0) /* timeout or error */
+               }
+               res = ast_waitfor(chan, ms);
+               if (res <= 0)  {/* timeout or error */
                        break;
-               timeout = res;  /* update timeout */
+               }
                f = ast_read(chan);
-               if (f == NULL)
+               if (f == NULL) {
                        break; /* no frame */
-               if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_HANGUP)
-                       done = 1;       /* force a break */
-               else if (f->frametype == AST_FRAME_TEXT) {              /* what we want */
+               }
+               if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_HANGUP) {
+                       ast_frfree(f);
+                       break;
+               } else if (f->frametype == AST_FRAME_TEXT) {            /* what we want */
                        buf = ast_strndup((char *) f->data.ptr, f->datalen);    /* dup and break */
-                       done = 1;
+                       ast_frfree(f);
+                       break;
                }
                ast_frfree(f);
        }
@@ -5466,18 +5474,19 @@ struct ast_channel *__ast_request_and_dial(const char *type, format_t format, co
        if (ast_call(chan, data, 0)) {  /* ast_call failed... */
                ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
        } else {
+               struct timeval start = ast_tvnow();
                res = 1;        /* mark success in case chan->_state is already AST_STATE_UP */
                while (timeout && chan->_state != AST_STATE_UP) {
                        struct ast_frame *f;
-                       res = ast_waitfor(chan, timeout);
+                       int ms = ast_remaining_ms(start, timeout);
+
+                       res = ast_waitfor(chan, ms);
                        if (res == 0) { /* timeout, treat it like ringing */
                                *outstate = AST_CONTROL_RINGING;
                                break;
                        }
                        if (res < 0) /* error or done */
                                break;
-                       if (timeout > -1)
-                               timeout = res;
                        if (!ast_strlen_zero(chan->call_forward)) {
                                if (!(chan = ast_call_forward(NULL, chan, NULL, format, oh, outstate))) {
                                        return NULL;
index 56f0fed72a2aa257f8d24dce61ab9c9606bbd51c..3d15b8db8f499cd25946a27744c712d725d0d967 100644 (file)
@@ -8721,13 +8721,15 @@ static void *async_wait(void *data)
        int res;
        struct ast_frame *f;
        struct ast_app *app;
+       struct timeval start = ast_tvnow();
+       int ms;
 
-       while (timeout && (chan->_state != AST_STATE_UP)) {
-               res = ast_waitfor(chan, timeout);
+       while ((ms = ast_remaining_ms(start, timeout)) &&
+                       chan->_state != AST_STATE_UP) {
+               res = ast_waitfor(chan, ms);
                if (res < 1)
                        break;
-               if (timeout > -1)
-                       timeout = res;
+
                f = ast_read(chan);
                if (!f)
                        break;
index 45cc1630479d92cbc8d3a0d51ab17a5b9e2806f6..118fadfc2687e7c8b9a93aec94ed8e4f00cccf65 100644 (file)
@@ -810,6 +810,7 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a
        enum ast_bridge_result res = AST_BRIDGE_FAILED;
        struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, };
        struct ast_frame *fr = NULL;
+       struct timeval start;
 
        /* Start locally bridging both instances */
        if (instance0->engine->local_bridge && instance0->engine->local_bridge(instance0, instance1)) {
@@ -840,7 +841,9 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a
        cs[0] = c0;
        cs[1] = c1;
        cs[2] = NULL;
+       start = ast_tvnow();
        for (;;) {
+               int ms;
                /* If the underlying formats have changed force this bridge to break */
                if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) {
                        ast_debug(1, "rtp-engine-local-bridge: Oooh, formats changed, backing out\n");
@@ -865,8 +868,9 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a
                        break;
                }
                /* Wait on a channel to feed us a frame */
-               if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
-                       if (!timeoutms) {
+               ms = ast_remaining_ms(start, timeoutms);
+               if (!(who = ast_waitfor_n(cs, 2, &ms))) {
+                       if (!ms) {
                                res = AST_BRIDGE_RETRY;
                                break;
                        }
@@ -985,6 +989,7 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct
        struct ast_sockaddr ac1 = {{0,}}, vac1 = {{0,}}, tac1 = {{0,}}, ac0 = {{0,}}, vac0 = {{0,}}, tac0 = {{0,}};
        struct ast_sockaddr t1 = {{0,}}, vt1 = {{0,}}, tt1 = {{0,}}, t0 = {{0,}}, vt0 = {{0,}}, tt0 = {{0,}};
        struct ast_frame *fr = NULL;
+       struct timeval start;
 
        /* Test the first channel */
        if (!(glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0))) {
@@ -1024,7 +1029,9 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct
        cs[0] = c0;
        cs[1] = c1;
        cs[2] = NULL;
+       start = ast_tvnow();
        for (;;) {
+               int ms;
                /* Check if anything changed */
                if ((c0->tech_pvt != pvt0) ||
                    (c1->tech_pvt != pvt1) ||
@@ -1115,9 +1122,10 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct
                        oldcodec0 = codec0;
                }
 
+               ms = ast_remaining_ms(start, timeoutms);
                /* Wait for frame to come in on the channels */
-               if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
-                       if (!timeoutms) {
+               if (!(who = ast_waitfor_n(cs, 2, &ms))) {
+                       if (!ms) {
                                res = AST_BRIDGE_RETRY;
                                break;
                        }
index 898f9d1017ab7df9f431ffa49d11c2608b3e5f74..bfdf92197853adafd81e3ccb065e424e1b836124 100644 (file)
@@ -1432,6 +1432,23 @@ struct timeval ast_tvsub(struct timeval a, struct timeval b)
        }
        return a;
 }
+
+int ast_remaining_ms(struct timeval start, int max_ms)
+{
+       int ms;
+
+       if (max_ms < 0) {
+               ms = max_ms;
+       } else {
+               ms = max_ms - ast_tvdiff_ms(ast_tvnow(), start);
+               if (ms < 0) {
+                       ms = 0;
+               }
+       }
+
+       return ms;
+}
+
 #undef ONE_MILLION
 
 /*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
index 93d92d222351acf1bf6b96fe30048df2e8feace5..6a96cbe109a74da90bd36e674e66bb4364b8c3f0 100644 (file)
@@ -1084,9 +1084,11 @@ static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_det
 
 static int disable_t38(struct ast_channel *chan)
 {
-       int ms;
+       int timeout_ms;
        struct ast_frame *frame = NULL;
        struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
+       struct timeval start;
+       int ms;
 
        ast_debug(1, "Shutting down T.38 on %s\n", chan->name);
        if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0) {
@@ -1095,20 +1097,19 @@ static int disable_t38(struct ast_channel *chan)
        }
 
        /* wait up to five seconds for negotiation to complete */
-       ms = 5000;
-
-       while (ms > 0) {
+       timeout_ms = 5000;
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
                ms = ast_waitfor(chan, ms);
+
+               if (ms == 0) {
+                       break;
+               }
                if (ms < 0) {
                        ast_debug(1, "error while disabling T.38 on channel '%s'\n", chan->name);
                        return -1;
                }
 
-               if (ms == 0) { /* all done, nothing happened */
-                       ast_debug(1, "channel '%s' timed-out during T.38 shutdown\n", chan->name);
-                       break;
-               }
-
                if (!(frame = ast_read(chan))) {
                        return -1;
                }
@@ -1136,6 +1137,10 @@ static int disable_t38(struct ast_channel *chan)
                ast_frfree(frame);
        }
 
+       if (ms == 0) { /* all done, nothing happened */
+               ast_debug(1, "channel '%s' timed-out during T.38 shutdown\n", chan->name);
+       }
+
        return 0;
 }
 
@@ -1151,7 +1156,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 {
        int ms;
        int timeout = RES_FAX_TIMEOUT;
-       int res = 0, chancount;
+       int res, chancount;
        unsigned int expected_frametype = -1;
        union ast_frame_subclass expected_framesubclass = { .integer = -1 };
        unsigned int t38negotiated = (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED);
@@ -1161,6 +1166,8 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
        struct ast_frame *frame = NULL;
        struct ast_channel *c = chan;
        unsigned int orig_write_format = 0, orig_read_format = 0;
+       int remaining_time;
+       struct timeval start;
 
        chancount = 1;
 
@@ -1239,8 +1246,9 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
        ast_debug(5, "channel %s will wait on FAX fd %d\n", chan->name, fax->fd);
 
        /* handle frames for the session */
-       ms = 1000;
-       while ((res > -1) && (ms > -1) && (timeout > 0)) {
+       remaining_time = timeout;
+       start = ast_tvnow();
+       while (remaining_time > 0) {
                struct ast_channel *ready_chan;
                int ofd, exception;
 
@@ -1257,7 +1265,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                                GENERIC_FAX_EXEC_SET_VARS(fax, chan, "HANGUP", "remote channel hungup");
                                c = NULL;
                                chancount = 0;
-                               timeout -= (1000 - ms);
+                               remaining_time = ast_remaining_ms(start, timeout);
                                fax->tech->cancel_session(fax);
                                if (fax->tech->generate_silence) {
                                        fax->tech->generate_silence(fax);
@@ -1326,7 +1334,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                                        fax->tech->write(fax, frame);
                                        fax->frames_received++;
                                }
-                               timeout = RES_FAX_TIMEOUT;
+                               start = ast_tvnow();
                        }
                        ast_frfree(frame);
                } else if (ofd == fax->fd) {
@@ -1343,36 +1351,30 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                        ast_write(chan, frame);
                        fax->frames_sent++;
                        ast_frfree(frame);
-                       timeout = RES_FAX_TIMEOUT;
+                       start = ast_tvnow();
                } else {
                        if (ms && (ofd < 0)) {
                                if ((errno == 0) || (errno == EINTR)) {
-                                       timeout -= (1000 - ms);
-                                       if (timeout <= 0)
+                                       remaining_time = ast_remaining_ms(start, timeout);
+                                       if (remaining_time <= 0)
                                                GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
                                        continue;
                                } else {
                                        ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", chan->name);
                                        GENERIC_FAX_EXEC_ERROR(fax, chan, "UNKNOWN", "error polling data");
-                                       res = ms;
                                        break;
                                }
                        } else {
                                /* nothing happened */
-                               if (timeout > 0) {
-                                       timeout -= 1000;
-                                       if (timeout <= 0)
-                                               GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
-                                       continue;
-                               } else {
-                                       ast_log(LOG_WARNING, "channel '%s' timed-out during the FAX transmission.\n", chan->name);
+                               remaining_time = ast_remaining_ms(start, timeout);
+                               if (remaining_time <= 0) {
                                        GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
                                        break;
                                }
                        }
                }
        }
-       ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, ms: %d, res: %d }\n", chan->name, timeout, ms, res);
+       ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", chan->name, timeout, remaining_time);
 
        set_channel_variables(chan, details);
 
@@ -1407,9 +1409,11 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 
 static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
-       int ms;
+       int timeout_ms;
        struct ast_frame *frame = NULL;
        struct ast_control_t38_parameters t38_parameters;
+       struct timeval start;
+       int ms;
 
        t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
 
@@ -1421,9 +1425,11 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
                        return -1;
                }
 
-               ms = 3000;
-               while (ms > 0) {
+               timeout_ms = 3000;
+               start = ast_tvnow();
+               while ((ms = ast_remaining_ms(start, timeout_ms))) {
                        ms = ast_waitfor(chan, ms);
+
                        if (ms < 0) {
                                ast_log(LOG_ERROR, "error while generating CED tone on %s\n", chan->name);
                                ast_playtones_stop(chan);
@@ -1480,7 +1486,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
        ast_debug(1, "Negotiating T.38 for receive on %s\n", chan->name);
 
        /* wait up to five seconds for negotiation to complete */
-       ms = 5000;
+       timeout_ms = 5000;
 
        /* set parameters based on the session's parameters */
        t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
@@ -1489,13 +1495,15 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
                return -1;
        }
 
-       while (ms > 0) {
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
+               int break_loop = 0;
+
                ms = ast_waitfor(chan, ms);
                if (ms < 0) {
                        ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
                        return -1;
                }
-
                if (ms == 0) { /* all done, nothing happened */
                        ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", chan->name);
                        details->caps &= ~AST_FAX_TECH_T38;
@@ -1523,21 +1531,24 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
                                t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                details->caps &= ~AST_FAX_TECH_AUDIO;
                                report_fax_status(chan, details, "T.38 Negotiated");
-                               ms = 0;
+                               break_loop = 1;
                                break;
                        case AST_T38_REFUSED:
                                ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
                                details->caps &= ~AST_FAX_TECH_T38;
-                               ms = 0;
+                               break_loop = 1;
                                break;
                        default:
                                ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
                                details->caps &= ~AST_FAX_TECH_T38;
-                               ms = 0;
+                               break_loop = 1;
                                break;
                        }
                }
                ast_frfree(frame);
+               if (break_loop) {
+                       break;
+               }
        }
 
        /* if T.38 was negotiated, we are done initializing */
@@ -1805,9 +1816,11 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
 
 static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
-       int ms;
+       int timeout_ms;
        struct ast_frame *frame = NULL;
        struct ast_control_t38_parameters t38_parameters;
+       struct timeval start;
+       int ms;
 
        t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
 
@@ -1817,7 +1830,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
         *
         * 10500 is enough time for 3 CNG tones
         */
-       ms = 10500;
+       timeout_ms = 10500;
 
        /* don't send any audio if we've already received a T.38 reinvite */
        if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) {
@@ -1827,8 +1840,11 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                }
        }
 
-       while (ms > 0) {
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
+               int break_loop = 0;
                ms = ast_waitfor(chan, ms);
+
                if (ms < 0) {
                        ast_log(LOG_ERROR, "error while generating CNG tone on %s\n", chan->name);
                        ast_playtones_stop(chan);
@@ -1865,13 +1881,16 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                                t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                details->caps &= ~AST_FAX_TECH_AUDIO;
                                report_fax_status(chan, details, "T.38 Negotiated");
-                               ms = 0;
+                               break_loop = 1;
                                break;
                        default:
                                break;
                        }
                }
                ast_frfree(frame);
+               if (break_loop) {
+                       break;
+               }
        }
 
        ast_playtones_stop(chan);
@@ -1885,7 +1904,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                ast_debug(1, "Negotiating T.38 for send on %s\n", chan->name);
 
                /* wait up to five seconds for negotiation to complete */
-               ms = 5000;
+               timeout_ms = 5000;
 
                /* set parameters based on the session's parameters */
                t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
@@ -1894,13 +1913,15 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                        return -1;
                }
 
-               while (ms > 0) {
+               start = ast_tvnow();
+               while ((ms = ast_remaining_ms(start, timeout_ms))) {
+                       int break_loop = 0;
+
                        ms = ast_waitfor(chan, ms);
                        if (ms < 0) {
                                ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
                                return -1;
                        }
-
                        if (ms == 0) { /* all done, nothing happened */
                                ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", chan->name);
                                details->caps &= ~AST_FAX_TECH_T38;
@@ -1928,21 +1949,24 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                                        t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                        details->caps &= ~AST_FAX_TECH_AUDIO;
                                        report_fax_status(chan, details, "T.38 Negotiated");
-                                       ms = 0;
+                                       break_loop = 1;
                                        break;
                                case AST_T38_REFUSED:
                                        ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
                                        details->caps &= ~AST_FAX_TECH_T38;
-                                       ms = 0;
+                                       break_loop = 1;
                                        break;
                                default:
                                        ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
                                        details->caps &= ~AST_FAX_TECH_T38;
-                                       ms = 0;
+                                       break_loop = 1;
                                        break;
                                }
                        }
                        ast_frfree(frame);
+                       if (break_loop) {
+                               break;
+                       }
                }
 
                /* if T.38 was negotiated, we are done initializing */
@@ -1958,15 +1982,17 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                                return -1;
                        }
 
-                       ms = 3500;
-                       while (ms > 0) {
+                       timeout_ms = 3500;
+                       start = ast_tvnow();
+                       while ((ms = ast_remaining_ms(start, timeout_ms))) {
+                               int break_loop = 0;
+
                                ms = ast_waitfor(chan, ms);
                                if (ms < 0) {
                                        ast_log(LOG_ERROR, "error while generating second CNG tone on %s\n", chan->name);
                                        ast_playtones_stop(chan);
                                        return -1;
                                }
-
                                if (ms == 0) { /* all done, nothing happened */
                                        break;
                                }
@@ -1997,13 +2023,16 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                                                t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                                details->caps &= ~AST_FAX_TECH_AUDIO;
                                                report_fax_status(chan, details, "T.38 Negotiated");
-                                               ms = 0;
+                                               break_loop = 1;
                                                break;
                                        default:
                                                break;
                                        }
                                }
                                ast_frfree(frame);
+                               if (break_loop) {
+                                       break;
+                               }
                        }
 
                        ast_playtones_stop(chan);