Include poll-compat.h
[asterisk/asterisk.git] / main / timing.c
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
20 /*! \file
21  *
22  * \brief Timing source management
23  *
24  * \author Kevin P. Fleming <kpfleming@digium.com>
25  * \author Russell Bryant <russell@digium.com>
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include "asterisk/_private.h"
33
34 #include "asterisk/timing.h"
35 #include "asterisk/lock.h"
36 #include "asterisk/cli.h"
37 #include "asterisk/utils.h"
38 #include "asterisk/time.h"
39 #include "asterisk/heap.h"
40 #include "asterisk/module.h"
41 #include "asterisk/poll-compat.h"
42
43 struct timing_holder {
44         /*! Do _not_ move this from the beginning of the struct. */
45         ssize_t __heap_index;
46         struct ast_module *mod;
47         struct ast_timing_interface *iface;
48 };
49
50 static struct ast_heap *timing_interfaces;
51
52 static int timing_holder_cmp(void *_h1, void *_h2)
53 {
54         struct timing_holder *h1 = _h1;
55         struct timing_holder *h2 = _h2;
56
57         if (h1->iface->priority > h2->iface->priority) {
58                 return 1;
59         } else if (h1->iface->priority == h2->iface->priority) {
60                 return 0;
61         } else {
62                 return -1;
63         }
64 }
65
66 void *_ast_register_timing_interface(struct ast_timing_interface *funcs, 
67                 struct ast_module *mod)
68 {
69         struct timing_holder *h;
70
71         if (!funcs->timer_open ||
72             !funcs->timer_close ||
73                 !funcs->timer_set_rate ||
74             !funcs->timer_ack ||
75             !funcs->timer_get_event ||
76                 !funcs->timer_get_max_rate ||
77             !funcs->timer_enable_continuous ||
78             !funcs->timer_disable_continuous) {
79                 return NULL;
80         }
81
82         if (!(h = ast_calloc(1, sizeof(*h)))) {
83                 return NULL;
84         }
85
86         h->iface = funcs;
87         h->mod = mod;
88
89         ast_heap_wrlock(timing_interfaces);
90         ast_heap_push(timing_interfaces, h);
91         ast_heap_unlock(timing_interfaces);
92
93         return h;
94 }
95
96 int ast_unregister_timing_interface(void *handle)
97 {
98         struct timing_holder *h = handle;
99         int res = -1;
100
101         ast_heap_wrlock(timing_interfaces);
102         h = ast_heap_remove(timing_interfaces, h);
103         ast_heap_unlock(timing_interfaces);
104
105         if (h) {
106                 ast_free(h);
107                 h = NULL;
108                 res = 0;
109         }
110
111         return res;
112 }
113
114 int ast_timer_open(void)
115 {
116         int fd = -1;
117         struct timing_holder *h;
118
119         ast_heap_rdlock(timing_interfaces);
120
121         if ((h = ast_heap_peek(timing_interfaces, 1))) {
122                 fd = h->iface->timer_open();
123                 ast_module_ref(h->mod);
124         }
125
126         ast_heap_unlock(timing_interfaces);
127
128         return fd;
129 }
130
131 void ast_timer_close(int timer)
132 {
133         struct timing_holder *h;
134
135         ast_heap_rdlock(timing_interfaces);
136
137         if ((h = ast_heap_peek(timing_interfaces, 1))) {
138                 h->iface->timer_close(timer);
139                 ast_module_unref(h->mod);
140         }
141
142         ast_heap_unlock(timing_interfaces);
143 }
144
145 int ast_timer_set_rate(int handle, unsigned int rate)
146 {
147         struct timing_holder *h;
148         int res = -1;
149
150         ast_heap_rdlock(timing_interfaces);
151
152         if ((h = ast_heap_peek(timing_interfaces, 1))) {
153                 res = h->iface->timer_set_rate(handle, rate);
154         }
155
156         ast_heap_unlock(timing_interfaces);
157
158         return res;
159 }
160
161 void ast_timer_ack(int handle, unsigned int quantity)
162 {
163         struct timing_holder *h;
164
165         ast_heap_rdlock(timing_interfaces);
166
167         if ((h = ast_heap_peek(timing_interfaces, 1))) {
168                 h->iface->timer_ack(handle, quantity);
169         }
170
171         ast_heap_unlock(timing_interfaces);
172 }
173
174 int ast_timer_enable_continuous(int handle)
175 {
176         struct timing_holder *h;
177         int res = -1;
178
179         ast_heap_rdlock(timing_interfaces);
180
181         if ((h = ast_heap_peek(timing_interfaces, 1))) {
182                 res = h->iface->timer_enable_continuous(handle);
183         }
184
185         ast_heap_unlock(timing_interfaces);
186
187         return res;
188 }
189
190 int ast_timer_disable_continuous(int handle)
191 {
192         struct timing_holder *h;
193         int res = -1;
194
195         ast_heap_rdlock(timing_interfaces);
196
197         if ((h = ast_heap_peek(timing_interfaces, 1))) {
198                 res = h->iface->timer_disable_continuous(handle);
199         }
200
201         ast_heap_unlock(timing_interfaces);
202
203         return res;
204 }
205
206 enum ast_timer_event ast_timer_get_event(int handle)
207 {
208         struct timing_holder *h;
209         enum ast_timer_event res = -1;
210
211         ast_heap_rdlock(timing_interfaces);
212
213         if ((h = ast_heap_peek(timing_interfaces, 1))) {
214                 res = h->iface->timer_get_event(handle);
215         }
216
217         ast_heap_unlock(timing_interfaces);
218
219         return res;
220 }
221
222 unsigned int ast_timer_get_max_rate(int handle)
223 {
224         struct timing_holder *h;
225         unsigned int res = 0;
226
227         ast_heap_rdlock(timing_interfaces);
228
229         if ((h = ast_heap_peek(timing_interfaces, 1))) {
230                 res = h->iface->timer_get_max_rate(handle);
231         }
232
233         ast_heap_unlock(timing_interfaces);
234
235         return res;
236 }
237
238 static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
239 {
240         int fd, count = 0;
241         struct timeval start, end;
242         unsigned int test_rate = 50;
243         struct timing_holder *h;
244
245         switch (cmd) {
246         case CLI_INIT:
247                 e->command = "timing test";
248                 e->usage = "Usage: timing test <rate>\n"
249                            "   Test a timer with a specified rate, 100/sec by default.\n"
250                            "";
251                 return NULL;
252         case CLI_GENERATE:
253                 return NULL;
254         }
255
256         if (a->argc != 2 && a->argc != 3) {
257                 return CLI_SHOWUSAGE;
258         }
259
260         if (a->argc == 3) {
261                 unsigned int rate;
262                 if (sscanf(a->argv[2], "%u", &rate) == 1) {
263                         test_rate = rate;
264                 } else {
265                         ast_cli(a->fd, "Invalid rate '%s', using default of %u\n", a->argv[2], test_rate);      
266                 }
267         }
268
269         ast_cli(a->fd, "Attempting to test a timer with %u ticks per second.\n", test_rate);
270
271         if ((fd = ast_timer_open()) == -1) {
272                 ast_cli(a->fd, "Failed to open timing fd\n");
273                 return CLI_FAILURE;
274         }
275
276         ast_heap_rdlock(timing_interfaces);
277         if ((h = ast_heap_peek(timing_interfaces, 1))) {
278                 ast_cli(a->fd, "Using the '%s' timing module for this test.\n", h->iface->name);
279                 h = NULL;
280         }
281         ast_heap_unlock(timing_interfaces);
282
283         start = ast_tvnow();
284
285         ast_timer_set_rate(fd, test_rate);
286
287         while (ast_tvdiff_ms((end = ast_tvnow()), start) < 1000) {
288                 int res;
289                 struct pollfd pfd = {
290                         .fd = fd,
291                         .events = POLLIN | POLLPRI,
292                 };
293
294                 res = ast_poll(&pfd, 1, 100);
295
296                 if (res == 1) {
297                         count++;
298                         ast_timer_ack(fd, 1);
299                 } else if (!res) {
300                         ast_cli(a->fd, "poll() timed out!  This is bad.\n");
301                 } else if (errno != EAGAIN && errno != EINTR) {
302                         ast_cli(a->fd, "poll() returned error: %s\n", strerror(errno));
303                 }
304         }
305
306         ast_timer_close(fd);
307
308         ast_cli(a->fd, "It has been %d milliseconds, and we got %d timer ticks\n", 
309                 ast_tvdiff_ms(end, start), count);
310
311         return CLI_SUCCESS;
312 }
313
314 static struct ast_cli_entry cli_timing[] = {
315         AST_CLI_DEFINE(timing_test, "Run a timing test"),
316 };
317
318 int ast_timing_init(void)
319 {
320         if (!(timing_interfaces = ast_heap_create(2, timing_holder_cmp, 0))) {
321                 return -1;
322         }
323
324         return ast_cli_register_multiple(cli_timing, ARRAY_LEN(cli_timing));
325 }