Multiple revisions 375993-375994
authorMark Michelson <mmichelson@digium.com>
Wed, 7 Nov 2012 19:15:26 +0000 (19:15 +0000)
committerMark Michelson <mmichelson@digium.com>
Wed, 7 Nov 2012 19:15:26 +0000 (19:15 +0000)
........
  r375993 | mmichelson | 2012-11-07 11:01:13 -0600 (Wed, 07 Nov 2012) | 30 lines

  Fix misuses of timeouts throughout the code.

  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/
........
  r375994 | mmichelson | 2012-11-07 11:08:44 -0600 (Wed, 07 Nov 2012) | 3 lines

  Remove some debugging that accidentally made it in the last commit.
........

Merged revisions 375993-375994 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 375995 from http://svn.asterisk.org/svn/asterisk/branches/10
........

Merged revisions 376014 from http://svn.asterisk.org/svn/asterisk/branches/11

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

18 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/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 310326d..b162a41 100644 (file)
@@ -1116,6 +1116,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) {
@@ -1158,7 +1159,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
        }
 #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;
@@ -1702,10 +1703,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(ast_channel_cdr(in));
+       }
+
+       if (!*to) {
+               ast_verb(3, "Nobody picked up in %d ms\n", orig);
+       }
+       if (!*to || ast_check_hangup(in)) {
+               ast_cdr_noanswer(ast_channel_cdr(in));
        }
 
 #ifdef HAVE_EPOLL
index 10964f4..d1bdfa3 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 c2bfccd..da66c59 100644 (file)
@@ -1917,7 +1917,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 a6ec766..775f343 100644 (file)
@@ -4052,6 +4052,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);
 
@@ -4068,7 +4069,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
        }
 #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;
@@ -4341,10 +4342,10 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                                        rna(endtime * 1000, qe, on, membername, qe->parent->autopausebusy);
                                                        if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
                                                                if (qe->parent->timeoutrestart) {
-                                                                       *to = orig;
+                                                                       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);
                                                                }
@@ -4362,9 +4363,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                                        do_hang(o);
                                                        if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
                                                                if (qe->parent->timeoutrestart) {
-                                                                       *to = orig;
+                                                                       start_time_tv = ast_tvnow();
                                                                }
-                                                               if (*to > 500) {
+                                                               if (ast_remaining_ms(start_time_tv, orig) > 500) {
                                                                        ring_one(qe, outgoing, &numbusies);
                                                                        starttime = (long) time(NULL);
                                                                }
@@ -4457,9 +4458,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                        do_hang(o);
                                        if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
                                                if (qe->parent->timeoutrestart) {
-                                                       *to = orig;
+                                                       start_time_tv = ast_tvnow();
                                                }
-                                               if (*to > 500) {
+                                               if (ast_remaining_ms(start_time_tv, orig) > 500) {
                                                        ring_one(qe, outgoing, &numbusies);
                                                        starttime = (long) time(NULL);
                                                }
@@ -4538,10 +4539,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);
                }
 
                if (ast_channel_cdr(in)
index ff233d6..051f97b 100644 (file)
@@ -160,7 +160,6 @@ static int record_exec(struct ast_channel *chan, const char *data)
        int terminator = '#';
        struct ast_format rfmt;
        int ioflags;
-       int waitres;
        struct ast_silence_generator *silgen = NULL;
        struct ast_flags flags = { 0, };
        AST_DECLARE_APP_ARGS(args,
@@ -169,8 +168,11 @@ static int record_exec(struct ast_channel *chan, const char *data)
                AST_APP_ARG(maxduration);
                AST_APP_ARG(options);
        );
+       int ms;
+       struct timeval start;
 
        ast_format_clear(&rfmt);
+
        /* 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");
@@ -331,14 +333,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);
@@ -390,6 +393,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 bd0353b..fc02de3 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 1c68d11..0f0c4d2 100644 (file)
@@ -1046,6 +1046,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 */
 
@@ -1053,12 +1055,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;
@@ -1078,7 +1082,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 b69bf3f..ce2d233 100644 (file)
@@ -6240,6 +6240,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_debug(1, "No frame read on channel %s, going out ...\n", ast_channel_name(chan));
@@ -7411,6 +7412,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;
@@ -7629,6 +7631,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 */
@@ -7645,8 +7648,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 != ast_channel_fd(c0, 0)) ||
@@ -7694,7 +7697,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;
@@ -10756,6 +10759,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", ast_channel_name(chan));
                                dahdi_setlinear(p->subs[idx].dfd, 0);
