Resolve issues with choppy sound when using res_timing_pthread.
[asterisk/asterisk.git] / res / res_timing_pthread.c
index 8eea1a0..5e3c313 100644 (file)
@@ -75,7 +75,6 @@ enum {
 enum pthread_timer_state {
        TIMER_STATE_IDLE,
        TIMER_STATE_TICKING,
-       TIMER_STATE_CONTINUOUS,
 };
 
 struct pthread_timer {
@@ -85,13 +84,15 @@ struct pthread_timer {
        /*! Interval in ms for current rate */
        unsigned int interval;
        unsigned int tick_count;
+       unsigned int pending_ticks;
        struct timeval start;
+       unsigned int continuous:1;
 };
 
 static void pthread_timer_destructor(void *obj);
 static struct pthread_timer *find_timer(int handle, int unlinkobj);
-static void write_byte(int wr_fd);
-static void read_pipe(int rd_fd, unsigned int num, int clear);
+static void write_byte(struct pthread_timer *timer);
+static void read_pipe(struct pthread_timer *timer, unsigned int num);
 
 /*!
  * \brief Data for the timing thread
@@ -148,23 +149,6 @@ static void pthread_timer_close(int handle)
        ao2_ref(timer, -1);
 }
 
-static void set_state(struct pthread_timer *timer)
-{
-       unsigned int rate = timer->rate;
-
-       if (rate) {
-               timer->state = TIMER_STATE_TICKING;
-               timer->interval = roundf(1000.0 / ((float) rate));
-               timer->start = ast_tvnow();
-       } else {
-               timer->state = TIMER_STATE_IDLE;
-               timer->interval = 0;
-               timer->start = ast_tv(0, 0);
-       }
-
-       timer->tick_count = 0;
-}
-
 static int pthread_timer_set_rate(int handle, unsigned int rate)
 {
        struct pthread_timer *timer;
@@ -175,17 +159,24 @@ static int pthread_timer_set_rate(int handle, unsigned int rate)
        }
 
        if (rate > MAX_RATE) {
-               ast_log(LOG_ERROR, "res_timing_pthread only supports timers at a max rate of %d / sec\n",
-                       MAX_RATE);
+               ast_log(LOG_ERROR, "res_timing_pthread only supports timers at a "
+                               "max rate of %d / sec\n", MAX_RATE);
                errno = EINVAL;
                return -1;
        }
 
        ao2_lock(timer);
-       timer->rate = rate;
-       if (timer->state != TIMER_STATE_CONTINUOUS) {
-               set_state(timer);
+
+       if ((timer->rate = rate)) {
+               timer->interval = roundf(1000.0 / ((float) rate));
+               timer->start = ast_tvnow();
+               timer->state = TIMER_STATE_TICKING;
+       } else {
+               timer->interval = 0;
+               timer->start = ast_tv(0, 0);
+               timer->state = TIMER_STATE_IDLE;
        }
+       timer->tick_count = 0;
 
        ao2_unlock(timer);
 
@@ -204,12 +195,9 @@ static void pthread_timer_ack(int handle, unsigned int quantity)
                return;
        }
 
-       if (timer->state == TIMER_STATE_CONTINUOUS) {
-               /* Leave the pipe alone, please! */
-               return;
-       }
-
-       read_pipe(timer->pipe[PIPE_READ], quantity, 0);
+       ao2_lock(timer);
+       read_pipe(timer, quantity);
+       ao2_unlock(timer);
 
        ao2_ref(timer, -1);
 }
@@ -224,8 +212,10 @@ static int pthread_timer_enable_continuous(int handle)
        }
 
        ao2_lock(timer);
-       timer->state = TIMER_STATE_CONTINUOUS;
-       write_byte(timer->pipe[PIPE_WRITE]);
+       if (!timer->continuous) {
+               timer->continuous = 1;
+               write_byte(timer);
+       }
        ao2_unlock(timer);
 
        ao2_ref(timer, -1);
@@ -243,8 +233,10 @@ static int pthread_timer_disable_continuous(int handle)
        }
 
        ao2_lock(timer);
-       set_state(timer);
-       read_pipe(timer->pipe[PIPE_READ], 0, 1);
+       if (timer->continuous) {
+               timer->continuous = 0;
+               read_pipe(timer, 1);
+       }
        ao2_unlock(timer);
 
        ao2_ref(timer, -1);
