res_fax.c: Add chan locked precondition comments.
[asterisk/asterisk.git] / res / res_timing_pthread.c
index 8cb2898..6476e74 100644 (file)
  * \brief pthread timing interface
  */
 
+/*** MODULEINFO
+       <support_level>extended</support_level>
+ ***/
+
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
+ASTERISK_REGISTER_FILE();
 
+#include <stdbool.h>
 #include <math.h>
-#include <sys/select.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include "asterisk/module.h"
 #include "asterisk/timing.h"
@@ -36,18 +42,18 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 #include "asterisk/astobj2.h"
 #include "asterisk/time.h"
 #include "asterisk/lock.h"
-#include "asterisk/poll-compat.h"
 
 static void *timing_funcs_handle;
 
-static int pthread_timer_open(void);
-static void pthread_timer_close(int handle);
-static int pthread_timer_set_rate(int handle, unsigned int rate);
-static void pthread_timer_ack(int handle, unsigned int quantity);
-static int pthread_timer_enable_continuous(int handle);
-static int pthread_timer_disable_continuous(int handle);
-static enum ast_timer_event pthread_timer_get_event(int handle);
-static unsigned int pthread_timer_get_max_rate(int handle);
+static void *pthread_timer_open(void);
+static void pthread_timer_close(void *data);
+static int pthread_timer_set_rate(void *data, unsigned int rate);
+static int pthread_timer_ack(void *data, unsigned int quantity);
+static int pthread_timer_enable_continuous(void *data);
+static int pthread_timer_disable_continuous(void *data);
+static enum ast_timer_event pthread_timer_get_event(void *data);
+static unsigned int pthread_timer_get_max_rate(void *data);
+static int pthread_timer_fd(void *data);
 
 static struct ast_timing_interface pthread_timing = {
        .name = "pthread",
@@ -60,6 +66,7 @@ static struct ast_timing_interface pthread_timing = {
        .timer_disable_continuous = pthread_timer_disable_continuous,
        .timer_get_event = pthread_timer_get_event,
        .timer_get_max_rate = pthread_timer_get_max_rate,
+       .timer_fd = pthread_timer_fd,
 };
 
 /* 1 tick / 10 ms */
@@ -87,13 +94,14 @@ struct pthread_timer {
        unsigned int tick_count;
        unsigned int pending_ticks;
        struct timeval start;
-       unsigned int continuous:1;
+       bool continuous:1;
+       bool pipe_signaled:1;
 };
 
 static void pthread_timer_destructor(void *obj);
-static struct pthread_timer *find_timer(int handle, int unlinkobj);
-static void write_byte(struct pthread_timer *timer);
-static void read_pipe(struct pthread_timer *timer, unsigned int num);
+static void signal_pipe(struct pthread_timer *timer);
+static void unsignal_pipe(struct pthread_timer *timer);
+static void ack_ticks(struct pthread_timer *timer, unsigned int num);
 
 /*!
  * \brief Data for the timing thread
@@ -105,14 +113,14 @@ static struct {
        unsigned int stop:1;
 } timing_thread;
 
-static int pthread_timer_open(void)
+static void *pthread_timer_open(void)
 {
        struct pthread_timer *timer;
-       int fd;
+       int i;
 
        if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) {
                errno = ENOMEM;
-               return -1;
+               return NULL;
        }
 
        timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1;
@@ -120,44 +128,38 @@ static int pthread_timer_open(void)
 
        if (pipe(timer->pipe)) {
                ao2_ref(timer, -1);
-               return -1;
+               return NULL;
        }
 
+       for (i = 0; i < ARRAY_LEN(timer->pipe); ++i) {
+               int flags = fcntl(timer->pipe[i], F_GETFL);
+               flags |= O_NONBLOCK;
+               fcntl(timer->pipe[i], F_SETFL, flags);
+       }
+       
        ao2_lock(pthread_timers);
        if (!ao2_container_count(pthread_timers)) {
                ast_mutex_lock(&timing_thread.lock);
                ast_cond_signal(&timing_thread.cond);
                ast_mutex_unlock(&timing_thread.lock);
        }
-       ao2_link(pthread_timers, timer);
+       ao2_link_flags(pthread_timers, timer, OBJ_NOLOCK);
        ao2_unlock(pthread_timers);
 
-       fd = timer->pipe[PIPE_READ];
-
-       ao2_ref(timer, -1);
-
-       return fd;
+       return timer;
 }
 
-static void pthread_timer_close(int handle)
+static void pthread_timer_close(void *data)
 {
-       struct pthread_timer *timer;
-
-       if (!(timer = find_timer(handle, 1))) {
-               return;
-       }
+       struct pthread_timer *timer = data;
 
+       ao2_unlink(pthread_timers, timer);
        ao2_ref(timer, -1);
 }
 
-static int pthread_timer_set_rate(int handle, unsigned int rate)
+static int pthread_timer_set_rate(void *data, unsigned int rate)
 {
-       struct pthread_timer *timer;
-
-       if (!(timer = find_timer(handle, 0))) {
-               errno = EINVAL;
-               return -1;
-       }
+       struct pthread_timer *timer = data;
 
        if (rate > MAX_RATE) {
                ast_log(LOG_ERROR, "res_timing_pthread only supports timers at a "
@@ -181,113 +183,74 @@ static int pthread_timer_set_rate(int handle, unsigned int rate)
 
        ao2_unlock(timer);
 
-       ao2_ref(timer, -1);
-
        return 0;
 }
 
-static void pthread_timer_ack(int handle, unsigned int quantity)
+static int pthread_timer_ack(void *data, unsigned int quantity)
 {
-       struct pthread_timer *timer;
+       struct pthread_timer *timer = data;
 
        ast_assert(quantity > 0);
 
-       if (!(timer = find_timer(handle, 0))) {
-               return;
-       }
-
        ao2_lock(timer);
-       read_pipe(timer, quantity);
+       ack_ticks(timer, quantity);
        ao2_unlock(timer);
 
-       ao2_ref(timer, -1);
+       return 0;
 }
 
-static int pthread_timer_enable_continuous(int handle)
+static int pthread_timer_enable_continuous(void *data)
 {
-       struct pthread_timer *timer;
-
-       if (!(timer = find_timer(handle, 0))) {
-               errno = EINVAL;
-               return -1;
-       }
+       struct pthread_timer *timer = data;
 
        ao2_lock(timer);
        if (!timer->continuous) {
-               timer->continuous = 1;
-               write_byte(timer);
+               timer->continuous = true;
+               signal_pipe(timer);
        }
        ao2_unlock(timer);
 
-       ao2_ref(timer, -1);
-
        return 0;
 }
 
-static int pthread_timer_disable_continuous(int handle)
+static int pthread_timer_disable_continuous(void *data)
 {
-       struct pthread_timer *timer;
-
-       if (!(timer = find_timer(handle, 0))) {
-               errno = EINVAL;
-               return -1;
-       }
+       struct pthread_timer *timer = data;
 
        ao2_lock(timer);
        if (timer->continuous) {
-               timer->continuous = 0;
-               read_pipe(timer, 1);
+               timer->continuous = false;
+               unsignal_pipe(timer);
        }
        ao2_unlock(timer);
 
-       ao2_ref(timer, -1);
-
        return 0;
 }
 
-static enum ast_timer_event pthread_timer_get_event(int handle)
+static enum ast_timer_event pthread_timer_get_event(void *data)
 {
-       struct pthread_timer *timer;
+       struct pthread_timer *timer = data;
        enum ast_timer_event res = AST_TIMING_EVENT_EXPIRED;
 
-       if (!(timer = find_timer(handle, 0))) {
-               return res;
-       }
-
        ao2_lock(timer);
-       if (timer->continuous && timer->pending_ticks == 1) {
+       if (timer->continuous) {
                res = AST_TIMING_EVENT_CONTINUOUS;
        }
        ao2_unlock(timer);
 
-       ao2_ref(timer, -1);
-
        return res;
 }
 
-static unsigned int pthread_timer_get_max_rate(int handle)
+static unsigned int pthread_timer_get_max_rate(void *data)
 {
        return MAX_RATE;
 }
 
-static struct pthread_timer *find_timer(int handle, int unlinkobj)
+static int pthread_timer_fd(void *data)
 {
-       struct pthread_timer *timer;
-       struct pthread_timer tmp_timer;
-       int flags = OBJ_POINTER;
+       struct pthread_timer *timer = data;
 
-       tmp_timer.pipe[PIPE_READ] = handle;
-
-       if (unlinkobj) {
-               flags |= OBJ_UNLINK;
-       }
-
-       if (!(timer = ao2_find(pthread_timers, &tmp_timer, flags))) {
-               ast_assert(timer != NULL);
-               return NULL;
-       }
-
-       return timer;
+       return timer->pipe[PIPE_READ];
 }
 
 static void pthread_timer_destructor(void *obj)
@@ -355,17 +318,12 @@ static int check_timer(struct pthread_timer *timer)
  * \internal
  * \pre timer is locked
  */
-static void read_pipe(struct pthread_timer *timer, unsigned int quantity)
+static void ack_ticks(struct pthread_timer *timer, unsigned int quantity)
 {
-       int rd_fd = timer->pipe[PIPE_READ];
        int pending_ticks = timer->pending_ticks;
 
        ast_assert(quantity);
 
-       if (timer->continuous && pending_ticks) {
-               pending_ticks--;
-       }
-
        if (quantity > pending_ticks) {
                quantity = pending_ticks;
        }
@@ -374,55 +332,54 @@ static void read_pipe(struct pthread_timer *timer, unsigned int quantity)
                return;
        }
 
-       do {
-               unsigned char buf[1024];
-               ssize_t res;
-               struct pollfd pfd = {
-                       .fd = rd_fd,
-                       .events = POLLIN,
-               };
-
-               if (ast_poll(&pfd, 1, 0) != 1) {
-                       ast_debug(1, "Reading not available on timing pipe, "
-                                       "quantity: %u\n", quantity);
-                       break;
-               }
-
-               res = read(rd_fd, buf,
-                       (quantity < sizeof(buf)) ? quantity : sizeof(buf));
-
-               if (res == -1) {
-                       if (errno == EAGAIN) {
-                               continue;
-                       }
-                       ast_log(LOG_ERROR, "read failed on timing pipe: %s\n",
-                                       strerror(errno));
-                       break;
-               }
+       timer->pending_ticks -= quantity;
 
-               quantity -= res;
-               timer->pending_ticks -= res;
-       } while (quantity);
+       if ((0 == timer->pending_ticks) && !timer->continuous) {
+               unsignal_pipe(timer);
+       }
 }
 
 /*!
  * \internal
  * \pre timer is locked
  */
