Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

sched.c

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

Generated on Fri Oct 31 07:05:08 2003 for Asterisk by doxygen 1.3.4