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 Sat Jun 12 16:40:59 2004 for Asterisk by doxygen 1.3.7