[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