Mon Jun 27 16:50:56 2011

Asterisk developer's documentation


res_timing_kqueue.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2010, Digium, Inc.
00005  *
00006  * Tilghman Lesher <tlesher AT digium DOT com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Tilghman Lesher <tlesher AT digium DOT com>
00022  *
00023  * \brief kqueue timing interface
00024  */
00025 
00026 /*** MODULEINFO
00027    <depend>kqueue</depend>
00028    <conflict>launchd</conflict>
00029  ***/
00030 
00031 #include "asterisk.h"
00032 
00033 #include <sys/types.h>
00034 #include <sys/event.h>
00035 #include <sys/time.h>
00036 
00037 #include "asterisk/module.h"
00038 #include "asterisk/astobj2.h"
00039 #include "asterisk/timing.h"
00040 #include "asterisk/logger.h"
00041 #include "asterisk/utils.h"
00042 #include "asterisk/time.h"
00043 #include "asterisk/test.h"
00044 #include "asterisk/poll-compat.h"       /* for ast_poll() */
00045 
00046 static void *timing_funcs_handle;
00047 
00048 static int kqueue_timer_open(void);
00049 static void kqueue_timer_close(int handle);
00050 static int kqueue_timer_set_rate(int handle, unsigned int rate);
00051 static void kqueue_timer_ack(int handle, unsigned int quantity);
00052 static int kqueue_timer_enable_continuous(int handle);
00053 static int kqueue_timer_disable_continuous(int handle);
00054 static enum ast_timer_event kqueue_timer_get_event(int handle);
00055 static unsigned int kqueue_timer_get_max_rate(int handle);
00056 
00057 static struct ast_timing_interface kqueue_timing = {
00058    .name = "kqueue",
00059    .priority = 150,
00060    .timer_open = kqueue_timer_open,
00061    .timer_close = kqueue_timer_close,
00062    .timer_set_rate = kqueue_timer_set_rate,
00063    .timer_ack = kqueue_timer_ack,
00064    .timer_enable_continuous = kqueue_timer_enable_continuous,
00065    .timer_disable_continuous = kqueue_timer_disable_continuous,
00066    .timer_get_event = kqueue_timer_get_event,
00067    .timer_get_max_rate = kqueue_timer_get_max_rate,
00068 };
00069 
00070 static struct ao2_container *kqueue_timers;
00071 
00072 struct kqueue_timer {
00073    int handle;
00074    uint64_t nsecs;
00075    uint64_t unacked;
00076    unsigned int is_continuous:1;
00077 };
00078 
00079 static int kqueue_timer_hash(const void *obj, const int flags)
00080 {
00081    const struct kqueue_timer *timer = obj;
00082 
00083    return timer->handle;
00084 }
00085 
00086 static int kqueue_timer_cmp(void *obj, void *args, int flags)
00087 {
00088    struct kqueue_timer *timer1 = obj, *timer2 = args;
00089    return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
00090 }
00091 
00092 static void timer_destroy(void *obj)
00093 {
00094    struct kqueue_timer *timer = obj;
00095    close(timer->handle);
00096 }
00097 
00098 #define lookup_timer(a) _lookup_timer(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
00099 static struct kqueue_timer *_lookup_timer(int handle, const char *file, int line, const char *func)
00100 {
00101    struct kqueue_timer *our_timer, find_helper = {
00102       .handle = handle,
00103    };
00104 
00105    if (!(our_timer = ao2_find(kqueue_timers, &find_helper, OBJ_POINTER))) {
00106       ast_log(__LOG_ERROR, file, line, func, "Couldn't find timer with handle %d\n", handle);
00107       /* API says we set errno */
00108       errno = ESRCH;
00109       return NULL;
00110    }
00111    return our_timer;
00112 }
00113 
00114 static int kqueue_timer_open(void)
00115 {
00116    struct kqueue_timer *timer;
00117    int handle;
00118 
00119    if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
00120       ast_log(LOG_ERROR, "Could not allocate memory for kqueue_timer structure\n");
00121       return -1;
00122    }
00123    if ((handle = kqueue()) < 0) {
00124       ast_log(LOG_ERROR, "Failed to create kqueue timer: %s\n", strerror(errno));
00125       ao2_ref(timer, -1);
00126       return -1;
00127    }
00128 
00129    timer->handle = handle;
00130    ao2_link(kqueue_timers, timer);
00131    /* Get rid of the reference from the allocation */
00132    ao2_ref(timer, -1);
00133    return handle;
00134 }
00135 
00136 static void kqueue_timer_close(int handle)
00137 {
00138    struct kqueue_timer *our_timer;
00139 
00140    if (!(our_timer = lookup_timer(handle))) {
00141       return;
00142    }
00143 
00144    ao2_unlink(kqueue_timers, our_timer);
00145    ao2_ref(our_timer, -1);
00146 }
00147 
00148 static void kqueue_set_nsecs(struct kqueue_timer *our_timer, uint64_t nsecs)
00149 {
00150    struct timespec nowait = { 0, 1 };
00151 #ifdef HAVE_KEVENT64
00152    struct kevent64_s kev;
00153 
00154    EV_SET64(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_NSECONDS,
00155       nsecs, 0, 0, 0);
00156    kevent64(our_timer->handle, &kev, 1, NULL, 0, 0, &nowait);
00157 #else
00158    struct kevent kev;
00159 
00160    EV_SET(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE,
00161 #ifdef NOTE_NSECONDS
00162       nsecs <= 0xFFffFFff ? NOTE_NSECONDS :
00163 #endif
00164 #ifdef NOTE_USECONDS
00165       NOTE_USECONDS
00166 #else /* Milliseconds, if no constants are defined */
00167       0
00168 #endif
00169       ,
00170 #ifdef NOTE_NSECONDS
00171       nsecs <= 0xFFffFFff ? nsecs :
00172 #endif
00173 #ifdef NOTE_USECONDS
00174    nsecs / 1000
00175 #else /* Milliseconds, if nothing else is defined */
00176    nsecs / 1000000
00177 #endif
00178    , NULL);
00179    kevent(our_timer->handle, &kev, 1, NULL, 0, &nowait);
00180 #endif
00181 }
00182 
00183 static int kqueue_timer_set_rate(int handle, unsigned int rate)
00184 {
00185    struct kqueue_timer *our_timer;
00186 
00187    if (!(our_timer = lookup_timer(handle))) {
00188       return -1;
00189    }
00190 
00191    kqueue_set_nsecs(our_timer, (our_timer->nsecs = rate ? (long) (1000000000 / rate) : 0L));
00192    ao2_ref(our_timer, -1);
00193 
00194    return 0;
00195 }
00196 
00197 static void kqueue_timer_ack(int handle, unsigned int quantity)
00198 {
00199    struct kqueue_timer *our_timer;
00200 
00201    if (!(our_timer = lookup_timer(handle))) {
00202       return;
00203    }
00204 
00205    if (our_timer->unacked < quantity) {
00206       ast_debug(1, "Acking more events than have expired?!!\n");
00207       our_timer->unacked = 0;
00208    } else {
00209       our_timer->unacked -= quantity;
00210    }
00211 }
00212 
00213 static int kqueue_timer_enable_continuous(int handle)
00214 {
00215    struct kqueue_timer *our_timer;
00216 
00217    if (!(our_timer = lookup_timer(handle))) {
00218       return -1;
00219    }
00220 
00221    kqueue_set_nsecs(our_timer, 1);
00222    our_timer->is_continuous = 1;
00223    our_timer->unacked = 0;
00224    ao2_ref(our_timer, -1);
00225    return 0;
00226 }
00227 
00228 static int kqueue_timer_disable_continuous(int handle)
00229 {
00230    struct kqueue_timer *our_timer;
00231 
00232    if (!(our_timer = lookup_timer(handle))) {
00233       return -1;
00234    }
00235 
00236    kqueue_set_nsecs(our_timer, our_timer->nsecs);
00237    our_timer->is_continuous = 0;
00238    our_timer->unacked = 0;
00239    ao2_ref(our_timer, -1);
00240    return 0;
00241 }
00242 
00243 static enum ast_timer_event kqueue_timer_get_event(int handle)
00244 {
00245    enum ast_timer_event res = -1;
00246    struct kqueue_timer *our_timer;
00247    struct timespec sixty_seconds = { 60, 0 };
00248    struct kevent kev;
00249 
00250    if (!(our_timer = lookup_timer(handle))) {
00251       return -1;
00252    }
00253 
00254    /* If we have non-ACKed events, just return immediately */
00255    if (our_timer->unacked == 0) {
00256       if (kevent(handle, NULL, 0, &kev, 1, &sixty_seconds) > 0) {
00257          our_timer->unacked += kev.data;
00258       }
00259    }
00260 
00261    if (our_timer->unacked > 0) {
00262       res = our_timer->is_continuous ? AST_TIMING_EVENT_CONTINUOUS : AST_TIMING_EVENT_EXPIRED;
00263    }
00264 
00265    ao2_ref(our_timer, -1);
00266    return res;
00267 }
00268 
00269 static unsigned int kqueue_timer_get_max_rate(int handle)
00270 {
00271    /* Actually, the max rate is 2^64-1 seconds, but that's not representable in a 32-bit integer. */
00272    return UINT_MAX;
00273 }
00274 
00275 #ifdef TEST_FRAMEWORK
00276 AST_TEST_DEFINE(test_kqueue_timing)
00277 {
00278    int res = AST_TEST_PASS, handle, i;
00279    uint64_t diff;
00280    struct pollfd pfd = { 0, POLLIN, 0 };
00281    struct kqueue_timer *kt;
00282    struct timeval start;
00283 
00284    switch (cmd) {
00285    case TEST_INIT:
00286       info->name = "test_kqueue_timing";
00287       info->category = "/res/res_timing_kqueue/";
00288       info->summary = "Test KQueue timing interface";
00289       info->description = "Verify that the KQueue timing interface correctly generates timing events";
00290       return AST_TEST_NOT_RUN;
00291    case TEST_EXECUTE:
00292       break;
00293    }
00294 
00295    if (!(handle = kqueue_timer_open())) {
00296       ast_test_status_update(test, "Cannot open timer!\n");
00297       return AST_TEST_FAIL;
00298    }
00299 
00300    do {
00301       pfd.fd = handle;
00302       if (kqueue_timer_set_rate(handle, 1000)) {
00303          ast_test_status_update(test, "Cannot set timer rate to 1000/s\n");
00304          res = AST_TEST_FAIL;
00305          break;
00306       }
00307       if (ast_poll(&pfd, 1, 1000) < 1) {
00308          ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
00309          res = AST_TEST_FAIL;
00310          break;
00311       }
00312       if (pfd.revents != POLLIN) {
00313          ast_test_status_update(test, "poll() should have returned POLLIN, but instead returned %hd\n", pfd.revents);
00314          res = AST_TEST_FAIL;
00315          break;
00316       }
00317       if (!(kt = lookup_timer(handle))) {
00318          ast_test_status_update(test, "Could not find timer structure in container?!!\n");
00319          res = AST_TEST_FAIL;
00320          break;
00321       }
00322       if (kqueue_timer_get_event(handle) <= 0) {
00323          ast_test_status_update(test, "No events generated after a poll returned successfully?!!\n");
00324          res = AST_TEST_FAIL;
00325          break;
00326       }
00327 #if 0
00328       if (kt->unacked == 0) {
00329          ast_test_status_update(test, "Unacked events is 0, but there should be at least 1.\n");
00330          res = AST_TEST_FAIL;
00331          break;
00332       }
00333 #endif
00334       kqueue_timer_enable_continuous(handle);
00335       start = ast_tvnow();
00336       for (i = 0; i < 100; i++) {
00337          if (ast_poll(&pfd, 1, 1000) < 1) {
00338             ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
00339             res = AST_TEST_FAIL;
00340             break;
00341          }
00342          if (kqueue_timer_get_event(handle) <= 0) {
00343             ast_test_status_update(test, "No events generated in continuous mode after 1 microsecond?!!\n");
00344             res = AST_TEST_FAIL;
00345             break;
00346          }
00347       }
00348       diff = ast_tvdiff_us(ast_tvnow(), start);
00349       ast_test_status_update(test, "diff is %llu\n", diff);
00350       /*
00351       if (abs(diff - kt->unacked) == 0) {
00352          ast_test_status_update(test, "Unacked events should be around 1000, not %llu\n", kt->unacked);
00353          res = AST_TEST_FAIL;
00354       }
00355       */
00356    } while (0);
00357    kqueue_timer_close(handle);
00358    return res;
00359 }
00360 #endif
00361 
00362 static int load_module(void)
00363 {
00364    if (!(kqueue_timers = ao2_container_alloc(563, kqueue_timer_hash, kqueue_timer_cmp))) {
00365       return AST_MODULE_LOAD_DECLINE;
00366    }
00367 
00368    if (!(timing_funcs_handle = ast_register_timing_interface(&kqueue_timing))) {
00369       ao2_ref(kqueue_timers, -1);
00370       return AST_MODULE_LOAD_DECLINE;
00371    }
00372 
00373    AST_TEST_REGISTER(test_kqueue_timing);
00374    return AST_MODULE_LOAD_SUCCESS;
00375 }
00376 
00377 static int unload_module(void)
00378 {
00379    int res;
00380 
00381    AST_TEST_UNREGISTER(test_kqueue_timing);
00382    if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
00383       ao2_ref(kqueue_timers, -1);
00384       kqueue_timers = NULL;
00385    }
00386 
00387    return res;
00388 }
00389 
00390 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "KQueue Timing Interface",
00391       .load = load_module,
00392       .unload = unload_module,
00393       .load_pri = AST_MODPRI_CHANNEL_DEPEND,
00394       );

Generated on Mon Jun 27 16:50:56 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7