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 <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_NOTICE, "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 Tue Aug 17 16:13:54 2004 for Asterisk by doxygen 1.3.8