This source file includes following definitions.
- gettimeofday
- run_main_schedule
- FT_DECLARE
- FT_DECLARE
- FT_DECLARE
- FT_DECLARE
- FT_DECLARE
- FT_DECLARE
- FT_DECLARE
- FT_DECLARE
- FT_DECLARE
- FT_DECLARE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 #include "private/ftdm_core.h"
36
37 #ifdef __WINDOWS__
38 struct ftdm_timezone {
39 int tz_minuteswest;
40 int tz_dsttime;
41 };
42 int gettimeofday(struct timeval *tv, struct ftdm_timezone *tz)
43 {
44 FILETIME ft;
45 unsigned __int64 tmpres = 0;
46 static int tzflag;
47 if (NULL != tv) {
48 GetSystemTimeAsFileTime(&ft);
49 tmpres |= ft.dwHighDateTime;
50 tmpres <<= 32;
51 tmpres |= ft.dwLowDateTime;
52
53
54 tmpres /= 10;
55 tmpres -= DELTA_EPOCH_IN_MICROSECS;
56 tv->tv_sec = (long) (tmpres / 1000000UL);
57 tv->tv_usec = (long) (tmpres % 1000000UL);
58 }
59 if (NULL != tz) {
60 if (!tzflag) {
61 _tzset();
62 tzflag++;
63 }
64 tz->tz_minuteswest = _timezone / 60;
65 tz->tz_dsttime = _daylight;
66 }
67 return 0;
68 }
69 #endif
70
71 typedef struct ftdm_timer ftdm_timer_t;
72
73 static struct {
74 ftdm_sched_t *freeruns;
75 ftdm_mutex_t *mutex;
76 ftdm_bool_t running;
77 } sched_globals;
78
79 struct ftdm_sched {
80 char name[80];
81 ftdm_timer_id_t currid;
82 ftdm_mutex_t *mutex;
83 ftdm_timer_t *timers;
84 int freerun;
85 ftdm_sched_t *next;
86 ftdm_sched_t *prev;
87 };
88
89 struct ftdm_timer {
90 char name[80];
91 ftdm_timer_id_t id;
92 struct timeval time;
93 void *usrdata;
94 ftdm_sched_callback_t callback;
95 ftdm_timer_t *next;
96 ftdm_timer_t *prev;
97 };
98
99
100 #define SCHED_MAX_SLEEP 100
101 static void *run_main_schedule(ftdm_thread_t *thread, void *data)
102 {
103 int32_t timeto;
104 int32_t sleepms;
105 ftdm_status_t status;
106 ftdm_sched_t *current = NULL;
107 #ifdef __WINDOWS__
108 UNREFERENCED_PARAMETER(data);
109 UNREFERENCED_PARAMETER(thread);
110 #endif
111 while (ftdm_running()) {
112
113 sleepms = SCHED_MAX_SLEEP;
114
115 ftdm_mutex_lock(sched_globals.mutex);
116
117 if (!sched_globals.freeruns) {
118
119
120 ftdm_mutex_unlock(sched_globals.mutex);
121
122 ftdm_sleep(sleepms);
123 }
124
125 for (current = sched_globals.freeruns; current; current = current->next) {
126
127
128 ftdm_sched_run(current);
129
130
131 status = ftdm_sched_get_time_to_next_timer(current, &timeto);
132 if (status != FTDM_SUCCESS) {
133 ftdm_log(FTDM_LOG_WARNING, "Failed to get time to next timer for schedule %s, skipping\n", current->name);
134 continue;
135 }
136
137
138 if (timeto != -1 && sleepms > timeto) {
139 sleepms = timeto;
140 }
141 }
142
143 ftdm_mutex_unlock(sched_globals.mutex);
144
145 ftdm_sleep(sleepms);
146 }
147 ftdm_log(FTDM_LOG_NOTICE, "Main scheduling thread going out ...\n");
148 sched_globals.running = 0;
149 return NULL;
150 }
151
152 FT_DECLARE(ftdm_status_t) ftdm_sched_global_init()
153 {
154 ftdm_log(FTDM_LOG_DEBUG, "Initializing scheduling API\n");
155 memset(&sched_globals, 0, sizeof(sched_globals));
156 if (ftdm_mutex_create(&sched_globals.mutex) == FTDM_SUCCESS) {
157 return FTDM_SUCCESS;
158 }
159 return FTDM_FAIL;
160 }
161
162 FT_DECLARE(ftdm_status_t) ftdm_sched_free_run(ftdm_sched_t *sched)
163 {
164 ftdm_status_t status = FTDM_FAIL;
165 ftdm_assert_return(sched != NULL, FTDM_EINVAL, "invalid pointer\n");
166
167 ftdm_mutex_lock(sched->mutex);
168
169 ftdm_mutex_lock(sched_globals.mutex);
170
171 if (sched->freerun) {
172 ftdm_log(FTDM_LOG_ERROR, "Schedule %s is already running in free run\n", sched->name);
173 goto done;
174 }
175 sched->freerun = 1;
176
177 if (sched_globals.running == FTDM_FALSE) {
178 ftdm_log(FTDM_LOG_NOTICE, "Launching main schedule thread\n");
179 status = ftdm_thread_create_detached(run_main_schedule, NULL);
180 if (status != FTDM_SUCCESS) {
181 ftdm_log(FTDM_LOG_CRIT, "Failed to launch main schedule thread\n");
182 goto done;
183 }
184 sched_globals.running = FTDM_TRUE;
185 }
186
187 ftdm_log(FTDM_LOG_DEBUG, "Running schedule %s in the main schedule thread\n", sched->name);
188 status = FTDM_SUCCESS;
189
190
191 if (!sched_globals.freeruns) {
192 sched_globals.freeruns = sched;
193 } else {
194 sched->next = sched_globals.freeruns;
195 sched_globals.freeruns->prev = sched;
196 sched_globals.freeruns = sched;
197 }
198
199 done:
200 ftdm_mutex_unlock(sched_globals.mutex);
201
202 ftdm_mutex_unlock(sched->mutex);
203 return status;
204 }
205
206 FT_DECLARE(ftdm_bool_t) ftdm_free_sched_running(void)
207 {
208 return sched_globals.running;
209 }
210
211 FT_DECLARE(ftdm_bool_t) ftdm_free_sched_stop(void)
212 {
213
214
215 uint32_t sanity = 100;
216 while (ftdm_free_sched_running() && --sanity) {
217 ftdm_log(FTDM_LOG_DEBUG, "Waiting for main schedule thread to finish\n");
218 ftdm_sleep(100);
219 }
220
221 if (!sanity) {
222 ftdm_log(FTDM_LOG_CRIT, "schedule thread did not stop running, we may crash on shutdown\n");
223 return FTDM_FALSE;
224 }
225
226 return FTDM_TRUE;
227 }
228
229 FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched, const char *name)
230 {
231 ftdm_sched_t *newsched = NULL;
232
233 ftdm_assert_return(sched != NULL, FTDM_EINVAL, "invalid pointer\n");
234 ftdm_assert_return(name != NULL, FTDM_EINVAL, "invalid sched name\n");
235
236 *sched = NULL;
237
238 newsched = ftdm_calloc(1, sizeof(*newsched));
239 if (!newsched) {
240 return FTDM_MEMERR;
241 }
242
243 if (ftdm_mutex_create(&newsched->mutex) != FTDM_SUCCESS) {
244 goto failed;
245 }
246
247 ftdm_set_string(newsched->name, name);
248 newsched->currid = 1;
249
250 *sched = newsched;
251 ftdm_log(FTDM_LOG_DEBUG, "Created schedule %s\n", name);
252 return FTDM_SUCCESS;
253
254 failed:
255 ftdm_log(FTDM_LOG_CRIT, "Failed to create schedule\n");
256
257 if (newsched) {
258 if (newsched->mutex) {
259 ftdm_mutex_destroy(&newsched->mutex);
260 }
261 ftdm_safe_free(newsched);
262 }
263 return FTDM_FAIL;
264 }
265
266 FT_DECLARE(ftdm_status_t) ftdm_sched_run(ftdm_sched_t *sched)
267 {
268 ftdm_status_t status = FTDM_FAIL;
269 ftdm_timer_t *runtimer;
270 ftdm_timer_t *timer;
271 ftdm_sched_callback_t callback;
272 int ms = 0;
273 int rc = -1;
274 void *data;
275 struct timeval now;
276
277 ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n");
278
279 tryagain:
280
281 ftdm_mutex_lock(sched->mutex);
282
283 rc = gettimeofday(&now, NULL);
284 if (rc == -1) {
285 ftdm_log(FTDM_LOG_ERROR, "Failed to retrieve time of day\n");
286 goto done;
287 }
288
289 timer = sched->timers;
290 while (timer) {
291 runtimer = timer;
292 timer = runtimer->next;
293
294 ms = ((runtimer->time.tv_sec - now.tv_sec) * 1000) +
295 ((runtimer->time.tv_usec - now.tv_usec) / 1000);
296
297 if (ms <= 0) {
298
299 if (runtimer == sched->timers) {
300 sched->timers = runtimer->next;
301 if (sched->timers) {
302 sched->timers->prev = NULL;
303 }
304 }
305
306 callback = runtimer->callback;
307 data = runtimer->usrdata;
308 if (runtimer->next) {
309 runtimer->next->prev = runtimer->prev;
310 }
311 if (runtimer->prev) {
312 runtimer->prev->next = runtimer->next;
313 }
314
315 runtimer->id = 0;
316 ftdm_safe_free(runtimer);
317
318
319 ftdm_mutex_unlock(sched->mutex);
320
321 callback(data);
322
323
324
325 goto tryagain;
326 }
327 }
328
329 status = FTDM_SUCCESS;
330
331 done:
332
333 ftdm_mutex_unlock(sched->mutex);
334 #ifdef __WINDOWS__
335 UNREFERENCED_PARAMETER(sched);
336 #endif
337
338 return status;
339 }
340
341 FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name,
342 int ms, ftdm_sched_callback_t callback, void *data, ftdm_timer_id_t *timerid)
343 {
344 ftdm_status_t status = FTDM_FAIL;
345 struct timeval now;
346 int rc = 0;
347 ftdm_timer_t *newtimer;
348
349 ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n");
350 ftdm_assert_return(name != NULL, FTDM_EINVAL, "timer name is null!\n");
351 ftdm_assert_return(callback != NULL, FTDM_EINVAL, "sched callback is null!\n");
352 ftdm_assert_return(ms > 0, FTDM_EINVAL, "milliseconds must be bigger than 0!\n");
353
354 if (timerid) {
355 *timerid = 0;
356 }
357
358 rc = gettimeofday(&now, NULL);
359 if (rc == -1) {
360 ftdm_log(FTDM_LOG_ERROR, "Failed to retrieve time of day\n");
361 return FTDM_FAIL;
362 }
363
364 ftdm_mutex_lock(sched->mutex);
365
366 newtimer = ftdm_calloc(1, sizeof(*newtimer));
367 if (!newtimer) {
368 goto done;
369 }
370 newtimer->id = sched->currid;
371 sched->currid++;
372 if (!sched->currid) {
373 ftdm_log(FTDM_LOG_NOTICE, "Timer id wrap around for sched %s\n", sched->name);
374
375
376
377 sched->currid++;
378 }
379
380 ftdm_set_string(newtimer->name, name);
381 newtimer->callback = callback;
382 newtimer->usrdata = data;
383
384 newtimer->time.tv_sec = now.tv_sec + (ms / 1000);
385 newtimer->time.tv_usec = now.tv_usec + (ms % 1000) * 1000;
386 if (newtimer->time.tv_usec >= FTDM_MICROSECONDS_PER_SECOND) {
387 newtimer->time.tv_sec += 1;
388 newtimer->time.tv_usec -= FTDM_MICROSECONDS_PER_SECOND;
389 }
390
391 if (!sched->timers) {
392 sched->timers = newtimer;
393 } else {
394 newtimer->next = sched->timers;
395 sched->timers->prev = newtimer;
396 sched->timers = newtimer;
397 }
398
399 if (timerid) {
400 *timerid = newtimer->id;
401 }
402
403 status = FTDM_SUCCESS;
404 done:
405
406 ftdm_mutex_unlock(sched->mutex);
407 #ifdef __WINDOWS__
408 UNREFERENCED_PARAMETER(sched);
409 UNREFERENCED_PARAMETER(name);
410 UNREFERENCED_PARAMETER(ms);
411 UNREFERENCED_PARAMETER(callback);
412 UNREFERENCED_PARAMETER(data);
413 UNREFERENCED_PARAMETER(timerid);
414 #endif
415 return status;
416 }
417
418 FT_DECLARE(ftdm_status_t) ftdm_sched_get_time_to_next_timer(const ftdm_sched_t *sched, int32_t *timeto)
419 {
420 ftdm_status_t status = FTDM_FAIL;
421 int res = -1;
422 int ms = 0;
423 struct timeval currtime;
424 ftdm_timer_t *current = NULL;
425 ftdm_timer_t *winner = NULL;
426
427
428 *timeto = -1;
429
430 ftdm_mutex_lock(sched->mutex);
431
432 res = gettimeofday(&currtime, NULL);
433 if (-1 == res) {
434 ftdm_log(FTDM_LOG_ERROR, "Failed to get next event time\n");
435 goto done;
436 }
437 status = FTDM_SUCCESS;
438 current = sched->timers;
439 while (current) {
440
441 if (!winner) {
442 winner = current;
443 }
444 current = current->next;
445
446 if (!current) {
447 ms = (((winner->time.tv_sec - currtime.tv_sec) * 1000) +
448 ((winner->time.tv_usec - currtime.tv_usec) / 1000));
449
450
451 if (ms < 0) {
452 *timeto = 0;
453 break;
454 }
455 *timeto = ms;
456 break;
457 }
458
459
460 if (winner->time.tv_sec > current->time.tv_sec
461 || (winner->time.tv_sec == current->time.tv_sec &&
462 winner->time.tv_usec > current->time.tv_usec)) {
463 winner = current;
464 }
465 }
466
467 done:
468 ftdm_mutex_unlock(sched->mutex);
469 #ifdef __WINDOWS__
470 UNREFERENCED_PARAMETER(timeto);
471 UNREFERENCED_PARAMETER(sched);
472 #endif
473
474 return status;
475 }
476
477 FT_DECLARE(ftdm_status_t) ftdm_sched_cancel_timer(ftdm_sched_t *sched, ftdm_timer_id_t timerid)
478 {
479 ftdm_status_t status = FTDM_FAIL;
480 ftdm_timer_t *timer;
481
482 ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n");
483
484 if (!timerid) {
485 return FTDM_SUCCESS;
486 }
487
488 ftdm_mutex_lock(sched->mutex);
489
490
491 for (timer = sched->timers; timer; timer = timer->next) {
492 if (timer->id == timerid) {
493 if (timer == sched->timers) {
494
495 sched->timers = timer->next;
496 }
497 if (timer->prev) {
498 timer->prev->next = timer->next;
499 }
500 if (timer->next) {
501 timer->next->prev = timer->prev;
502 }
503 ftdm_safe_free(timer);
504 status = FTDM_SUCCESS;
505 break;
506 }
507 }
508
509 ftdm_mutex_unlock(sched->mutex);
510
511 return status;
512 }
513
514 FT_DECLARE(ftdm_status_t) ftdm_sched_destroy(ftdm_sched_t **insched)
515 {
516 ftdm_sched_t *sched = NULL;
517 ftdm_timer_t *timer;
518 ftdm_timer_t *deltimer;
519 ftdm_assert_return(insched != NULL, FTDM_EINVAL, "sched is null!\n");
520 ftdm_assert_return(*insched != NULL, FTDM_EINVAL, "sched is null!\n");
521
522 sched = *insched;
523
524
525 ftdm_mutex_lock(sched_globals.mutex);
526
527
528 if (sched == sched_globals.freeruns) {
529 sched_globals.freeruns = sched->next;
530 }
531
532 if (sched->prev) {
533 sched->prev->next = sched->next;
534 }
535
536 if (sched->next) {
537 sched->next->prev = sched->prev;
538 }
539
540 ftdm_mutex_unlock(sched_globals.mutex);
541
542
543 ftdm_mutex_lock(sched->mutex);
544
545 timer = sched->timers;
546 while (timer) {
547 deltimer = timer;
548 timer = timer->next;
549 ftdm_safe_free(deltimer);
550 }
551
552 ftdm_log(FTDM_LOG_DEBUG, "Destroying schedule %s\n", sched->name);
553
554 ftdm_mutex_unlock(sched->mutex);
555
556 ftdm_mutex_destroy(&sched->mutex);
557
558 ftdm_safe_free(sched);
559
560 *insched = NULL;
561 return FTDM_SUCCESS;
562 }
563
564
565
566
567
568
569
570
571
572
573