-static void write_byte(struct pthread_timer *timer)
+static void signal_pipe(struct pthread_timer *timer)
 {
        ssize_t res;
        unsigned char x = 42;
 
-       do {
-               res = write(timer->pipe[PIPE_WRITE], &x, 1);
-       } while (res == -1 && errno == EAGAIN);
+       if (timer->pipe_signaled) {
+               return;
+       }
 
-       if (res == -1) {
+       res = write(timer->pipe[PIPE_WRITE], &x, 1);
+       if (-1 == res) {
                ast_log(LOG_ERROR, "Error writing to timing pipe: %s\n",
                                strerror(errno));
        } else {
-               timer->pending_ticks++;
+               timer->pipe_signaled = true;
+       }
+}
+
+/*!
+ * \internal
+ * \pre timer is locked
+ */
+static void unsignal_pipe(struct pthread_timer *timer)
+{
+       ssize_t res;
+       unsigned long buffer;
+
+       if (!timer->pipe_signaled) {
+               return;
+       }
+
+       res = read(timer->pipe[PIPE_READ], &buffer, sizeof(buffer));
+       if (-1 == res) {
+               ast_log(LOG_ERROR, "Error reading from pipe: %s\n",
+                               strerror(errno));
+       } else {
+               timer->pipe_signaled = false;
        }
 }
 
@@ -436,7 +393,8 @@ static int run_timer(void *obj, void *arg, int flags)
 
        ao2_lock(timer);
        if (check_timer(timer)) {
-               write_byte(timer);
+               timer->pending_ticks++;
+               signal_pipe(timer);
        }
        ao2_unlock(timer);
 
@@ -519,7 +477,8 @@ static int unload_module(void)
        return res;
 }
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "pthread Timing Interface",
-               .load = load_module,
-               .unload = unload_module,
-               .load_pri = AST_MODPRI_CHANNEL_DEPEND,
-               );
+       .support_level = AST_MODULE_SUPPORT_EXTENDED,
+       .load = load_module,
+       .unload = unload_module,
+       .load_pri = AST_MODPRI_TIMING,
+);