minor code cleanup
[asterisk/asterisk.git] / sched.c
diff --git a/sched.c b/sched.c
old mode 100755 (executable)
new mode 100644 (file)
index a6a2ca5..56a16bd
--- a/sched.c
+++ b/sched.c
@@ -1,14 +1,26 @@
 /*
- * Asterisk
- * 
- * Mark Spencer <markster@marko.net>
+ * Asterisk -- An open source telephony toolkit.
  *
- * Copyright(C) 1999, Adtran, Inc.
- * 
- * Distributed under the terms of the GNU General Public License (GPL) Version 2
+ * Copyright (C) 1999 - 2005, Digium, Inc.
  *
- * Scheduler Routines (form cheops-NG)
+ * Mark Spencer <markster@digium.com>
  *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Scheduler Routines (from cheops-NG)
+ *
+ * \author Mark Spencer <markster@digium.com>
  */
 
 #ifdef DEBUG_SCHEDULER
 #include <stdlib.h>
 #include <sys/time.h>
 #include <unistd.h>
-#include <asterisk/sched.h>
-#include <asterisk/logger.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/sched.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
 
 /* Determine if a is sooner than b */
 #define SOONER(a,b) (((b).tv_sec > (a).tv_sec) || \
                                         (((b).tv_sec == (a).tv_sec) && ((b).tv_usec > (a).tv_usec)))
 
 struct sched {
-       struct sched *next;                             /* Next event in the list */
-       int id;                                                 /* ID number of event */
-       struct timeval when;                    /* Absolute time event should take place */
-       int resched;                                    /* When to reschedule */
-       void *data;                                     /* Data */
-       ast_sched_cb callback;          /* Callback */
+       struct sched *next;             /*!< Next event in the list */
+       int id;                         /*!< ID number of event */
+       struct timeval when;            /*!< Absolute time event should take place */
+       int resched;                    /*!< When to reschedule */
+       int variable;                   /*!< Use return value from callback to reschedule */
+       void *data;                     /*!< Data */
+       ast_sched_cb callback;          /*!< Callback */
 };
 
 struct sched_context {
-       /* Number of events processed */
-       int eventcnt;
-
-       /* Number of outstanding schedule events */
-       int schedcnt;
-
-       /* Schedule entry and main queue */
-       struct sched *schedq;
+       ast_mutex_t lock;
+       int eventcnt;                   /*!< Number of events processed */
+       int schedcnt;                   /*!< Number of outstanding schedule events */
+       struct sched *schedq;           /*!< Schedule entry and main queue */
 
 #ifdef SCHED_MAX_CACHE
-       /* Cache of unused schedule structures and how many */
-       struct sched *schedc;
+       struct sched *schedc;           /*!< Cache of unused schedule structures and how many */
        int schedccnt;
 #endif
 };
@@ -57,20 +74,21 @@ struct sched_context {
 struct sched_context *sched_context_create(void)
 {
        struct sched_context *tmp;
-       tmp = malloc(sizeof(struct sched_context));
-       if (tmp) {
-               tmp->eventcnt = 1;
-               tmp->schedcnt = 0;
-               tmp->schedq = NULL;
-               tmp->schedc = NULL;
-               tmp->schedccnt = 0;
-       }
+
+       if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+               return NULL;
+
+       ast_mutex_init(&tmp->lock);
+       tmp->eventcnt = 1;
+       
        return tmp;
 }
 
 void sched_context_destroy(struct sched_context *con)
 {
        struct sched *s, *sl;
+       ast_mutex_lock(&con->lock);
+#ifdef SCHED_MAX_CACHE
        /* Eliminate the cache */
        s = con->schedc;
        while(s) {
@@ -78,6 +96,7 @@ void sched_context_destroy(struct sched_context *con)
                s = s->next;
                free(sl);
        }
+#endif
        /* And the queue */
        s = con->schedq;
        while(s) {
@@ -86,6 +105,8 @@ void sched_context_destroy(struct sched_context *con)
                free(sl);
        }
        /* And the context */
+       ast_mutex_unlock(&con->lock);
+       ast_mutex_destroy(&con->lock);
        free(con);
 }
 
@@ -124,37 +145,35 @@ static void sched_release(struct sched_context *con, struct sched *tmp)
                free(tmp);
 }
 
+/*! \brief
+ * Return the number of milliseconds 
+ * until the next scheduled event
+ */
 int ast_sched_wait(struct sched_context *con)
 {
-       /*
-        * Return the number of milliseconds 
-        * until the next scheduled event
-        */
-       struct timeval tv;
        int ms;
        DEBUG(ast_log(LOG_DEBUG, "ast_sched_wait()\n"));
-       if (!con->schedq)
-               return -1;
-       if (gettimeofday(&tv, NULL) < 0) {
-               /* This should never happen */
-               return 0;
-       };
-       ms = (con->schedq->when.tv_sec - tv.tv_sec) * 1000;
-       ms += (con->schedq->when.tv_usec - tv.tv_usec) / 1000;
-       if (ms < 0)
-               ms = 0;
+       ast_mutex_lock(&con->lock);
+       if (!con->schedq) {
+               ms = -1;
+       } else {
+               ms = ast_tvdiff_ms(con->schedq->when, ast_tvnow());
+               if (ms < 0)
+                       ms = 0;
+       }
+       ast_mutex_unlock(&con->lock);
        return ms;
        
 }
 
 
