sched.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999 - 2005, Digium, Inc.
  5. *
  6. * Mark Spencer <markster@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief Scheduler Routines (from cheops-NG)
  21. *
  22. */
  23. #ifdef DEBUG_SCHEDULER
  24. #define DEBUG(a) DEBUG_M(a)
  25. #else
  26. #define DEBUG(a)
  27. #endif
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <sys/time.h>
  31. #include <unistd.h>
  32. #include <string.h>
  33. #include "asterisk.h"
  34. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  35. #include "asterisk/sched.h"
  36. #include "asterisk/logger.h"
  37. #include "asterisk/channel.h"
  38. #include "asterisk/lock.h"
  39. #include "asterisk/utils.h"
  40. /* Determine if a is sooner than b */
  41. #define SOONER(a,b) (((b).tv_sec > (a).tv_sec) || \
  42. (((b).tv_sec == (a).tv_sec) && ((b).tv_usec > (a).tv_usec)))
  43. struct sched {
  44. struct sched *next; /* Next event in the list */
  45. int id; /* ID number of event */
  46. struct timeval when; /* Absolute time event should take place */
  47. int resched; /* When to reschedule */
  48. int variable; /* Use return value from callback to reschedule */
  49. void *data; /* Data */
  50. ast_sched_cb callback; /* Callback */
  51. };
  52. struct sched_context {
  53. ast_mutex_t lock;
  54. /* Number of events processed */
  55. int eventcnt;
  56. /* Number of outstanding schedule events */
  57. int schedcnt;
  58. /* Schedule entry and main queue */
  59. struct sched *schedq;
  60. #ifdef SCHED_MAX_CACHE
  61. /* Cache of unused schedule structures and how many */
  62. struct sched *schedc;
  63. int schedccnt;
  64. #endif
  65. };
  66. struct sched_context *sched_context_create(void)
  67. {
  68. struct sched_context *tmp;
  69. tmp = malloc(sizeof(struct sched_context));
  70. if (tmp) {
  71. memset(tmp, 0, sizeof(struct sched_context));
  72. ast_mutex_init(&tmp->lock);
  73. tmp->eventcnt = 1;
  74. tmp->schedcnt = 0;
  75. tmp->schedq = NULL;
  76. #ifdef SCHED_MAX_CACHE
  77. tmp->schedc = NULL;
  78. tmp->schedccnt = 0;
  79. #endif
  80. }
  81. return tmp;
  82. }
  83. void sched_context_destroy(struct sched_context *con)
  84. {
  85. struct sched *s, *sl;
  86. ast_mutex_lock(&con->lock);
  87. #ifdef SCHED_MAX_CACHE
  88. /* Eliminate the cache */
  89. s = con->schedc;
  90. while(s) {
  91. sl = s;
  92. s = s->next;
  93. free(sl);
  94. }
  95. #endif
  96. /* And the queue */
  97. s = con->schedq;
  98. while(s) {
  99. sl = s;
  100. s = s->next;
  101. free(sl);
  102. }
  103. /* And the context */
  104. ast_mutex_unlock(&con->lock);
  105. ast_mutex_destroy(&con->lock);
  106. free(con);
  107. }
  108. static struct sched *sched_alloc(struct sched_context *con)
  109. {
  110. /*
  111. * We keep a small cache of schedule entries
  112. * to minimize the number of necessary malloc()'s
  113. */
  114. struct sched *tmp;
  115. #ifdef SCHED_MAX_CACHE
  116. if (con->schedc) {
  117. tmp = con->schedc;
  118. con->schedc = con->schedc->next;
  119. con->schedccnt--;
  120. } else
  121. #endif
  122. tmp = malloc(sizeof(struct sched));
  123. return tmp;
  124. }
  125. static void sched_release(struct sched_context *con, struct sched *tmp)
  126. {
  127. /*
  128. * Add to the cache, or just free() if we
  129. * already have too many cache entries
  130. */
  131. #ifdef SCHED_MAX_CACHE
  132. if (con->schedccnt < SCHED_MAX_CACHE) {
  133. tmp->next = con->schedc;
  134. con->schedc = tmp;
  135. con->schedccnt++;
  136. } else
  137. #endif
  138. free(tmp);
  139. }
  140. int ast_sched_wait(struct sched_context *con)
  141. {
  142. /*
  143. * Return the number of milliseconds
  144. * until the next scheduled event
  145. */
  146. int ms;
  147. DEBUG(ast_log(LOG_DEBUG, "ast_sched_wait()\n"));
  148. ast_mutex_lock(&con->lock);
  149. if (!con->schedq) {
  150. ms = -1;
  151. } else {
  152. ms = ast_tvdiff_ms(con->schedq->when, ast_tvnow());
  153. if (ms < 0)
  154. ms = 0;
  155. }
  156. ast_mutex_unlock(&con->lock);
  157. return ms;
  158. }
  159. static void schedule(struct sched_context *con, struct sched *s)
  160. {
  161. /*
  162. * Take a sched structure and put it in the
  163. * queue, such that the soonest event is
  164. * first in the list.
  165. */
  166. struct sched *last=NULL;
  167. struct sched *current=con->schedq;
  168. while(current) {
  169. if (SOONER(s->when, current->when))
  170. break;
  171. last = current;
  172. current = current->next;
  173. }
  174. /* Insert this event into the schedule */
  175. s->next = current;
  176. if (last)
  177. last->next = s;
  178. else
  179. con->schedq = s;
  180. con->schedcnt++;
  181. }
  182. /*
  183. * given the last event *tv and the offset in milliseconds 'when',
  184. * computes the next value,
  185. */
  186. static int sched_settime(struct timeval *tv, int when)
  187. {
  188. struct timeval now = ast_tvnow();
  189. /*ast_log(LOG_DEBUG, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
  190. if (ast_tvzero(*tv)) /* not supplied, default to now */
  191. *tv = now;
  192. *tv = ast_tvadd(*tv, ast_samp2tv(when, 1000));
  193. if (ast_tvcmp(*tv, now) < 0) {
  194. ast_log(LOG_DEBUG, "Request to schedule in the past?!?!\n");
  195. *tv = now;
  196. }
  197. return 0;
  198. }
  199. int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, void *data, int variable)
  200. {
  201. /*
  202. * Schedule callback(data) to happen when ms into the future
  203. */
  204. struct sched *tmp;
  205. int res = -1;
  206. DEBUG(ast_log(LOG_DEBUG, "ast_sched_add()\n"));
  207. if (!when) {
  208. ast_log(LOG_NOTICE, "Scheduled event in 0 ms?\n");
  209. return -1;
  210. }
  211. ast_mutex_lock(&con->lock);
  212. if ((tmp = sched_alloc(con))) {
  213. tmp->id = con->eventcnt++;
  214. tmp->callback = callback;
  215. tmp->data = data;
  216. tmp->resched = when;
  217. tmp->variable = variable;
  218. tmp->when = ast_tv(0, 0);
  219. if (sched_settime(&tmp->when, when)) {
  220. sched_release(con, tmp);
  221. } else {
  222. schedule(con, tmp);
  223. res = tmp->id;
  224. }
  225. }
  226. #ifdef DUMP_SCHEDULER
  227. /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
  228. ast_sched_dump(con);
  229. #endif
  230. ast_mutex_unlock(&con->lock);
  231. return res;
  232. }
  233. int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data)
  234. {
  235. return ast_sched_add_variable(con, when, callback, data, 0);
  236. }
  237. int ast_sched_del(struct sched_context *con, int id)
  238. {
  239. /*
  240. * Delete the schedule entry with number
  241. * "id". It's nearly impossible that there
  242. * would be two or more in the list with that
  243. * id.
  244. */
  245. struct sched *last=NULL, *s;
  246. DEBUG(ast_log(LOG_DEBUG, "ast_sched_del()\n"));
  247. ast_mutex_lock(&con->lock);
  248. s = con->schedq;
  249. while(s) {
  250. if (s->id == id) {
  251. if (last)
  252. last->next = s->next;
  253. else
  254. con->schedq = s->next;
  255. con->schedcnt--;
  256. sched_release(con, s);
  257. break;
  258. }
  259. last = s;
  260. s = s->next;
  261. }
  262. #ifdef DUMP_SCHEDULER
  263. /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
  264. ast_sched_dump(con);
  265. #endif
  266. ast_mutex_unlock(&con->lock);
  267. if (!s) {
  268. ast_log(LOG_NOTICE, "Attempted to delete nonexistent schedule entry %d!\n", id);
  269. #ifdef DO_CRASH
  270. CRASH;
  271. #endif
  272. return -1;
  273. } else
  274. return 0;
  275. }
  276. void ast_sched_dump(const struct sched_context *con)
  277. {
  278. /*
  279. * Dump the contents of the scheduler to
  280. * stderr
  281. */
  282. struct sched *q;
  283. struct timeval tv = ast_tvnow();
  284. #ifdef SCHED_MAX_CACHE
  285. ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt);
  286. #else
  287. ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total)\n", con->schedcnt, con->eventcnt - 1);
  288. #endif
  289. ast_log(LOG_DEBUG, "=============================================================\n");
  290. ast_log(LOG_DEBUG, "|ID Callback Data Time (sec:ms) |\n");
  291. ast_log(LOG_DEBUG, "+-----+-----------------+-----------------+-----------------+\n");
  292. for (q = con->schedq; q; q = q->next) {
  293. struct timeval delta = ast_tvsub(q->when, tv);
  294. ast_log(LOG_DEBUG, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
  295. q->id,
  296. q->callback,
  297. q->data,
  298. delta.tv_sec,
  299. (long int)delta.tv_usec);
  300. }
  301. ast_log(LOG_DEBUG, "=============================================================\n");
  302. }
  303. int ast_sched_runq(struct sched_context *con)
  304. {
  305. /*
  306. * Launch all events which need to be run at this time.
  307. */
  308. struct sched *current;
  309. struct timeval tv;
  310. int x=0;
  311. int res;
  312. DEBUG(ast_log(LOG_DEBUG, "ast_sched_runq()\n"));
  313. ast_mutex_lock(&con->lock);
  314. for(;;) {
  315. if (!con->schedq)
  316. break;
  317. /* schedule all events which are going to expire within 1ms.
  318. * We only care about millisecond accuracy anyway, so this will
  319. * help us get more than one event at one time if they are very
  320. * close together.
  321. */
  322. tv = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
  323. if (SOONER(con->schedq->when, tv)) {
  324. current = con->schedq;
  325. con->schedq = con->schedq->next;
  326. con->schedcnt--;
  327. /*
  328. * At this point, the schedule queue is still intact. We
  329. * have removed the first event and the rest is still there,
  330. * so it's permissible for the callback to add new events, but
  331. * trying to delete itself won't work because it isn't in
  332. * the schedule queue. If that's what it wants to do, it
  333. * should return 0.
  334. */
  335. ast_mutex_unlock(&con->lock);
  336. res = current->callback(current->data);
  337. ast_mutex_lock(&con->lock);
  338. if (res) {
  339. /*
  340. * If they return non-zero, we should schedule them to be
  341. * run again.
  342. */
  343. if (sched_settime(&current->when, current->variable? res : current->resched)) {
  344. sched_release(con, current);
  345. } else
  346. schedule(con, current);
  347. } else {
  348. /* No longer needed, so release it */
  349. sched_release(con, current);
  350. }
  351. x++;
  352. } else
  353. break;
  354. }
  355. ast_mutex_unlock(&con->lock);
  356. return x;
  357. }
  358. long ast_sched_when(struct sched_context *con,int id)
  359. {
  360. struct sched *s;
  361. long secs;
  362. DEBUG(ast_log(LOG_DEBUG, "ast_sched_when()\n"));
  363. ast_mutex_lock(&con->lock);
  364. s=con->schedq;
  365. while (s!=NULL) {
  366. if (s->id==id) break;
  367. s=s->next;
  368. }
  369. secs=-1;
  370. if (s!=NULL) {
  371. struct timeval now = ast_tvnow();
  372. secs=s->when.tv_sec-now.tv_sec;
  373. }
  374. ast_mutex_unlock(&con->lock);
  375. return secs;
  376. }