@@ -10766,10 +10772,12 @@ static void *analog_ss_thread(void *data)
                                 * can drop some of them.
                                 */
                                ast_set_flag(ast_channel_flags(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()
@@ -10789,7 +10797,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 (ast_channel_state(chan) == AST_STATE_RING ||
@@ -10812,6 +10820,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);
@@ -10888,10 +10899,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");
@@ -11019,12 +11033,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");
@@ -11040,7 +11060,7 @@ static void *analog_ss_thread(void *data)
                                        if (f->frametype == AST_FRAME_DTMF) {
                                                dtmfbuf[k++] = f->subclass.integer;
                                                ast_debug(1, "CID got digit '%c'\n", f->subclass.integer);
-                                               res = 2000;
+                                               start = ast_tvnow();
                                        }
                                        ast_frfree(f);
 
index 272ad83..758b2bc 100644 (file)
@@ -5627,6 +5627,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)
@@ -13893,6 +13898,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++) {
@@ -13917,8 +13924,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)
@@ -13929,7 +13937,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 395f1a5..593d033 100644 (file)
@@ -2391,6 +2391,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", ast_channel_name(chan));
 
@@ -2403,10 +2406,12 @@ static void *__analog_ss_thread(void *data)
                                 * can drop some of them.
                                 */
                                ast_set_flag(ast_channel_flags(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()
@@ -2427,7 +2432,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 (ast_channel_state(chan) == AST_STATE_RING ||
@@ -2461,6 +2466,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));
 
@@ -2498,10 +2506,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 93a77ef..e01cead 100644 (file)
@@ -1881,7 +1881,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;
        struct ast_callid *callid;
 
        if ((callid = ast_channel_callid(chan))) {
@@ -1896,7 +1898,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 */
@@ -1922,7 +1929,6 @@ static void *do_idle_thread(void *v_pvt)
                        };
                }
                ast_frfree(f);
-               ms = newms;
        }
        /* Hangup the channel since nothing happend */
        ast_hangup(chan);
index ce9ef7a..0157d76 100644 (file)
@@ -1680,7 +1680,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.
@@ -1690,7 +1690,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 4fa0872..dd68db7 100644 (file)
@@ -152,6 +152,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
  */
 AST_INLINE_API(
index 126e44c..9998225 100644 (file)
@@ -1669,11 +1669,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);
@@ -1683,8 +1685,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;
                }
@@ -3005,12 +3009,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", ast_channel_name(chan), strerror(errno));
@@ -3568,11 +3575,13 @@ 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;
 }
@@ -3628,6 +3637,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(ast_channel_flags(c), AST_FLAG_ZOMBIE) || ast_check_hangup(c))
@@ -3639,19 +3649,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,
@@ -4690,25 +4690,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;
+       struct timeval start = ast_tvnow();
+       int ms;
 
-       while (!done) {
+       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);
        }
