Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:56 2009

## Asterisk developer's documentation

- Main Page
- Related Pages
- Modules
- Data Structures
- Files
- Directories

# sched.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999 - 2005, Digium, Inc.
    00005  *
    00006  * Mark Spencer <markster@digium.com>
    00007  *
    00008  * See http://www.asterisk.org for more information about
    00009  * the Asterisk project. Please do not directly contact
    00010  * any of the maintainers of this project for assistance;
    00011  * the project provides a web site, mailing lists and IRC
    00012  * channels for your use.
    00013  *
    00014  * This program is free software, distributed under the terms of
    00015  * the GNU General Public License Version 2. See the LICENSE file
    00016  * at the top of the source tree.
    00017  */
    00018
    00019 /*! \file
    00020  *
    00021  * \brief Scheduler Routines (from cheops-NG)
    00022  *
    00023  */
    00024
    00025 #ifdef DEBUG_SCHEDULER
    00026 #define DEBUG(a) DEBUG_M(a)
    00027 #else
    00028 #define DEBUG(a)
    00029 #endif
    00030
    00031 #include <stdio.h>
    00032 #include <stdlib.h>
    00033 #include <sys/time.h>
    00034 #include <unistd.h>
    00035 #include <string.h>
    00036
    00037 #include "asterisk.h"
    00038
    00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
    00040
    00041 #include "asterisk/sched.h"
    00042 #include "asterisk/logger.h"
    00043 #include "asterisk/channel.h"
    00044 #include "asterisk/lock.h"
    00045 #include "asterisk/utils.h"
    00046
    00047 /* Determine if a is sooner than b */
    00048 #define SOONER(a,b) (((b).tv_sec > (a).tv_sec) || \
    00049                 (((b).tv_sec == (a).tv_sec) && ((b).tv_usec > (a).tv_usec)))
    00050
    00051 struct sched {
    00052    struct sched *next;     /* Next event in the list */
    00053    int id;        /* ID number of event */
    00054    struct timeval when;    /* Absolute time event should take place */
    00055    int resched;         /* When to reschedule */
    00056    int variable;     /* Use return value from callback to reschedule */
    00057    void *data;          /* Data */
    00058    ast_sched_cb callback;     /* Callback */
    00059 };
    00060
    00061 struct sched_context {
    00062    ast_mutex_t lock;
    00063    /* Number of events processed */
    00064    int eventcnt;
    00065
    00066    /* Number of outstanding schedule events */
    00067    int schedcnt;
    00068
    00069    /* Schedule entry and main queue */
    00070    struct sched *schedq;
    00071
    00072 #ifdef SCHED_MAX_CACHE
    00073    /* Cache of unused schedule structures and how many */
    00074    struct sched *schedc;
    00075    int schedccnt;
    00076 #endif
    00077 };
    00078
    00079 struct sched_context *sched_context_create(void)
    00080 {
    00081    struct sched_context *tmp;
    00082    tmp = malloc(sizeof(struct sched_context));
    00083    if (tmp) {
    00084             memset(tmp, 0, sizeof(struct sched_context));
    00085       ast_mutex_init(&tmp->lock);
    00086       tmp->eventcnt = 1;
    00087       tmp->schedcnt = 0;
    00088       tmp->schedq = NULL;
    00089 #ifdef SCHED_MAX_CACHE
    00090       tmp->schedc = NULL;
    00091       tmp->schedccnt = 0;
    00092 #endif
    00093    }
    00094    return tmp;
    00095 }
    00096
    00097 void sched_context_destroy(struct sched_context *con)
    00098 {
    00099    struct sched *s, *sl;
    00100    ast_mutex_lock(&con->lock);
    00101 #ifdef SCHED_MAX_CACHE
    00102    /* Eliminate the cache */
    00103    s = con->schedc;
    00104    while(s) {
    00105       sl = s;
    00106       s = s->next;
    00107       free(sl);
    00108    }
    00109 #endif
    00110    /* And the queue */
    00111    s = con->schedq;
    00112    while(s) {
    00113       sl = s;
    00114       s = s->next;
    00115       free(sl);
    00116    }
    00117    /* And the context */
    00118    ast_mutex_unlock(&con->lock);
    00119    ast_mutex_destroy(&con->lock);
    00120    free(con);
    00121 }
    00122
    00123 static struct sched *sched_alloc(struct sched_context *con)
    00124 {
    00125    /*
    00126     * We keep a small cache of schedule entries
    00127     * to minimize the number of necessary malloc()'s
    00128     */
    00129    struct sched *tmp;
    00130 #ifdef SCHED_MAX_CACHE
    00131    if (con->schedc) {
    00132       tmp = con->schedc;
    00133       con->schedc = con->schedc->next;
    00134       con->schedccnt--;
    00135    } else
    00136 #endif
    00137       tmp = malloc(sizeof(struct sched));
    00138    return tmp;
    00139 }
    00140
    00141 static void sched_release(struct sched_context *con, struct sched *tmp)
    00142 {
    00143    /*
    00144     * Add to the cache, or just free() if we
    00145     * already have too many cache entries
    00146     */
    00147
    00148 #ifdef SCHED_MAX_CACHE
    00149    if (con->schedccnt < SCHED_MAX_CACHE) {
    00150       tmp->next = con->schedc;
    00151       con->schedc = tmp;
    00152       con->schedccnt++;
    00153    } else
    00154 #endif
    00155       free(tmp);
    00156 }
    00157
    00158 int ast_sched_wait(struct sched_context *con)
    00159 {
    00160    /*
    00161     * Return the number of milliseconds
    00162     * until the next scheduled event
    00163     */
    00164    int ms;
    00165    DEBUG(ast_log(LOG_DEBUG, "ast_sched_wait()\n"));
    00166    ast_mutex_lock(&con->lock);
    00167    if (!con->schedq) {
    00168       ms = -1;
    00169    } else {
    00170       ms = ast_tvdiff_ms(con->schedq->when, ast_tvnow());
    00171       if (ms < 0)
    00172          ms = 0;
    00173    }
    00174    ast_mutex_unlock(&con->lock);
    00175    return ms;
    00176
    00177 }
    00178
    00179
    00180 static void schedule(struct sched_context *con, struct sched *s)
    00181 {
    00182    /*
    00183     * Take a sched structure and put it in the
    00184     * queue, such that the soonest event is
    00185     * first in the list.
    00186     */
    00187
    00188    struct sched *last=NULL;
    00189    struct sched *current=con->schedq;
    00190    while(current) {
    00191       if (SOONER(s->when, current->when))
    00192          break;
    00193       last = current;
    00194       current = current->next;
    00195    }
    00196    /* Insert this event into the schedule */
    00197    s->next = current;
    00198    if (last)
    00199       last->next = s;
    00200    else
    00201       con->schedq = s;
    00202    con->schedcnt++;
    00203 }
    00204
    00205 /*
    00206  * given the last event *tv and the offset in milliseconds 'when',
    00207  * computes the next value,
    00208  */
    00209 static int sched_settime(struct timeval *tv, int when)
    00210 {
    00211    struct timeval now = ast_tvnow();
    00212
    00213    /*ast_log(LOG_DEBUG, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
    00214    if (ast_tvzero(*tv)) /* not supplied, default to now */
    00215       *tv = now;
    00216    *tv = ast_tvadd(*tv, ast_samp2tv(when, 1000));
    00217    if (ast_tvcmp(*tv, now) < 0) {
    00218       ast_log(LOG_DEBUG, "Request to schedule in the past?!?!\n");
    00219       *tv = now;
    00220    }
    00221    return 0;
    00222 }
    00223
    00224
    00225 int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, void *data, int variable)
    00226 {
    00227    /*
    00228     * Schedule callback(data) to happen when ms into the future
    00229     */
    00230    struct sched *tmp;
    00231    int res = -1;
    00232    DEBUG(ast_log(LOG_DEBUG, "ast_sched_add()\n"));
    00233    if (!when) {
    00234       ast_log(LOG_NOTICE, "Scheduled event in 0 ms?\n");
    00235       return -1;
    00236    }
    00237    ast_mutex_lock(&con->lock);
    00238    if ((tmp = sched_alloc(con))) {
    00239       tmp->id = con->eventcnt++;
    00240       tmp->callback = callback;
    00241       tmp->data = data;
    00242       tmp->resched = when;
    00243       tmp->variable = variable;
    00244       tmp->when = ast_tv(0, 0);
    00245       if (sched_settime(&tmp->when, when)) {
    00246          sched_release(con, tmp);
    00247       } else {
    00248          schedule(con, tmp);
    00249          res = tmp->id;
    00250       }
    00251    }
    00252 #ifdef DUMP_SCHEDULER
    00253    /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
    00254    ast_sched_dump(con);
    00255 #endif
    00256    ast_mutex_unlock(&con->lock);
    00257    return res;
    00258 }
    00259
    00260 int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data)
    00261 {
    00262    return ast_sched_add_variable(con, when, callback, data, 0);
    00263 }
    00264
    00265 int ast_sched_del(struct sched_context *con, int id)
    00266 {
    00267    /*
    00268     * Delete the schedule entry with number
    00269     * "id".  It's nearly impossible that there
    00270     * would be two or more in the list with that
    00271     * id.
    00272     */
    00273    struct sched *last=NULL, *s;
    00274    DEBUG(ast_log(LOG_DEBUG, "ast_sched_del()\n"));
    00275    ast_mutex_lock(&con->lock);
    00276    s = con->schedq;
    00277    while(s) {
    00278       if (s->id == id) {
    00279          if (last)
    00280             last->next = s->next;
    00281          else
    00282             con->schedq = s->next;
    00283          con->schedcnt--;
    00284          sched_release(con, s);
    00285          break;
    00286       }
    00287       last = s;
    00288       s = s->next;
    00289    }
    00290 #ifdef DUMP_SCHEDULER
    00291    /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
    00292    ast_sched_dump(con);
    00293 #endif
    00294    ast_mutex_unlock(&con->lock);
    00295    if (!s) {
    00296       ast_log(LOG_NOTICE, "Attempted to delete nonexistent schedule entry %d!\n", id);
    00297 #ifdef DO_CRASH
    00298       CRASH;
    00299 #endif
    00300       return -1;
    00301    } else
    00302       return 0;
    00303 }
    00304
    00305 void ast_sched_dump(const struct sched_context *con)
    00306 {
    00307    /*
    00308     * Dump the contents of the scheduler to
    00309     * stderr
    00310     */
    00311    struct sched *q;
    00312    struct timeval tv = ast_tvnow();
    00313 #ifdef SCHED_MAX_CACHE
    00314    ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt);
    00315 #else
    00316    ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total)\n", con->schedcnt, con->eventcnt - 1);
    00317 #endif
    00318
    00319    ast_log(LOG_DEBUG, "=============================================================\n");
    00320    ast_log(LOG_DEBUG, "|ID    Callback          Data              Time  (sec:ms)   |\n");
    00321    ast_log(LOG_DEBUG, "+-----+-----------------+-----------------+-----------------+\n");
    00322    for (q = con->schedq; q; q = q->next) {
    00323       struct timeval delta =  ast_tvsub(q->when, tv);
    00324
    00325       ast_log(LOG_DEBUG, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
    00326          q->id,
    00327          q->callback,
    00328          q->data,
    00329          delta.tv_sec,
    00330          (long int)delta.tv_usec);
    00331    }
    00332    ast_log(LOG_DEBUG, "=============================================================\n");
    00333
    00334 }
    00335
    00336 int ast_sched_runq(struct sched_context *con)
    00337 {
    00338    /*
    00339     * Launch all events which need to be run at this time.
    00340     */
    00341    struct sched *current;
    00342    struct timeval tv;
    00343    int x=0;
    00344    int res;
    00345    DEBUG(ast_log(LOG_DEBUG, "ast_sched_runq()\n"));
    00346
    00347    ast_mutex_lock(&con->lock);
    00348    for(;;) {
    00349       if (!con->schedq)
    00350          break;
    00351
    00352       /* schedule all events which are going to expire within 1ms.
    00353        * We only care about millisecond accuracy anyway, so this will
    00354        * help us get more than one event at one time if they are very
    00355        * close together.
    00356        */
    00357       tv = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
    00358       if (SOONER(con->schedq->when, tv)) {
    00359          current = con->schedq;
    00360          con->schedq = con->schedq->next;
    00361          con->schedcnt--;
    00362
    00363          /*
    00364           * At this point, the schedule queue is still intact.  We
    00365           * have removed the first event and the rest is still there,
    00366           * so it's permissible for the callback to add new events, but
    00367           * trying to delete itself won't work because it isn't in
    00368           * the schedule queue.  If that's what it wants to do, it
    00369           * should return 0.
    00370           */
    00371
    00372          ast_mutex_unlock(&con->lock);
    00373          res = current->callback(current->data);
    00374          ast_mutex_lock(&con->lock);
    00375
    00376          if (res) {
    00377             /*
    00378              * If they return non-zero, we should schedule them to be
    00379              * run again.
    00380              */
    00381             if (sched_settime(&current->when, current->variable? res : current->resched)) {
    00382                sched_release(con, current);
    00383             } else
    00384                schedule(con, current);
    00385          } else {
    00386             /* No longer needed, so release it */
    00387             sched_release(con, current);
    00388          }
    00389          x++;
    00390       } else
    00391          break;
    00392    }
    00393    ast_mutex_unlock(&con->lock);
    00394    return x;
    00395 }
    00396
    00397 long ast_sched_when(struct sched_context *con,int id)
    00398 {
    00399    struct sched *s;
    00400    long secs;
    00401    DEBUG(ast_log(LOG_DEBUG, "ast_sched_when()\n"));
    00402
    00403    ast_mutex_lock(&con->lock);
    00404    s=con->schedq;
    00405    while (s!=NULL) {
    00406       if (s->id==id) break;
    00407       s=s->next;
    00408    }
    00409    secs=-1;
    00410    if (s!=NULL) {
    00411       struct timeval now = ast_tvnow();
    00412       secs=s->when.tv_sec-now.tv_sec;
    00413    }
    00414    ast_mutex_unlock(&con->lock);
    00415    return secs;
    00416 }