+/*! \brief
+ * Take a sched structure and put it in the
+ * queue, such that the soonest event is
+ * first in the list. 
+ */
 static void schedule(struct sched_context *con, struct sched *s)
 {
-       /*
-        * Take a sched structure and put it in the
-        * queue, such that the soonest event is
-        * first in the list. 
-        */
         
        struct sched *last=NULL;
        struct sched *current=con->schedq;
@@ -173,58 +192,77 @@ static void schedule(struct sched_context *con, struct sched *s)
        con->schedcnt++;
 }
 
-static inline int sched_settime(struct timeval *tv, int when)
+/*! \brief
+ * given the last event *tv and the offset in milliseconds 'when',
+ * computes the next value,
+ */
+static int sched_settime(struct timeval *tv, int when)
 {
-       if (gettimeofday(tv, NULL) < 0) {
-                       /* This shouldn't ever happen, but let's be sure */
-                       ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
-                       return -1;
-       }
-       tv->tv_sec += when/1000;
-       tv->tv_usec += (when % 1000) * 1000;
-       if (tv->tv_usec > 1000000) {
-               tv->tv_sec++;
-               tv->tv_usec-= 1000000;
+       struct timeval now = ast_tvnow();
+
+       /*ast_log(LOG_DEBUG, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
+       if (ast_tvzero(*tv))    /* not supplied, default to now */
+               *tv = now;
+       *tv = ast_tvadd(*tv, ast_samp2tv(when, 1000));
+       if (ast_tvcmp(*tv, now) < 0) {
+               ast_log(LOG_DEBUG, "Request to schedule in the past?!?!\n");
+               *tv = now;
        }
        return 0;
 }
 
-int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data)
+
+/*! \brief
+ * Schedule callback(data) to happen when ms into the future
+ */
+int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, void *data, int variable)
 {
-       /*
-        * Schedule callback(data) to happen when ms into the future
-        */
        struct sched *tmp;
+       int res = -1;
        DEBUG(ast_log(LOG_DEBUG, "ast_sched_add()\n"));
        if (!when) {
-               ast_log(LOG_NOTICE, "Scheduled event in 0 ms?");
+               ast_log(LOG_NOTICE, "Scheduled event in 0 ms?\n");
                return -1;
        }
+       ast_mutex_lock(&con->lock);
        if ((tmp = sched_alloc(con))) {
                tmp->id = con->eventcnt++;
                tmp->callback = callback;
                tmp->data = data;
                tmp->resched = when;
+               tmp->variable = variable;
+               tmp->when = ast_tv(0, 0);
                if (sched_settime(&tmp->when, when)) {
                        sched_release(con, tmp);
-                       return -1;
-               } else
+               } else {
                        schedule(con, tmp);
-       } else 
-               return -1;
-       return tmp->id;
+                       res = tmp->id;
+               }
+       }
+#ifdef DUMP_SCHEDULER
+       /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
+       ast_sched_dump(con);
+#endif
+       ast_mutex_unlock(&con->lock);
+       return res;
 }
 
+int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data)
+{
+       return ast_sched_add_variable(con, when, callback, data, 0);
+}
+
+/*! \brief
+ * Delete the schedule entry with number
+ * "id".  It's nearly impossible that there
+ * would be two or more in the list with that
+ * id.
+ */
 int ast_sched_del(struct sched_context *con, int id)
 {
-       /*
-        * Delete the schedule entry with number
-        * "id".  It's nearly impossible that there
-        * would be two or more in the list with that
-        * id.
-        */
        struct sched *last=NULL, *s;
        DEBUG(ast_log(LOG_DEBUG, "ast_sched_del()\n"));
+       ast_mutex_lock(&con->lock);
        s = con->schedq;
        while(s) {
                if (s->id == id) {
@@ -233,75 +271,80 @@ int ast_sched_del(struct sched_context *con, int id)
                        else
                                con->schedq = s->next;
                        con->schedcnt--;
-                       return 0;
+                       sched_release(con, s);
+                       break;
                }
                last = s;
                s = s->next;
        }
-       ast_log(LOG_NOTICE, "Attempted to delete non-existant schedule entry %d!\n", id);
-#ifdef FORCE_CRASH
-       crash();
+#ifdef DUMP_SCHEDULER
+       /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
+       ast_sched_dump(con);
 #endif
-       return -1;
+       ast_mutex_unlock(&con->lock);
+       if (!s) {
+               ast_log(LOG_NOTICE, "Attempted to delete nonexistent schedule entry %d!\n", id);
+#ifdef DO_CRASH
+               CRASH;
+#endif
+               return -1;
+       } else
+               return 0;
 }
 
