timing.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. #include "asterisk/_private.h"
  31. #include "asterisk/timing.h"
  32. #include "asterisk/lock.h"
  33. #include "asterisk/cli.h"
  34. #include "asterisk/utils.h"
  35. #include "asterisk/time.h"
  36. #include "asterisk/heap.h"
  37. #include "asterisk/module.h"
  38. #include "asterisk/poll-compat.h"
  39. struct timing_holder {
  40. /*! Do _not_ move this from the beginning of the struct. */
  41. ssize_t __heap_index;
  42. struct ast_module *mod;
  43. struct ast_timing_interface *iface;
  44. };
  45. static struct ast_heap *timing_interfaces;
  46. struct ast_timer {
  47. void *data;
  48. struct timing_holder *holder;
  49. };
  50. static int timing_holder_cmp(void *_h1, void *_h2)
  51. {
  52. struct timing_holder *h1 = _h1;
  53. struct timing_holder *h2 = _h2;
  54. if (h1->iface->priority > h2->iface->priority) {
  55. return 1;
  56. } else if (h1->iface->priority == h2->iface->priority) {
  57. return 0;
  58. } else {
  59. return -1;
  60. }
  61. }
  62. void *_ast_register_timing_interface(struct ast_timing_interface *funcs,
  63. struct ast_module *mod)
  64. {
  65. struct timing_holder *h;
  66. if (!funcs->timer_open ||
  67. !funcs->timer_close ||
  68. !funcs->timer_set_rate ||
  69. !funcs->timer_ack ||
  70. !funcs->timer_get_event ||
  71. !funcs->timer_get_max_rate ||
  72. !funcs->timer_enable_continuous ||
  73. !funcs->timer_disable_continuous ||
  74. !funcs->timer_fd) {
  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. void *data = NULL;
  104. struct timing_holder *h;
  105. struct ast_timer *t = NULL;
  106. int idx = 1;
  107. ast_heap_rdlock(timing_interfaces);
  108. while ((h = ast_heap_peek(timing_interfaces, idx))) {
  109. if (ast_module_running_ref(h->mod)) {
  110. data = h->iface->timer_open();
  111. break;
  112. }
  113. idx++;
  114. }
  115. if (data) {
  116. if (!(t = ast_calloc(1, sizeof(*t)))) {
  117. h->iface->timer_close(data);
  118. ast_module_unref(h->mod);
  119. } else {
  120. t->data = data;
  121. t->holder = h;
  122. }
  123. }
  124. ast_heap_unlock(timing_interfaces);
  125. return t;
  126. }
  127. void ast_timer_close(struct ast_timer *handle)
  128. {
  129. handle->holder->iface->timer_close(handle->data);
  130. ast_module_unref(handle->holder->mod);
  131. ast_free(handle);
  132. }
  133. int ast_timer_fd(const struct ast_timer *handle)
  134. {
  135. return handle->holder->iface->timer_fd(handle->data);
  136. }
  137. int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
  138. {
  139. return handle->holder->iface->timer_set_rate(handle->data, rate);
  140. }
  141. int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
  142. {
  143. return handle->holder->iface->timer_ack(handle->data, quantity);
  144. }
  145. int ast_timer_enable_continuous(const struct ast_timer *handle)
  146. {
  147. return handle->holder->iface->timer_enable_continuous(handle->data);
  148. }
  149. int ast_timer_disable_continuous(const struct ast_timer *handle)
  150. {
  151. return handle->holder->iface->timer_disable_continuous(handle->data);
  152. }
  153. enum ast_timer_event ast_timer_get_event(const struct ast_timer *handle)
  154. {
  155. return handle->holder->iface->timer_get_event(handle->data);
  156. }
  157. unsigned int ast_timer_get_max_rate(const struct ast_timer *handle)
  158. {
  159. return handle->holder->iface->timer_get_max_rate(handle->data);
  160. }
  161. const char *ast_timer_get_name(const struct ast_timer *handle)
  162. {
  163. return handle->holder->iface->name;
  164. }
  165. static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  166. {
  167. struct ast_timer *timer;
  168. int count = 0;
  169. struct timeval start, end;
  170. unsigned int test_rate = 50;
  171. switch (cmd) {
  172. case CLI_INIT:
  173. e->command = "timing test";
  174. e->usage = "Usage: timing test <rate>\n"
  175. " Test a timer with a specified rate, 50/sec by default.\n"
  176. "";
  177. return NULL;
  178. case CLI_GENERATE:
  179. return NULL;
  180. }
  181. if (a->argc != 2 && a->argc != 3) {
  182. return CLI_SHOWUSAGE;
  183. }
  184. if (a->argc == 3) {
  185. unsigned int rate;
  186. if (sscanf(a->argv[2], "%30u", &rate) == 1) {
  187. test_rate = rate;
  188. } else {
  189. ast_cli(a->fd, "Invalid rate '%s', using default of %u\n", a->argv[2], test_rate);
  190. }
  191. }
  192. ast_cli(a->fd, "Attempting to test a timer with %u ticks per second.\n", test_rate);
  193. if (!(timer = ast_timer_open())) {
  194. ast_cli(a->fd, "Failed to open timing fd\n");
  195. return CLI_FAILURE;
  196. }
  197. ast_cli(a->fd, "Using the '%s' timing module for this test.\n", timer->holder->iface->name);
  198. start = ast_tvnow();
  199. ast_timer_set_rate(timer, test_rate);
  200. while (ast_tvdiff_ms((end = ast_tvnow()), start) < 1000) {
  201. int res;
  202. struct pollfd pfd = {
  203. .fd = ast_timer_fd(timer),
  204. .events = POLLIN | POLLPRI,
  205. };
  206. res = ast_poll(&pfd, 1, 100);
  207. if (res == 1) {
  208. count++;
  209. if (ast_timer_ack(timer, 1) < 0) {
  210. ast_cli(a->fd, "Timer failed to acknowledge.\n");
  211. ast_timer_close(timer);
  212. return CLI_FAILURE;
  213. }
  214. } else if (!res) {
  215. ast_cli(a->fd, "poll() timed out! This is bad.\n");
  216. } else if (errno != EAGAIN && errno != EINTR) {
  217. ast_cli(a->fd, "poll() returned error: %s\n", strerror(errno));
  218. }
  219. }
  220. ast_timer_close(timer);
  221. timer = NULL;
  222. ast_cli(a->fd, "It has been %" PRIi64 " milliseconds, and we got %d timer ticks\n",
  223. ast_tvdiff_ms(end, start), count);
  224. return CLI_SUCCESS;
  225. }
  226. static struct ast_cli_entry cli_timing[] = {
  227. AST_CLI_DEFINE(timing_test, "Run a timing test"),
  228. };
  229. static void timing_shutdown(void)
  230. {
  231. ast_cli_unregister_multiple(cli_timing, ARRAY_LEN(cli_timing));
  232. ast_heap_destroy(timing_interfaces);
  233. timing_interfaces = NULL;
  234. }
  235. int ast_timing_init(void)
  236. {
  237. if (!(timing_interfaces = ast_heap_create(2, timing_holder_cmp, 0))) {
  238. return -1;
  239. }
  240. ast_register_cleanup(timing_shutdown);
  241. return ast_cli_register_multiple(cli_timing, ARRAY_LEN(cli_timing));
  242. }