@@ -261,9 +253,11 @@ static enum ast_timer_event pthread_timer_get_event(int handle)
                return res;
        }
 
-       if (timer->state == TIMER_STATE_CONTINUOUS) {
+       ao2_lock(timer);
+       if (timer->continuous && timer->pending_ticks == 1) {
                res = AST_TIMING_EVENT_CONTINUOUS;
        }
+       ao2_unlock(timer);
 
        ao2_ref(timer, -1);
 
@@ -338,7 +332,7 @@ static int check_timer(struct pthread_timer *timer)
 {
        struct timeval now;
 
-       if (timer->state == TIMER_STATE_IDLE || timer->state == TIMER_STATE_CONTINUOUS) {
+       if (timer->state == TIMER_STATE_IDLE) {
                return 0;
        }
 
@@ -347,6 +341,7 @@ static int check_timer(struct pthread_timer *timer)
        if (timer->tick_count < (ast_tvdiff_ms(now, timer->start) / timer->interval)) {
                timer->tick_count++;
                if (!timer->tick_count) {
+                       /* Handle overflow. */
                        timer->start = now;
                }
                return 1;
@@ -355,13 +350,16 @@ static int check_timer(struct pthread_timer *timer)
        return 0;
 }
 
-static void read_pipe(int rd_fd, unsigned int quantity, int clear)
+/*!
+ * \internal
+ * \pre timer is locked
+ */
+static void read_pipe(struct pthread_timer *timer, unsigned int quantity)
 {
-       ast_assert(quantity || clear);
+       int rd_fd = timer->pipe[PIPE_READ];
 
-       if (!quantity && clear) {
-               quantity = 1;
-       }
+       ast_assert(quantity);
+       ast_assert(quantity >= timer->pending_ticks);
 
        do {
                unsigned char buf[1024];
@@ -376,6 +374,8 @@ static void read_pipe(int rd_fd, unsigned int quantity, int clear)
                FD_SET(rd_fd, &rfds);
 
                if (select(rd_fd + 1, &rfds, NULL, NULL, &timeout) != 1) {
+                       ast_debug(1, "Reading not available on timing pipe, "
+                                       "quantity: %u\n", quantity);
                        break;
                }
 
@@ -386,33 +386,35 @@ static void read_pipe(int rd_fd, unsigned int quantity, int clear)
                        if (errno == EAGAIN) {
                                continue;
                        }
-                       ast_log(LOG_ERROR, "read failed on timing pipe: %s\n", strerror(errno));
+                       ast_log(LOG_ERROR, "read failed on timing pipe: %s\n",
+                                       strerror(errno));
                        break;
                }
 
-               if (clear) {
-                       continue;
-               }
-
                quantity -= res;
+               timer->pending_ticks -= res;
        } while (quantity);
 }
 
-static void write_byte(int wr_fd)
+/*!
+ * \internal
+ * \pre timer is locked
+ */
+static void write_byte(struct pthread_timer *timer)
 {
-       do {
-               ssize_t res;
-               unsigned char x = 42;
+       ssize_t res;
+       unsigned char x = 42;
 
-               res = write(wr_fd, &x, 1);
+       do {
+               res = write(timer->pipe[PIPE_WRITE], &x, 1);
+       } while (res == -1 && errno == EAGAIN);
 
-               if (res == -1) {
-                       if (errno == EAGAIN) {
-                               continue;
-                       }
-                       ast_log(LOG_ERROR, "Error writing to timing pipe: %s\n", strerror(errno));
-               }
-       } while (0);
+       if (res == -1) {
+               ast_log(LOG_ERROR, "Error writing to timing pipe: %s\n",
+                               strerror(errno));
+       } else {
+               timer->pending_ticks++;
+       }
 }
 
 static int run_timer(void *obj, void *arg, int flags)
@@ -424,11 +426,9 @@ static int run_timer(void *obj, void *arg, int flags)
        }
 
        ao2_lock(timer);
-
        if (check_timer(timer)) {
-               write_byte(timer->pipe[PIPE_WRITE]);
+               write_byte(timer);
        }
-
        ao2_unlock(timer);
 
        return 0;