Fri Sep 29 11:12:32 2006

Asterisk developer's documentation


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 }

Generated on Fri Sep 29 11:12:32 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7