36 #include <sys/types.h>
37 #include <sys/event.h>
47 #include "asterisk/poll-compat.h"
49 static void *timing_funcs_handle;
51 static void *kqueue_timer_open(
void);
52 static void kqueue_timer_close(
void *data);
53 static int kqueue_timer_set_rate(
void *data,
unsigned int rate);
54 static int kqueue_timer_ack(
void *data,
unsigned int quantity);
55 static int kqueue_timer_enable_continuous(
void *data);
56 static int kqueue_timer_disable_continuous(
void *data);
57 static enum ast_timer_event kqueue_timer_get_event(
void *data);
58 static unsigned int kqueue_timer_get_max_rate(
void *data);
59 static int kqueue_timer_fd(
void *data);
64 .timer_open = kqueue_timer_open,
65 .timer_close = kqueue_timer_close,
66 .timer_set_rate = kqueue_timer_set_rate,
67 .timer_ack = kqueue_timer_ack,
68 .timer_enable_continuous = kqueue_timer_enable_continuous,
69 .timer_disable_continuous = kqueue_timer_disable_continuous,
70 .timer_get_event = kqueue_timer_get_event,
71 .timer_get_max_rate = kqueue_timer_get_max_rate,
72 .timer_fd = kqueue_timer_fd,
80 unsigned int continuous_fd_valid:1;
82 unsigned int is_continuous:1;
86 #define CONTINUOUS_EVFILT_TYPE EVFILT_USER
87 static int kqueue_timer_init_continuous_event(
struct kqueue_timer *timer)
92 static int kqueue_timer_enable_continuous_event(
struct kqueue_timer *timer)
96 EV_SET(&kev[0], (uintptr_t)timer, EVFILT_USER, EV_ADD | EV_ENABLE,
98 EV_SET(&kev[1], (uintptr_t)timer, EVFILT_USER, 0, NOTE_TRIGGER,
100 return kevent(timer->handle, kev, 2, NULL, 0, NULL);
103 static int kqueue_timer_disable_continuous_event(
struct kqueue_timer *timer)
107 EV_SET(&kev, (uintptr_t)timer, EVFILT_USER, EV_DELETE, 0, 0, NULL);
108 return kevent(timer->handle, &kev, 1, NULL, 0, NULL);
111 static void kqueue_timer_fini_continuous_event(
struct kqueue_timer *timer)
117 #define CONTINUOUS_EVFILT_TYPE EVFILT_READ
118 static int kqueue_timer_init_continuous_event(
struct kqueue_timer *timer)
123 retval = pipe(pipefds);
125 timer->continuous_fd = pipefds[0];
126 timer->continuous_fd_valid = 1;
132 static void kqueue_timer_fini_continuous_event(
struct kqueue_timer *timer)
134 if (timer->continuous_fd_valid) {
135 close(timer->continuous_fd);
139 static int kqueue_timer_enable_continuous_event(
struct kqueue_timer *timer)
143 EV_SET(&kev, timer->continuous_fd, EVFILT_READ, EV_ADD | EV_ENABLE,
145 return kevent(timer->handle, &kev, 1, NULL, 0, NULL);
148 static int kqueue_timer_disable_continuous_event(
struct kqueue_timer *timer)
152 EV_SET(&kev, timer->continuous_fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
153 return kevent(timer->handle, &kev, 1, NULL, 0, NULL);
157 static void timer_destroy(
void *obj)
160 ast_debug(5,
"[%d]: Timer Destroy\n", timer->handle);
161 kqueue_timer_fini_continuous_event(timer);
162 if (timer->handle > -1) {
163 close(timer->handle);
167 static void *kqueue_timer_open(
void)
171 if (!(timer = ao2_alloc(
sizeof(*timer), timer_destroy))) {
172 ast_log(LOG_ERROR,
"Alloc failed for kqueue_timer structure\n");
176 if ((timer->handle = kqueue()) < 0) {
177 ast_log(LOG_ERROR,
"Failed to create kqueue fd: %s\n",
183 if (kqueue_timer_init_continuous_event(timer) != 0) {
184 ast_log(LOG_ERROR,
"Failed to create continuous event: %s\n",
189 ast_debug(5,
"[%d]: Create timer\n", timer->handle);
193 static void kqueue_timer_close(
void *data)
197 ast_debug(5,
"[%d]: Timer Close\n", timer->handle);
205 static intptr_t kqueue_scale_period(
unsigned int period_ns,
int *units)
207 uint64_t period = period_ns;
210 if (period < INTPTR_MAX) {
211 *units = NOTE_NSECONDS;
215 if (period < INTPTR_MAX) {
216 *units = NOTE_USECONDS;
220 *units = NOTE_MSECONDS;
226 *units = NOTE_MSECONDS;
233 if (period > INTPTR_MAX) {
239 static int kqueue_timer_set_rate(
void *data,
unsigned int rate)
251 if (timer->period == 0) {
259 flags = EV_ADD | EV_ENABLE;
260 period_ns = (uint64_t)1000000000 / rate;
261 timer->period = kqueue_scale_period(period_ns, &units);
264 timer->handle, units, (uintmax_t)timer->period);
265 EV_SET(&kev, timer->handle, EVFILT_TIMER, flags, units,
266 timer->period, NULL);
267 retval = kevent(timer->handle, &kev, 1, NULL, 0, NULL);
270 ast_log(LOG_ERROR,
"[%d]: Error queing timer: %s\n",
271 timer->handle, strerror(errno));
279 static int kqueue_timer_ack(
void *data,
unsigned int quantity)
281 static struct timespec ts_nowait = { 0, 0 };
283 struct kevent kev[2];
288 retval = kevent(timer->handle, NULL, 0, kev, 2, &ts_nowait);
290 ast_log(LOG_ERROR,
"[%d]: Error sampling kqueue: %s\n",
291 timer->handle, strerror(errno));
296 for (i = 0; i < retval; i++) {
299 if (kev[i].data > quantity) {
300 ast_log(LOG_ERROR,
"[%d]: Missed %ju\n",
302 (uintmax_t)kev[i].data - quantity);
305 case CONTINUOUS_EVFILT_TYPE:
306 if (!timer->is_continuous) {
308 "[%d]: Spurious user event\n",
313 ast_log(LOG_ERROR,
"[%d]: Spurious kevent type %d.\n",
314 timer->handle, kev[i].filter);
323 static int kqueue_timer_enable_continuous(
void *data)
330 if (!timer->is_continuous) {
331 ast_debug(5,
"[%d]: Enable Continuous\n", timer->handle);
332 retval = kqueue_timer_enable_continuous_event(timer);
335 "[%d]: Error signaling continuous event: %s\n",
336 timer->handle, strerror(errno));
338 timer->is_continuous = 1;
346 static int kqueue_timer_disable_continuous(
void *data)
353 if (timer->is_continuous) {
354 ast_debug(5,
"[%d]: Disable Continuous\n", timer->handle);
355 retval = kqueue_timer_disable_continuous_event(timer);
358 "[%d]: Error clearing continuous event: %s\n",
359 timer->handle, strerror(errno));
361 timer->is_continuous = 0;
369 static enum ast_timer_event kqueue_timer_get_event(
void *data)
372 enum ast_timer_event res;
374 if (timer->is_continuous) {
375 res = AST_TIMING_EVENT_CONTINUOUS;
377 res = AST_TIMING_EVENT_EXPIRED;
383 static unsigned int kqueue_timer_get_max_rate(
void *data)
385 return INTPTR_MAX > UINT_MAX ? UINT_MAX : INTPTR_MAX;
388 static int kqueue_timer_fd(
void *data)
392 return timer->handle;
395 #ifdef TEST_FRAMEWORK
398 int res = AST_TEST_PASS, i;
400 struct pollfd pfd = { 0, POLLIN, 0 };
402 struct timeval start;
406 info->name =
"test_kqueue_timing";
407 info->category =
"/res/res_timing_kqueue/";
408 info->summary =
"Test KQueue timing interface";
409 info->description =
"Verify that the KQueue timing interface correctly generates timing events";
410 return AST_TEST_NOT_RUN;
415 if (!(kt = kqueue_timer_open())) {
416 ast_test_status_update(
test,
"Cannot open timer!\n");
417 return AST_TEST_FAIL;
421 pfd.fd = kqueue_timer_fd(kt);
422 if (kqueue_timer_set_rate(kt, 1000)) {
423 ast_test_status_update(
test,
"Cannot set timer rate to 1000/s\n");
427 if (ast_poll(&pfd, 1, 1000) < 1) {
428 ast_test_status_update(
test,
"Polling on a kqueue doesn't work\n");
432 if (pfd.revents != POLLIN) {
433 ast_test_status_update(
test,
"poll() should have returned POLLIN, but instead returned %hd\n", pfd.revents);
437 if (kqueue_timer_get_event(kt) <= 0) {
438 ast_test_status_update(
test,
"No events generated after a poll returned successfully?!!\n");
442 if (kqueue_timer_ack(kt, 1) != 0) {
443 ast_test_status_update(
test,
"Acking event failed.\n");
448 kqueue_timer_enable_continuous(kt);
450 for (i = 0; i < 100; i++) {
451 if (ast_poll(&pfd, 1, 1000) < 1) {
452 ast_test_status_update(
test,
"Polling on a kqueue doesn't work\n");
456 if (kqueue_timer_get_event(kt) <= 0) {
457 ast_test_status_update(
test,
"No events generated in continuous mode after 1 microsecond?!!\n");
461 if (kqueue_timer_ack(kt, 1) != 0) {
462 ast_test_status_update(
test,
"Acking event failed.\n");
469 ast_test_status_update(
test,
"diff is %llu\n", diff);
471 kqueue_timer_close(kt);
492 AST_TEST_REGISTER(test_kqueue_timing);
496 static int unload_module(
void)
498 AST_TEST_UNREGISTER(test_kqueue_timing);
503 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"KQueue Timing Interface",
504 .support_level = AST_MODULE_SUPPORT_EXTENDED,
506 .unload = unload_module,
Asterisk main include file. File version handling, generic pbx functions.
Time-related functions and macros.
int ast_unregister_timing_interface(void *handle)
Unregister a previously registered timing interface.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
static int load_module(void)
Load the module.
#define ast_debug(level,...)
Log a DEBUG message.
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
#define AST_TEST_DEFINE(hdr)
#define ast_register_timing_interface(i)
Register a set of timing functions.
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
Timing source management.