-void ast_sched_dump(struct sched_context *con)
+void ast_sched_dump(const struct sched_context *con)
 {
        /*
         * Dump the contents of the scheduler to
         * stderr
         */
        struct sched *q;
-       struct timeval tv;
-       time_t s, ms;
-       gettimeofday(&tv, NULL);
-       ast_log(LOG_DEBUG, "Cheops Schedule Dump (%d in Q, %d Total, %d Cache)\n", 
-                                                        con-> schedcnt, con->eventcnt - 1, con->schedccnt);
-       ast_log(LOG_DEBUG, "=================================================\n");
-       ast_log(LOG_DEBUG, "|ID    Callback    Data        Time  (sec:ms)   |\n");
-       ast_log(LOG_DEBUG, "+-----+-----------+-----------+-----------------+\n");
-       q = con->schedq;
-       while(q) {
-               s =  q->when.tv_sec - tv.tv_sec;
-               ms = q->when.tv_usec - tv.tv_usec;
-               if (ms < 0) {
-                       ms += 1000000;
-                       s--;
-               }
-               ast_log(LOG_DEBUG, "|%.4d | %p | %p | %.6ld : %.6ld |\n", 
-                               q->id,
-                               q->callback,
-                               q->data,
-                               s,
-                               ms);
-               q=q->next;
+       struct timeval tv = ast_tvnow();
+#ifdef SCHED_MAX_CACHE
+       ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt);
+#else
+       ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total)\n", con->schedcnt, con->eventcnt - 1);
+#endif
+
+       ast_log(LOG_DEBUG, "=============================================================\n");
+       ast_log(LOG_DEBUG, "|ID    Callback          Data              Time  (sec:ms)   |\n");
+       ast_log(LOG_DEBUG, "+-----+-----------------+-----------------+-----------------+\n");
+       for (q = con->schedq; q; q = q->next) {
+               struct timeval delta =  ast_tvsub(q->when, tv);
+
+               ast_log(LOG_DEBUG, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n", 
+                       q->id,
+                       q->callback,
+                       q->data,
+                       delta.tv_sec,
+                       (long int)delta.tv_usec);
        }
-       ast_log(LOG_DEBUG, "=================================================\n");
+       ast_log(LOG_DEBUG, "=============================================================\n");
        
 }
 
+/*! \brief
+ * Launch all events which need to be run at this time.
+ */
 int ast_sched_runq(struct sched_context *con)
 {
-       /*
-        * Launch all events which need to be run at this time.
-        */
        struct sched *current;
        struct timeval tv;
        int x=0;
+       int res;
        DEBUG(ast_log(LOG_DEBUG, "ast_sched_runq()\n"));
                
+       ast_mutex_lock(&con->lock);
        for(;;) {
                if (!con->schedq)
                        break;
-               if (gettimeofday(&tv, NULL)) {
-                       /* This should never happen */
-                       ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
-                       return 0;
-               }
-               /* We only care about millisecond accuracy anyway, so this will
-                  help us get more than one event at one time if they are very
-                  close together. */
-               tv.tv_usec += 1000;
+               
+               /* schedule all events which are going to expire within 1ms.
+                * We only care about millisecond accuracy anyway, so this will
+                * help us get more than one event at one time if they are very
+                * close together.
+                */
+               tv = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
                if (SOONER(con->schedq->when, tv)) {
                        current = con->schedq;
                        con->schedq = con->schedq->next;
@@ -315,12 +358,17 @@ int ast_sched_runq(struct sched_context *con)
                         * the schedule queue.  If that's what it wants to do, it 
                         * should return 0.
                         */
-                       if (current->callback(current->data)) {
+                       
+                       ast_mutex_unlock(&con->lock);
+                       res = current->callback(current->data);
+                       ast_mutex_lock(&con->lock);
+                       
+                       if (res) {
                                /*
                                 * If they return non-zero, we should schedule them to be
                                 * run again.
                                 */
-                               if (sched_settime(&current->when, current->resched)) {
+                               if (sched_settime(&current->when, current->variable? res : current->resched)) {
                                        sched_release(con, current);
                                } else
                                        schedule(con, current);
@@ -332,5 +380,26 @@ int ast_sched_runq(struct sched_context *con)
                } else
                        break;
        }
+       ast_mutex_unlock(&con->lock);
        return x;
 }
+
+long ast_sched_when(struct sched_context *con,int id)
+{
+       struct sched *s;
+       long secs;
+       DEBUG(ast_log(LOG_DEBUG, "ast_sched_when()\n"));
+
+       ast_mutex_lock(&con->lock);
+       for (s = con->schedq; s; s = s->next) {
+               if (s->id == id)
+                       break;
+       }
+       secs=-1;
+       if (s!=NULL) {
+               struct timeval now = ast_tvnow();
+               secs = s->when.tv_sec - now.tv_sec;
+       }
+       ast_mutex_unlock(&con->lock);
+       return secs;
+}