[asterisk-commits] tilghman: trunk r262852 - in /trunk: ./ build_tools/ include/asterisk/ main/s...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu May 13 00:37:35 CDT 2010
Author: tilghman
Date: Thu May 13 00:37:31 2010
New Revision: 262852
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=262852
Log:
Add kqueue(2) implementation to Asterisk in various places.
This will save a considerable amount of CPU on the BSDs, including Mac OS X,
as it eliminates several places in the code that we previously used a busy
loop. Additionally, this adds a res_timing interface, using kqueue timers.
Review: https://reviewboard.asterisk.org/r/543/
Added:
trunk/res/res_timing_kqueue.c (with props)
Modified:
trunk/Makefile
trunk/build_tools/menuselect-deps.in
trunk/configure
trunk/configure.ac
trunk/include/asterisk/autoconfig.h.in
trunk/main/stdtime/localtime.c
trunk/pbx/pbx_spool.c
trunk/tests/test_time.c
Modified: trunk/Makefile
URL: http://svnview.digium.com/svn/asterisk/trunk/Makefile?view=diff&rev=262852&r1=262851&r2=262852
==============================================================================
--- trunk/Makefile (original)
+++ trunk/Makefile Thu May 13 00:37:31 2010
@@ -158,6 +158,9 @@
ASTVARLIBDIR=$(localstatedir)/lib/asterisk
ASTDBDIR=$(ASTVARLIBDIR)
endif
+ifneq ($(findstring darwin,$(OSARCH)),)
+ ASTVARRUNDIR=/Library/Application Support/Asterisk/Run
+endif
ASTKEYDIR=$(ASTVARLIBDIR)
endif
ifeq ($(ASTDATADIR),)
@@ -559,7 +562,7 @@
mkdir -p $(DESTDIR)$(ASTSBINDIR)
mkdir -p $(DESTDIR)$(ASTETCDIR)
mkdir -p $(DESTDIR)$(ASTBINDIR)
- mkdir -p $(DESTDIR)$(ASTVARRUNDIR)
+ mkdir -p "$(DESTDIR)$(ASTVARRUNDIR)"
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/dictate
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/system
@@ -572,7 +575,7 @@
$(LN) -sf asterisk $(DESTDIR)$(ASTSBINDIR)/rasterisk
$(INSTALL) -m 755 contrib/scripts/astgenkey $(DESTDIR)$(ASTSBINDIR)/
$(INSTALL) -m 755 contrib/scripts/autosupport $(DESTDIR)$(ASTSBINDIR)/
- if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ]; then \
+ if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk -a ! -f /sbin/launchd ]; then \
cat contrib/scripts/safe_asterisk | sed 's|__ASTERISK_SBIN_DIR__|$(ASTSBINDIR)|;s|__ASTERISK_VARRUN_DIR__|$(ASTVARRUNDIR)|;' > $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ;\
chmod 755 $(DESTDIR)$(ASTSBINDIR)/safe_asterisk;\
fi
Modified: trunk/build_tools/menuselect-deps.in
URL: http://svnview.digium.com/svn/asterisk/trunk/build_tools/menuselect-deps.in?view=diff&rev=262852&r1=262851&r2=262852
==============================================================================
--- trunk/build_tools/menuselect-deps.in (original)
+++ trunk/build_tools/menuselect-deps.in Thu May 13 00:37:31 2010
@@ -21,6 +21,7 @@
ISDNNET=@PBX_ISDNNET@
IXJUSER=@PBX_IXJUSER@
JACK=@PBX_JACK@
+KQUEUE=@PBX_KQUEUE@
LDAP=@PBX_LDAP@
LIBXML2=@PBX_LIBXML2@
LTDL=@PBX_LTDL@
Modified: trunk/configure.ac
URL: http://svnview.digium.com/svn/asterisk/trunk/configure.ac?view=diff&rev=262852&r1=262851&r2=262852
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Thu May 13 00:37:31 2010
@@ -319,6 +319,7 @@
AST_EXT_LIB_SETUP([IODBC], [iODBC], [iodbc])
AST_EXT_LIB_SETUP([ISDNNET], [ISDN4Linux], [isdnnet])
AST_EXT_LIB_SETUP([JACK], [Jack Audio Connection Kit], [jack])
+AST_EXT_LIB_SETUP([KQUEUE], [kqueue support], [kqueue])
AST_EXT_LIB_SETUP([LDAP], [OpenLDAP], [ldap])
AST_EXT_LIB_SETUP([LIBXML2], [LibXML2], [libxml2])
AST_EXT_LIB_SETUP([LTDL], [libtool], [ltdl])
@@ -389,7 +390,7 @@
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h libintl.h limits.h locale.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h strings.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utime.h arpa/nameser.h sys/io.h])
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h libintl.h limits.h locale.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h strings.h sys/event.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utime.h arpa/nameser.h sys/io.h])
AC_CHECK_HEADERS([winsock.h winsock2.h])
@@ -539,6 +540,22 @@
then
AC_CHECK_FILE(/dev/urandom, AC_DEFINE([HAVE_DEV_URANDOM], 1, [Define to 1 if your system has /dev/urandom.]))
fi
+
+AC_MSG_CHECKING(for O_EVTONLY in fcntl.h)
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM([#include <fcntl.h>], [int a = O_EVTONLY;]),
+AC_MSG_RESULT(yes)
+AC_DEFINE([HAVE_O_EVTONLY], 1, [Define to 1 if your system defines the file flag O_EVTONLY in fcntl.h]),
+AC_MSG_RESULT(no)
+)
+
+AC_MSG_CHECKING(for O_SYMLINK in fcntl.h)
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM([#include <fcntl.h>], [int a = O_SYMLINK;]),
+AC_MSG_RESULT(yes)
+AC_DEFINE([HAVE_O_SYMLINK], 1, [Define to 1 if your system defines the file flag O_SYMLINK in fcntl.h]),
+AC_MSG_RESULT(no)
+)
AST_C_DEFINE_CHECK([PTHREAD_RWLOCK_INITIALIZER], [PTHREAD_RWLOCK_INITIALIZER], [pthread.h])
@@ -1435,6 +1452,12 @@
AST_EXT_LIB_CHECK([JACK], [jack], [jack_activate], [jack/jack.h])
+# BSD (and OS X) equivalent of inotify
+AST_EXT_LIB_CHECK([KQUEUE], [c], [kqueue], [sys/event.h])
+
+# 64-bit version of kevent (from kqueue) on OS X
+AC_CHECK_FUNCS([kevent64])
+
# Needed by unixodbc
AST_EXT_LIB_CHECK([LTDL], [ltdl], [lt_dlinit], [ltdl.h], [])
Modified: trunk/include/asterisk/autoconfig.h.in
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/autoconfig.h.in?view=diff&rev=262852&r1=262851&r2=262852
==============================================================================
--- trunk/include/asterisk/autoconfig.h.in (original)
+++ trunk/include/asterisk/autoconfig.h.in Thu May 13 00:37:31 2010
@@ -342,6 +342,12 @@
/* Define to 1 if you have the Jack Audio Connection Kit library. */
#undef HAVE_JACK
+/* Define to 1 if you have the `kevent64' function. */
+#undef HAVE_KEVENT64
+
+/* Define to 1 if you have the kqueue support library. */
+#undef HAVE_KQUEUE
+
/* Define to 1 if you have the OpenLDAP library. */
#undef HAVE_LDAP
@@ -490,6 +496,12 @@
/* Define to 1 if OSX atomic operations are supported. */
#undef HAVE_OSX_ATOMICS
+/* Define to 1 if your system defines the file flag O_EVTONLY in fcntl.h */
+#undef HAVE_O_EVTONLY
+
+/* Define to 1 if your system defines the file flag O_SYMLINK in fcntl.h */
+#undef HAVE_O_SYMLINK
+
/* Define to indicate the PostgreSQL library */
#undef HAVE_PGSQL
@@ -817,6 +829,9 @@
*/
#undef HAVE_SYS_ENDIAN_SWAP16
+/* Define to 1 if you have the <sys/event.h> header file. */
+#undef HAVE_SYS_EVENT_H
+
/* Define to 1 if you have the <sys/file.h> header file. */
#undef HAVE_SYS_FILE_H
@@ -1026,6 +1041,9 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
+/* Define to 1 if the C compiler supports function prototypes. */
+#undef PROTOTYPES
+
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE
@@ -1044,6 +1062,11 @@
/* Define to the type of arg 5 for `select'. */
#undef SELECT_TYPE_ARG5
+
+/* Define to 1 if the `setvbuf' function takes the buffering type as its
+ second argument and the buffer pointer as the third, as on System V before
+ release 3. */
+#undef SETVBUF_REVERSED
/* The size of `char *', as computed by sizeof. */
#undef SIZEOF_CHAR_P
@@ -1074,46 +1097,50 @@
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
-/* Enable extensions on AIX 3, Interix. */
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
-/* Enable threading extensions on Solaris. */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+#undef _LARGEFILE_SOURCE
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Enable extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
-/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
-/* Enable general extensions on Solaris. */
-#ifndef __EXTENSIONS__
-# undef __EXTENSIONS__
-#endif
-
-
-/* Number of bits in a file offset, on hosts where this is settable. */
-#undef _FILE_OFFSET_BITS
-
-/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
-#undef _LARGEFILE_SOURCE
-
-/* Define for large files, on AIX-style hosts. */
-#undef _LARGE_FILES
-
-/* Define to 1 if on MINIX. */
-#undef _MINIX
-
-/* Define to 2 if the system does not provide POSIX.1 features except with
- this defined. */
-#undef _POSIX_1_SOURCE
-
-/* Define to 1 if you need to in order for `stat' and other things to work. */
-#undef _POSIX_SOURCE
+
+/* Define like PROTOTYPES; this can be used by system headers. */
+#undef __PROTOTYPES
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
Modified: trunk/main/stdtime/localtime.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/stdtime/localtime.c?view=diff&rev=262852&r1=262851&r2=262852
==============================================================================
--- trunk/main/stdtime/localtime.c (original)
+++ trunk/main/stdtime/localtime.c Thu May 13 00:37:31 2010
@@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 1999 - 2005, Digium, Inc.
+ * Copyright (C) 1999 - 2010, Digium, Inc.
*
* Mark Spencer <markster at digium.com>
*
@@ -54,6 +54,13 @@
#include <float.h>
#ifdef HAVE_INOTIFY
#include <sys/inotify.h>
+#elif HAVE_KQUEUE
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#endif
#include "private.h"
@@ -153,6 +160,13 @@
struct lsinfo lsis[TZ_MAX_LEAPS];
#ifdef HAVE_INOTIFY
int wd[2];
+#elif defined(HAVE_KQUEUE)
+ int fd;
+# ifdef HAVE_O_SYMLINK
+ int fds;
+# else
+ DIR *dir;
+# endif /* defined(HAVE_O_SYMLINK) */
#else
time_t mtime[2];
#endif
@@ -298,7 +312,7 @@
/* Give the thread a chance to initialize */
ast_cond_wait(&initialization, &initialization_lock);
} else {
- ast_log(LOG_ERROR, "Unable to start notification thread\n");
+ fprintf(stderr, "Unable to start notification thread\n");
ast_mutex_unlock(&initialization_lock);
return;
}
@@ -319,6 +333,180 @@
| IN_DONT_FOLLOW
#endif
);
+ }
+}
+#elif HAVE_KQUEUE
+static int queue_fd = -1;
+
+static void *kqueue_daemon(void *data)
+{
+ struct kevent kev;
+ struct state *sp;
+ struct timespec no_wait = { 0, 1 };
+
+ ast_mutex_lock(&initialization_lock);
+ if ((queue_fd = kqueue()) < 0) {
+ /* ast_log uses us to format messages, so if we called ast_log, we'd be
+ * in for a nasty loop (seen already in testing) */
+ fprintf(stderr, "Unable to initialize kqueue(): %s\n", strerror(errno));
+ inotify_thread = AST_PTHREADT_NULL;
+
+ /* Okay to proceed */
+ ast_cond_signal(&initialization);
+ ast_mutex_unlock(&initialization_lock);
+ return NULL;
+ }
+
+ ast_cond_signal(&initialization);
+ ast_mutex_unlock(&initialization_lock);
+
+ for (;/*ever*/;) {
+ if (kevent(queue_fd, NULL, 0, &kev, 1, NULL) < 0) {
+ AST_LIST_LOCK(&zonelist);
+ ast_cond_broadcast(&initialization);
+ AST_LIST_UNLOCK(&zonelist);
+ continue;
+ }
+
+ sp = kev.udata;
+
+ /*!\note
+ * If the file event fired, then the file was removed, so we'll need
+ * to reparse the entry. The directory event is a bit more
+ * interesting. Unfortunately, the queue doesn't contain information
+ * about the file that changed (only the directory itself), so unless
+ * we kept a record of the directory state before, it's not really
+ * possible to know what change occurred. But if we act paranoid and
+ * just purge the associated file, then it will get reparsed, and
+ * everything works fine. It may be more work, but it's a vast
+ * improvement over the alternative implementation, which is to stat
+ * the file repeatedly in what is essentially a busy loop. */
+ AST_LIST_LOCK(&zonelist);
+ AST_LIST_REMOVE(&zonelist, sp, list);
+ AST_LIST_UNLOCK(&zonelist);
+
+ /* If the directory event fired, remove the file event */
+ EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
+ kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
+ close(sp->fd);
+
+#ifdef HAVE_O_SYMLINK
+ if (sp->fds > -1) {
+ /* If the file event fired, remove the symlink event */
+ EV_SET(&kev, sp->fds, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
+ kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
+ close(sp->fds);
+ }
+#else
+ if (sp->dir) {
+ /* If the file event fired, remove the directory event */
+ EV_SET(&kev, dirfd(sp->dir), EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
+ kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
+ closedir(sp->dir);
+ }
+#endif
+ free(sp);
+
+ /* Just in case the signal was sent late */
+ AST_LIST_LOCK(&zonelist);
+ ast_cond_broadcast(&initialization);
+ AST_LIST_UNLOCK(&zonelist);
+ }
+}
+
+static void add_notify(struct state *sp, const char *path)
+{
+ struct kevent kev;
+ struct timespec no_wait = { 0, 1 };
+ char watchdir[PATH_MAX + 1] = "";
+
+ if (inotify_thread == AST_PTHREADT_NULL) {
+ ast_cond_init(&initialization, NULL);
+ ast_mutex_init(&initialization_lock);
+ ast_mutex_lock(&initialization_lock);
+ if (!(ast_pthread_create_background(&inotify_thread, NULL, kqueue_daemon, NULL))) {
+ /* Give the thread a chance to initialize */
+ ast_cond_wait(&initialization, &initialization_lock);
+ }
+ ast_mutex_unlock(&initialization_lock);
+ }
+
+ if (queue_fd < 0) {
+ /* Error already sent */
+ return;
+ }
+
+#ifdef HAVE_O_SYMLINK
+ if (readlink(path, watchdir, sizeof(watchdir) - 1) != -1 && (sp->fds = open(path, O_RDONLY | O_SYMLINK
+# ifdef HAVE_O_EVTONLY
+ | O_EVTONLY
+# endif
+ )) >= 0) {
+ EV_SET(&kev, sp->fds, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
+ if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
+ /* According to the API docs, we may get -1 return value, due to the
+ * NULL space for a returned event, but errno should be 0 unless
+ * there's a real error. Otherwise, kevent will return 0 to indicate
+ * that the time limit expired. */
+ fprintf(stderr, "Unable to watch '%s': %s\n", path, strerror(errno));
+ close(sp->fds);
+ sp->fds = -1;
+ }
+ }
+#else
+ if (readlink(path, watchdir, sizeof(watchdir) - 1) != -1) {
+ /* Special -- watch the directory for changes, because we cannot directly watch a symlink */
+ char *slash;
+
+ ast_copy_string(watchdir, path, sizeof(watchdir));
+
+ if ((slash = strrchr(watchdir, '/'))) {
+ *slash = '\0';
+ }
+ if (!(sp->dir = opendir(watchdir))) {
+ fprintf(stderr, "Unable to watch directory with symlink '%s': %s\n", path, strerror(errno));
+ goto watch_file;
+ }
+
+ /*!\note
+ * You may be wondering about whether there is a potential conflict
+ * with the kqueue interface, because we might be watching the same
+ * directory for multiple zones. The answer is no, because kqueue
+ * looks at the descriptor to know if there's a duplicate. Since we
+ * (may) have opened the directory multiple times, each represents a
+ * different event, so no replacement of an existing event will occur.
+ * Likewise, there's no potential leak of a descriptor.
+ */
+ EV_SET(&kev, dirfd(sp->dir), EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
+ if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
+ fprintf(stderr, "Unable to watch '%s': %s\n", watchdir, strerror(errno));
+ closedir(sp->dir);
+ sp->dir = NULL;
+ }
+ }
+
+watch_file:
+#endif
+
+ if ((sp->fd = open(path, O_RDONLY
+# ifdef HAVE_O_EVTONLY
+ | O_EVTONLY
+# endif
+ )) < 0) {
+ fprintf(stderr, "Unable to watch '%s' for changes: %s\n", path, strerror(errno));
+ return;
+ }
+
+ EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
+ if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
+ /* According to the API docs, we may get -1 return value, due to the
+ * NULL space for a returned event, but errno should be 0 unless
+ * there's a real error. Otherwise, kevent will return 0 to indicate
+ * that the time limit expired. */
+ fprintf(stderr, "Unable to watch '%s': %s\n", path, strerror(errno));
+ close(sp->fd);
+ sp->fd = -1;
}
}
#else
Modified: trunk/pbx/pbx_spool.c
URL: http://svnview.digium.com/svn/asterisk/trunk/pbx/pbx_spool.c?view=diff&rev=262852&r1=262851&r2=262852
==============================================================================
--- trunk/pbx/pbx_spool.c (original)
+++ trunk/pbx/pbx_spool.c Thu May 13 00:37:31 2010
@@ -32,6 +32,11 @@
#include <dirent.h>
#ifdef HAVE_INOTIFY
#include <sys/inotify.h>
+#elif HAVE_KQUEUE
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <fcntl.h>
#endif
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
@@ -435,7 +440,7 @@
return res;
}
-#ifdef HAVE_INOTIFY
+#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
struct direntry {
AST_LIST_ENTRY(direntry) list;
time_t mtime;
@@ -470,6 +475,15 @@
when = st.st_mtime;
}
+
+#ifndef HAVE_INOTIFY
+ /* Need to check the existing list for kqueue(2), in order to avoid duplicates. */
+ AST_LIST_TRAVERSE(&dirlist, cur, list) {
+ if (cur->mtime == when && !strcmp(filename, cur->name)) {
+ return;
+ }
+ }
+#endif
if ((res = when) > now || (res = scan_service(filename, now)) > 0) {
if (!(new = ast_calloc(1, sizeof(*new) + strlen(filename) + 1))) {
@@ -501,11 +515,11 @@
{
DIR *dir;
struct dirent *de;
- int res;
time_t now;
struct timespec ts = { .tv_sec = 1 };
+#ifdef HAVE_INOTIFY
+ int res;
int inotify_fd = inotify_init();
- struct direntry *cur;
struct {
struct inotify_event iev;
/* It may not look like we're using this element, but when we read
@@ -514,17 +528,31 @@
char name[FILENAME_MAX + 1];
} buf;
struct pollfd pfd = { .fd = inotify_fd, .events = POLLIN };
+#else
+ struct timespec nowait = { 0, 1 };
+ int inotify_fd = kqueue();
+ struct kevent kev;
+#endif
+ struct direntry *cur;
while (!ast_fully_booted) {
nanosleep(&ts, NULL);
}
if (inotify_fd < 0) {
- ast_log(LOG_ERROR, "Unable to initialize inotify(7)\n");
+ ast_log(LOG_ERROR, "Unable to initialize "
+#ifdef HAVE_INOTIFY
+ "inotify(7)"
+#else
+ "kqueue(2)"
+#endif
+ "\n");
return NULL;
}
+#ifdef HAVE_INOTIFY
inotify_add_watch(inotify_fd, qdir, IN_CREATE | IN_ATTRIB | IN_MOVED_TO);
+#endif
/* First, run through the directory and clear existing entries */
if (!(dir = opendir(qdir))) {
@@ -532,11 +560,21 @@
return NULL;
}
+#ifndef HAVE_INOTIFY
+ EV_SET(&kev, dirfd(dir), EVFILT_VNODE, EV_ADD | EV_ENABLE, NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB, 0, NULL);
+ if (kevent(inotify_fd, &kev, 1, NULL, 0, &nowait) < 0 && errno != 0) {
+ ast_log(LOG_ERROR, "Unable to watch directory %s: %s\n", qdir, strerror(errno));
+ }
+#endif
now = time(NULL);
while ((de = readdir(dir))) {
queue_file(de->d_name, 0);
}
+
+#ifdef HAVE_INOTIFY
+ /* Directory needs to remain open for kqueue(2) */
closedir(dir);
+#endif
/* Wait for either a) next timestamp to occur, or b) a change to happen */
for (;/* ever */;) {
@@ -544,6 +582,7 @@
time(&now);
if (next > now) {
+#ifdef HAVE_INOTIFY
int stage = 0;
/* Convert from seconds to milliseconds, unless there's nothing
* in the queue already, in which case, we wait forever. */
@@ -556,6 +595,19 @@
} else if (res < 0 && errno != EINTR && errno != EAGAIN) {
ast_debug(1, "Got an error back from %s(2): %s\n", stage ? "read" : "poll", strerror(errno));
}
+#else
+ struct timespec ts2 = { next - now, 0 };
+ if (kevent(inotify_fd, NULL, 0, &kev, 1, &ts2) <= 0) {
+ /* Interrupt or timeout, restart calculations */
+ continue;
+ } else {
+ /* Directory changed, rescan */
+ rewinddir(dir);
+ while ((de = readdir(dir))) {
+ queue_file(de->d_name, 0);
+ }
+ }
+#endif
time(&now);
}
Added: trunk/res/res_timing_kqueue.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_timing_kqueue.c?view=auto&rev=262852
==============================================================================
--- trunk/res/res_timing_kqueue.c (added)
+++ trunk/res/res_timing_kqueue.c Thu May 13 00:37:31 2010
@@ -1,0 +1,390 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * Tilghman Lesher <tlesher AT digium DOT com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \author Tilghman Lesher <tlesher AT digium DOT com>
+ *
+ * \brief kqueue timing interface
+ */
+
+/*** MODULEINFO
+ <depend>kqueue</depend>
+ ***/
+
+#include "asterisk.h"
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include "asterisk/module.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/timing.h"
+#include "asterisk/logger.h"
+#include "asterisk/utils.h"
+#include "asterisk/time.h"
+#include "asterisk/test.h"
+
+static void *timing_funcs_handle;
+
+static int kqueue_timer_open(void);
+static void kqueue_timer_close(int handle);
+static int kqueue_timer_set_rate(int handle, unsigned int rate);
+static void kqueue_timer_ack(int handle, unsigned int quantity);
+static int kqueue_timer_enable_continuous(int handle);
+static int kqueue_timer_disable_continuous(int handle);
+static enum ast_timer_event kqueue_timer_get_event(int handle);
+static unsigned int kqueue_timer_get_max_rate(int handle);
+
+static struct ast_timing_interface kqueue_timing = {
+ .name = "kqueue",
+ .priority = 150,
+ .timer_open = kqueue_timer_open,
+ .timer_close = kqueue_timer_close,
+ .timer_set_rate = kqueue_timer_set_rate,
+ .timer_ack = kqueue_timer_ack,
+ .timer_enable_continuous = kqueue_timer_enable_continuous,
+ .timer_disable_continuous = kqueue_timer_disable_continuous,
+ .timer_get_event = kqueue_timer_get_event,
+ .timer_get_max_rate = kqueue_timer_get_max_rate,
+};
+
+static struct ao2_container *kqueue_timers;
+
+struct kqueue_timer {
+ int handle;
+ uint64_t nsecs;
+ uint64_t unacked;
+ unsigned int is_continuous:1;
+};
+
+static int kqueue_timer_hash(const void *obj, const int flags)
+{
+ const struct kqueue_timer *timer = obj;
+
+ return timer->handle;
+}
+
+static int kqueue_timer_cmp(void *obj, void *args, int flags)
+{
+ struct kqueue_timer *timer1 = obj, *timer2 = args;
+ return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void timer_destroy(void *obj)
+{
+ struct kqueue_timer *timer = obj;
+ close(timer->handle);
+}
+
+#define lookup_timer(a) _lookup_timer(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+static struct kqueue_timer *_lookup_timer(int handle, const char *file, int line, const char *func)
+{
+ struct kqueue_timer *our_timer, find_helper = {
+ .handle = handle,
+ };
+
+ if (!(our_timer = ao2_find(kqueue_timers, &find_helper, OBJ_POINTER))) {
+ ast_log(__LOG_ERROR, file, line, func, "Couldn't find timer with handle %d\n", handle);
+ /* API says we set errno */
+ errno = ESRCH;
+ return NULL;
+ }
+ return our_timer;
+}
+
+static int kqueue_timer_open(void)
+{
+ struct kqueue_timer *timer;
+ int handle;
+
+ if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for kqueue_timer structure\n");
+ return -1;
+ }
+ if ((handle = kqueue()) < 0) {
+ ast_log(LOG_ERROR, "Failed to create kqueue timer: %s\n", strerror(errno));
+ ao2_ref(timer, -1);
+ return -1;
+ }
+
+ timer->handle = handle;
+ ao2_link(kqueue_timers, timer);
+ /* Get rid of the reference from the allocation */
+ ao2_ref(timer, -1);
+ return handle;
+}
+
+static void kqueue_timer_close(int handle)
+{
+ struct kqueue_timer *our_timer;
+
+ if (!(our_timer = lookup_timer(handle))) {
+ return;
+ }
+
+ ao2_unlink(kqueue_timers, our_timer);
+ ao2_ref(our_timer, -1);
+}
+
+static void kqueue_set_nsecs(struct kqueue_timer *our_timer, uint64_t nsecs)
+{
+ struct timespec nowait = { 0, 1 };
+#ifdef HAVE_KEVENT64
+ struct kevent64 kev;
+
+ EV_SET64(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_NSECONDS,
+ nsecs, NULL, 0, 0);
+ kevent64(our_timer->handle, &kev, 1, NULL, 0, &nowait);
+#else
+ struct kevent kev;
+
+ EV_SET(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE,
+#ifdef NOTE_NSECONDS
+ nsecs <= 0xFFffFFff ? NOTE_NSECONDS :
+#endif
+#ifdef NOTE_USECONDS
+ NOTE_USECONDS
+#else /* Milliseconds, if no constants are defined */
+ 0
+#endif
+ ,
+#ifdef NOTE_NSECONDS
+ nsecs <= 0xFFffFFff ? nsecs :
+#endif
+#ifdef NOTE_USECONDS
+ nsecs / 1000
+#else /* Milliseconds, if nothing else is defined */
+ nsecs / 1000000
+#endif
+ , NULL);
+ kevent(our_timer->handle, &kev, 1, NULL, 0, &nowait);
+#endif
+}
+
+static int kqueue_timer_set_rate(int handle, unsigned int rate)
+{
+ struct kqueue_timer *our_timer;
+
+ if (!(our_timer = lookup_timer(handle))) {
+ return -1;
+ }
+
+ kqueue_set_nsecs(our_timer, (our_timer->nsecs = rate ? (long) (1000000000 / rate) : 0L));
+ ao2_ref(our_timer, -1);
+
+ return 0;
+}
+
+static void kqueue_timer_ack(int handle, unsigned int quantity)
+{
+ struct kqueue_timer *our_timer;
+
+ if (!(our_timer = lookup_timer(handle))) {
+ return;
+ }
+
+ if (our_timer->unacked < quantity) {
+ ast_debug(1, "Acking more events than have expired?!!\n");
+ our_timer->unacked = 0;
+ } else {
+ our_timer->unacked -= quantity;
+ }
+}
+
+static int kqueue_timer_enable_continuous(int handle)
+{
+ struct kqueue_timer *our_timer;
+
+ if (!(our_timer = lookup_timer(handle))) {
+ return -1;
+ }
+
+ kqueue_set_nsecs(our_timer, 1);
+ our_timer->is_continuous = 1;
+ our_timer->unacked = 0;
+ ao2_ref(our_timer, -1);
+ return 0;
+}
+
+static int kqueue_timer_disable_continuous(int handle)
+{
+ struct kqueue_timer *our_timer;
+
+ if (!(our_timer = lookup_timer(handle))) {
+ return -1;
+ }
+
+ kqueue_set_nsecs(our_timer, our_timer->nsecs);
+ our_timer->is_continuous = 0;
+ our_timer->unacked = 0;
+ ao2_ref(our_timer, -1);
+ return 0;
+}
+
+static enum ast_timer_event kqueue_timer_get_event(int handle)
+{
+ enum ast_timer_event res = -1;
+ struct kqueue_timer *our_timer;
+ struct timespec sixty_seconds = { 60, 0 };
+ struct kevent kev;
+
+ if (!(our_timer = lookup_timer(handle))) {
+ return -1;
+ }
+
+ /* If we have non-ACKed events, just return immediately */
+ if (our_timer->unacked == 0) {
+ if (kevent(handle, NULL, 0, &kev, 1, &sixty_seconds) > 0) {
+ our_timer->unacked += kev.data;
+ }
+ }
+
+ if (our_timer->unacked > 0) {
+ res = our_timer->is_continuous ? AST_TIMING_EVENT_CONTINUOUS : AST_TIMING_EVENT_EXPIRED;
+ }
+
+ ao2_ref(our_timer, -1);
+ return res;
+}
+
+static unsigned int kqueue_timer_get_max_rate(int handle)
+{
+ /* Actually, the max rate is 2^64-1 seconds, but that's not representable in a 32-bit integer. */
+ return UINT_MAX;
+}
+
+#ifdef TEST_FRAMEWORK
+AST_TEST_DEFINE(test_kqueue_timing)
+{
+ int res = AST_TEST_PASS, handle, i;
+ uint64_t diff;
+ struct pollfd pfd = { 0, POLLIN, 0 };
+ struct kqueue_timer *kt;
+ struct timeval start;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "test_kqueue_timing";
+ info->category = "res/res_timing_kqueue";
+ info->summary = "Test KQueue timing interface";
+ info->description = "Verify that the KQueue timing interface correctly generates timing events";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ if (!(handle = kqueue_timer_open())) {
+ ast_test_status_update(test, "Cannot open timer!\n");
+ return AST_TEST_FAIL;
+ }
+
+ do {
+ pfd.fd = handle;
+ if (kqueue_timer_set_rate(handle, 1000)) {
+ ast_test_status_update(test, "Cannot set timer rate to 1000/s\n");
+ res = AST_TEST_FAIL;
+ break;
+ }
+ if (ast_poll(&pfd, 1, 1000) < 1) {
+ ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
+ res = AST_TEST_FAIL;
+ break;
+ }
+ if (pfd.revents != POLLIN) {
+ ast_test_status_update(test, "poll() should have returned POLLIN, but instead returned %hd\n", pfd.revents);
+ res = AST_TEST_FAIL;
+ break;
+ }
+ if (!(kt = lookup_timer(handle))) {
+ ast_test_status_update(test, "Could not find timer structure in container?!!\n");
+ res = AST_TEST_FAIL;
+ break;
+ }
+ if (kqueue_timer_get_event(handle) <= 0) {
+ ast_test_status_update(test, "No events generated after a poll returned successfully?!!\n");
+ res = AST_TEST_FAIL;
+ break;
+ }
+ if (kt->unacked == 0) {
+ ast_test_status_update(test, "Unacked events is 0, but there should be at least 1.\n");
+ res = AST_TEST_FAIL;
+ break;
+ }
+ kqueue_timer_enable_continuous(handle);
+ start = ast_tvnow();
+ for (i = 0; i < 100; i++) {
+ if (ast_poll(&pfd, 1, 1000) < 1) {
+ ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
+ res = AST_TEST_FAIL;
+ break;
+ }
+ if (kqueue_timer_get_event(handle) <= 0) {
+ ast_test_status_update(test, "No events generated in continuous mode after 1 microsecond?!!\n");
+ res = AST_TEST_FAIL;
+ break;
+ }
+ }
+ diff = ast_tvdiff_us(ast_tvnow(), start);
+ ast_test_status_update(test, "diff is %llu\n", diff);
+ /*
+ if (abs(diff - kt->unacked) == 0) {
+ ast_test_status_update(test, "Unacked events should be around 1000, not %llu\n", kt->unacked);
+ res = AST_TEST_FAIL;
+ }
+ */
+ } while (0);
+ kqueue_timer_close(handle);
+ return res;
+}
+#endif
+
+static int load_module(void)
+{
+ if (!(kqueue_timers = ao2_container_alloc(563, kqueue_timer_hash, kqueue_timer_cmp))) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (!(timing_funcs_handle = ast_register_timing_interface(&kqueue_timing))) {
+ ao2_ref(kqueue_timers, -1);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ AST_TEST_REGISTER(test_kqueue_timing);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ AST_TEST_UNREGISTER(test_kqueue_timing);
+ if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
+ ao2_ref(kqueue_timers, -1);
+ kqueue_timers = NULL;
+ }
+
+ return res;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "KQueue Timing Interface",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = 10,
+ );
Propchange: trunk/res/res_timing_kqueue.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: trunk/res/res_timing_kqueue.c
------------------------------------------------------------------------------
svn:keywords = 'Date Author Id Revision Yoyo'
Propchange: trunk/res/res_timing_kqueue.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: trunk/tests/test_time.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_time.c?view=diff&rev=262852&r1=262851&r2=262852
==============================================================================
--- trunk/tests/test_time.c (original)
+++ trunk/tests/test_time.c Thu May 13 00:37:31 2010
@@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 2009, Digium, Inc.
+ * Copyright (C) 2010, Digium, Inc.
*
* Tilghman Lesher <tlesher AT digium DOT com>
*
@@ -48,10 +48,10 @@
AST_TEST_DEFINE(test_timezone_watch)
{
- const char *zones[2] = { "America/Chicago", "America/New_York" };
+ const char *zones[] = { "America/Chicago", "America/New_York" };
int type, i, res = AST_TEST_PASS;
struct timeval tv = ast_tvnow();
- struct ast_tm atm[2];
+ struct ast_tm atm[ARRAY_LEN(zones)];
char tmpdir[] = "/tmp/timezone.XXXXXX";
char tzfile[50], syscmd[256];
@@ -73,15 +73,12 @@
}
snprintf(tzfile, sizeof(tzfile), "%s/test", tmpdir);
- /* Allow system(3) to function correctly */
- ast_replace_sigchld();
-
for (type = 0; type < 2; type++) {
ast_test_status_update(test, "Executing %s test...\n", type == 0 ? "deletion" : "symlink");
for (i = 0; i < ARRAY_LEN(zones); i++) {
int system_res;
snprintf(syscmd, sizeof(syscmd), "%s " TZDIR "/%s %s", type == 0 ? "cp" : "ln -sf", zones[i], tzfile);
- if ((system_res = system(syscmd))) {
+ if ((system_res = ast_safe_system(syscmd))) {
ast_log(LOG_WARNING, "system(%s) returned non-zero: %d\n", syscmd, system_res);
}
ast_localtime_wakeup_monitor();
@@ -93,18 +90,17 @@
}
}
- /* stat(2) only has resolution to 1 second - must wait, or the mtime is the same */
- usleep(1100000);
+ if (i + 1 != ARRAY_LEN(zones)) {
+ /* stat(2) only has resolution to 1 second - must wait, or the mtime is the same */
+ usleep(1100000);
+ }
}
}
snprintf(syscmd, sizeof(syscmd), "rm -rf %s", tmpdir);
- if (system(syscmd)) {
+ if (ast_safe_system(syscmd)) {
ast_log(LOG_WARNING, "system(%s) returned non-zero.\n", syscmd);
}
-
- /* Restore SIGCHLD handler */
- ast_unreplace_sigchld();
return res;
}
More information about the asterisk-commits
mailing list