@@ -5724,18 +5731,19 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
        if (ast_call(chan, addr, 0)) {  /* ast_call failed... */
                ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, addr);
        } else {
+               struct timeval start = ast_tvnow();
                res = 1;        /* mark success in case chan->_state is already AST_STATE_UP */
                while (timeout && ast_channel_state(chan) != 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(ast_channel_call_forward(chan))) {
                                if (!(chan = ast_call_forward(NULL, chan, NULL, cap, oh, outstate))) {
                                        return NULL;
index 375c232..ba66fca 100644 (file)
@@ -9767,6 +9767,8 @@ static void *async_wait(void *data)
        struct ast_frame *f;
        struct ast_app *app;
        int have_early_media = 0;
+       struct timeval start = ast_tvnow();
+       int ms;
 
        if (chan) {
                struct ast_callid *callid = ast_channel_callid(chan);
@@ -9776,12 +9778,12 @@ static void *async_wait(void *data)
                }
        }
 
-       while (timeout && (ast_channel_state(chan) != AST_STATE_UP)) {
-               res = ast_waitfor(chan, timeout);
+       while ((ms = ast_remaining_ms(start, timeout)) &&
+                       ast_channel_state(chan) != 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 55ca0f2..29c9286 100644 (file)
@@ -949,6 +949,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)) {
@@ -979,7 +980,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 ((ast_format_cmp(ast_channel_rawreadformat(c0), ast_channel_rawwriteformat(c1)) == AST_FORMAT_CMP_NOT_EQUAL) ||
                        (ast_format_cmp(ast_channel_rawreadformat(c1), ast_channel_rawwriteformat(c0)) == AST_FORMAT_CMP_NOT_EQUAL)) {
@@ -1005,8 +1008,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;
                        }
@@ -1145,6 +1149,7 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0,
        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;
 
        if (!oldcap0 || !oldcap1) {
                ast_channel_unlock(c0);
@@ -1189,7 +1194,9 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0,
        cs[0] = c0;
        cs[1] = c1;
        cs[2] = NULL;
+       start = ast_tvnow();
        for (;;) {
+               int ms;
                /* Check if anything changed */
                if ((ast_channel_tech_pvt(c0) != pvt0) ||
                    (ast_channel_tech_pvt(c1) != pvt1) ||
@@ -1284,9 +1291,10 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0,
                        ast_format_cap_copy(oldcap0, cap0);
                }
 
+               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 0ff33cb..3476729 100644 (file)
@@ -1470,6 +1470,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 cc3e23f..32225d3 100644 (file)
@@ -1262,9 +1262,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", ast_channel_name(chan));
        if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0) {
@@ -1273,20 +1275,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", ast_channel_name(chan));
                        return -1;
                }
 
-               if (ms == 0) { /* all done, nothing happened */
-                       ast_debug(1, "channel '%s' timed-out during T.38 shutdown\n", ast_channel_name(chan));
-                       break;
-               }
-
                if (!(frame = ast_read(chan))) {
                        return -1;
                }
@@ -1314,6 +1315,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", ast_channel_name(chan));
+       }
+
        return 0;
 }
 
@@ -1322,7 +1327,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);
@@ -1333,6 +1338,8 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
        struct ast_channel *c = chan;
        struct ast_format orig_write_format;
        struct ast_format orig_read_format;
+       int remaining_time;
+       struct timeval start;
 
        ast_format_clear(&orig_write_format);
        ast_format_clear(&orig_read_format);
@@ -1413,8 +1420,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", ast_channel_name(chan), 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;
 
@@ -1431,7 +1439,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);
@@ -1500,7 +1508,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) {
@@ -1517,36 +1525,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", ast_channel_name(chan));
                                        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", ast_channel_name(chan));
+                               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", ast_channel_name(chan), timeout, ms, res);
+       ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
 
        set_channel_variables(chan, details);
 
@@ -1580,9 +1582,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;
 
        /* don't send any audio if we've already received a T.38 reinvite */
        if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) {
@@ -1592,9 +1596,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", ast_channel_name(chan));
                                ast_playtones_stop(chan);
@@ -1651,7 +1657,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", ast_channel_name(chan));
 
        /* 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);
@@ -1660,13 +1666,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", ast_channel_name(chan));
                        return -1;
                }
-
                if (ms == 0) { /* all done, nothing happened */
                        ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", ast_channel_name(chan));
                        details->caps &= ~AST_FAX_TECH_T38;
@@ -1694,21 +1702,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", ast_channel_name(chan));
                                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", ast_channel_name(chan));
                                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 */
@@ -1976,9 +1987,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;
 
        /* send CNG tone while listening for the receiver to initiate a switch
         * to T.38 mode; if they do, stop sending the CNG tone and proceed with
@@ -1986,7 +1999,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) {
@@ -1996,8 +2009,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", ast_channel_name(chan));
                        ast_playtones_stop(chan);
@@ -2034,13 +2050,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);
@@ -2054,7 +2073,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", ast_channel_name(chan));
 
                /* 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);
@@ -2063,13 +2082,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", ast_channel_name(chan));
                                return -1;
                        }
-
                        if (ms == 0) { /* all done, nothing happened */
                                ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", ast_channel_name(chan));
                                details->caps &= ~AST_FAX_TECH_T38;
@@ -2097,21 +2118,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", ast_channel_name(chan));
                                        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", ast_channel_name(chan));
                                        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 */
@@ -2127,15 +2151,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", ast_channel_name(chan));
                                        ast_playtones_stop(chan);
                                        return -1;
                                }
-
                                if (ms == 0) { /* all done, nothing happened */
                                        break;
                                }
@@ -2166,13 +2192,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);