00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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
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;
00034 int id;
00035 struct timeval when;
00036 int resched;
00037 void *data;
00038 ast_sched_cb callback;
00039 };
00040
00041 struct sched_context {
00042
00043 int eventcnt;
00044
00045
00046 int schedcnt;
00047
00048
00049 struct sched *schedq;
00050
00051 #ifdef SCHED_MAX_CACHE
00052
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
00079 s = con->schedc;
00080 while(s) {
00081 sl = s;
00082 s = s->next;
00083 free(sl);
00084 }
00085 #endif
00086
00087 s = con->schedq;
00088 while(s) {
00089 sl = s;
00090 s = s->next;
00091 free(sl);
00092 }
00093
00094 free(con);
00095 }
00096
00097 static struct sched *sched_alloc(struct sched_context *con)
00098 {
00099
00100
00101
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
00119
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
00136
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
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
00160
00161
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
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
00188 ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
00189 return -1;
00190 }
00191
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
00201 error_sec = 0;
00202 error_usec = 0;
00203 }
00204
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
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
00252
00253
00254
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
00283
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
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
00335 ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
00336 return 0;
00337 }
00338
00339
00340
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
00349
00350
00351
00352
00353
00354
00355 if (current->callback(current->data)) {
00356
00357
00358
00359
00360 if (sched_settime(¤t->when, current->resched)) {
00361 sched_release(con, current);
00362 } else
00363 schedule(con, current);
00364 } else {
00365
00366 sched_release(con, current);
00367 }
00368 x++;
00369 } else
00370 break;
00371 }
00372 return x;
00373 }