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 <pthread.h>
00025
00026
#include <asterisk/sched.h>
00027
#include <asterisk/logger.h>
00028
#include <asterisk/channel.h>
00029
00030
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;
00036 int id;
00037 struct timeval when;
00038 int resched;
00039 void *
data;
00040 ast_sched_cb callback;
00041 };
00042
00043 struct sched_context {
00044 ast_mutex_t lock;
00045
00046 int eventcnt;
00047
00048
00049 int schedcnt;
00050
00051
00052 struct sched *
schedq;
00053
00054
#ifdef SCHED_MAX_CACHE
00055
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
00084
s = con->schedc;
00085
while(
s) {
00086 sl =
s;
00087
s =
s->next;
00088
free(sl);
00089 }
00090
#endif
00091
00092
s = con->
schedq;
00093
while(
s) {
00094 sl =
s;
00095
s =
s->next;
00096
free(sl);
00097 }
00098
00099
ast_mutex_unlock(&con->
lock);
00100
free(con);
00101 }
00102
00103
static struct sched *sched_alloc(
struct sched_context *con)
00104 {
00105
00106
00107
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
00125
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
00142
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
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
00169
00170
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
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
00197
ast_log(LOG_NOTICE,
"gettimeofday() failed!\n");
00198
return -1;
00199 }
00200
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
00210 error_sec = 0;
00211 error_usec = 0;
00212 }
00213
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
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
00264
00265
00266
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
00300
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
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
00354
ast_log(
LOG_NOTICE,
"gettimeofday() failed!\n");
00355
break;
00356 }
00357
00358
00359
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
00368
00369
00370
00371
00372
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
00382
00383
00384
if (sched_settime(¤t->when, current->resched)) {
00385 sched_release(con, current);
00386 }
else
00387 schedule(con, current);
00388 }
else {
00389
00390 sched_release(con, current);
00391 }
00392 x++;
00393 }
else
00394
break;
00395 }
00396
ast_mutex_unlock(&con->
lock);
00397
return x;
00398 }