e6b7c25c3f707456bc4a7a65222ee0e15bbc8e7e
[asterisk/asterisk.git] / main / timing.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Digium, Inc.
5  *
6  * Kevin P. Fleming <kpfleming@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
19 /*! \file
20  *
21  * \brief Timing source management
22  *
23  * \author Kevin P. Fleming <kpfleming@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/_private.h"
31
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
38 AST_RWLOCK_DEFINE_STATIC(lock);
39
40 static struct ast_timing_functions timer_funcs;
41
42 void *ast_install_timing_functions(struct ast_timing_functions *funcs)
43 {
44         if (!funcs->timer_open ||
45             !funcs->timer_close ||
46                 !funcs->timer_set_rate ||
47             !funcs->timer_ack ||
48             !funcs->timer_get_event ||
49             !funcs->timer_enable_continuous ||
50             !funcs->timer_disable_continuous) {
51                 return NULL;
52         }
53
54         ast_rwlock_wrlock(&lock);
55
56         if (timer_funcs.timer_open) {
57                 ast_rwlock_unlock(&lock);
58                 ast_log(LOG_NOTICE, "Multiple timing modules are loaded.  You should only load one.\n");
59                 return NULL;
60         }
61         
62         timer_funcs = *funcs;
63
64         ast_rwlock_unlock(&lock);
65
66         return &timer_funcs;
67 }
68
69 void ast_uninstall_timing_functions(void *handle)
70 {
71         ast_rwlock_wrlock(&lock);
72
73         if (handle != &timer_funcs) {
74                 ast_rwlock_unlock(&lock);
75                 return;
76         }
77
78         memset(&timer_funcs, 0, sizeof(timer_funcs));
79
80         ast_rwlock_unlock(&lock);
81 }
82
83 int ast_timer_open(void)
84 {
85         int timer;
86
87         ast_rwlock_rdlock(&lock);
88
89         if (!timer_funcs.timer_open) {
90                 ast_rwlock_unlock(&lock);
91                 return -1;
92         }
93
94         timer = timer_funcs.timer_open();
95
96         ast_rwlock_unlock(&lock);
97
98         return timer;
99 }
100
101 void ast_timer_close(int timer)
102 {
103         ast_rwlock_rdlock(&lock);
104
105         if (!timer_funcs.timer_close) {
106                 ast_rwlock_unlock(&lock);
107                 return;
108         }
109
110         timer_funcs.timer_close(timer);
111
112         ast_rwlock_unlock(&lock);
113 }
114
115 int ast_timer_set_rate(int handle, unsigned int rate)
116 {
117         int res;
118
119         ast_rwlock_rdlock(&lock);
120
121         if (!timer_funcs.timer_set_rate) {
122                 ast_rwlock_unlock(&lock);
123                 return -1;
124         }
125
126         res = timer_funcs.timer_set_rate(handle, rate);
127
128         ast_rwlock_unlock(&lock);
129
130         return res;
131 }
132
133 void ast_timer_ack(int handle, unsigned int quantity)
134 {
135         ast_rwlock_rdlock(&lock);
136
137         if (!timer_funcs.timer_ack) {
138                 ast_rwlock_unlock(&lock);
139                 return;
140         }
141
142         timer_funcs.timer_ack(handle, quantity);
143
144         ast_rwlock_unlock(&lock);
145 }
146
147 int ast_timer_enable_continuous(int handle)
148 {
149         int result;
150
151         ast_rwlock_rdlock(&lock);
152
153         if (!timer_funcs.timer_enable_continuous) {
154                 ast_rwlock_unlock(&lock);
155                 return -1;
156         }
157
158         result = timer_funcs.timer_enable_continuous(handle);
159
160         ast_rwlock_unlock(&lock);
161
162         return result;
163 }
164
165 int ast_timer_disable_continuous(int handle)
166 {
167         int result;
168
169         ast_rwlock_rdlock(&lock);
170
171         if (!timer_funcs.timer_disable_continuous) {
172                 ast_rwlock_unlock(&lock);
173                 return -1;
174         }
175
176         result = timer_funcs.timer_disable_continuous(handle);
177
178         ast_rwlock_unlock(&lock);
179
180         return result;
181 }
182
183 enum ast_timing_event ast_timer_get_event(int handle)
184 {
185         enum ast_timing_event result;
186
187         ast_rwlock_rdlock(&lock);
188
189         if (!timer_funcs.timer_get_event) {
190                 ast_rwlock_unlock(&lock);
191                 return -1;
192         }
193
194         result = timer_funcs.timer_get_event(handle);
195
196         ast_rwlock_unlock(&lock);
197
198         return result;
199 }
200
201 static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
202 {
203         int fd, count = 0;
204         struct timeval start, end;
205
206         switch (cmd) {
207         case CLI_INIT:
208                 e->command = "timing test";
209                 e->usage = "Usage: timing test\n";
210                 return NULL;
211         case CLI_GENERATE:
212                 return NULL;
213         }
214
215         ast_cli(a->fd, "Attempting to test a timer with 50 ticks per second ...\n");
216
217         if ((fd = ast_timer_open()) == -1) {
218                 ast_cli(a->fd, "Failed to open timing fd\n");
219                 return CLI_FAILURE;
220         }
221
222         start = ast_tvnow();
223
224         ast_timer_set_rate(fd, 50);
225
226         while (ast_tvdiff_ms((end = ast_tvnow()), start) < 1000) {
227                 int res;
228                 struct pollfd pfd = {
229                         .fd = fd,
230                         .events = POLLIN | POLLPRI,
231                 };
232
233                 res = poll(&pfd, 1, 100);
234
235                 if (res == 1) {
236                         count++;
237                         ast_timer_ack(fd, 1);
238                 } else if (!res) {
239                         ast_cli(a->fd, "poll() timed out!  This is bad.\n");
240                 } else if (errno != EAGAIN && errno != EINTR) {
241                         ast_cli(a->fd, "poll() returned error: %s\n", strerror(errno));
242                 }
243         }
244
245         ast_timer_close(fd);
246
247         ast_cli(a->fd, "It has been %d milliseconds, and we got %d timer ticks\n", 
248                 ast_tvdiff_ms(end, start), count);
249
250         return CLI_SUCCESS;
251 }
252
253 static struct ast_cli_entry cli_timing[] = {
254         AST_CLI_DEFINE(timing_test, "Run a timing test"),
255 };
256
257 int ast_timing_init(void)
258 {
259         return ast_cli_register_multiple(cli_timing, ARRAY_LEN(cli_timing));
260 }