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 <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
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;
00037 int id;
00038 struct timeval when;
00039 int resched;
00040 void *
data;
00041 ast_sched_cb callback;
00042 };
00043
00044 struct sched_context {
00045 ast_mutex_t lock;
00046
00047 int eventcnt;
00048
00049
00050 int schedcnt;
00051
00052
00053 struct sched *
schedq;
00054
00055
#ifdef SCHED_MAX_CACHE
00056
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
00086
s = con->schedc;
00087
while(
s) {
00088 sl =
s;
00089
s =
s->next;
00090
free(sl);
00091 }
00092
#endif
00093
00094
s = con->
schedq;
00095
while(
s) {
00096 sl =
s;
00097
s =
s->next;
00098
free(sl);
00099 }
00100
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
00110
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
00128
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
00145
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
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
00172
00173
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
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
00200
ast_log(LOG_NOTICE,
"gettimeofday() failed!\n");
00201
return -1;
00202 }
00203
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
00213 error_sec = 0;
00214 error_usec = 0;
00215 }
00216
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
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
00267
00268
00269
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
00303
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
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
00357
ast_log(
LOG_NOTICE,
"gettimeofday() failed!\n");
00358
break;
00359 }
00360
00361
00362
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
00371
00372
00373
00374
00375
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
00385
00386
00387
if (sched_settime(¤t->when, current->resched)) {
00388 sched_release(con, current);
00389 }
else
00390 schedule(con, current);
00391 }
else {
00392
00393 sched_release(con, current);
00394 }
00395 x++;
00396 }
else
00397
break;
00398 }
00399
ast_mutex_unlock(&con->
lock);
00400
return x;
00401 }