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

Generated on Sun Apr 18 23:33:55 2004 for Asterisk by doxygen 1.3.6-20040222