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

Generated on Sat Nov 25 19:09:49 2006 for Asterisk by  doxygen 1.4.2