timing.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2008 - 2009, Digium, Inc.
  5. *
  6. * Kevin P. Fleming <kpfleming@digium.com>
  7. * Russell Bryant <russell@digium.com>
  8. *
  9. * See http://www.asterisk.org for more information about
  10. * the Asterisk project. Please do not directly contact
  11. * any of the maintainers of this project for assistance;
  12. * the project provides a web site, mailing lists and IRC
  13. * channels for your use.
  14. *
  15. * This program is free software, distributed under the terms of
  16. * the GNU General Public License Version 2. See the LICENSE file
  17. * at the top of the source tree.
  18. */
  19. /*! \file
  20. *
  21. * \brief Timing source management
  22. *
  23. * \author Kevin P. Fleming <kpfleming@digium.com>
  24. * \author Russell Bryant <russell@digium.com>
  25. */
  26. /*** MODULEINFO
  27. <support_level>core</support_level>
  28. ***/
  29. #include "asterisk.h"
  30. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  31. #include "asterisk/_private.h"
  32. #include "asterisk/timing.h"
  33. #include "asterisk/lock.h"
  34. #include "asterisk/cli.h"
  35. #include "asterisk/utils.h"
  36. #include "asterisk/time.h"
  37. #include "asterisk/heap.h"
  38. #include "asterisk/module.h"
  39. #include "asterisk/poll-compat.h"
  40. struct timing_holder {
  41. /*! Do _not_ move this from the beginning of the struct. */
  42. ssize_t __heap_index;
  43. struct ast_module *mod;
  44. struct ast_timing_interface *iface;
  45. };
  46. static struct ast_heap *timing_interfaces;
  47. struct ast_timer {
  48. int fd;
  49. struct timing_holder *holder;
  50. };
  51. static int timing_holder_cmp(void *_h1, void *_h2)
  52. {
  53. struct timing_holder *h1 = _h1;
  54. struct timing_holder *h2 = _h2;
  55. if (h1->iface->priority > h2->iface->priority) {
  56. return 1;
  57. } else if (h1->iface->priority == h2->iface->priority) {
  58. return 0;
  59. } else {
  60. return -1;
  61. }
  62. }
  63. void *_ast_register_timing_interface(struct ast_timing_interface *funcs,
  64. struct ast_module *mod)
  65. {
  66. struct timing_holder *h;
  67. if (!funcs->timer_open ||
  68. !funcs->timer_close ||
  69. !funcs->timer_set_rate ||
  70. !funcs->timer_ack ||
  71. !funcs->timer_get_event ||
  72. !funcs->timer_get_max_rate ||
  73. !funcs->timer_enable_continuous ||
  74. !funcs->timer_disable_continuous) {
  75. return NULL;
  76. }
  77. if (!(h = ast_calloc(1, sizeof(*h)))) {
  78. return NULL;
  79. }
  80. h->iface = funcs;
  81. h->mod = mod;
  82. ast_heap_wrlock(timing_interfaces);
  83. ast_heap_push(timing_interfaces, h);
  84. ast_heap_unlock(timing_interfaces);
  85. return h;
  86. }
  87. int ast_unregister_timing_interface(void *handle)
  88. {
  89. struct timing_holder *h = handle;
  90. int res = -1;
  91. ast_heap_wrlock(timing_interfaces);
  92. h = ast_heap_remove(timing_interfaces, h);
  93. ast_heap_unlock(timing_interfaces);
  94. if (h) {
  95. ast_free(h);
  96. h = NULL;
  97. res = 0;
  98. }
  99. return res;
  100. }
  101. struct ast_timer *ast_timer_open(void)
  102. {
  103. int fd = -1;
  104. struct timing_holder *h;
  105. struct ast_timer *t = NULL;
  106. ast_heap_rdlock(timing_interfaces);
  107. if ((h = ast_heap_peek(timing_interfaces, 1))) {
  108. fd = h->iface->timer_open();
  109. ast_module_ref(h->mod);
  110. }
  111. if (fd != -1) {
  112. if (!(t = ast_calloc(1, sizeof(*t)))) {
  113. h->iface->timer_close(fd);
  114. } else {
  115. t->fd = fd;
  116. t->holder = h;
  117. }
  118. }
  119. ast_heap_unlock(timing_interfaces);
  120. return t;
  121. }
  122. void ast_timer_close(struct ast_timer *handle)
  123. {
  124. handle->holder->iface->timer_close(handle->fd);
  125. handle->fd = -1;
  126. ast_module_unref(handle->holder->mod);
  127. ast_free(handle);
  128. }
  129. int ast_timer_fd(const struct ast_timer *handle)
  130. {
  131. return handle->fd;
  132. }
  133. int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
  134. {
  135. int res = -1;
  136. res = handle->holder->iface->timer_set_rate(handle->fd, rate);
  137. return res;
  138. }
  139. int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
  140. {
  141. int res = -1;
  142. res = handle->holder->iface->timer_ack(handle->fd, quantity);
  143. return res;
  144. }
  145. int ast_timer_enable_continuous(const struct ast_timer *handle)
  146. {
  147. int res = -1;
  148. res = handle->holder->iface->timer_enable_continuous(handle->fd);
  149. return res;
  150. }
  151. int ast_timer_disable_continuous(const struct ast_timer *handle)
  152. {
  153. int res = -1;
  154. res = handle->holder->iface->timer_disable_continuous(handle->fd);
  155. return res;
  156. }
  157. enum ast_timer_event ast_timer_get_event(const struct ast_timer *handle)
  158. {
  159. enum ast_timer_event res = -1;
  160. res = handle->holder->iface->timer_get_event(handle->fd);
  161. return res;
  162. }
  163. unsigned int ast_timer_get_max_rate(const struct ast_timer *handle)
  164. {
  165. unsigned int res = 0;
  166. res = handle->holder->iface->timer_get_max_rate(handle->fd);
  167. return res;
  168. }
  169. const char *ast_timer_get_name(const struct ast_timer *handle)
  170. {
  171. return handle->holder->iface->name;
  172. }
  173. static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  174. {
  175. struct ast_timer *timer;
  176. int count = 0;
  177. struct timeval start, end;
  178. unsigned int test_rate = 50;
  179. switch (cmd) {
  180. case CLI_INIT:
  181. e->command = "timing test";
  182. e->usage = "Usage: timing test <rate>\n"
  183. " Test a timer with a specified rate, 50/sec by default.\n"
  184. "";
  185. return NULL;
  186. case CLI_GENERATE:
  187. return NULL;
  188. }
  189. if (a->argc != 2 && a->argc != 3) {
  190. return CLI_SHOWUSAGE;
  191. }
  192. if (a->argc == 3) {
  193. unsigned int rate;
  194. if (sscanf(a->argv[2], "%30u", &rate) == 1) {
  195. test_rate = rate;
  196. } else {
  197. ast_cli(a->fd, "Invalid rate '%s', using default of %u\n", a->argv[2], test_rate);
  198. }
  199. }
  200. ast_cli(a->fd, "Attempting to test a timer with %u ticks per second.\n", test_rate);
  201. if (!(timer = ast_timer_open())) {
  202. ast_cli(a->fd, "Failed to open timing fd\n");
  203. return CLI_FAILURE;
  204. }
  205. ast_cli(a->fd, "Using the '%s' timing module for this test.\n", timer->holder->iface->name);
  206. start = ast_tvnow();
  207. ast_timer_set_rate(timer, test_rate);
  208. while (ast_tvdiff_ms((end = ast_tvnow()), start) < 1000) {
  209. int res;
  210. struct pollfd pfd = {
  211. .fd = ast_timer_fd(timer),
  212. .events = POLLIN | POLLPRI,
  213. };
  214. res = ast_poll(&pfd, 1, 100);
  215. if (res == 1) {
  216. count++;
  217. if (ast_timer_ack(timer, 1) < 0) {
  218. ast_cli(a->fd, "Timer failed to acknowledge.\n");
  219. ast_timer_close(timer);
  220. return CLI_FAILURE;
  221. }
  222. } else if (!res) {
  223. ast_cli(a->fd, "poll() timed out! This is bad.\n");
  224. } else if (errno != EAGAIN && errno != EINTR) {
  225. ast_cli(a->fd, "poll() returned error: %s\n", strerror(errno));
  226. }
  227. }
  228. ast_timer_close(timer);
  229. timer = NULL;
  230. ast_cli(a->fd, "It has been %" PRIi64 " milliseconds, and we got %d timer ticks\n",
  231. ast_tvdiff_ms(end, start), count);
  232. return CLI_SUCCESS;
  233. }
  234. static struct ast_cli_entry cli_timing[] = {
  235. AST_CLI_DEFINE(timing_test, "Run a timing test"),
  236. };
  237. static void timing_shutdown(void)
  238. {
  239. ast_cli_unregister_multiple(cli_timing, ARRAY_LEN(cli_timing));
  240. ast_heap_destroy(timing_interfaces);
  241. timing_interfaces = NULL;
  242. }
  243. int ast_timing_init(void)
  244. {
  245. if (!(timing_interfaces = ast_heap_create(2, timing_holder_cmp, 0))) {
  246. return -1;
  247. }
  248. ast_register_atexit(timing_shutdown);
  249. return ast_cli_register_multiple(cli_timing, ARRAY_LEN(cli_timing));
  250. }