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