Merge "translate: Skip matrix_rebuild during shutdown."
[asterisk/asterisk.git] / main / sched.c
index 8f9e84b..a4ca260 100644 (file)
@@ -30,8 +30,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_REGISTER_FILE()
-
 #ifdef DEBUG_SCHEDULER
 #define DEBUG(a) do { \
        if (option_debug) \
@@ -83,6 +81,14 @@ struct sched {
        /*! The ID that has been popped off the scheduler context's queue */
        struct sched_id *sched_id;
        struct timeval when;          /*!< Absolute time event should take place */
+       /*!
+        * \brief Tie breaker in case the when is the same for multiple entries.
+        *
+        * \note The oldest expiring entry in the scheduler heap goes first.
+        * This is possible when multiple events are scheduled to expire at
+        * the same time by internal coding.
+        */
+       unsigned int tie_breaker;
        int resched;                  /*!< When to reschedule */
        int variable;                 /*!< Use return value from callback to reschedule */
        const void *data;             /*!< Data */
@@ -107,6 +113,8 @@ struct ast_sched_context {
        ast_mutex_t lock;
        unsigned int eventcnt;                  /*!< Number of events processed */
        unsigned int highwater;                                 /*!< highest count so far */
+       /*! Next tie breaker in case events expire at the same time. */
+       unsigned int tie_breaker;
        struct ast_heap *sched_heap;
        struct sched_thread *sched_thread;
        /*! The scheduled task that is currently executing */
@@ -213,9 +221,17 @@ int ast_sched_start_thread(struct ast_sched_context *con)
        return 0;
 }
 
-static int sched_time_cmp(void *a, void *b)
+static int sched_time_cmp(void *va, void *vb)
 {
-       return ast_tvcmp(((struct sched *) b)->when, ((struct sched *) a)->when);
+       struct sched *a = va;
+       struct sched *b = vb;
+       int cmp;
+
+       cmp = ast_tvcmp(b->when, a->when);
+       if (!cmp) {
+               cmp = b->tie_breaker - a->tie_breaker;
+       }
+       return cmp;
 }
 
 struct ast_sched_context *ast_sched_context_create(void)
@@ -315,9 +331,16 @@ static int add_ids(struct ast_sched_context *con)
                if (!new_id) {
                        break;
                }
-               new_id->id = i;
+
+               /*
+                * According to the API doxygen a sched ID of 0 is valid.
+                * Unfortunately, 0 was never returned historically and
+                * several users incorrectly coded usage of the returned
+                * sched ID assuming that 0 was invalid.
+                */
+               new_id->id = ++con->id_queue_size;
+
                AST_LIST_INSERT_TAIL(&con->id_queue, new_id, list);
-               ++con->id_queue_size;
        }
 
        return con->id_queue_size - original_size;
@@ -435,11 +458,28 @@ int ast_sched_wait(struct ast_sched_context *con)
  */
 static void schedule(struct ast_sched_context *con, struct sched *s)
 {
-       ast_heap_push(con->sched_heap, s);
+       size_t size;
+
+       size = ast_heap_size(con->sched_heap);
 
-       if (ast_heap_size(con->sched_heap) > con->highwater) {
-               con->highwater = ast_heap_size(con->sched_heap);
+       /* Record the largest the scheduler heap became for reporting purposes. */
+       if (con->highwater <= size) {
+               con->highwater = size + 1;
        }
+
+       /* Determine the tie breaker value for the new entry. */
+       if (size) {
+               ++con->tie_breaker;
+       } else {
+               /*
+                * Restart the sequence for the first entry to make integer
+                * roll over more unlikely.
+                */
+               con->tie_breaker = 0;
+       }
+       s->tie_breaker = con->tie_breaker;
+
+       ast_heap_push(con->sched_heap, s);
 }
 
 /*! \brief
@@ -450,6 +490,17 @@ static int sched_settime(struct timeval *t, int when)
 {
        struct timeval now = ast_tvnow();
 
+       if (when < 0) {
+               /*
+                * A negative when value is likely a bug as it
+                * represents a VERY large timeout time.
+                */
+               ast_log(LOG_WARNING,
+                       "Bug likely: Negative time interval %d (interpreted as %u ms) requested!\n",
+                       when, (unsigned int) when);
+               ast_assert(0);
+       }
+
        /*ast_debug(1, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
        if (ast_tvzero(*t))     /* not supplied, default to now */
                *t = now;