libkqueue-1.0.4/0000755000175000017500000000000011607445337013054 5ustar mheilymheilylibkqueue-1.0.4/Makefile0000644000175000017500000001265711607445336014526 0ustar mheilymheily# # Copyright (c) 2009-2011 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # REPOSITORY=svn+ssh://mark.heily.com/$$HOME/svn/$(PROGRAM) DIST=heily.com:$$HOME/public_html/$(PROGRAM)/dist DISTFILE=$(PROGRAM)-$(VERSION).tar.gz include config.mk .PHONY :: install uninstall check dist dist-upload publish-www clean merge distclean fresh-build rpm edit cscope all: $(PROGRAM).so $(PROGRAM).a %.o: %.c $(DEPS) $(CC) -c -o $@ -I./include -I./src/common $(CFLAGS) $< $(PROGRAM).a: $(OBJS) $(AR) rcs $(PROGRAM).a $(OBJS) $(PROGRAM).so: $(OBJS) $(LD) $(LDFLAGS) $(OBJS) $(LDADD) $(LN) -sf $(PROGRAM).so.$(ABI_VERSION) $(PROGRAM).so install: $(PROGRAM).so $(INSTALL) -d -m 755 $(INCLUDEDIR)/kqueue/sys $(INSTALL) -m 644 include/sys/event.h $(INCLUDEDIR)/kqueue/sys/event.h $(INSTALL) -d -m 755 $(LIBDIR) $(INSTALL) -m 644 $(PROGRAM).so.$(ABI_VERSION) $(LIBDIR) $(LN) -sf $(PROGRAM).so.$(ABI_VERSION) $(LIBDIR)/$(PROGRAM).so.$(ABI_MAJOR) $(LN) -sf $(PROGRAM).so.$(ABI_VERSION) $(LIBDIR)/$(PROGRAM).so $(INSTALL) -m 644 $(PROGRAM).la $(LIBDIR) $(INSTALL) -m 644 $(PROGRAM).a $(LIBDIR) $(INSTALL) -d -m 755 $(LIBDIR)/pkgconfig $(INSTALL) -m 644 libkqueue.pc $(LIBDIR)/pkgconfig $(INSTALL) -d -m 755 $(MANDIR)/man2 $(INSTALL) -m 644 kqueue.2 $(MANDIR)/man2/kqueue.2 $(INSTALL) -m 644 kqueue.2 $(MANDIR)/man2/kevent.2 uninstall: rm -f $(INCLUDEDIR)/kqueue/sys/event.h rm -f $(LIBDIR)/libkqueue.so rm -f $(LIBDIR)/pkgconfig/libkqueue.pc rm -f $(MANDIR)/man2/kqueue.2 rm -f $(MANDIR)/man2/kevent.2 rmdir $(INCLUDEDIR)/kqueue/sys $(INCLUDEDIR)/kqueue check: $(PROGRAM).a cd test && ./configure && make check debug-check: clean $(PROGRAM).a cd test && ./configure && KQUEUE_DEBUG=y make check $(DISTFILE): $(SOURCES) $(HEADERS) mkdir $(PROGRAM)-$(VERSION) cp Makefile ChangeLog configure config.inc \ $(MANS) $(EXTRA_DIST) \ $(PROGRAM)-$(VERSION) cp -R $(SUBDIRS) $(PROGRAM)-$(VERSION) rm -rf `find $(PROGRAM)-$(VERSION) -type d -name .svn -o -name .libs` cd $(PROGRAM)-$(VERSION) && ./configure && cd test && ./configure && cd .. && make distclean tar zcf $(PROGRAM)-$(VERSION).tar.gz $(PROGRAM)-$(VERSION) rm -rf $(PROGRAM)-$(VERSION) dist: rm -f $(DISTFILE) make $(DISTFILE) dist-upload: $(DISTFILE) scp $(DISTFILE) $(DIST) clean: rm -f tags *.a *.so *.so.* find src -name '*.o' -exec rm {} \; rm -rf pkg cd test && make clean || true distclean: clean rm -f *.tar.gz config.mk config.h $(PROGRAM).pc $(PROGRAM).la rpm.spec rm -f test/config.mk test/config.h rm -rf $(PROGRAM)-$(VERSION) 2>/dev/null || true fresh-build: rm -rf /tmp/$(PROGRAM)-testbuild svn co svn://mark.heily.com/libkqueue/trunk /tmp/$(PROGRAM)-testbuild cd /tmp/$(PROGRAM)-testbuild && ./configure && make check rm -rf /tmp/$(PROGRAM)-testbuild merge: svn diff $(REPOSITORY)/branches/stable $(REPOSITORY)/trunk | gvim - @printf "Merge changes from the trunk to the stable branch [y/N]? " @read x && test "$$x" = "y" echo "ok" tags: $(SOURCES) $(HEADERS) ctags $(SOURCES) $(HEADERS) edit: tags $(EDITOR) $(SOURCES) $(HEADERS) cscope: tags cscope $(SOURCES) $(HEADERS) # Creates an ~/rpmbuild tree rpmbuild: mkdir -p $$HOME/rpmbuild cd $$HOME/rpmbuild && mkdir -p BUILD RPMS SOURCES SPECS SRPMS grep _topdir $$HOME/.rpmmacros || \ echo "%_topdir %(echo $$HOME/rpmbuild)" >> $$HOME/.rpmmacros rpm: rpmbuild clean $(DISTFILE) mkdir -p pkg cp $(DISTFILE) $$HOME/rpmbuild/SOURCES rpmbuild -bb rpm.spec find $$HOME/rpmbuild -name '$(PROGRAM)-$(VERSION)*.rpm' -exec mv {} ./pkg \; deb: clean $(DISTFILE) mkdir pkg && cd pkg ; \ tar zxf ../$(DISTFILE) ; \ cp ../$(DISTFILE) $(PROGRAM)_$(VERSION).orig.tar.gz ; \ cp -R ../ports/debian $(PROGRAM)-$(VERSION) ; \ rm -rf `find $(PROGRAM)-$(VERSION)/debian -type d -name .svn` ; \ perl -pi -e 's/\@\@VERSION\@\@/$(VERSION)/' $(PROGRAM)-$(VERSION)/debian/changelog ; \ cd $(PROGRAM)-$(VERSION) && dpkg-buildpackage -uc -us lintian -i pkg/*.deb @printf "\nThe following packages have been created:\n" @find ./pkg -name '*.deb' | sed 's/^/ /' debug-install: ./configure --prefix=/usr --debug=yes make clean && make && sudo make install diff: if [ "`pwd | grep /trunk`" != "" ] ; then \ (cd .. ; $(DIFF) branches/stable trunk | less) ; \ fi if [ "`pwd | grep /branches/stable`" != "" ] ; then \ (cd ../.. ; $(DIFF) branches/stable trunk | less) ; \ fi # Copy to/from the host to the Solaris guest VM # solaris-push: ssh -p 2222 localhost 'rm -rf /export/home/mheily/libkqueue' cd .. ; scp -rq -P 2222 trunk localhost:/export/home/mheily/libkqueue solaris-test: solaris-push ssh -p 2222 localhost 'cd /export/home/mheily/libkqueue && /usr/sfw/bin/gmake distclean && ./configure && /usr/sfw/bin/gmake clean all check' solaris-pull: scp -rq -P 2222 localhost:/export/home/mheily/libkqueue/\* . # # libkqueue-1.0.4/ChangeLog0000644000175000017500000001371211607445336014631 0ustar mheilymheily2011-07-13 v1.0.4 r530 ------------------------------------------------------------------------ * Revert the change to use /usr/lib64 on 64-bit hosts because it breaks on Debian. 2011-07-09 v1.0.3 r529 ------------------------------------------------------------------------ * Don't allow the user to override essential CFLAGS such as -fpic * Add _BSD_SOURCE to CFLAGS 2011-03-24 v1.0.2 r462 ------------------------------------------------------------------------ * When building on a 64-bit Linux host, install libraries in /usr/lib64. 2010-09-18 v1.0.1 r453 ------------------------------------------------------------------------ * Eliminate an unused assignment in signal_handler() and evfilt_signal_copyout(). Fixes a build failure on Fedora. 2010-09-18 v1.0 r344 ------------------------------------------------------------------------ * Support older Linux kernels that do not have the EPOLLRDHUP flag. * Add a portable implementation of the EVFILT_TIMER filter. * Add Solaris to the list of supported platforms. * Fixed the 'make rpm' target to work on CentOS 5. * Modified the manpage to remove unimplemented features. 2010-08-05 v0.9.3 r309 ------------------------------------------------------------------------ * Fix a typo in kevent_copyin() that caused EV_RECEIPT to set the data field incorrectly in some cases. (Credit to Julien Blache for discovering and fixing this bug) 2010-08-05 v0.9.2 r289 ------------------------------------------------------------------------ * Fix some build failures on 32-bit platforms related to the debugging codepaths being enabled by default. 2010-08-04 v0.9.1 r286 ------------------------------------------------------------------------ * Prevent dumping of EVFILT_VNODE debugging information to STDOUT. * Fix the 'make clean' target in the testsuite. 2010-08-01 v0.9 r285 ------------------------------------------------------------------------ * Set kevent.data = 1 for passive sockets that have at least one pending connection. (Credit to Julien Blache for finding and researching this bug) * Fix various compilation errors under Solaris. (Credit to Joakim Johansson for testing and providing patches) * Use the KQUEUE_DEBUG environment variable to turn on debugging output. 2010-07-21 v0.8 r264 ------------------------------------------------------------------------ * Fix a bug that prevented a knote with the EV_DISPATCH flag from being re-enabled after an event had been triggered. (Credit to Julien Blache for finding and researching this bug) 2010-06-08 v0.7 r248 ------------------------------------------------------------------------ * Add Debian packaging to the ports/ directory and improve the 'make deb' target. * Set the library soname version. * Switch from -fPIC to -fpic as the default in CFLAGS. 2010-03-28 v0.6 r238 ------------------------------------------------------------------------ * Experimental Linux kernel module. * Implement knote modification for signals. * Implement POSIX signal.c 2010-02-09 v0.5 r200 ------------------------------------------------------------------------ * Prevent namespace pollution by hiding all ELF symbols except for kqueue() and kevent(). * Add reference counting to the garbage collection mechanism so that a kqueue object is never destroyed while multiple threads are using it. * Improve scalability by using red-black trees instead of linked lists. * Refactor the internal API to promote modularity and code reuse. Five methods are added to each filter: create, modify, delete, enable, disable. These replace the copyin() method, which was overly complicated. * Remove the fine-grained locking at the filter level, and replace it with coarse locking inside kevent(). This simplifys the locking implementation and provides a stronger guarantee of reentrancy. * Initial attempt at writing a Linux kernel module. It fails to link because sys_epoll_create() and other event-related syscalls are not available to kernelspace (?). Need to ask LKML for guidance. * Make unit tests threadsafe and created a stresstest which runs the unit tests in parallel. * Use helper functions to reduce the amount of duplicate code in the unit tests. 2009-12-26 v0.4 r133 ------------------------------------------------------------------------ * Incomplete and experimental support for Solaris added. * Lots of work on the test suite. * Replace the buggy GC thread with an event-based alternative. * Do not implicitly set EV_CLEAR in the EVFILT_USER filter. * Adjust the eventlist when EV_RECEIPT causes it to be modified. 2009-11-10 v0.3 r84 ------------------------------------------------------------------------ * The EVFILT_USER filter has been implemented, but needs additional testing. * The EVFILT_PROC filter is partially implemented on Linux, but is currently broken. * The unit tests have been moved to a separate subdirectory and now function under OpenBSD 4.4 using the native kqueue(2) and kevent(2) system calls. * The kqueue_free() function has been removed. * A helper thread performs garbage collection when close(2) is called on the file descriptor returned by kqueue(). * All symbols in that are not implemented are now undefined. * Major internal reorganization of the source tree. * A copy-and-paste error in vnode.c has been fixed. * The pthreads library is now required. 2009-11-07 v0.2 r59 ------------------------------------------------------------------------ * Implement EVFILT_TIMER on Linux. * Fix another 'make install' problem reported by Mario Schwalbe. * Do not link the test program with the pthreads library. * pkg-config no longer requires linking with -lpthread and -lrt. 2009-11-05 v0.1 r49 ------------------------------------------------------------------------ * Initial stable release. libkqueue-1.0.4/configure0000755000175000017500000001770211607445336014771 0ustar mheilymheily#!/bin/sh # # Copyright (c) 2009-2010 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # c_exports="program version target cflags" make_exports="program version target \ prefix libdir includedir mandir \ cflags ldflags ldadd libdepends \ sources objs deps mans headers extra_dist subdirs \ abi_major abi_minor abi_version \ cc cpp ld ln ar install diff" required_headers= optional_headers= pre_configure_hook() { return } post_configure_hook() { return } export_to_make() { for id in $* do # Prepend $DESTDIR to installation directories case "$id" in prefix|libdir|includedir|mandir) eval "$id=\"\\\$\\\$DESTDIR\$$id\"" esac uc_id=`echo $id | $tr '[:lower:]' '[:upper:]'`; eval "echo \"$uc_id=\"\$$id\"\" >> config.mk" done } export_to_c() { for id in $* do uc_id=`echo $id | $tr '[:lower:]' '[:upper:]'`; eval "echo \"#define $uc_id \\\"\$$id\\\"\" >> config.h" done } finalize() { uc_id=`echo \"$1\" | $tr '[:lower:]' '[:upper:]'`; eval "if [ \"\$$1\" = \"\" ] ; then $1=\"$2\" ; fi" } process_argv() { for arg in $* do id=`echo "$arg" | sed 's/=.*//; s/^--//;'` val=`echo "$arg" | sed 's/^.*=//'` if [ "$val" = "" ] ; then val=1 ; fi eval "$id=\"$val\"" done } process_env() { test -n "$CC" && cc="$CC" test -n "$CPP" && cpp="$CPP" test -n "$CPPFLAGS" && cppflags="$CPPFLAGS" test -n "$CFLAGS" && cflags="$CFLAGS" test -n "$LD" && ld="$LD" test -n "$LN" && ld="$LN" test -n "$LDFLAGS" && ldflags="$LDFLAGS" test -n "$AR" && ar="$AR" } check_header() { sym=`echo "have_$1" | sed 's,[./],_,g'` uc_sym=`echo "$sym" | $tr '[:lower:]' '[:upper:]'`; path=$1 printf "checking for $path.. " if [ -f "/usr/include/$path" ] ; then echo "yes" echo "#define $uc_sym 1" >> config.h eval "$sym=yes" return 0 else echo "no" echo "#undef $uc_sym" >> config.h eval "$sym=no" return 1 fi } # Determine the path to an executable binary check_binary() { id=$1 shift for path in $* do test -f $path if [ $? = 0 ] ; then eval "$id=\"$path\"" return fi done echo "not found" return } check_headers() { for header in $* do check_header "$header" done } check_symbol() { header=$1 symbol=$2 uc_symbol=`echo "HAVE_$symbol" | $tr '[:lower:]' '[:upper:]' | sed 's,[./],_,g'` lc_symbol=`echo "have_$symbol" | $tr '[:upper:]' '[:lower:]' | sed 's,[./],_,g'` if [ -f "$header" ] ; then path="$header" elif [ -f "/usr/include/$header" ] ; then path="/usr/include/$header" else echo "*** ERROR: Cannot find <$header>" exit 1 fi printf "checking $header for $symbol.. " if [ "`grep $symbol $path`" != "" ] ; then eval "$lc_symbol=yes" echo "#define $uc_symbol 1" >> config.h echo "yes" return 0 else eval "$lc_symbol=no" echo "no" echo "#undef $uc_symbol" >> config.h return 1 fi } check_install() { printf "checking for a BSD-compatible install.. " if [ "`uname -s`" = "SunOS" ] ; then default_install=/usr/ucb/install else default_install=/usr/bin/install fi finalize install "$default_install" echo "$install" } check_target() { printf "checking operating system type.. " default_target=`uname -s | $tr '[:upper:]' '[:lower:]'` if [ "$default_target" = "sunos" ] ; then default_target="solaris" fi if [ "$default_target" = "gnu/kfreebsd" ] ; then default_target="freebsd" fi finalize target "$default_target" echo "$target" } check_compiler() { printf "checking for a C compiler.. " check_binary default_cc "/usr/bin/cc" "/usr/bin/gcc" "/usr/sfw/bin/gcc" finalize cc "$default_cc" echo "$cc" } check_linker() { printf "checking for a suitable linker.. " # Workaround for "hidden symbol is referenced by DSO" linker error # seen when compiling libdispatch. # Appears to be a problem with GCC 4.0 and binutils # default_ld="$cc" ldflags="-shared -o $program.so.$abi_major.$abi_minor $ldflags" # FIXME: port to solaris if [ "$target" = "linux" ] ; then ldflags="$ldflags -Wl,-export-dynamic -Wl,-soname,$program.so.$abi_major" fi if [ "$target" = "solaris" ] ; then ldflags="$ldflags" fi finalize ld "$default_ld" echo "$ld" } check_archiver() { printf "checking for a suitable archiver.. " if [ "`uname -s`" = "SunOS" -a "`uname -v | grep Nexenta`" = "" ] ; then default_ar="/usr/sfw/bin/gar" else default_ar="/usr/bin/ar" fi finalize ar "$default_ar" echo "$ar" } err() { echo "*** ERROR *** $*" rm -f config.mk $program.pc config.h exit 1 } check_diff() { # TODO: Support non-GNU diff syntax # TODO: Search for the command printf "checking for a suitable diff(1) command.. " finalize diff "diff -ruN -dEbwBp -x .svn -x .o -x config.h -x config.mk" echo "found" } subst_vars() { outfile=$1 if [ ! -f "${outfile}.in" ] ; then return fi echo "Creating $outfile" rm -f $outfile sed -e " s,@@CWD@@,`pwd`,g; s,@@PROGRAM@@,$program,g; s,@@VERSION@@,$version,g; s,@@PREFIX@@,$prefix,g; s,@@LIBDIR@@,$libdir,g; s,@@INCLUDEDIR@@,$includedir,g; s,@@MANDIR@@,$mandir,g; s,@@LIBDEPENDS@@,$libdepends,g; s,@@PKG_SUMMARY@@,$pkg_summary,g; s,@@PKG_DESCRIPTION@@,$pkg_description,g; s,@@LICENSE@@,$license,g; s,@@AUTHOR@@,$author,g; " < ${outfile}.in > $outfile chmod 400 $outfile } ####################################################################### # # MAIN() # ####################################################################### # Workaround for Solaris "Bad string" issue when LOCALE is undefined tr="/usr/bin/tr" test -f /usr/xpg4/bin/tr && tr="/usr/xpg4/bin/tr" . ./config.inc # Initialize the output files # for output_file in config.mk $program.pc do rm -f $output_file echo "# AUTOMATICALLY GENERATED -- DO NOT EDIT" > $output_file done rm -f config.h echo "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */" > config.h process_argv "$*" process_env check_target check_compiler check_linker check_archiver check_install check_diff finalize program "$program" finalize version "$version" finalize abi_major "$abi_major" finalize abi_minor "$abi_minor" finalize abi_version "$abi_major.$abi_minor" finalize prefix "/usr/local" finalize includedir "${prefix}/include" finalize libdir "${prefix}/lib" finalize mandir "${prefix}/share/man" finalize cflags "$cflags" finalize libdepends "$libdepends" finalize ldadd "" finalize ldflags "" finalize deps "" finalize ln "`which ln`" pre_configure_hook for header in $required_headers do check_header "$header" || err "$header is required, but cannot be found." done check_headers $optional_headers post_configure_hook objs="`echo \"$sources\" | sed 's/\.c/\.o/g'`" subst_vars "$program.pc" subst_vars "$program.la" subst_vars "rpm.spec" echo "Creating config.h" export_to_c $c_exports echo "Creating config.mk" export_to_make "$make_exports" libkqueue-1.0.4/config.inc0000644000175000017500000000515611607445336015022 0ustar mheilymheilyprogram="libkqueue" version="1.0.4" abi_major="0" abi_minor="0" abi_version="$abi_major.$abi_minor" cflags="-g -O2" ldflags="" sources="src/common/filter.c src/common/knote.c src/common/kevent.c src/common/kqueue.c" libdepends="" deps="src/common/private.h" mans="kqueue.2" headers="src/common/private.h" extra_dist="*.in" subdirs="src include test" # Package metadata pkg_summary="Emulates the kqueue and kevent system calls" pkg_description="Emulates the kqueue and kevent system calls" license="BSD" author="Mark Heily" pre_configure_hook() { cflags="-fpic -Wall -Werror -std=c99 -D_XOPEN_SOURCE=600 -D_BSD_SOURCE $cflags" if [ "$debug" = "yes" ] ; then cflags="$cflags -g3 -O0 -rdynamic" fi optional_headers="err.h" libdepends=" -L$libdir" if [ $target = "linux" ] ; then check_symbol sys/epoll.h EPOLLRDHUP # Actually a GCC 4.X dependency cflags="$cflags -fvisibility=hidden" libdepends="$libdepends -lpthread -lrt" required_headers="sys/epoll.h sys/inotify.h" optional_headers="sys/signalfd.h sys/timerfd.h sys/eventfd.h" fi if [ $target = "solaris" ] ; then cflags="$cflags -m64" ldflags="$ldflags -m64" libdepends="$libdepends -lsocket -lnsl" fi } post_configure_hook() { finalize target "$target" kevent="src/posix/kevent.c" evfilt_signal="src/posix/signal.c" evfilt_proc="src/$target/proc.c" evfilt_socket="src/$target/socket.c" evfilt_timer="src/posix/timer.c" evfilt_user="src/posix/user.c" evfilt_vnode="src/$target/vnode.c" eventfd="src/posix/eventfd.c" if [ $target = "linux" ] ; then # FIXME: not tested #if [ "$have_sys_signalfd_h" = "yes" ] ; then # evfilt_signal="src/linux/signal.c" #fi if [ "$have_sys_timerfd_h" = "yes" ] ; then evfilt_timer="src/linux/timer.c" fi if [ "$have_sys_eventfd_h" = "yes" ] ; then eventfd="src/linux/eventfd.c" fi fi if [ $target = "solaris" ] ; then cflags="$cflags -D__EXTENSIONS__" kevent="src/solaris/kevent.c" evfilt_timer="src/solaris/timer.c" evfilt_user="src/solaris/user.c" evfilt_proc="" evfilt_vnode="" eventfd="" fi # FIXME: This will compile but not actually work if [ $target = "freebsd" ] ; then evfilt_signal="src/posix/signal.c" evfilt_proc="" evfilt_socket="" evfilt_timer="" evfilt_vnode="" fi sources="$sources $kevent $eventfd $evfilt_signal $evfilt_proc $evfilt_socket $evfilt_timer $evfilt_user $evfilt_vnode" } libkqueue-1.0.4/kqueue.20000644000175000017500000003330111607445336014435 0ustar mheilymheily.\" $FreeBSD: Revision: 197243$ .\" Copyright (c) 2010 Mark Heily .\" Copyright (c) 2000 Jonathan Lemon .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd September 17, 2010 .Dt KQUEUE 2 .Os .Sh NAME .Nm kqueue , .Nm kevent .Nd kernel event notification mechanism .Sh SYNOPSIS .In sys/types.h .In sys/event.h .In sys/time.h .Ft int .Fn kqueue "void" .Ft int .Fn kevent "int kq" "const struct kevent *changelist" "int nchanges" "struct kevent *eventlist" "int nevents" "const struct timespec *timeout" .Fn EV_SET "&kev" ident filter flags fflags data udata .Sh DESCRIPTION The .Fn kqueue system call provides a generic method of notifying the user when an event happens or a condition holds, based on the results of small pieces of kernel code termed filters. A kevent is identified by the (ident, filter) pair; there may only be one unique kevent per kqueue. .Pp The filter is executed upon the initial registration of a kevent in order to detect whether a preexisting condition is present, and is also executed whenever an event is passed to the filter for evaluation. If the filter determines that the condition should be reported, then the kevent is placed on the kqueue for the user to retrieve. .Pp The filter is also run when the user attempts to retrieve the kevent from the kqueue. If the filter indicates that the condition that triggered the event no longer holds, the kevent is removed from the kqueue and is not returned. .Pp Multiple events which trigger the filter do not result in multiple kevents being placed on the kqueue; instead, the filter will aggregate the events into a single struct kevent. Calling .Fn close on a file descriptor will remove any kevents that reference the descriptor. .Pp The .Fn kqueue system call creates a new kernel event queue and returns a descriptor. The queue is not inherited by a child created with .Xr fork 2 . However, if .Xr rfork 2 is called without the .Dv RFFDG flag, then the descriptor table is shared, which will allow sharing of the kqueue between two processes. .Pp The .Fn kevent system call is used to register events with the queue, and return any pending events to the user. The .Fa changelist argument is a pointer to an array of .Va kevent structures, as defined in .In sys/event.h . All changes contained in the .Fa changelist are applied before any pending events are read from the queue. The .Fa nchanges argument gives the size of .Fa changelist . The .Fa eventlist argument is a pointer to an array of kevent structures. The .Fa nevents argument determines the size of .Fa eventlist . When .Fa nevents is zero, .Fn kevent will return immediately even if there is a .Fa timeout specified unlike .Xr select 2 . If .Fa timeout is a non-NULL pointer, it specifies a maximum interval to wait for an event, which will be interpreted as a struct timespec. If .Fa timeout is a NULL pointer, .Fn kevent waits indefinitely. To effect a poll, the .Fa timeout argument should be non-NULL, pointing to a zero-valued .Va timespec structure. The same array may be used for the .Fa changelist and .Fa eventlist . .Pp The .Fn EV_SET macro is provided for ease of initializing a kevent structure. .Pp The .Va kevent structure is defined as: .Bd -literal struct kevent { uintptr_t ident; /* identifier for this event */ short filter; /* filter for event */ u_short flags; /* action flags for kqueue */ u_int fflags; /* filter flag value */ intptr_t data; /* filter data value */ void *udata; /* opaque user data identifier */ }; .Ed .Pp The fields of .Fa struct kevent are: .Bl -tag -width XXXfilter .It ident Value used to identify this event. The exact interpretation is determined by the attached filter, but often is a file descriptor. .It filter Identifies the kernel filter used to process this event. The pre-defined system filters are described below. .It flags Actions to perform on the event. .It fflags Filter-specific flags. .It data Filter-specific data value. .It udata Opaque user-defined value passed through the kernel unchanged. .El .Pp The .Va flags field can contain the following values: .Bl -tag -width XXXEV_ONESHOT .It EV_ADD Adds the event to the kqueue. Re-adding an existing event will modify the parameters of the original event, and not result in a duplicate entry. Adding an event automatically enables it, unless overridden by the EV_DISABLE flag. .It EV_ENABLE Permit .Fn kevent to return the event if it is triggered. .It EV_DISABLE Disable the event so .Fn kevent will not return it. The filter itself is not disabled. .It EV_DISPATCH Disable the event source immediately after delivery of an event. See .Dv EV_DISABLE above. .It EV_DELETE Removes the event from the kqueue. Events which are attached to file descriptors are automatically deleted on the last close of the descriptor. .It EV_RECEIPT This flag is useful for making bulk changes to a kqueue without draining any pending events. When passed as input, it forces .Dv EV_ERROR to always be returned. When a filter is successfully added the .Va data field will be zero. .It EV_ONESHOT Causes the event to return only the first occurrence of the filter being triggered. After the user retrieves the event from the kqueue, it is deleted. .It EV_CLEAR After the event is retrieved by the user, its state is reset. This is useful for filters which report state transitions instead of the current state. Note that some filters may automatically set this flag internally. .It EV_EOF Filters may set this flag to indicate filter-specific EOF condition. .It EV_ERROR See .Sx RETURN VALUES below. .El .Pp The predefined system filters are listed below. Arguments may be passed to and from the filter via the .Va fflags and .Va data fields in the kevent structure. .Bl -tag -width EVFILT_SIGNAL .It EVFILT_READ Takes a descriptor as the identifier, and returns whenever there is data available to read. The behavior of the filter is slightly different depending on the descriptor type. .Pp .Bl -tag -width 2n .It Sockets Sockets which have previously been passed to .Fn listen return when there is an incoming connection pending. .Va data contains the size of the listen backlog. .Pp Other socket descriptors return when there is data to be read, subject to the .Dv SO_RCVLOWAT value of the socket buffer. This may be overridden with a per-filter low water mark at the time the filter is added by setting the NOTE_LOWAT flag in .Va fflags , and specifying the new low water mark in .Va data . On return, .Va data contains the number of bytes of protocol data available to read. .Pp If the read direction of the socket has shutdown, then the filter also sets EV_EOF in .Va flags , and returns the socket error (if any) in .Va fflags . It is possible for EOF to be returned (indicating the connection is gone) while there is still data pending in the socket buffer. .It Vnodes Returns when the file pointer is not at the end of file. .Va data contains the offset from current position to end of file, and may be negative. .It "Fifos, Pipes" Returns when the there is data to read; .Va data contains the number of bytes available. .Pp When the last writer disconnects, the filter will set EV_EOF in .Va flags . This may be cleared by passing in EV_CLEAR, at which point the filter will resume waiting for data to become available before returning. .It "BPF devices" Returns when the BPF buffer is full, the BPF timeout has expired, or when the BPF has .Dq immediate mode enabled and there is any data to read; .Va data contains the number of bytes available. .El .It EVFILT_WRITE Takes a descriptor as the identifier, and returns whenever it is possible to write to the descriptor. For sockets, pipes and fifos, .Va data will contain the amount of space remaining in the write buffer. The filter will set EV_EOF when the reader disconnects, and for the fifo case, this may be cleared by use of EV_CLEAR. Note that this filter is not supported for vnodes or BPF devices. .Pp For sockets, the low water mark and socket error handling is identical to the EVFILT_READ case. .It EVFILT_VNODE Takes a file descriptor as the identifier and the events to watch for in .Va fflags , and returns when one or more of the requested events occurs on the descriptor. The events to monitor are: .Bl -tag -width XXNOTE_RENAME .It NOTE_DELETE The .Fn unlink system call was called on the file referenced by the descriptor. .It NOTE_WRITE A write occurred on the file referenced by the descriptor. .It NOTE_EXTEND The file referenced by the descriptor was extended. .It NOTE_ATTRIB The file referenced by the descriptor had its attributes changed. .It NOTE_LINK The link count on the file changed. .It NOTE_RENAME The file referenced by the descriptor was renamed. .El .Pp On return, .Va fflags contains the events which triggered the filter. .It EVFILT_SIGNAL Takes the signal number to monitor as the identifier and returns when the given signal is delivered to the process. This overrides the .Fn signal and .Fn sigaction facilities, and has a higher precedence. The filter will record all attempts to deliver a signal to a process, even if the signal has been marked as SIG_IGN. .Va data returns the number of times the signal has occurred since the last call to .Fn kevent . This filter automatically sets the EV_CLEAR flag internally. .It EVFILT_TIMER Establishes an arbitrary timer identified by .Va ident . When adding a timer, .Va data specifies the timeout period in milliseconds. The timer will be periodic unless EV_ONESHOT is specified. On return, .Va data contains the number of times the timeout has expired since the last call to .Fn kevent . This filter automatically sets the EV_CLEAR flag internally. There is a system wide limit on the number of timers which is controlled by the .Va kern.kq_calloutmax sysctl. .It Dv EVFILT_USER Establishes a user event identified by .Va ident which is not assosicated with any kernel mechanism but is triggered by user level code. The lower 24 bits of the .Va fflags may be used for user defined flags and manipulated using the following: .Bl -tag -width XXNOTE_FFLAGSMASK .It Dv NOTE_FFNOP Ignore the input .Va fflags . .It Dv NOTE_FFAND Bitwise AND .Va fflags . .It Dv NOTE_FFOR Bitwise OR .Va fflags . .It Dv NOTE_COPY Copy .Va fflags . .It Dv NOTE_FFCTRLMASK Control mask for .Va fflags . .It Dv NOTE_FFLAGSMASK User defined flag mask for .Va fflags . .El .Pp A user event is triggered for output with the following: .Bl -tag -width XXNOTE_FFLAGSMASK .It Dv NOTE_TRIGGER Cause the event to be triggered. .El .Pp On return, .Va fflags contains the users defined flags in the lower 24 bits. .El .Sh RETURN VALUES The .Fn kqueue system call creates a new kernel event queue and returns a file descriptor. If there was an error creating the kernel event queue, a value of -1 is returned and errno set. .Pp The .Fn kevent system call returns the number of events placed in the .Fa eventlist , up to the value given by .Fa nevents . If an error occurs while processing an element of the .Fa changelist and there is enough room in the .Fa eventlist , then the event will be placed in the .Fa eventlist with .Dv EV_ERROR set in .Va flags and the system error in .Va data . Otherwise, .Dv -1 will be returned, and .Dv errno will be set to indicate the error condition. If the time limit expires, then .Fn kevent returns 0. .Sh ERRORS The .Fn kqueue system call fails if: .Bl -tag -width Er .It Bq Er ENOMEM The kernel failed to allocate enough memory for the kernel queue. .It Bq Er EMFILE The per-process descriptor table is full. .It Bq Er ENFILE The system file table is full. .El .Pp The .Fn kevent system call fails if: .Bl -tag -width Er .It Bq Er EACCES The process does not have permission to register a filter. .It Bq Er EFAULT There was an error reading or writing the .Va kevent structure. .It Bq Er EBADF The specified descriptor is invalid. .It Bq Er EINTR A signal was delivered before the timeout expired and before any events were placed on the kqueue for return. .It Bq Er EINVAL The specified time limit or filter is invalid. .It Bq Er ENOENT The event could not be found to be modified or deleted. .It Bq Er ENOMEM No memory was available to register the event or, in the special case of a timer, the maximum number of timers has been exceeded. This maximum is configurable via the .Va kern.kq_calloutmax sysctl. .It Bq Er ESRCH The specified process to attach to does not exist. .El .Sh SEE ALSO .Xr aio_error 2 , .Xr aio_read 2 , .Xr aio_return 2 , .Xr poll 2 , .Xr read 2 , .Xr select 2 , .Xr sigaction 2 , .Xr write 2 , .Xr signal 3 .Sh HISTORY The .Fn kqueue and .Fn kevent system calls first appeared in .Fx 4.1 . .Sh AUTHORS The .Fn kqueue system and this manual page were written by .An Jonathan Lemon Aq jlemon@FreeBSD.org . libkqueue-1.0.4/libkqueue.la.in0000644000175000017500000000143111607445336015763 0ustar mheilymheily# @@PROGRAM@@.la - a libtool library file # Generated by ltmain.sh - GNU libtool 1.5.20 (1.1220.2.287 2005/08/31 18:54:15) # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='@@PROGRAM@@.so.0' # Names of this library. library_names='@@PROGRAM@@.so' # The name of the static archive. old_library='@@PROGRAM@@.a' # Libraries that this one depends upon. dependency_libs=' @@LIBDEPENDS@@' # Version information for @@PROGRAM@@. current=0 age=0 revision=0 # Is this an already installed library? installed=yes # Should we warn about portability when linking against -modules? shouldnotlink=no # Files to dlopen/dlpreopen dlopen='' dlpreopen='' # Directory that this library needs to be installed in: libdir='@@LIBDIR@@' libkqueue-1.0.4/libkqueue.pc.in0000644000175000017500000000043711607445336015776 0ustar mheilymheilyprefix=@@PREFIX@@ exec_prefix=${prefix} libdir=@@LIBDIR@@ includedir=@@INCLUDEDIR@@ Name: @@PROGRAM@@ Description: Emulates FreeBSD kqueue(2) on other platforms Version: @@VERSION@@ Requires: Libs: @@LIBDEPENDS@@ -lkqueue Libs.private: @@LIBDEPENDS@@ Cflags: -I${includedir}/kqueue libkqueue-1.0.4/rpm.spec.in0000644000175000017500000000323611607445336015136 0ustar mheilymheily# # Copyright (c) 2009 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # Name: @@PROGRAM@@ Summary: @@PKG_SUMMARY@@ Version: @@VERSION@@ Release: 1 License: @@LICENSE@@ Vendor: @@AUTHOR@@ Group: System Environment/Libraries Source0: %{name}-%version.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-build %description @@PKG_DESCRIPTION@@ %prep %setup -q -n @@PROGRAM@@-@@VERSION@@ %build ./configure --prefix=/usr make %install make DESTDIR=$RPM_BUILD_ROOT install gzip $RPM_BUILD_ROOT/usr/share/man/man2/kqueue.2 gzip $RPM_BUILD_ROOT/usr/share/man/man2/kevent.2 %clean [ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} %post /sbin/ldconfig %postun /sbin/ldconfig %files %defattr(-,root,root) /usr/include/kqueue/sys/event.h /usr/lib/libkqueue.so /usr/lib/libkqueue.so.0 /usr/lib/libkqueue.so.0.0 /usr/lib/libkqueue.la /usr/lib/libkqueue.a /usr/lib/pkgconfig/libkqueue.pc /usr/share/man/man2/kqueue.2.gz /usr/share/man/man2/kevent.2.gz %changelog libkqueue-1.0.4/src/0000755000175000017500000000000011607445336013642 5ustar mheilymheilylibkqueue-1.0.4/src/linux/0000755000175000017500000000000011607445336015001 5ustar mheilymheilylibkqueue-1.0.4/src/linux/timer.c0000644000175000017500000001432111607445336016266 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* Linux equivalents to kqueue(2) */ #include #include "sys/event.h" #include "private.h" #ifndef NDEBUG static char * itimerspec_dump(struct itimerspec *ts) { static char __thread buf[1024]; snprintf(buf, sizeof(buf), "itimer: [ interval=%lu s %lu ns, next expire=%lu s %lu ns ]", ts->it_interval.tv_sec, ts->it_interval.tv_nsec, ts->it_value.tv_sec, ts->it_value.tv_nsec ); return (buf); } #endif /* Convert milliseconds into seconds+nanoseconds */ static void convert_msec_to_itimerspec(struct itimerspec *dst, int src, int oneshot) { time_t sec, nsec; sec = src / 1000; nsec = (src % 1000) * 1000000; /* Set the interval */ if (oneshot) { dst->it_interval.tv_sec = 0; dst->it_interval.tv_nsec = 0; } else { dst->it_interval.tv_sec = sec; dst->it_interval.tv_nsec = nsec; } /* Set the initial expiration */ dst->it_value.tv_sec = sec; dst->it_value.tv_nsec = nsec; dbg_printf("%s", itimerspec_dump(dst)); } static int ktimer_delete(struct filter *filt, struct knote *kn) { int rv = 0; if (kn->data.pfd == -1) return (0); dbg_printf("removing timerfd %d from %d", kn->data.pfd, filt->kf_pfd); if (epoll_ctl(filt->kf_pfd, EPOLL_CTL_DEL, kn->data.pfd, NULL) < 0) { dbg_printf("epoll_ctl(2): %s", strerror(errno)); rv = -1; } if (close(kn->data.pfd) < 0) { dbg_printf("close(2): %s", strerror(errno)); rv = -1; } kn->data.pfd = -1; return (rv); } int evfilt_timer_init(struct filter *filt) { filt->kf_pfd = epoll_create(1); if (filt->kf_pfd < 0) return (-1); dbg_printf("timer epollfd = %d", filt->kf_pfd); return (0); } void evfilt_timer_destroy(struct filter *filt) { close (filt->kf_pfd);//LAME } /* TODO: This entire function is copy+pasted from socket.c with minor changes for timerfds. Perhaps it could be refactored into a generic epoll_copyout() that calls custom per-filter actions. */ int evfilt_timer_copyout(struct filter *filt, struct kevent *dst, int nevents) { struct epoll_event epevt[MAX_KEVENT]; struct epoll_event *ev; struct knote *kn; uint64_t expired; int i, nret; ssize_t n; for (;;) { nret = epoll_wait(filt->kf_pfd, &epevt[0], nevents, 0); if (nret < 0) { if (errno == EINTR) continue; dbg_perror("epoll_wait"); return (-1); } else { break; } } for (i = 0, nevents = 0; i < nret; i++) { ev = &epevt[i]; /* TODO: put in generic debug.c: epoll_event_dump(ev); */ kn = ev->data.ptr; memcpy(dst, &kn->kev, sizeof(*dst)); if (ev->events & EPOLLERR) dst->fflags = 1; /* FIXME: Return the actual timer error */ /* On return, data contains the number of times the timer has been trigered. */ n = read(kn->data.pfd, &expired, sizeof(expired)); if (n < 0 || n < sizeof(expired)) { dbg_puts("invalid read from timerfd"); expired = 1; /* Fail gracefully */ } dst->data = expired; if (kn->kev.flags & EV_DISPATCH) { KNOTE_DISABLE(kn); ktimer_delete(filt, kn); } else if (kn->kev.flags & EV_ONESHOT) { ktimer_delete(filt, kn); knote_free(filt, kn); } nevents++; dst++; } return (nevents); } int evfilt_timer_knote_create(struct filter *filt, struct knote *kn) { struct epoll_event ev; struct itimerspec ts; int tfd; kn->kev.flags |= EV_CLEAR; tfd = timerfd_create(CLOCK_MONOTONIC, 0); if (tfd < 0) { dbg_printf("timerfd_create(2): %s", strerror(errno)); return (-1); } dbg_printf("created timerfd %d", tfd); convert_msec_to_itimerspec(&ts, kn->kev.data, kn->kev.flags & EV_ONESHOT); if (timerfd_settime(tfd, 0, &ts, NULL) < 0) { dbg_printf("timerfd_settime(2): %s", strerror(errno)); close(tfd); return (-1); } memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; ev.data.ptr = kn; if (epoll_ctl(filt->kf_pfd, EPOLL_CTL_ADD, tfd, &ev) < 0) { dbg_printf("epoll_ctl(2): %d", errno); close(tfd); return (-1); } kn->data.pfd = tfd; return (0); } int evfilt_timer_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { return (0); /* STUB */ } int evfilt_timer_knote_delete(struct filter *filt, struct knote *kn) { return (ktimer_delete(filt,kn)); } int evfilt_timer_knote_enable(struct filter *filt, struct knote *kn) { return evfilt_timer_knote_create(filt, kn); } int evfilt_timer_knote_disable(struct filter *filt, struct knote *kn) { return evfilt_timer_knote_delete(filt, kn); } const struct filter evfilt_timer = { EVFILT_TIMER, evfilt_timer_init, evfilt_timer_destroy, evfilt_timer_copyout, evfilt_timer_knote_create, evfilt_timer_knote_modify, evfilt_timer_knote_delete, evfilt_timer_knote_enable, evfilt_timer_knote_disable, }; libkqueue-1.0.4/src/linux/proc.c0000644000175000017500000001375711607445336016125 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" /* XXX-FIXME Should only have one wait_thread per process. Now, there is one thread per kqueue */ struct evfilt_data { pthread_t wthr_id; pthread_cond_t wait_cond; pthread_mutex_t wait_mtx; }; //FIXME: WANT: static void * void * wait_thread(void *arg) { struct filter *filt = (struct filter *) arg; uint64_t counter = 1; const int options = WEXITED | WNOWAIT; struct knote *kn; siginfo_t si; sigset_t sigmask; /* Block all signals */ sigfillset (&sigmask); pthread_sigmask(SIG_BLOCK, &sigmask, NULL); for (;;) { /* Wait for a child process to exit(2) */ if (waitid(P_ALL, 0, &si, options) != 0) { if (errno == ECHILD) { dbg_puts("got ECHILD, waiting for wakeup condition"); pthread_mutex_lock(&filt->kf_data->wait_mtx); pthread_cond_wait(&filt->kf_data->wait_cond, &filt->kf_data->wait_mtx); pthread_mutex_unlock(&filt->kf_data->wait_mtx); dbg_puts("awoken from ECHILD-induced sleep"); continue; } dbg_puts(" waitid(2) returned"); if (errno == EINTR) continue; dbg_perror("waitid(2)"); break; } /* Scan the wait queue to see if anyone is interested */ kn = knote_lookup(filt, si.si_pid); if (kn == NULL) continue; /* Create a proc_event */ if (si.si_code == CLD_EXITED) { kn->kev.data = si.si_status; } else if (si.si_code == CLD_KILLED) { /* FIXME: probably not true on BSD */ /* FIXME: arbitrary non-zero number */ kn->kev.data = 254; } else { /* Should never happen. */ /* FIXME: arbitrary non-zero number */ kn->kev.data = 1; } knote_enqueue(filt, kn); /* Indicate read(2) readiness */ if (write(filt->kf_pfd, &counter, sizeof(counter)) < 0) { if (errno != EAGAIN) { dbg_printf("write(2): %s", strerror(errno)); /* TODO: set filter error flag */ break; } } } /* TODO: error handling */ return (NULL); } int evfilt_proc_init(struct filter *filt) { #if FIXME struct evfilt_data *ed; int efd = -1; if ((ed = calloc(1, sizeof(*ed))) == NULL) return (-1); filt->kf_data = ed; pthread_mutex_init(&ed->wait_mtx, NULL); pthread_cond_init(&ed->wait_cond, NULL); if ((efd = eventfd(0, 0)) < 0) goto errout; if (fcntl(filt->kf_pfd, F_SETFL, O_NONBLOCK) < 0) goto errout; filt->kf_pfd = efd; if (pthread_create(&ed->wthr_id, NULL, wait_thread, filt) != 0) goto errout; return (0); errout: if (efd >= 0) close(efd); free(ed); close(filt->kf_pfd); return (-1); #endif return (-1); /*STUB*/ } void evfilt_proc_destroy(struct filter *filt) { //TODO: pthread_cancel(filt->kf_data->wthr_id); close(filt->kf_pfd); } int evfilt_proc_copyout(struct filter *filt, struct kevent *dst, int maxevents) { struct knote *kn; int nevents = 0; uint64_t cur; /* Reset the counter */ if (read(filt->kf_pfd, &cur, sizeof(cur)) < sizeof(cur)) { dbg_printf("read(2): %s", strerror(errno)); return (-1); } dbg_printf(" counter=%llu", (unsigned long long) cur); for (kn = knote_dequeue(filt); kn != NULL; kn = knote_dequeue(filt)) { kevent_dump(&kn->kev); memcpy(dst, &kn->kev, sizeof(*dst)); if (kn->kev.flags & EV_DISPATCH) { KNOTE_DISABLE(kn); } if (kn->kev.flags & EV_ONESHOT) { knote_free(filt, kn); } else { kn->kev.data = 0; //why?? } if (++nevents > maxevents) break; dst++; } if (knote_events_pending(filt)) { /* XXX-FIXME: If there are leftover events on the waitq, re-arm the eventfd. list */ abort(); } return (nevents); } int evfilt_proc_knote_create(struct filter *filt, struct knote *kn) { return (0); /* STUB */ } int evfilt_proc_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { return (0); /* STUB */ } int evfilt_proc_knote_delete(struct filter *filt, struct knote *kn) { return (0); /* STUB */ } int evfilt_proc_knote_enable(struct filter *filt, struct knote *kn) { return (0); /* STUB */ } int evfilt_proc_knote_disable(struct filter *filt, struct knote *kn) { return (0); /* STUB */ } const struct filter evfilt_proc_DEADWOOD = { 0, //XXX-FIXME broken: EVFILT_PROC, evfilt_proc_init, evfilt_proc_destroy, evfilt_proc_copyout, evfilt_proc_knote_create, evfilt_proc_knote_modify, evfilt_proc_knote_delete, evfilt_proc_knote_enable, evfilt_proc_knote_disable, }; libkqueue-1.0.4/src/linux/socket.c0000644000175000017500000001516111607445336016441 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" static char * epoll_event_dump(struct epoll_event *evt) { static char __thread buf[128]; if (evt == NULL) return "(null)"; #define EPEVT_DUMP(attrib) \ if (evt->events & attrib) \ strcat(&buf[0], #attrib" "); snprintf(&buf[0], 128, " { data = %p, events = ", evt->data.ptr); EPEVT_DUMP(EPOLLIN); EPEVT_DUMP(EPOLLOUT); #if defined(HAVE_EPOLLRDHUP) EPEVT_DUMP(EPOLLRDHUP); #endif EPEVT_DUMP(EPOLLONESHOT); EPEVT_DUMP(EPOLLET); strcat(&buf[0], "}\n"); return (&buf[0]); #undef EPEVT_DUMP } static int epoll_update(int op, struct filter *filt, struct knote *kn, struct epoll_event *ev) { dbg_printf("op=%d fd=%d events=%s", op, (int)kn->kev.ident, epoll_event_dump(ev)); if (epoll_ctl(filt->kf_pfd, op, kn->kev.ident, ev) < 0) { dbg_printf("epoll_ctl(2): %s", strerror(errno)); return (-1); } return (0); } static int socket_knote_delete(int epfd, int fd) { return epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); } int evfilt_socket_init(struct filter *filt) { filt->kf_pfd = epoll_create(1); if (filt->kf_pfd < 0) return (-1); dbg_printf("socket epollfd = %d", filt->kf_pfd); return (0); } void evfilt_socket_destroy(struct filter *filt) { close(filt->kf_pfd); } int evfilt_socket_copyout(struct filter *filt, struct kevent *dst, int nevents) { struct epoll_event epevt[MAX_KEVENT]; struct epoll_event *ev; struct knote *kn; int i, nret; for (;;) { nret = epoll_wait(filt->kf_pfd, &epevt[0], nevents, 0); if (nret < 0) { if (errno == EINTR) continue; dbg_perror("epoll_wait"); return (-1); } else { break; } } for (i = 0, nevents = 0; i < nret; i++) { ev = &epevt[i]; epoll_event_dump(ev); kn = knote_lookup(filt, ev->data.fd); if (kn != NULL) { memcpy(dst, &kn->kev, sizeof(*dst)); #if defined(HAVE_EPOLLRDHUP) if (ev->events & EPOLLRDHUP || ev->events & EPOLLHUP) dst->flags |= EV_EOF; #else if (ev->events & EPOLLHUP) dst->flags |= EV_EOF; #endif if (ev->events & EPOLLERR) dst->fflags = 1; /* FIXME: Return the actual socket error */ if (kn->flags & KNFL_PASSIVE_SOCKET) { /* On return, data contains the length of the socket backlog. This is not available under Linux. */ dst->data = 1; } else { /* On return, data contains the number of bytes of protocol data available to read. */ if (ioctl(dst->ident, (dst->filter == EVFILT_READ) ? SIOCINQ : SIOCOUTQ, &dst->data) < 0) { /* race condition with socket close, so ignore this error */ dbg_puts("ioctl(2) of socket failed"); dst->data = 0; } } if (kn->kev.flags & EV_DISPATCH) { socket_knote_delete(filt->kf_pfd, kn->kev.ident); KNOTE_DISABLE(kn); } else if (kn->kev.flags & EV_ONESHOT) { socket_knote_delete(filt->kf_pfd, kn->kev.ident); knote_free(filt, kn); } nevents++; dst++; } } return (nevents); } int evfilt_socket_knote_create(struct filter *filt, struct knote *kn) { struct epoll_event ev; if (knote_get_socket_type(kn) < 0) return (-1); /* Convert the kevent into an epoll_event */ if (kn->kev.filter == EVFILT_READ) #if defined(HAVE_EPOLLRDHUP) kn->data.events = EPOLLIN | EPOLLRDHUP; #else kn->data.events = EPOLLIN; #endif else kn->data.events = EPOLLOUT; if (kn->kev.flags & EV_ONESHOT || kn->kev.flags & EV_DISPATCH) kn->data.events |= EPOLLONESHOT; if (kn->kev.flags & EV_CLEAR) kn->data.events |= EPOLLET; memset(&ev, 0, sizeof(ev)); ev.events = kn->data.events; ev.data.fd = kn->kev.ident; return epoll_update(EPOLL_CTL_ADD, filt, kn, &ev); } int evfilt_socket_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { return (-1); /* STUB */ } int evfilt_socket_knote_delete(struct filter *filt, struct knote *kn) { if (kn->kev.flags & EV_DISABLE) return (0); else return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL); } int evfilt_socket_knote_enable(struct filter *filt, struct knote *kn) { struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = kn->data.events; ev.data.fd = kn->kev.ident; return epoll_update(EPOLL_CTL_ADD, filt, kn, &ev); } int evfilt_socket_knote_disable(struct filter *filt, struct knote *kn) { return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL); } const struct filter evfilt_read = { EVFILT_READ, evfilt_socket_init, evfilt_socket_destroy, evfilt_socket_copyout, evfilt_socket_knote_create, evfilt_socket_knote_modify, evfilt_socket_knote_delete, evfilt_socket_knote_enable, evfilt_socket_knote_disable, }; const struct filter evfilt_write = { EVFILT_WRITE, evfilt_socket_init, evfilt_socket_destroy, evfilt_socket_copyout, evfilt_socket_knote_create, evfilt_socket_knote_modify, evfilt_socket_knote_delete, evfilt_socket_knote_enable, evfilt_socket_knote_disable, }; libkqueue-1.0.4/src/linux/eventfd.c0000644000175000017500000000517611607445336016611 0ustar mheilymheily/* * Copyright (c) 2010 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "private.h" /* A structure is used to allow other targets to emulate this */ struct eventfd { int fd; }; struct eventfd * eventfd_create(void) { struct eventfd *e; int evfd; e = malloc(sizeof(*e)); if (e == NULL) return (NULL); if ((evfd = eventfd(0, 0)) < 0) { free(e); return (NULL); } if (fcntl(evfd, F_SETFL, O_NONBLOCK) < 0) { free(e); close(evfd); return (NULL); } e->fd = evfd; return (e); } void eventfd_free(struct eventfd *e) { close(e->fd); free(e); } int eventfd_raise(struct eventfd *e) { uint64_t counter; int rv = 0; dbg_puts("raising event level"); counter = 1; if (write(e->fd, &counter, sizeof(counter)) < 0) { switch (errno) { case EAGAIN: /* Not considered an error */ break; case EINTR: rv = -EINTR; break; default: dbg_printf("write(2): %s", strerror(errno)); rv = -1; } } return (rv); } int eventfd_lower(struct eventfd *e) { uint64_t cur; int rv = 0; /* Reset the counter */ dbg_puts("lowering event level"); if (read(e->fd, &cur, sizeof(cur)) < sizeof(cur)) { switch (errno) { case EAGAIN: /* Not considered an error */ break; case EINTR: rv = -EINTR; break; default: dbg_printf("read(2): %s", strerror(errno)); rv = -1; } } return (rv); } int eventfd_reader(struct eventfd *e) { return (e->fd); } int eventfd_writer(struct eventfd *e) { return (e->fd); } libkqueue-1.0.4/src/linux/vnode.c0000644000175000017500000002013211607445336016256 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" static char * inotify_mask_dump(uint32_t mask) { static char __thread buf[1024]; #define INEVT_MASK_DUMP(attrib) \ if (mask & attrib) \ strcat(buf, #attrib" "); snprintf(buf, sizeof(buf), "mask = %d (", mask); INEVT_MASK_DUMP(IN_ACCESS); INEVT_MASK_DUMP(IN_MODIFY); INEVT_MASK_DUMP(IN_ATTRIB); INEVT_MASK_DUMP(IN_CLOSE_WRITE); INEVT_MASK_DUMP(IN_CLOSE_NOWRITE); INEVT_MASK_DUMP(IN_OPEN); INEVT_MASK_DUMP(IN_MOVED_FROM); INEVT_MASK_DUMP(IN_MOVED_TO); INEVT_MASK_DUMP(IN_CREATE); INEVT_MASK_DUMP(IN_DELETE); INEVT_MASK_DUMP(IN_DELETE_SELF); INEVT_MASK_DUMP(IN_MOVE_SELF); buf[strlen(buf) - 1] = ')'; return (buf); } static char * inotify_event_dump(struct inotify_event *evt) { static char __thread buf[1024]; snprintf(buf, sizeof(buf), "wd=%d mask=%s", evt->wd, inotify_mask_dump(evt->mask)); return (buf); } static int fd_to_path(char *buf, size_t bufsz, int fd) { char path[1024]; //TODO: Maxpathlen, etc. if (snprintf(&path[0], sizeof(path), "/proc/%d/fd/%d", getpid(), fd) < 0) return (-1); memset(buf, 0, bufsz); return (readlink(path, buf, bufsz)); } /* TODO: USE this to get events with name field */ int get_one_event(struct inotify_event *dst, int pfd) { ssize_t n; dbg_puts("reading one inotify event"); for (;;) { n = read(pfd, dst, sizeof(*dst)); if (n < 0) { if (errno == EINTR) continue; dbg_perror("read"); return (-1); } else { break; } } dbg_printf("read(2) from inotify wd: %zu bytes", n); /* FIXME-TODO: if len > 0, read(len) */ if (dst->len != 0) abort(); return (0); } static int add_watch(struct filter *filt, struct knote *kn) { char path[PATH_MAX]; uint32_t mask; /* Convert the fd to a pathname */ if (fd_to_path(&path[0], sizeof(path), kn->kev.ident) < 0) return (-1); /* Convert the fflags to the inotify mask */ mask = 0; if (kn->kev.fflags & NOTE_DELETE) mask |= IN_ATTRIB | IN_DELETE_SELF; if (kn->kev.fflags & NOTE_WRITE) mask |= IN_MODIFY | IN_ATTRIB; if (kn->kev.fflags & NOTE_EXTEND) mask |= IN_MODIFY | IN_ATTRIB; if ((kn->kev.fflags & NOTE_ATTRIB) || (kn->kev.fflags & NOTE_LINK)) mask |= IN_ATTRIB; if (kn->kev.fflags & NOTE_RENAME) mask |= IN_MOVE_SELF; if (kn->kev.flags & EV_ONESHOT) mask |= IN_ONESHOT; dbg_printf("inotify_add_watch(2); inofd=%d, %s, path=%s", filt->kf_pfd, inotify_mask_dump(mask), path); kn->kev.data = inotify_add_watch(filt->kf_pfd, path, mask); if (kn->kev.data < 0) { dbg_printf("inotify_add_watch(2): %s", strerror(errno)); return (-1); } return (0); } static int delete_watch(struct filter *filt, struct knote *kn) { if (kn->kev.data < 0) return (0); if (inotify_rm_watch(filt->kf_pfd, kn->kev.data) < 0) { dbg_printf("inotify_rm_watch(2): %s", strerror(errno)); return (-1); } dbg_printf("wd %d removed", (int) kn->kev.data); kn->kev.data = -1; return (0); } int evfilt_vnode_init(struct filter *filt) { filt->kf_pfd = inotify_init(); dbg_printf("inotify fd = %d", filt->kf_pfd); if (filt->kf_pfd < 0) return (-1); return (0); } void evfilt_vnode_destroy(struct filter *filt) { close(filt->kf_pfd); } int evfilt_vnode_copyout(struct filter *filt, struct kevent *dst, int nevents) { struct inotify_event evt; struct stat sb; struct knote *kn; if (get_one_event(&evt, filt->kf_pfd) < 0) return (-1); dbg_printf("inotify event: %s", inotify_event_dump(&evt)); if (evt.mask & IN_IGNORED) { /* TODO: possibly return error when fs is unmounted */ return (0); } kn = knote_lookup_data(filt, evt.wd); if (kn == NULL) { dbg_printf("no match for wd # %d", evt.wd); return (-1); } memcpy(dst, &kn->kev, sizeof(*dst)); dst->data = 0; /* No error checking because fstat(2) should rarely fail */ //FIXME: EINTR if ((evt.mask & IN_ATTRIB || evt.mask & IN_MODIFY) && fstat(kn->kev.ident, &sb) == 0) { if (sb.st_nlink == 0 && kn->kev.fflags & NOTE_DELETE) dst->fflags |= NOTE_DELETE; if (sb.st_nlink != kn->data.vnode.nlink && kn->kev.fflags & NOTE_LINK) dst->fflags |= NOTE_LINK; #if HAVE_NOTE_TRUNCATE if (sb.st_nsize == 0 && kn->kev.fflags & NOTE_TRUNCATE) dst->fflags |= NOTE_TRUNCATE; #endif if (sb.st_size > kn->data.vnode.size && kn->kev.fflags & NOTE_WRITE) dst->fflags |= NOTE_EXTEND; kn->data.vnode.nlink = sb.st_nlink; kn->data.vnode.size = sb.st_size; } if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE) dst->fflags |= NOTE_WRITE; if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB) dst->fflags |= NOTE_ATTRIB; if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME) dst->fflags |= NOTE_RENAME; if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE) dst->fflags |= NOTE_DELETE; if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE) dst->fflags |= NOTE_WRITE; if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB) dst->fflags |= NOTE_ATTRIB; if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME) dst->fflags |= NOTE_RENAME; if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE) dst->fflags |= NOTE_DELETE; if (kn->kev.flags & EV_DISPATCH) { delete_watch(filt, kn); /* TODO: error checking */ KNOTE_DISABLE(kn); } else if (kn->kev.flags & EV_ONESHOT) { delete_watch(filt, kn); /* TODO: error checking */ knote_free(filt, kn); } return (1); } int evfilt_vnode_knote_create(struct filter *filt, struct knote *kn) { struct stat sb; if (fstat(kn->kev.ident, &sb) < 0) { dbg_puts("fstat failed"); return (-1); } kn->data.vnode.nlink = sb.st_nlink; kn->data.vnode.size = sb.st_size; kn->kev.data = -1; return (add_watch(filt, kn)); } int evfilt_vnode_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { return (-1); /* FIXME - STUB */ } int evfilt_vnode_knote_delete(struct filter *filt, struct knote *kn) { return delete_watch(filt, kn); } int evfilt_vnode_knote_enable(struct filter *filt, struct knote *kn) { return add_watch(filt, kn); } int evfilt_vnode_knote_disable(struct filter *filt, struct knote *kn) { return delete_watch(filt, kn); } const struct filter evfilt_vnode = { EVFILT_VNODE, evfilt_vnode_init, evfilt_vnode_destroy, evfilt_vnode_copyout, evfilt_vnode_knote_create, evfilt_vnode_knote_modify, evfilt_vnode_knote_delete, evfilt_vnode_knote_enable, evfilt_vnode_knote_disable, }; libkqueue-1.0.4/src/linux/signal.c0000644000175000017500000001103111607445336016416 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" /* Highest signal number supported. POSIX standard signals are < 32 */ #define SIGNAL_MAX 32 static int update_sigmask(const struct filter *filt) { int rv; rv = signalfd(filt->kf_pfd, &filt->kf_sigmask, 0); dbg_printf("signalfd = %d", filt->kf_pfd); if (rv < 0 || rv != filt->kf_pfd) { dbg_printf("signalfd(2): %s", strerror(errno)); return (-1); } return (0); } int evfilt_signal_init(struct filter *filt) { sigemptyset(&filt->kf_sigmask); filt->kf_pfd = signalfd(-1, &filt->kf_sigmask, 0); dbg_printf("signalfd = %d", filt->kf_pfd); if (filt->kf_pfd < 0) return (-1); return (0); } void evfilt_signal_destroy(struct filter *filt) { close (filt->kf_pfd); } int evfilt_signal_copyout(struct filter *filt, struct kevent *dst, int nevents) { struct knote *kn; struct signalfd_siginfo sig[MAX_KEVENT]; int i; ssize_t n; n = read(filt->kf_pfd, &sig, nevents * sizeof(sig[0])); if (n < 0 || n < sizeof(sig[0])) { dbg_puts("invalid read from signalfd"); return (-1); } n /= sizeof(sig[0]); for (i = 0, nevents = 0; i < n; i++) { /* This is not an error because of this race condition: * 1. Signal arrives and is queued * 2. The kevent is deleted via kevent(..., EV_DELETE) * 3. The event is dequeued from the signalfd */ kn = knote_lookup(filt, sig[i].ssi_signo); if (kn == NULL) continue; dbg_printf("got signal %d", sig[i].ssi_signo); memcpy(dst, &kn->kev, sizeof(*dst)); /* TODO: dst->data should be the number of times the signal occurred */ dst->data = 1; if (kn->kev.flags & EV_DISPATCH || kn->kev.flags & EV_ONESHOT) { sigdelset(&filt->kf_sigmask, dst->ident); update_sigmask(filt); /* TODO: error checking */ } if (kn->kev.flags & EV_DISPATCH) KNOTE_DISABLE(kn); if (kn->kev.flags & EV_ONESHOT) knote_free(filt, kn); dst++; nevents++; } return (nevents); } int evfilt_signal_knote_create(struct filter *filt, struct knote *kn) { if (kn->kev.ident >= SIGNAL_MAX) { dbg_printf("bad signal number %u", (u_int) kn->kev.ident); return (-1); } kn->kev.flags |= EV_CLEAR; sigaddset(&filt->kf_sigmask, kn->kev.ident); return (update_sigmask(filt)); } int evfilt_signal_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { if (kev.ident >= SIGNAL_MAX) { dbg_printf("bad signal number %u", (u_int) kev.ident); return (-1); } /* Nothing to do since the sigmask does not change. */ return (0); } int evfilt_signal_knote_delete(struct filter *filt, struct knote *kn) { sigdelset(&filt->kf_sigmask, kn->kev.ident); return (update_sigmask(filt)); } int evfilt_signal_knote_enable(struct filter *filt, struct knote *kn) { sigaddset(&filt->kf_sigmask, kn->kev.ident); return (update_sigmask(filt)); } int evfilt_signal_knote_disable(struct filter *filt, struct knote *kn) { return (evfilt_signal_knote_delete(filt, kn)); } const struct filter evfilt_signal = { EVFILT_SIGNAL, evfilt_signal_init, evfilt_signal_destroy, evfilt_signal_copyout, evfilt_signal_knote_create, evfilt_signal_knote_modify, evfilt_signal_knote_delete, evfilt_signal_knote_enable, evfilt_signal_knote_disable, }; libkqueue-1.0.4/src/posix/0000755000175000017500000000000011607445336015004 5ustar mheilymheilylibkqueue-1.0.4/src/posix/user.c0000644000175000017500000001074611607445336016136 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" int evfilt_user_init(struct filter *filt) { filt->kf_efd = eventfd_create(); if (filt->kf_efd == NULL) return (-1); filt->kf_pfd = eventfd_reader(filt->kf_efd); if (filt->kf_pfd < 0) return (-1); return (0); } void evfilt_user_destroy(struct filter *filt) { eventfd_free(filt->kf_efd); return; } int evfilt_user_copyout(struct filter *filt, struct kevent *dst, int maxevents) { struct knote *kn; int nevents = 0; for (kn = knote_dequeue(filt); kn != NULL; kn = knote_dequeue(filt)) { memcpy(dst, &kn->kev, sizeof(*dst)); dst->fflags &= ~NOTE_FFCTRLMASK; //FIXME: Not sure if needed dst->fflags &= ~NOTE_TRIGGER; if (kn->kev.flags & EV_ADD) { /* NOTE: True on FreeBSD but not consistent behavior with other filters. */ dst->flags &= ~EV_ADD; } if (kn->kev.flags & EV_CLEAR) kn->kev.fflags &= ~NOTE_TRIGGER; if (kn->kev.flags & (EV_DISPATCH | EV_CLEAR | EV_ONESHOT)) eventfd_lower(filt->kf_efd); if (kn->kev.flags & EV_DISPATCH) { KNOTE_DISABLE(kn); kn->kev.fflags &= ~NOTE_TRIGGER; } else if (kn->kev.flags & EV_ONESHOT) { knote_free(filt, kn); } dst++; if (++nevents == maxevents) break; } /* This should normally never happen but is here for debugging */ if (nevents == 0) { dbg_puts("spurious wakeup"); eventfd_lower(filt->kf_efd); } return (nevents); } int evfilt_user_knote_create(struct filter *filt, struct knote *kn) { #if TODO u_int ffctrl; //determine if EV_ADD + NOTE_TRIGGER in the same kevent will cause a trigger */ if ((!(dst->kev.flags & EV_DISABLE)) && src->fflags & NOTE_TRIGGER) { dst->kev.fflags |= NOTE_TRIGGER; eventfd_raise(filt->kf_pfd); } #endif return (0); } int evfilt_user_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { unsigned int ffctrl; unsigned int fflags; /* Excerpted from sys/kern/kern_event.c in FreeBSD HEAD */ ffctrl = kev->fflags & NOTE_FFCTRLMASK; fflags = kev->fflags & NOTE_FFLAGSMASK; switch (ffctrl) { case NOTE_FFNOP: break; case NOTE_FFAND: kn->kev.fflags &= fflags; break; case NOTE_FFOR: kn->kev.fflags |= fflags; break; case NOTE_FFCOPY: kn->kev.fflags = fflags; break; default: /* XXX Return error? */ break; } if ((!(kn->kev.flags & EV_DISABLE)) && kev->fflags & NOTE_TRIGGER) { kn->kev.fflags |= NOTE_TRIGGER; knote_enqueue(filt, kn); eventfd_raise(filt->kf_efd); } return (0); } int evfilt_user_knote_delete(struct filter *filt, struct knote *kn) { return (0); } int evfilt_user_knote_enable(struct filter *filt, struct knote *kn) { /* FIXME: what happens if NOTE_TRIGGER is in fflags? should the event fire? */ return (0); } int evfilt_user_knote_disable(struct filter *filt, struct knote *kn) { return (0); } const struct filter evfilt_user = { EVFILT_USER, evfilt_user_init, evfilt_user_destroy, evfilt_user_copyout, evfilt_user_knote_create, evfilt_user_knote_modify, evfilt_user_knote_delete, evfilt_user_knote_enable, evfilt_user_knote_disable, }; libkqueue-1.0.4/src/posix/timer.c0000644000175000017500000001652611607445336016302 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" /* A request to sleep for a certain time */ struct sleepreq { int pfd; /* fd to poll for ACKs */ int wfd; /* fd to wake up when sleep is over */ uintptr_t ident; /* from kevent */ intptr_t interval; /* sleep time, in milliseconds */ struct sleepstat *stat; }; /* Information about a successful sleep operation */ struct sleepinfo { uintptr_t ident; /* from kevent */ uintptr_t counter; /* number of times the timer expired */ }; static void * sleeper_thread(void *arg) { struct sleepreq sr; struct sleepinfo si; struct timespec req, rem; sigset_t mask; ssize_t cnt; bool cts = true; /* Clear To Send */ char buf[1]; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); /* Copyin the request */ memcpy(&sr, arg, sizeof(sr)); free(arg); /* Initialize the response */ si.ident = sr.ident; si.counter = 0; /* Convert milliseconds into seconds+nanoseconds */ req.tv_sec = sr.interval / 1000; req.tv_nsec = (sr.interval % 1000) * 1000000; /* Block all signals */ sigfillset(&mask); (void) pthread_sigmask(SIG_BLOCK, &mask, NULL); for (;;) { /* Sleep */ if (nanosleep(&req, &rem) < 0) { //TODO: handle eintr, spurious wakeups dbg_perror("nanosleep(2)"); } si.counter++; dbg_printf(" -------- sleep over (CTS=%d)----------", cts); /* Test if the previous wakeup has been acknowledged */ if (!cts) { cnt = read(sr.wfd, &buf, 1); if (cnt < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { ; } else { dbg_perror("read(2)"); break; } } else if (cnt == 0) { dbg_perror("short read(2)"); break; } else { cts = true; } } /* Wake up kevent waiters if they are ready */ if (cts) { cnt = write(sr.wfd, &si, sizeof(si)); if (cnt < 0) { /* FIXME: handle EAGAIN and EINTR */ dbg_perror("write(2)"); } else if (cnt < sizeof(si)) { dbg_puts("FIXME: handle short write"); } cts = false; si.counter = 0; } } return (NULL); } static int _timer_create(struct filter *filt, struct knote *kn) { pthread_attr_t attr; struct sleepreq *req; kn->kev.flags |= EV_CLEAR; req = malloc(sizeof(*req)); if (req == NULL) { dbg_perror("malloc"); return (-1); } req->pfd = filt->kf_pfd; req->wfd = filt->kf_wfd; req->ident = kn->kev.ident; req->interval = kn->kev.data; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&kn->data.tid, &attr, sleeper_thread, req) != 0) { dbg_perror("pthread_create"); pthread_attr_destroy(&attr); free(req); return (-1); } pthread_attr_destroy(&attr); return (0); } static int _timer_delete(struct knote *kn) { if (pthread_cancel(kn->data.tid) != 0) { /* Race condition: sleeper_thread exits before it is cancelled */ if (errno == ENOENT) return (0); dbg_perror("pthread_cancel(3)"); return (-1); } return (0); } int evfilt_timer_init(struct filter *filt) { int fd[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) { dbg_perror("socketpair(3)"); return (-1); } if (fcntl(fd[0], F_SETFL, O_NONBLOCK) < 0 || fcntl(fd[1], F_SETFL, O_NONBLOCK) < 0) { dbg_perror("fcntl(2)"); close(fd[0]); close(fd[1]); return (-1); } filt->kf_wfd = fd[0]; filt->kf_pfd = fd[1]; return (0); } void evfilt_timer_destroy(struct filter *filt) { (void) close(filt->kf_wfd); (void) close(filt->kf_pfd); } int evfilt_timer_copyout(struct filter *filt, struct kevent *dst, int nevents) { struct sleepinfo si; ssize_t cnt; struct knote *kn; /* Read the ident */ cnt = read(filt->kf_pfd, &si, sizeof(si)); if (cnt < 0) { /* FIXME: handle EAGAIN and EINTR */ dbg_printf("read(2): %s", strerror(errno)); return (-1); } else if (cnt < sizeof(si)) { dbg_puts("error: short read"); return (-1); } /* Acknowlege receipt */ cnt = write(filt->kf_pfd, ".", 1); if (cnt < 0) { /* FIXME: handle EAGAIN and EINTR */ dbg_printf("write(2): %s", strerror(errno)); return (-1); } else if (cnt < 1) { dbg_puts("error: short write"); return (-1); } kn = knote_lookup(filt, si.ident); /* Race condition: timer events remain queued even after the knote is deleted. Ignore these events */ if (kn == NULL) return (0); dbg_printf("knote=%p", kn); memcpy(dst, &kn->kev, sizeof(*dst)); dst->data = si.counter; if (kn->kev.flags & EV_DISPATCH) { KNOTE_DISABLE(kn); _timer_delete(kn); } else if (kn->kev.flags & EV_ONESHOT) { _timer_delete(kn); knote_free(filt, kn); } return (1); } int evfilt_timer_knote_create(struct filter *filt, struct knote *kn) { return _timer_create(filt, kn); } int evfilt_timer_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { return (-1); /* STUB */ } int evfilt_timer_knote_delete(struct filter *filt, struct knote *kn) { if (kn->kev.flags & EV_DISABLE) return (0); dbg_printf("deleting timer # %d", (int) kn->kev.ident); return _timer_delete(kn); } int evfilt_timer_knote_enable(struct filter *filt, struct knote *kn) { return evfilt_timer_knote_create(filt, kn); } int evfilt_timer_knote_disable(struct filter *filt, struct knote *kn) { return evfilt_timer_knote_delete(filt, kn); } const struct filter evfilt_timer = { EVFILT_TIMER, evfilt_timer_init, evfilt_timer_destroy, evfilt_timer_copyout, evfilt_timer_knote_create, evfilt_timer_knote_modify, evfilt_timer_knote_delete, evfilt_timer_knote_enable, evfilt_timer_knote_disable, }; libkqueue-1.0.4/src/posix/proc.c0000644000175000017500000001151211607445336016113 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" pthread_cond_t wait_cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t wait_mtx = PTHREAD_MUTEX_INITIALIZER; struct evfilt_data { pthread_t wthr_id; }; static void * wait_thread(void *arg) { struct filter *filt = (struct filter *) arg; struct knote *kn; int status, result; pid_t pid; sigset_t sigmask; /* Block all signals */ sigfillset (&sigmask); sigdelset(&sigmask, SIGCHLD); pthread_sigmask(SIG_BLOCK, &sigmask, NULL); for (;;) { /* Wait for a child process to exit(2) */ if ((pid = waitpid(-1, &status, 0)) < 0) { if (errno == ECHILD) { dbg_puts("got ECHILD, waiting for wakeup condition"); pthread_mutex_lock(&wait_mtx); pthread_cond_wait(&wait_cond, &wait_mtx); pthread_mutex_unlock(&wait_mtx); dbg_puts("awoken from ECHILD-induced sleep"); continue; } if (errno == EINTR) continue; dbg_printf("wait(2): %s", strerror(errno)); break; } /* Create a proc_event */ if (WIFEXITED(status)) { result = WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { /* FIXME: probably not true on BSD */ result = WTERMSIG(status); } else { dbg_puts("unexpected code path"); result = 234; /* arbitrary error value */ } /* Scan the wait queue to see if anyone is interested */ pthread_mutex_lock(&filt->kf_mtx); kn = knote_lookup(filt, pid); if (kn != NULL) { kn->kev.data = result; kn->kev.fflags = NOTE_EXIT; LIST_REMOVE(kn, entries); LIST_INSERT_HEAD(&filt->kf_eventlist, kn, entries); /* Indicate read(2) readiness */ /* TODO: error handling */ filter_raise(filt); } pthread_mutex_unlock(&filt->kf_mtx); } /* TODO: error handling */ return (NULL); } int evfilt_proc_init(struct filter *filt) { struct evfilt_data *ed; if ((ed = calloc(1, sizeof(*ed))) == NULL) return (-1); if (filter_socketpair(filt) < 0) goto errout; if (pthread_create(&ed->wthr_id, NULL, wait_thread, filt) != 0) goto errout; return (0); errout: free(ed); return (-1); } void evfilt_proc_destroy(struct filter *filt) { //TODO: pthread_cancel(filt->kf_data->wthr_id); close(filt->kf_pfd); } int evfilt_proc_copyin(struct filter *filt, struct knote *dst, const struct kevent *src) { if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) { memcpy(&dst->kev, src, sizeof(*src)); /* TODO: think about locking the mutex first.. */ pthread_cond_signal(&wait_cond); } if (src->flags & EV_ADD || src->flags & EV_ENABLE) { /* Nothing to do.. */ } return (0); } int evfilt_proc_copyout(struct filter *filt, struct kevent *dst, int maxevents) { struct knote *kn; int nevents = 0; filter_lower(filt); LIST_FOREACH(kn, &filt->kf_eventlist, entries) { kevent_dump(&kn->kev); memcpy(dst, &kn->kev, sizeof(*dst)); dst->fflags = NOTE_EXIT; if (kn->kev.flags & EV_DISPATCH) { KNOTE_DISABLE(kn); } #if FIXME /* XXX - NEED TO use safe foreach instead */ if (kn->kev.flags & EV_ONESHOT) knote_free(kn); #endif if (++nevents > maxevents) break; dst++; } if (!LIST_EMPTY(&filt->kf_eventlist)) filter_raise(filt); return (nevents); } const struct filter evfilt_proc = { EVFILT_PROC, evfilt_proc_init, evfilt_proc_destroy, evfilt_proc_copyin, evfilt_proc_copyout, }; libkqueue-1.0.4/src/posix/eventfd.c0000644000175000017500000000440411607445336016605 0ustar mheilymheily/* * Copyright (c) 2010 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "private.h" struct eventfd { int fd[2]; }; struct eventfd * eventfd_create(void) { struct eventfd *e; e = malloc(sizeof(*e)); if (e == NULL) return (NULL); if (socketpair(AF_UNIX, SOCK_STREAM, 0, e->fd) < 0) { free(e); return (NULL); } if ((fcntl(e->fd[0], F_SETFL, O_NONBLOCK) < 0) || (fcntl(e->fd[1], F_SETFL, O_NONBLOCK) < 0)) { free(e); close(e->fd[0]); close(e->fd[1]); return (NULL); } return (e); } void eventfd_free(struct eventfd *e) { close(e->fd[0]); close(e->fd[1]); free(e); } int eventfd_raise(struct eventfd *e) { dbg_puts("raising event level"); if (write(e->fd[0], ".", 1) < 0) { /* FIXME: handle EAGAIN and EINTR */ dbg_printf("write(2): %s", strerror(errno)); return (-1); } return (0); } int eventfd_lower(struct eventfd *e) { char buf[1024]; /* Reset the counter */ dbg_puts("lowering event level"); if (read(e->fd[1], &buf, sizeof(buf)) < 0) { /* FIXME: handle EAGAIN and EINTR */ /* FIXME: loop so as to consume all data.. may need mutex */ dbg_printf("read(2): %s", strerror(errno)); return (-1); } return (0); } int eventfd_reader(struct eventfd *e) { return (e->fd[1]); } int eventfd_writer(struct eventfd *e) { return (e->fd[0]); } libkqueue-1.0.4/src/posix/kevent.c0000644000175000017500000000424711607445336016453 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "sys/event.h" #include "private.h" const struct filter evfilt_proc = EVFILT_NOTIMPL; int kevent_wait(struct kqueue *kq, const struct timespec *timeout) { int n; dbg_puts("waiting for events"); kq->kq_rfds = kq->kq_fds; n = pselect(kq->kq_nfds, &kq->kq_rfds, NULL , NULL, timeout, NULL); if (n < 0) { if (errno == EINTR) { dbg_puts("signal caught"); return (-1); } dbg_perror("pselect(2)"); return (-1); } return (n); } int kevent_copyout(struct kqueue *kq, int nready, struct kevent *eventlist, int nevents) { struct filter *filt; int i, rv, nret; nret = 0; for (i = 0; (i < EVFILT_SYSCOUNT && nready > 0 && nevents > 0); i++) { // dbg_printf("eventlist: n = %d nevents = %d", nready, nevents); filt = &kq->kq_filt[i]; // dbg_printf("pfd[%d] = %d", i, filt->kf_pfd); if (FD_ISSET(filt->kf_pfd, &kq->kq_rfds)) { dbg_printf("pending events for filter %d (%s)", filt->kf_id, filter_name(filt->kf_id)); rv = filt->kf_copyout(filt, eventlist, nevents); if (rv < 0) { dbg_puts("kevent_copyout failed"); nret = -1; break; } nret += rv; eventlist += rv; nevents -= rv; nready--; } } return (nret); } libkqueue-1.0.4/src/posix/signal.c0000644000175000017500000001204211607445336016424 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" /* Highest signal number supported. POSIX standard signals are < 32 */ #define SIGNAL_MAX 32 struct sentry { struct filter *s_filt; struct knote *s_knote; volatile uint32_t s_cnt; }; static pthread_mutex_t sigtbl_mtx = PTHREAD_MUTEX_INITIALIZER; static struct sentry sigtbl[SIGNAL_MAX]; static void signal_handler(int sig) { struct sentry *s = &sigtbl[sig]; dbg_printf("caught sig=%d", sig); atomic_inc(&s->s_cnt); #if defined(__sun__) if (port_send(s->s_filt->kf_kqueue->kq_port, X_PORT_SOURCE_SIGNAL, &sigtbl[sig]) < 0) { return; //FIXME: errorhandling } #else (void)write(s->s_filt->kf_wfd, &sig, sizeof(sig));//FIXME:errhandling #endif } static int catch_signal(struct filter *filt, struct knote *kn) { int sig; struct sigaction sa; sig = kn->kev.ident; memset(&sa, 0, sizeof(sa)); sa.sa_handler = signal_handler; sa.sa_flags |= SA_RESTART; sigfillset(&sa.sa_mask); if (sigaction(kn->kev.ident, &sa, NULL) == -1) { dbg_perror("sigaction"); return (-1); } /* FIXME: will clobber previous entry, if any */ pthread_mutex_lock(&sigtbl_mtx); sigtbl[kn->kev.ident].s_filt = filt; sigtbl[kn->kev.ident].s_knote = kn; pthread_mutex_unlock(&sigtbl_mtx); dbg_printf("installed handler for signal %d", sig); return (0); } static int ignore_signal(int sig) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); if (sigaction(sig, &sa, NULL) == -1) { dbg_perror("sigaction"); return (-1); } pthread_mutex_lock(&sigtbl_mtx); sigtbl[sig].s_filt = NULL; sigtbl[sig].s_knote = NULL; pthread_mutex_unlock(&sigtbl_mtx); dbg_printf("removed handler for signal %d", sig); return (0); } int evfilt_signal_init(struct filter *filt) { return filter_socketpair(filt); } void evfilt_signal_destroy(struct filter *filt) { close(filt->kf_pfd); } int evfilt_signal_knote_create(struct filter *filt, struct knote *kn) { if (kn->kev.ident >= SIGNAL_MAX) { dbg_printf("unsupported signal number %u", (unsigned int) kn->kev.ident); return (-1); } kn->kev.flags |= EV_CLEAR; return catch_signal(filt, kn); } int evfilt_signal_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { kn->kev.flags = kev->flags | EV_CLEAR; return (0); } int evfilt_signal_knote_delete(struct filter *filt, struct knote *kn) { return ignore_signal(kn->kev.ident); } int evfilt_signal_knote_enable(struct filter *filt, struct knote *kn) { return catch_signal(filt, kn); } int evfilt_signal_knote_disable(struct filter *filt, struct knote *kn) { return ignore_signal(kn->kev.ident); } int evfilt_signal_copyout(struct filter *filt, struct kevent *dst, int nevents) { struct sentry *s; struct knote *kn; int sig; #if defined(__sun__) port_event_t *pe = (port_event_t *) pthread_getspecific(filt->kf_kqueue->kq_port_event); s = (struct sentry *) pe->portev_user; sig = s - &sigtbl[0]; #else read(filt->kf_pfd, &sig, sizeof(sig));//FIXME:errhandling s = &sigtbl[sig]; #endif kn = s->s_knote; //TODO: READ counter: s->s_knote->kev.data = ?; /* TODO: dst->data should be the number of times the signal occurred */ dst->ident = sig; dst->filter = EVFILT_SIGNAL; dst->udata = kn->kev.udata; dst->flags = kn->kev.flags; dst->fflags = 0; dst->data = 1; if (kn->kev.flags & EV_DISPATCH) { ignore_signal(kn->kev.ident); KNOTE_DISABLE(kn); } else if (kn->kev.flags & EV_ONESHOT) { ignore_signal(kn->kev.ident); knote_free(filt, kn); } return (1); } const struct filter evfilt_signal = { EVFILT_SIGNAL, evfilt_signal_init, evfilt_signal_destroy, evfilt_signal_copyout, evfilt_signal_knote_create, evfilt_signal_knote_modify, evfilt_signal_knote_delete, evfilt_signal_knote_enable, evfilt_signal_knote_disable, }; libkqueue-1.0.4/src/common/0000755000175000017500000000000011607445336015132 5ustar mheilymheilylibkqueue-1.0.4/src/common/kqueue.c0000644000175000017500000001227011607445336016577 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" #ifndef NDEBUG int KQUEUE_DEBUG = 0; #endif static RB_HEAD(kqt, kqueue) kqtree = RB_INITIALIZER(&kqtree); static pthread_rwlock_t kqtree_mtx = PTHREAD_RWLOCK_INITIALIZER; static int kqueue_cmp(struct kqueue *a, struct kqueue *b) { return memcmp(&a->kq_sockfd[1], &b->kq_sockfd[1], sizeof(int)); } RB_GENERATE(kqt, kqueue, entries, kqueue_cmp) /* Must hold the kqtree_mtx when calling this */ static void kqueue_free(struct kqueue *kq) { RB_REMOVE(kqt, &kqtree, kq); filter_unregister_all(kq); #if defined(__sun__) port_event_t *pe = (port_event_t *) pthread_getspecific(kq->kq_port_event); if (kq->kq_port > 0) close(kq->kq_port); free(pe); #endif free(kq); } static int kqueue_gc(void) { int rv; struct kqueue *n1, *n2; /* Free any kqueue descriptor that is no longer needed */ /* Sadly O(N), however needed in the case that a descriptor is closed and kevent(2) will never again be called on it. */ for (n1 = RB_MIN(kqt, &kqtree); n1 != NULL; n1 = n2) { n2 = RB_NEXT(kqt, &kqtree, n1); if (n1->kq_ref == 0) { kqueue_free(n1); } else { rv = kqueue_validate(n1); if (rv == 0) kqueue_free(n1); else if (rv < 0) return (-1); } } return (0); } int kqueue_validate(struct kqueue *kq) { int rv; char buf[1]; struct pollfd pfd; pfd.fd = kq->kq_sockfd[0]; pfd.events = POLLIN | POLLHUP; pfd.revents = 0; rv = poll(&pfd, 1, 0); if (rv == 0) return (1); if (rv < 0) { dbg_perror("poll(2)"); return (-1); } if (rv > 0) { /* NOTE: If the caller accidentally writes to the kqfd, it will be considered invalid. */ rv = recv(kq->kq_sockfd[0], buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT); if (rv == 0) return (0); else return (-1); } return (0); } void kqueue_put(struct kqueue *kq) { atomic_dec(&kq->kq_ref); } struct kqueue * kqueue_get(int kq) { struct kqueue query; struct kqueue *ent = NULL; query.kq_sockfd[1] = kq; pthread_rwlock_rdlock(&kqtree_mtx); ent = RB_FIND(kqt, &kqtree, &query); pthread_rwlock_unlock(&kqtree_mtx); /* Check for invalid kqueue objects still in the tree */ if (ent != NULL && (ent->kq_sockfd[0] < 0 || ent->kq_ref == 0)) ent = NULL; else atomic_inc(&ent->kq_ref); return (ent); } /* Non-portable kqueue initalization code. */ static int kqueue_sys_init(struct kqueue *kq) { #if defined(__sun__) port_event_t *pe; if ((kq->kq_port = port_create()) < 0) { dbg_perror("port_create(2)"); return (-1); } if (pthread_key_create(&kq->kq_port_event, NULL) != 0) abort(); if ((pe = calloc(1, sizeof(*pe))) == NULL) abort(); if (pthread_setspecific(kq->kq_port_event, pe) != 0) abort(); #endif return (0); } int __attribute__((visibility("default"))) kqueue(void) { struct kqueue *kq; int tmp; kq = calloc(1, sizeof(*kq)); if (kq == NULL) return (-1); kq->kq_ref = 1; pthread_mutex_init(&kq->kq_mtx, NULL); #ifdef NDEBUG KQUEUE_DEBUG = 0; #else KQUEUE_DEBUG = (getenv("KQUEUE_DEBUG") == NULL) ? 0 : 1; #endif if (socketpair(AF_UNIX, SOCK_STREAM, 0, kq->kq_sockfd) < 0) goto errout_unlocked; if (kqueue_sys_init(kq) < 0) goto errout_unlocked; pthread_rwlock_wrlock(&kqtree_mtx); if (kqueue_gc() < 0) goto errout; /* TODO: move outside of the lock if it is safe */ if (filter_register_all(kq) < 0) goto errout; RB_INSERT(kqt, &kqtree, kq); pthread_rwlock_unlock(&kqtree_mtx); dbg_printf("created kqueue, fd=%d", kq->kq_sockfd[1]); return (kq->kq_sockfd[1]); errout: pthread_rwlock_unlock(&kqtree_mtx); errout_unlocked: if (kq->kq_sockfd[0] != kq->kq_sockfd[1]) { tmp = errno; (void)close(kq->kq_sockfd[0]); (void)close(kq->kq_sockfd[1]); errno = tmp; } #if defined(__sun__) if (kq->kq_port > 0) close(kq->kq_port); #endif free(kq); return (-1); } libkqueue-1.0.4/src/common/private.h0000644000175000017500000001577411607445336016773 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _KQUEUE_PRIVATE_H #define _KQUEUE_PRIVATE_H #if defined (__SVR4) && defined (__sun) # define SOLARIS # include /* Used to set portev_events for PORT_SOURCE_USER */ # define X_PORT_SOURCE_SIGNAL 101 # define X_PORT_SOURCE_USER 102 #endif #include #include #include #include #include #include #include "../../include/sys/event.h" #include "tree.h" /* GCC atomic builtins. * See: http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html */ #ifdef __sun # include # define atomic_inc atomic_inc_32 # define atomic_dec atomic_dec_32 #else # define atomic_inc(p) __sync_add_and_fetch((p), 1) # define atomic_dec(p) __sync_sub_and_fetch((p), 1) #endif /* Maximum events returnable in a single kevent() call */ #define MAX_KEVENT 512 #ifndef NDEBUG extern int KQUEUE_DEBUG; #define dbg_puts(str) do { \ if (KQUEUE_DEBUG) \ fprintf(stderr, "KQ: %s(): %s\n", __func__,str); \ } while (0) #define dbg_printf(fmt,...) do { \ if (KQUEUE_DEBUG) \ fprintf(stderr, "KQ: %s(): "fmt"\n", __func__,__VA_ARGS__); \ } while (0) #define dbg_perror(str) do { \ if (KQUEUE_DEBUG) \ fprintf(stderr, "KQ: %s(): %s: %s (errno=%d)\n", \ __func__, str, strerror(errno), errno); \ } while (0) # define reset_errno() do { errno = 0; } while (0) #else /* NDEBUG */ # define dbg_puts(str) ; # define dbg_printf(fmt,...) ; # define dbg_perror(str) ; # define reset_errno() ; #endif struct kqueue; struct kevent; struct evfilt_data; /* * Flags used by knote->flags */ #define KNFL_PASSIVE_SOCKET (0x01) /* Socket is in listen(2) mode */ /* TODO: Make this a variable length structure and allow each filter to add custom fields at the end. */ struct knote { struct kevent kev; int flags; union { int pfd; /* Used by timerfd */ int events; /* Used by socket */ struct { nlink_t nlink; /* Used by vnode */ off_t size; /* Used by vnode */ } vnode; timer_t timerid; pthread_t tid; /* Used by posix/timer.c */ } data; TAILQ_ENTRY(knote) event_ent; /* Used by filter->kf_event */ RB_ENTRY(knote) kntree_ent; /* Used by filter->kntree */ }; LIST_HEAD(knotelist, knote); #define KNOTE_ENABLE(ent) do { \ (ent)->kev.flags &= ~EV_DISABLE; \ } while (0/*CONSTCOND*/) #define KNOTE_DISABLE(ent) do { \ (ent)->kev.flags |= EV_DISABLE; \ } while (0/*CONSTCOND*/) struct filter { short kf_id; /* filter operations */ int (*kf_init)(struct filter *); void (*kf_destroy)(struct filter *); int (*kf_copyout)(struct filter *, struct kevent *, int); /* knote operations */ int (*kn_create)(struct filter *, struct knote *); int (*kn_modify)(struct filter *, struct knote *, const struct kevent *); int (*kn_delete)(struct filter *, struct knote *); int (*kn_enable)(struct filter *, struct knote *); int (*kn_disable)(struct filter *, struct knote *); struct eventfd *kf_efd; /* Used by user.c */ int kf_pfd; /* fd to poll(2) for readiness */ int kf_wfd; /* fd to write when an event occurs */ sigset_t kf_sigmask; struct evfilt_data *kf_data; /* filter-specific data */ RB_HEAD(knt, knote) kf_knote; TAILQ_HEAD(, knote) kf_event; /* events that have occurred */ struct kqueue *kf_kqueue; }; /* Use this to declare a filter that is not implemented */ #define EVFILT_NOTIMPL { 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } struct kqueue { int kq_sockfd[2]; struct filter kq_filt[EVFILT_SYSCOUNT]; fd_set kq_fds, kq_rfds; int kq_nfds; pthread_mutex_t kq_mtx; #ifdef __sun__ int kq_port; /* see: port_create(2) */ pthread_key_t kq_port_event; #endif volatile uint32_t kq_ref; RB_ENTRY(kqueue) entries; }; struct knote * knote_lookup(struct filter *, short); struct knote * knote_lookup_data(struct filter *filt, intptr_t); struct knote * knote_new(void); void knote_free(struct filter *, struct knote *); void knote_free_all(struct filter *); void knote_insert(struct filter *, struct knote *); int knote_get_socket_type(struct knote *); /* TODO: these deal with the eventlist, should use a different prefix */ void knote_enqueue(struct filter *, struct knote *); struct knote * knote_dequeue(struct filter *); int knote_events_pending(struct filter *); struct eventfd * eventfd_create(void); void eventfd_free(struct eventfd *); int eventfd_raise(struct eventfd *); int eventfd_lower(struct eventfd *); int eventfd_reader(struct eventfd *); int eventfd_writer(struct eventfd *); int filter_lookup(struct filter **, struct kqueue *, short); int filter_socketpair(struct filter *); int filter_register_all(struct kqueue *); void filter_unregister_all(struct kqueue *); const char *filter_name(short); int filter_lower(struct filter *); int filter_raise(struct filter *); int kevent_wait(struct kqueue *, const struct timespec *); int kevent_copyout(struct kqueue *, int, struct kevent *, int); void kevent_free(struct kqueue *); const char *kevent_dump(const struct kevent *); struct kqueue * kqueue_get(int); void kqueue_put(struct kqueue *); #define kqueue_lock(kq) pthread_mutex_lock(&(kq)->kq_mtx) #define kqueue_unlock(kq) pthread_mutex_unlock(&(kq)->kq_mtx) int kqueue_validate(struct kqueue *); #endif /* ! _KQUEUE_PRIVATE_H */ libkqueue-1.0.4/src/common/knote.c0000644000175000017500000000773011607445336016425 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "private.h" static int knote_cmp(struct knote *a, struct knote *b) { return memcmp(&a->kev.ident, &b->kev.ident, sizeof(a->kev.ident)); } RB_GENERATE(knt, knote, kntree_ent, knote_cmp) struct knote * knote_new(void) { struct knote *dst; if ((dst = calloc(1, sizeof(*dst))) == NULL) return (NULL); return (dst); } void knote_insert(struct filter *filt, struct knote *kn) { RB_INSERT(knt, &filt->kf_knote, kn); } void knote_free(struct filter *filt, struct knote *kn) { dbg_printf("filter=%s, ident=%u", filter_name(kn->kev.filter), (unsigned int) kn->kev.ident); RB_REMOVE(knt, &filt->kf_knote, kn); if (kn->event_ent.tqe_prev) //XXX-FIXME what if this is the 1st entry?? TAILQ_REMOVE(&filt->kf_event, kn, event_ent); filt->kn_delete(filt, kn); free(kn); } void knote_free_all(struct filter *filt) { struct knote *n1, *n2; /* Destroy all pending events */ for (n1 = TAILQ_FIRST(&filt->kf_event); n1 != NULL; n1 = n2) { n2 = TAILQ_NEXT(n1, event_ent); free(n1); } /* Distroy all knotes */ for (n1 = RB_MIN(knt, &filt->kf_knote); n1 != NULL; n1 = n2) { n2 = RB_NEXT(knt, filt->kf_knote, n1); RB_REMOVE(knt, &filt->kf_knote, n1); free(n1); } } /* TODO: rename to knote_lookup_ident */ struct knote * knote_lookup(struct filter *filt, short ident) { struct knote query; struct knote *ent = NULL; query.kev.ident = ident; ent = RB_FIND(knt, &filt->kf_knote, &query); dbg_printf("id=%d ent=%p", ident, ent); return (ent); } struct knote * knote_lookup_data(struct filter *filt, intptr_t data) { struct knote *kn; RB_FOREACH(kn, knt, &filt->kf_knote) { if (data == kn->kev.data) break; } return (kn); } void knote_enqueue(struct filter *filt, struct knote *kn) { /* XXX-FIXME: check if the knote is already on the eventlist */ TAILQ_INSERT_TAIL(&filt->kf_event, kn, event_ent); } struct knote * knote_dequeue(struct filter *filt) { struct knote *kn; if (TAILQ_EMPTY(&filt->kf_event)) { kn = NULL; dbg_puts("no events are pending"); } else { kn = TAILQ_FIRST(&filt->kf_event); TAILQ_REMOVE(&filt->kf_event, kn, event_ent); memset(&kn->event_ent, 0, sizeof(kn->event_ent)); } return (kn); } int knote_events_pending(struct filter *filt) { int res; res = TAILQ_EMPTY(&filt->kf_event); return (res); } /* * Test if a socket is active or passive. */ int knote_get_socket_type(struct knote *kn) { socklen_t slen; int i, lsock; slen = sizeof(lsock); lsock = 0; i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_ACCEPTCONN, &lsock, &slen); if (i < 0) { switch (errno) { case ENOTSOCK: /* same as lsock = 0 */ return (0); break; default: dbg_printf("getsockopt(3) failed: %s", strerror(errno)); return (-1); } } else { if (lsock) kn->flags |= KNFL_PASSIVE_SOCKET; return (0); } } libkqueue-1.0.4/src/common/tree.h0000644000175000017500000006233411607445336016252 0ustar mheilymheily/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ /* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ /* $FreeBSD: src/sys/sys/tree.h,v 1.9.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $ */ /*- * Copyright 2002 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (/*CONSTCOND*/ 0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (/*CONSTCOND*/ 0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (/*CONSTCOND*/ 0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (/*CONSTCOND*/ 0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (/*CONSTCOND*/ 0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (/*CONSTCOND*/ 0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (/*CONSTCOND*/ 0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (/*CONSTCOND*/ 0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (/*CONSTCOND*/ 0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (/*CONSTCOND*/ 0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (/*CONSTCOND*/ 0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) != NULL && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)) \ != NULL) \ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)) \ != NULL) \ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field)) != NULL) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field)) != NULL); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_FROM(x, name, y) \ for ((x) = (y); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ (x) = (y)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #define RB_FOREACH_REVERSE_FROM(x, name, y) \ for ((x) = (y); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ (x) = (y)) #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ for ((x) = RB_MAX(name, head); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ (x) = (y)) #endif /* _SYS_TREE_H_ */ libkqueue-1.0.4/src/common/filter.c0000644000175000017500000001262311607445336016567 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "private.h" extern const struct filter evfilt_read; extern const struct filter evfilt_write; extern const struct filter evfilt_signal; extern const struct filter evfilt_vnode; extern const struct filter evfilt_proc; extern const struct filter evfilt_timer; extern const struct filter evfilt_user; static int filter_register(struct kqueue *kq, short filter, const struct filter *src) { struct filter *dst; unsigned int filt; int rv = 0; filt = (-1 * filter) - 1; if (filt >= EVFILT_SYSCOUNT) return (-1); dst = &kq->kq_filt[filt]; memcpy(dst, src, sizeof(*src)); dst->kf_kqueue = kq; RB_INIT(&dst->kf_knote); TAILQ_INIT(&dst->kf_event); if (src->kf_id == 0) { dbg_puts("filter is not implemented"); return (0); } assert(src->kf_init); assert(src->kf_destroy); assert(src->kf_copyout); assert(src->kn_create); assert(src->kn_modify); assert(src->kn_delete); assert(src->kn_enable); assert(src->kn_disable); rv = src->kf_init(dst); if (rv < 0) { dbg_puts("filter failed to initialize"); dst->kf_id = 0; return (-1); } /* Add the filter's event descriptor to the main fdset */ if (dst->kf_pfd > 0) { FD_SET(dst->kf_pfd, &kq->kq_fds); if (dst->kf_pfd > kq->kq_nfds) kq->kq_nfds = dst->kf_pfd; dbg_printf("fds: added %d (nfds=%d)", dst->kf_pfd, kq->kq_nfds); } dbg_printf("filter %d (%s) registered", filter, filter_name(filter)); return (0); } int filter_register_all(struct kqueue *kq) { int rv; FD_ZERO(&kq->kq_fds); rv = 0; rv += filter_register(kq, EVFILT_READ, &evfilt_read); rv += filter_register(kq, EVFILT_WRITE, &evfilt_write); rv += filter_register(kq, EVFILT_SIGNAL, &evfilt_signal); rv += filter_register(kq, EVFILT_VNODE, &evfilt_vnode); rv += filter_register(kq, EVFILT_PROC, &evfilt_proc); rv += filter_register(kq, EVFILT_TIMER, &evfilt_timer); rv += filter_register(kq, EVFILT_USER, &evfilt_user); kq->kq_nfds++; if (rv != 0) { filter_unregister_all(kq); return (-1); } else { return (0); } } void filter_unregister_all(struct kqueue *kq) { int i; for (i = 0; i < EVFILT_SYSCOUNT; i++) { if (kq->kq_filt[i].kf_id == 0) continue; if (kq->kq_filt[i].kf_destroy != NULL) kq->kq_filt[i].kf_destroy(&kq->kq_filt[i]); knote_free_all(&kq->kq_filt[i]); } memset(&kq->kq_filt[0], 0, sizeof(kq->kq_filt)); } int filter_socketpair(struct filter *filt) { int sockfd[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) return (-1); fcntl(sockfd[0], F_SETFL, O_NONBLOCK); filt->kf_wfd = sockfd[0]; filt->kf_pfd = sockfd[1]; return (0); } int filter_lookup(struct filter **filt, struct kqueue *kq, short id) { if (~id < 0 || ~id >= EVFILT_SYSCOUNT) { dbg_printf("invalid id: id %d ~id %d", id, (~id)); errno = EINVAL; *filt = NULL; return (-1); } *filt = &kq->kq_filt[~id]; if ((*filt)->kf_copyout == NULL) { errno = ENOSYS; *filt = NULL; return (-1); } return (0); } const char * filter_name(short filt) { unsigned int id; const char *fname[EVFILT_SYSCOUNT] = { "EVFILT_READ", "EVFILT_WRITE", "EVFILT_AIO", "EVFILT_VNODE", "EVFILT_PROC", "EVFILT_SIGNAL", "EVFILT_TIMER", "EVFILT_NETDEV", "EVFILT_FS", "EVFILT_LIO", "EVFILT_USER" }; id = ~filt; if (id < 0 || id >= EVFILT_SYSCOUNT) return "EVFILT_INVALID"; else return fname[id]; } int filter_raise(struct filter *filt) { for (;;) { if (write(filt->kf_wfd, " ", 1) < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { dbg_printf("write(2): %s", strerror(errno)); /* TODO: set filter error flag */ return (-1); } } break; } return (0); } int filter_lower(struct filter *filt) { char buf[1024]; ssize_t n; for (;;) { n = read(filt->kf_pfd, &buf, sizeof(buf)); if (n < 0) { if (errno == EINTR) continue; if (errno == EAGAIN) break; dbg_printf("read(2): %s", strerror(errno)); return (-1); } break; } return (0); } libkqueue-1.0.4/src/common/kevent.c0000644000175000017500000001752411607445336016603 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* To get asprintf(3) */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" static char * kevent_filter_dump(const struct kevent *kev) { static char __thread buf[64]; snprintf(&buf[0], sizeof(buf), "%d (%s)", kev->filter, filter_name(kev->filter)); return (&buf[0]); } static char * kevent_fflags_dump(const struct kevent *kev) { static char __thread buf[1024]; #define KEVFFL_DUMP(attrib) \ if (kev->fflags & attrib) \ strncat(buf, #attrib" ", 64); snprintf(buf, sizeof(buf), "fflags=0x%04x (", kev->fflags); if (kev->filter == EVFILT_VNODE) { KEVFFL_DUMP(NOTE_DELETE); KEVFFL_DUMP(NOTE_WRITE); KEVFFL_DUMP(NOTE_EXTEND); KEVFFL_DUMP(NOTE_ATTRIB); KEVFFL_DUMP(NOTE_LINK); KEVFFL_DUMP(NOTE_RENAME); } else if (kev->filter == EVFILT_USER) { KEVFFL_DUMP(NOTE_FFNOP); KEVFFL_DUMP(NOTE_FFAND); KEVFFL_DUMP(NOTE_FFOR); KEVFFL_DUMP(NOTE_FFCOPY); KEVFFL_DUMP(NOTE_TRIGGER); } else { strncat(buf, " ", 1); } buf[strlen(buf) - 1] = ')'; #undef KEVFFL_DUMP return (buf); } static char * kevent_flags_dump(const struct kevent *kev) { static char __thread buf[1024]; #define KEVFL_DUMP(attrib) \ if (kev->flags & attrib) \ strncat(buf, #attrib" ", 64); snprintf(buf, sizeof(buf), "flags=0x%04x (", kev->flags); KEVFL_DUMP(EV_ADD); KEVFL_DUMP(EV_ENABLE); KEVFL_DUMP(EV_DISABLE); KEVFL_DUMP(EV_DELETE); KEVFL_DUMP(EV_ONESHOT); KEVFL_DUMP(EV_CLEAR); KEVFL_DUMP(EV_EOF); KEVFL_DUMP(EV_ERROR); KEVFL_DUMP(EV_DISPATCH); KEVFL_DUMP(EV_RECEIPT); buf[strlen(buf) - 1] = ')'; #undef KEVFL_DUMP return (buf); } const char * kevent_dump(const struct kevent *kev) { static char __thread buf[1024]; snprintf(buf, sizeof(buf), "{ ident=%d, filter=%s, %s, %s, data=%d, udata=%p }", (u_int) kev->ident, kevent_filter_dump(kev), kevent_flags_dump(kev), kevent_fflags_dump(kev), (int) kev->data, kev->udata); return (buf); } static int kevent_copyin_one(struct kqueue *kq, const struct kevent *src) { struct knote *kn = NULL; struct filter *filt; int rv; if (src->flags & EV_DISPATCH && src->flags & EV_ONESHOT) { errno = EINVAL; return (-1); } if (filter_lookup(&filt, kq, src->filter) < 0) return (-1); //dbg_printf("src=%s\n", kevent_dump(src)); kn = knote_lookup(filt, src->ident); if (kn == NULL) { if (src->flags & EV_ADD) { if ((kn = knote_new()) == NULL) { errno = ENOENT; return (-1); } memcpy(&kn->kev, src, sizeof(kn->kev)); kn->kev.flags &= ~EV_ENABLE; kn->kev.flags |= EV_ADD;//FIXME why? assert(filt->kn_create); if (filt->kn_create(filt, kn) < 0) { knote_free(filt, kn); errno = EFAULT; return (-1); } knote_insert(filt, kn); dbg_printf("created kevent %s\n", kevent_dump(src)); if (src->flags & EV_DISABLE) { kn->kev.flags |= EV_DISABLE; return (filt->kn_disable(filt, kn)); } return (0); } else { errno = ENOENT; return (-1); } } if (src->flags & EV_DELETE) { rv = filt->kn_delete(filt, kn); knote_free(filt, kn); return (rv); } else if (src->flags & EV_DISABLE) { kn->kev.flags |= EV_DISABLE; return (filt->kn_disable(filt, kn)); } else if (src->flags & EV_ENABLE) { kn->kev.flags &= ~EV_DISABLE; return (filt->kn_enable(filt, kn)); } /* Implicit EV_ADD */ kn->kev.udata = src->udata; return (filt->kn_modify(filt, kn, src)); #if DEADWOOD /* Special case for EVFILT_USER: Ignore user-generated events that are not of interest */ if (src->fflags & NOTE_TRIGGER) { filter_unlock(filt); continue; } #endif } /** @return number of events added to the eventlist */ static int kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges, struct kevent *eventlist, int nevents) { int status, nret; dbg_printf("nchanges=%d nevents=%d", nchanges, nevents); /* TODO: refactor, this has become convoluted to support EV_RECEIPT */ for (nret = 0; nchanges > 0; src++, nchanges--) { if (kevent_copyin_one(kq, src) < 0) { dbg_printf("errno=%s",strerror(errno)); status = errno; goto err_path; } else { if (src->flags & EV_RECEIPT) { status = 0; goto err_path; } } continue; err_path: if (nevents > 0) { memcpy(eventlist, src, sizeof(*src)); eventlist->data = status; nevents--; eventlist++; nret++; } else { return (-1); } } return (nret); } int __attribute__((visibility("default"))) kevent(int kqfd, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) { struct kqueue *kq; int rv, n, nret; nret = 0; kq = kqueue_get(kqfd); if (kq == NULL) { errno = ENOENT; return (-1); } rv = kqueue_validate(kq); if (rv < 0) { return (-1); } else if (rv == 0) { errno = EBADF; return (-1); } /* * Process each kevent on the changelist. */ if (nchanges) { kqueue_lock(kq); rv = kevent_copyin(kq, changelist, nchanges, eventlist, nevents); kqueue_unlock(kq); dbg_printf("changelist: rv=%d", rv); if (rv < 0) goto errout; if (rv > 0) { eventlist += rv; nevents -= rv; } } /* Determine if we need to wait for events. */ if (nevents > MAX_KEVENT) nevents = MAX_KEVENT; if (nevents == 0) goto out; /* Handle spurious wakeups where no events are generated. */ for (nret = 0; nret == 0;) { /* Wait for one or more events. */ n = kevent_wait(kq, timeout); if (n < 0) { dbg_puts("kevent_wait failed"); goto errout; } if (n == 0) goto out; /* Timeout */ /* Copy the events to the caller */ kqueue_lock(kq); nret = kevent_copyout(kq, n, eventlist, nevents); kqueue_unlock(kq); } if (KQUEUE_DEBUG) { dbg_printf("returning %d events", nret); for (n = 0; n < nret; n++) { dbg_printf("eventlist[%d] = %s", n, kevent_dump(&eventlist[n])); } } goto out; errout: nret = -1; out: kqueue_put(kq); return (nret); } libkqueue-1.0.4/src/solaris/0000755000175000017500000000000011607445336015316 5ustar mheilymheilylibkqueue-1.0.4/src/solaris/user.c0000644000175000017500000001027111607445336016441 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "sys/event.h" #include "private.h" int evfilt_user_init(struct filter *filt) { return (0); } void evfilt_user_destroy(struct filter *filt) { return; } int evfilt_user_copyout(struct filter *filt, struct kevent *dst, int maxevents) { struct knote *kn; int nevents = 0; for (kn = knote_dequeue(filt); kn != NULL; kn = knote_dequeue(filt)) { memcpy(dst, &kn->kev, sizeof(*dst)); dst->fflags &= ~NOTE_FFCTRLMASK; //FIXME: Not sure if needed dst->fflags &= ~NOTE_TRIGGER; if (kn->kev.flags & EV_ADD) { /* NOTE: True on FreeBSD but not consistent behavior with other filters. */ dst->flags &= ~EV_ADD; } if (kn->kev.flags & EV_CLEAR) kn->kev.fflags &= ~NOTE_TRIGGER; /* FIXME: This shouldn't be necessary in Solaris... if (kn->kev.flags & (EV_DISPATCH | EV_CLEAR | EV_ONESHOT)) eventfd_lower(filt->kf_efd); */ if (kn->kev.flags & EV_DISPATCH) { KNOTE_DISABLE(kn); kn->kev.fflags &= ~NOTE_TRIGGER; } else if (kn->kev.flags & EV_ONESHOT) { knote_free(filt, kn); } dst++; if (++nevents == maxevents) break; } /* This should normally never happen but is here for debugging */ if (nevents == 0) { dbg_puts("spurious wakeup"); /* FIXME: NOT IMPLEMENTED: eventfd_lower(filt->kf_efd); */ } return (nevents); } int evfilt_user_knote_create(struct filter *filt, struct knote *kn) { #if TODO u_int ffctrl; //determine if EV_ADD + NOTE_TRIGGER in the same kevent will cause a trigger */ if ((!(dst->kev.flags & EV_DISABLE)) && src->fflags & NOTE_TRIGGER) { dst->kev.fflags |= NOTE_TRIGGER; eventfd_raise(filt->kf_pfd); } #endif return (0); } int evfilt_user_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { unsigned int ffctrl; unsigned int fflags; /* Excerpted from sys/kern/kern_event.c in FreeBSD HEAD */ ffctrl = kev->fflags & NOTE_FFCTRLMASK; fflags = kev->fflags & NOTE_FFLAGSMASK; switch (ffctrl) { case NOTE_FFNOP: break; case NOTE_FFAND: kn->kev.fflags &= fflags; break; case NOTE_FFOR: kn->kev.fflags |= fflags; break; case NOTE_FFCOPY: kn->kev.fflags = fflags; break; default: /* XXX Return error? */ break; } if ((!(kn->kev.flags & EV_DISABLE)) && kev->fflags & NOTE_TRIGGER) { kn->kev.fflags |= NOTE_TRIGGER; knote_enqueue(filt, kn); return (port_send(filt->kf_kqueue->kq_port, X_PORT_SOURCE_USER, NULL)); } return (0); } int evfilt_user_knote_delete(struct filter *filt, struct knote *kn) { return (0); } int evfilt_user_knote_enable(struct filter *filt, struct knote *kn) { /* FIXME: what happens if NOTE_TRIGGER is in fflags? should the event fire? */ return (0); } int evfilt_user_knote_disable(struct filter *filt, struct knote *kn) { return (0); } const struct filter evfilt_user = { EVFILT_USER, evfilt_user_init, evfilt_user_destroy, evfilt_user_copyout, evfilt_user_knote_create, evfilt_user_knote_modify, evfilt_user_knote_delete, evfilt_user_knote_enable, evfilt_user_knote_disable, }; libkqueue-1.0.4/src/solaris/timer.c0000644000175000017500000001172611607445336016611 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" #ifndef NDEBUG static char * itimerspec_dump(struct itimerspec *ts) { static char __thread buf[1024]; snprintf(buf, sizeof(buf), "itimer: [ interval=%lu s %lu ns, next expire=%lu s %lu ns ]", ts->it_interval.tv_sec, ts->it_interval.tv_nsec, ts->it_value.tv_sec, ts->it_value.tv_nsec ); return (buf); } #endif /* Convert milliseconds into seconds+nanoseconds */ static void convert_msec_to_itimerspec(struct itimerspec *dst, int src, int oneshot) { time_t sec, nsec; sec = src / 1000; nsec = (src % 1000) * 1000000; /* Set the interval */ if (oneshot) { dst->it_interval.tv_sec = 0; dst->it_interval.tv_nsec = 0; } else { dst->it_interval.tv_sec = sec; dst->it_interval.tv_nsec = nsec; } /* Set the initial expiration */ dst->it_value.tv_sec = sec; dst->it_value.tv_nsec = nsec; dbg_printf("%s", itimerspec_dump(dst)); } static int ktimer_create(struct filter *filt, struct knote *kn) { port_notify_t pn; struct sigevent se; struct itimerspec ts; timer_t timerid; kn->kev.flags |= EV_CLEAR; pn.portnfy_port = filt->kf_kqueue->kq_port; pn.portnfy_user = (void *) kn->kev.ident; se.sigev_notify = SIGEV_PORT; se.sigev_value.sival_ptr = &pn; if (timer_create (CLOCK_MONOTONIC, &se, &timerid) < 0) { dbg_perror("timer_create(2)"); return (-1); } convert_msec_to_itimerspec(&ts, kn->kev.data, kn->kev.flags & EV_ONESHOT); if (timer_settime(timerid, 0, &ts, NULL) < 0) { dbg_perror("timer_settime(2)"); (void) timer_delete(timerid); return (-1); } kn->data.timerid = timerid; dbg_printf("created timer with id #%lu", (unsigned long) timerid); return (0); } int evfilt_timer_init(struct filter *filt) { return (0); } void evfilt_timer_destroy(struct filter *filt) { return; } int evfilt_timer_copyout(struct filter *filt, struct kevent *dst, int nevents) { port_event_t *pe = (port_event_t *) pthread_getspecific(filt->kf_kqueue->kq_port_event); long buf; timer_t timerid; struct knote *kn; /* XXX-FIXME: danger here -- there has to be a better way */ buf = (long) pe->portev_user; timerid = (timer_t) buf; /* ^^^^^^^^^ */ kn = knote_lookup(filt, timerid); dbg_printf("knote=%p", kn); memcpy(dst, &kn->kev, sizeof(*dst)); //TODO: //if (ev->events & EPOLLERR) // dst->fflags = 1; /* FIXME: Return the actual timer error */ /* FIXME: On return, data contains the number of times the timer has been trigered. */ dst->data = 1; //workaround if (kn->kev.flags & EV_DISPATCH) { KNOTE_DISABLE(kn); timer_delete(kn->data.timerid); } else if (kn->kev.flags & EV_ONESHOT) { timer_delete(kn->data.timerid); knote_free(filt, kn); } return (1); } int evfilt_timer_knote_create(struct filter *filt, struct knote *kn) { return ktimer_create(filt, kn); } int evfilt_timer_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { return (-1); /* STUB */ } int evfilt_timer_knote_delete(struct filter *filt, struct knote *kn) { if (kn->kev.flags & EV_DISABLE) return (0); dbg_printf("deleting timer # %d", kn->data.timerid); return timer_delete(kn->data.timerid); } int evfilt_timer_knote_enable(struct filter *filt, struct knote *kn) { return evfilt_timer_knote_create(filt, kn); } int evfilt_timer_knote_disable(struct filter *filt, struct knote *kn) { return evfilt_timer_knote_delete(filt, kn); } const struct filter evfilt_timer = { EVFILT_TIMER, evfilt_timer_init, evfilt_timer_destroy, evfilt_timer_copyout, evfilt_timer_knote_create, evfilt_timer_knote_modify, evfilt_timer_knote_delete, evfilt_timer_knote_enable, evfilt_timer_knote_disable, }; libkqueue-1.0.4/src/solaris/socket.c0000644000175000017500000001224211607445336016753 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" static int socket_knote_create(int port, int filter, int fd, void *udata) { int rv, events; switch (filter) { case EVFILT_READ: events = POLLIN; break; case EVFILT_WRITE: events = POLLOUT; break; default: dbg_puts("invalid filter"); return (-1); } rv = port_associate(port, PORT_SOURCE_FD, fd, events, udata); if (rv < 0) { dbg_perror("port_associate(2)"); return (-1); } return (0); } static int socket_knote_delete(int port, int fd) { if (port_dissociate(port, PORT_SOURCE_FD, fd) < 0) { dbg_perror("port_dissociate(2)"); return (-1); } else { return (0); } } int evfilt_socket_init(struct filter *filt) { return (0); } void evfilt_socket_destroy(struct filter *filt) { ; } int evfilt_socket_knote_create(struct filter *filt, struct knote *kn) { return socket_knote_create(filt->kf_kqueue->kq_port, kn->kev.filter, kn->kev.ident, filt); } int evfilt_socket_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { return (-1); /* STUB */ } int evfilt_socket_knote_delete(struct filter *filt, struct knote *kn) { if (kn->kev.flags & EV_DISABLE) return (0); else return (socket_knote_delete(filt->kf_kqueue->kq_port, kn->kev.ident)); } int evfilt_socket_knote_enable(struct filter *filt, struct knote *kn) { return socket_knote_create(filt->kf_kqueue->kq_port, kn->kev.filter, kn->kev.ident, filt); } int evfilt_socket_knote_disable(struct filter *filt, struct knote *kn) { return socket_knote_delete(filt->kf_kqueue->kq_port, kn->kev.ident); } int evfilt_socket_copyout(struct filter *filt, struct kevent *dst, int nevents) { port_event_t *pe = (port_event_t *) pthread_getspecific(filt->kf_kqueue->kq_port_event); struct knote *kn; kn = knote_lookup(filt, pe->portev_object); if (kn == NULL) return (-1); memcpy(dst, &kn->kev, sizeof(*dst)); if (pe->portev_events == 8) //XXX-FIXME Should be POLLHUP) dst->flags |= EV_EOF; else if (pe->portev_events & POLLERR) dst->fflags = 1; /* FIXME: Return the actual socket error */ if (pe->portev_events & POLLIN) { if (kn->flags & KNFL_PASSIVE_SOCKET) { /* On return, data contains the length of the socket backlog. This is not available under Solaris (?). */ dst->data = 1; } else { /* On return, data contains the number of bytes of protocol data available to read. */ #if FIXME if (ioctl(dst->ident, (dst->filter == EVFILT_READ) ? SIOCINQ : SIOCOUTQ, &dst->data) < 0) { /* race condition with socket close, so ignore this error */ dbg_puts("ioctl(2) of socket failed"); dst->data = 0; } #else /* Workaround */ dst->data = 1; #endif } } if (kn->kev.flags & EV_DISPATCH) { socket_knote_delete(filt->kf_kqueue->kq_port, kn->kev.ident); KNOTE_DISABLE(kn); } else if (kn->kev.flags & EV_ONESHOT) { socket_knote_delete(filt->kf_kqueue->kq_port, kn->kev.ident); knote_free(filt, kn); } else { /* Solaris automatically disassociates a FD event after it is delivered. This effectively disables the knote. */ KNOTE_DISABLE(kn); } return (1); } const struct filter evfilt_read = { EVFILT_READ, evfilt_socket_init, evfilt_socket_destroy, evfilt_socket_copyout, evfilt_socket_knote_create, evfilt_socket_knote_modify, evfilt_socket_knote_delete, evfilt_socket_knote_enable, evfilt_socket_knote_disable, }; const struct filter evfilt_write = { EVFILT_WRITE, evfilt_socket_init, evfilt_socket_destroy, evfilt_socket_copyout, evfilt_socket_knote_create, evfilt_socket_knote_modify, evfilt_socket_knote_delete, evfilt_socket_knote_enable, evfilt_socket_knote_disable, }; libkqueue-1.0.4/src/solaris/kevent.c0000644000175000017500000001035411607445336016761 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "sys/event.h" #include "private.h" const struct filter evfilt_vnode = EVFILT_NOTIMPL; const struct filter evfilt_proc = EVFILT_NOTIMPL; /* Dump a poll(2) events bitmask */ static char * poll_events_dump(short events) { static char __thread buf[512]; #define _PL_DUMP(attrib) \ if (events == attrib) \ strcat(&buf[0], " "#attrib); snprintf(&buf[0], 512, "events = %hd 0x%o (", events, events); _PL_DUMP(POLLIN); _PL_DUMP(POLLPRI); _PL_DUMP(POLLOUT); _PL_DUMP(POLLRDNORM); _PL_DUMP(POLLRDBAND); _PL_DUMP(POLLWRBAND); _PL_DUMP(POLLERR); _PL_DUMP(POLLHUP); _PL_DUMP(POLLNVAL); strcat(&buf[0], ")"); return (&buf[0]); #undef _PL_DUMP } static char * port_event_dump(port_event_t *evt) { static char __thread buf[512]; if (evt == NULL) return "(null)"; #define PE_DUMP(attrib) \ if (evt->portev_source == attrib) \ strcat(&buf[0], #attrib); snprintf(&buf[0], 512, " { object = %u, user = %p, %s, source = %d (", (unsigned int) evt->portev_object, evt->portev_user, poll_events_dump(evt->portev_events), evt->portev_source); PE_DUMP(PORT_SOURCE_AIO); PE_DUMP(PORT_SOURCE_FD); PE_DUMP(PORT_SOURCE_TIMER); PE_DUMP(PORT_SOURCE_USER); PE_DUMP(PORT_SOURCE_ALERT); strcat(&buf[0], ") }\n"); return (&buf[0]); #undef PE_DUMP } int kevent_wait(struct kqueue *kq, const struct timespec *timeout) { port_event_t *pe = (port_event_t *) pthread_getspecific(kq->kq_port_event); int rv; uint_t nget = 1; reset_errno(); dbg_printf("waiting for events (timeout=%p)", timeout); rv = port_getn(kq->kq_port, pe, 1, &nget, (struct timespec *) timeout); dbg_printf("rv=%d errno=%d (%s) nget=%d", rv, errno, strerror(errno), nget); if (rv < 0) { if (errno == ETIME) { dbg_puts("no events within the given timeout"); return (0); } if (errno == EINTR) { dbg_puts("signal caught"); return (-1); } dbg_perror("port_get(2)"); return (-1); } return (nget); } int kevent_copyout(struct kqueue *kq, int nready, struct kevent *eventlist, int nevents) { port_event_t *pe = (port_event_t *) pthread_getspecific(kq->kq_port_event); struct filter *filt; int rv; dbg_printf("%s", port_event_dump(pe)); switch (pe->portev_source) { case PORT_SOURCE_FD: filt = pe->portev_user; rv = filt->kf_copyout(filt, eventlist, nevents); break; case PORT_SOURCE_TIMER: filter_lookup(&filt, kq, EVFILT_TIMER); rv = filt->kf_copyout(filt, eventlist, nevents); break; case PORT_SOURCE_USER: switch (pe->portev_events) { case X_PORT_SOURCE_SIGNAL: filter_lookup(&filt, kq, EVFILT_SIGNAL); rv = filt->kf_copyout(filt, eventlist, nevents); break; case X_PORT_SOURCE_USER: filter_lookup(&filt, kq, EVFILT_USER); rv = filt->kf_copyout(filt, eventlist, nevents); break; default: dbg_puts("unsupported portev_events"); abort(); } break; default: dbg_puts("unsupported source"); abort(); } if (rv < 0) { dbg_puts("kevent_copyout failed"); return (-1); } return (1); } libkqueue-1.0.4/include/0000755000175000017500000000000011607445336014476 5ustar mheilymheilylibkqueue-1.0.4/include/sys/0000755000175000017500000000000011607445336015314 5ustar mheilymheilylibkqueue-1.0.4/include/sys/event.h0000644000175000017500000001462511607445336016616 0ustar mheilymheily/*- * Copyright (c) 2009 Mark Heily * Copyright (c) 1999,2000,2001 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD SVN Revision 197533$ */ #ifndef _SYS_EVENT_H_ #define _SYS_EVENT_H_ #include #ifdef __KERNEL__ #define intptr_t long #else #include #include #include #define LIBKQUEUE 1 #endif struct timespec; #define EVFILT_READ (-1) #define EVFILT_WRITE (-2) #define EVFILT_AIO (-3) /* attached to aio requests */ #define EVFILT_VNODE (-4) /* attached to vnodes */ #define EVFILT_PROC (-5) /* attached to struct proc */ #define EVFILT_SIGNAL (-6) /* attached to struct proc */ #define EVFILT_TIMER (-7) /* timers */ #define EVFILT_NETDEV (-8) /* network devices */ #define EVFILT_FS (-9) /* filesystem events */ #define EVFILT_LIO (-10) /* attached to lio requests */ #define EVFILT_USER (-11) /* User events */ #define EVFILT_SYSCOUNT 11 #define EV_SET(kevp_, a, b, c, d, e, f) do { \ struct kevent *kevp = (kevp_); \ (kevp)->ident = (a); \ (kevp)->filter = (b); \ (kevp)->flags = (c); \ (kevp)->fflags = (d); \ (kevp)->data = (e); \ (kevp)->udata = (f); \ } while(0) struct kevent { uintptr_t ident; /* identifier for this event */ short filter; /* filter for event */ unsigned short flags; unsigned int fflags; intptr_t data; void *udata; /* opaque user data identifier */ }; /* actions */ #define EV_ADD 0x0001 /* add event to kq (implies enable) */ #define EV_DELETE 0x0002 /* delete event from kq */ #define EV_ENABLE 0x0004 /* enable event */ #define EV_DISABLE 0x0008 /* disable event (not reported) */ /* flags */ #define EV_ONESHOT 0x0010 /* only report one occurrence */ #define EV_CLEAR 0x0020 /* clear event state after reporting */ #define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */ #define EV_DISPATCH 0x0080 /* disable event after reporting */ #define EV_SYSFLAGS 0xF000 /* reserved by system */ #define EV_FLAG1 0x2000 /* filter-specific flag */ /* returned values */ #define EV_EOF 0x8000 /* EOF detected */ #define EV_ERROR 0x4000 /* error, data contains errno */ /* * data/hint flags/masks for EVFILT_USER * * On input, the top two bits of fflags specifies how the lower twenty four * bits should be applied to the stored value of fflags. * * On output, the top two bits will always be set to NOTE_FFNOP and the * remaining twenty four bits will contain the stored fflags value. */ #define NOTE_FFNOP 0x00000000 /* ignore input fflags */ #define NOTE_FFAND 0x40000000 /* AND fflags */ #define NOTE_FFOR 0x80000000 /* OR fflags */ #define NOTE_FFCOPY 0xc0000000 /* copy fflags */ #define NOTE_FFCTRLMASK 0xc0000000 /* masks for operations */ #define NOTE_FFLAGSMASK 0x00ffffff #define NOTE_TRIGGER 0x01000000 /* Cause the event to be triggered for output. */ /* * data/hint flags for EVFILT_{READ|WRITE} */ #define NOTE_LOWAT 0x0001 /* low water mark */ #undef NOTE_LOWAT /* Not supported on Linux */ /* * data/hint flags for EVFILT_VNODE */ #define NOTE_DELETE 0x0001 /* vnode was removed */ #define NOTE_WRITE 0x0002 /* data contents changed */ #define NOTE_EXTEND 0x0004 /* size increased */ #define NOTE_ATTRIB 0x0008 /* attributes changed */ #define NOTE_LINK 0x0010 /* link count changed */ #define NOTE_RENAME 0x0020 /* vnode was renamed */ #define NOTE_REVOKE 0x0040 /* vnode access was revoked */ #undef NOTE_REVOKE /* Not supported on Linux */ /* * data/hint flags for EVFILT_PROC */ #define NOTE_EXIT 0x80000000 /* process exited */ #define NOTE_FORK 0x40000000 /* process forked */ #define NOTE_EXEC 0x20000000 /* process exec'd */ #define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */ #define NOTE_PDATAMASK 0x000fffff /* mask for pid */ /* additional flags for EVFILT_PROC */ #define NOTE_TRACK 0x00000001 /* follow across forks */ #define NOTE_TRACKERR 0x00000002 /* could not track child */ #define NOTE_CHILD 0x00000004 /* am a child process */ /* * data/hint flags for EVFILT_NETDEV */ #define NOTE_LINKUP 0x0001 /* link is up */ #define NOTE_LINKDOWN 0x0002 /* link is down */ #define NOTE_LINKINV 0x0004 /* link state is invalid */ /* KLUDGE: This is from on FreeBSD and is used by the EVFILT_FS filter. */ /* vfsquery flags */ #define VQ_NOTRESP 0x0001 /* server down */ #define VQ_NEEDAUTH 0x0002 /* server bad auth */ #define VQ_LOWDISK 0x0004 /* we're low on space */ #define VQ_MOUNT 0x0008 /* new filesystem arrived */ #define VQ_UNMOUNT 0x0010 /* filesystem has left */ #define VQ_DEAD 0x0020 /* filesystem is dead, needs force unmount */ #define VQ_ASSIST 0x0040 /* filesystem needs assistance from external program */ #define VQ_NOTRESPLOCK 0x0080 /* server lockd down */ #ifndef __KERNEL__ #ifdef __cplusplus extern "C" { #endif int kqueue(void); int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); #ifdef __cplusplus } #endif #endif /* !__KERNEL__* */ #endif /* !_SYS_EVENT_H_ */ libkqueue-1.0.4/test/0000755000175000017500000000000011607445337014033 5ustar mheilymheilylibkqueue-1.0.4/test/stress/0000755000175000017500000000000011607445336015355 5ustar mheilymheilylibkqueue-1.0.4/test/stress/main.c0000644000175000017500000000456511607445336016457 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "../config.h" #include #include #include #include #include #include /* Number of threads to create */ static const int nthreads = 64; /* Number of iterations performed by each thread */ static const int nrounds = 1000000; //pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; void test_kqueue_conc(void) { int i, fd; for (i = 0; i < 256; i++) { fd = kqueue(); if (i < 0) err(1, "kqueue"); close(fd); } } void * test_harness(void *arg) { int id = (long) arg; int i; int kqfd; kqfd = kqueue(); if (kqfd < 0) err(1, "kqueue"); printf("thread %d runs %d\n", id, id % 4); //test_kqueue_conc(); for (i = 0; i < nrounds; i++) { switch (id % 4) { case 0: test_evfilt_user(kqfd); break; case 1: test_evfilt_read(kqfd); break; case 2: test_evfilt_timer(kqfd); break; case 3: test_evfilt_vnode(kqfd); break; } printf("thread %d round %d / %d\n", id, i, nrounds); } printf("thread %d done\n", id); } int main(int argc, char **argv) { pthread_t tid[nthreads]; long i; for (i=0; i/dev/null ulimit -c 999999 ; ./$(PROGRAM) || true if [ -f core ] ; then gdb ./$(PROGRAM) core ; fi valgrind: $(PROGRAM) valgrind --tool=memcheck --leak-check=full --show-reachable=yes \ --num-callers=20 --track-fds=yes ./$(PROGRAM) clean: rm -f $(PROGRAM) core tags *.o edit: ctags $(SOURCES) $(EDITOR) $(SOURCES) distclean: clean rm -f $(PROGRAM) libkqueue-1.0.4/test/libdispatch/0000755000175000017500000000000011607445336016320 5ustar mheilymheilylibkqueue-1.0.4/test/libdispatch/main.c0000644000175000017500000000437211607445336017416 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; int testnum; void test_countdown(void); void say_hello(void *arg) { puts("hello"); test_countdown(); } void final_countdown(void *arg, size_t count) { static int europe = 10; if (europe == 0) { printf("It's the final countdown..\n"); exit(0); } else { printf("%d.. ", europe); fflush(stdout); } pthread_mutex_lock(&mtx); europe--; pthread_mutex_unlock(&mtx); } /* Adapted from: http://developer.apple.com/mac/articles/cocoa/introblocksgcd.html */ void test_timer() { dispatch_source_t timer; dispatch_time_t now; timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_current_queue()); //NOTE: q_default doesn't work now = dispatch_walltime(DISPATCH_TIME_NOW, 0); dispatch_source_set_timer(timer, now, 1, 1); dispatch_source_set_event_handler_f(timer, say_hello); puts("starting timer\n"); } void test_countdown(void) { dispatch_apply_f(15, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), NULL, final_countdown); } int main(int argc, char **argv) { while (argc) { #if TODO if (strcmp(argv[0], "--no-proc") == 0) test_proc = 0; #endif argv++; argc--; } test_timer(); dispatch_main(); printf("\n---\n" "+OK All %d tests completed.\n", testnum - 1); return (0); } libkqueue-1.0.4/test/libdispatch/Makefile0000644000175000017500000000036711607445336017766 0ustar mheilymheilyCFLAGS=`pkg-config --cflags libkqueue` LDADD=`pkg-config --libs libkqueue` -ldispatch all: disptest disptest: main.o $(CC) -o disptest $(CFLAGS) main.c $(LDADD) check: disptest ./disptest clean: rm -f *.o distclean: clean rm -f disptest libkqueue-1.0.4/test/benchmark/0000755000175000017500000000000011607445336015764 5ustar mheilymheilylibkqueue-1.0.4/test/benchmark/results.txt0000644000175000017500000001112211607445336020223 0ustar mheilymheilyTest system: Linux voltaire 2.6.31-20-generic #58-Ubuntu SMP Fri Mar 12 04:38:19 UTC 2010 x86_64 GNU/Linux THTTPD 2.25b + POLL(2) NO IDLE CONNECTIONS Time taken for tests: 0.515 seconds Complete requests: 5000 Failed requests: 0 Write errors: 0 Total transferred: 2760000 bytes HTML transferred: 1555000 bytes Requests per second: 9699.79 [#/sec] (mean) Time per request: 51.547 [ms] (mean) Time per request: 0.103 [ms] (mean, across all concurrent requests) Transfer rate: 5228.79 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 2 3.2 1 20 Processing: 3 12 5.1 10 42 Waiting: 2 11 4.7 9 41 Total: 3 14 6.3 11 61 Percentage of the requests served within a certain time (ms) 50% 11 66% 15 75% 18 80% 19 90% 20 95% 29 98% 37 99% 37 100% 61 (longest request) THTTPD 2.25b + LIBKQUEUE NO IDLE CONNECTIONS Requests per second: 8243.78 [#/sec] (mean) Time per request: 60.652 [ms] (mean) Time per request: 0.121 [ms] (mean, across all concurrent requests) Transfer rate: 4455.47 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 4 3.2 4 15 Processing: 3 11 8.6 11 265 Waiting: 2 9 8.2 10 260 Total: 7 16 8.9 16 277 Percentage of the requests served within a certain time (ms) 50% 16 66% 19 75% 19 80% 20 90% 21 95% 23 98% 28 99% 29 100% 277 (longest request) ================================================================== THTTPD 2.25b + POLL(2) 3000 IDLE CONNECTIONS Requests per second: 5902.01 [#/sec] (mean) Time per request: 84.717 [ms] (mean) Time per request: 0.169 [ms] (mean, across all concurrent requests) Transfer rate: 3181.55 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 3 3.7 1 18 Processing: 7 28 13.3 25 339 Waiting: 7 26 13.4 23 338 Total: 8 30 14.2 27 342 Percentage of the requests served within a certain time (ms) 50% 27 66% 32 75% 35 80% 38 90% 51 95% 54 98% 59 99% 62 100% 342 (longest request) THTTPD 2.25b + LIBKQUEUE 3000 IDLE CONNECTIONS Requests per second: 7945.49 [#/sec] (mean) Time per request: 62.929 [ms] (mean) Time per request: 0.126 [ms] (mean, across all concurrent requests) Transfer rate: 4283.11 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 2 2.6 1 12 Processing: 2 14 5.1 14 28 Waiting: 2 12 4.5 12 27 Total: 9 16 4.9 17 30 Percentage of the requests served within a certain time (ms) 50% 17 66% 19 75% 19 80% 20 90% 22 95% 24 98% 28 99% 29 100% 30 (longest request) =========================================================================== THTTPD 2.25b + POLL(2) 6000 IDLE CONNECTIONS Requests per second: 4650.43 [#/sec] (mean) Time per request: 107.517 [ms] (mean) Time per request: 0.215 [ms] (mean, across all concurrent requests) Transfer rate: 2507.37 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 3 5.5 1 23 Processing: 12 34 18.8 29 542 Waiting: 12 33 18.1 29 542 Total: 13 38 21.4 31 544 Percentage of the requests served within a certain time (ms) 50% 31 66% 35 75% 39 80% 41 90% 65 95% 85 98% 99 99% 99 100% 544 (longest request) THTTPD 2.25b + LIBKQUEUE 6000 IDLE CONNECTIONS Requests per second: 9023.58 [#/sec] (mean) Time per request: 55.410 [ms] (mean) Time per request: 0.111 [ms] (mean, across all concurrent requests) Transfer rate: 4864.27 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 4 4.6 3 18 Processing: 7 22 8.5 23 44 Waiting: 4 21 8.6 21 43 Total: 12 27 8.7 26 49 Percentage of the requests served within a certain time (ms) 50% 26 66% 29 75% 31 80% 34 90% 40 95% 45 98% 49 99% 49 100% 49 (longest request) libkqueue-1.0.4/test/benchmark/abtest0000755000175000017500000000126111607445336017174 0ustar mheilymheily#!/usr/bin/perl # # Test using ApacheBench against an HTTP server with lots of idle clients # use IO::Socket; use Getopt::Long; use strict; use warnings; our $NCLIENT = 3000; our $BASELINE = 0; our @CLIENT; sub create_client { my $socket = new IO::Socket::INET ( PeerAddr => '127.0.0.1', PeerPort => 8080, Proto => 'tcp', ) or die $!; push @CLIENT, $socket; } GetOptions("baseline" => \$BASELINE, "idle=i" => \$NCLIENT) or die; for (my $i = 0; $i < $NCLIENT; $i++) { create_client(); } print "====> Created $NCLIENT idle connections <=====\n"; system "ab -n 5000 -c 500 http://localhost:8080/"; libkqueue-1.0.4/test/benchmark/scalability.png0000644000175000017500000011453711607445336021005 0ustar mheilymheily‰PNG  IHDR à‚éxõ pHYsÄÄ•+™IDATx^ì\éǺ±1†R 0°QÄ:‹Sl±Å®;ÏúŸ]çÙÞÙžè­¨g b §"‚*"1& "½ÿwfv—ÝÜ…e™çûfŸyÞwföåÙßäºb±„Š.ß!$°ðB /T°ðB /T°ðEEQ4M“9±Ãâèà¹m~N[ùáè goÖGÓZ\¶0/0wõÁ.jþÓ¸Fñ‹Û5qö¥]Íáü/Î WVÕÍ6T‹ª·'Å À~ÐNÎþùŠhdºèÃQù 1»»ÚŒñe,sW™ÓTO t-j™ûÙÔÝâì‹ÔÅ?Ôà ÏÚ²œ*¡¨ NÓìç8Ér‡ôúìTZ’šm8»Jl­{Ãä“<¥†Ê)ªÂkª.ü×»èi¶SäÇ©š„Wüñ´–Ž”qsqpxâZFàãÒrù ÀŒ,p+Ëï ÕwÌ­˜½U£êÂË—Y[WFÈ U¾'ç  ”¤–q s“ØÒ_{ý²ìBQ– Wø”!ûµŽmÈ÷k,…)¼aãƒcù>&ÿÂS65éèç|o© ÿŸ +%‡‚Þɘï))t,ÃÌ/|üK‘+…YMtÉ'«“wV \i/'HeÆ÷R˜Â›wÊ>#ª.-ü ßuO¤ïh;¾·ˆ8¼v-†e‡ U7fQÕ¥å‘ágô³æ»TËÔÎð$XRÎA?1Sq‘á=l¥÷ÝëÛœ UÅmÆ/Øø“¤À^ä§ù^]eKN4¯Á\Oë*q))À„¾mùÞ<±ú̾K¡²ÅœÑv·®áuƒ†j”YÛBÏâ^†ü Ÿ ¤ÌÜ¡K­°f÷ ß+‡wÌ 2gžèY´QLdÈ¿ðÜã'ÔZf’áù%/Eä_xy~¨M]yªÜi¿$Q¸Â—¦’CA Ÿ}ª+uä_øÒZr(Háå9ô&kpm¾WcÉ¿ðÜ«ï”\«–¦’CA ?Ü{ßUZÈ¿ð¨ùÒIþ…ojWënä3Îþ‡ÎAîJ ù^VrBi*9¤ð9iØ÷؃ýùÞÂ’rßý€þñüŠ'*+wñŸäÒl£o›A~…ýVßSøÂî#w y!'øþÂÓak|×·úžÂ„à%=&·ÃÖ~sOD'þúËFùƒ¹k€>ÀkÿµS'ߺg®ý"è/sŠ¢Wd¢_ÆÔèç‘{.9°}Ü/³no\ekÙ{3Óª£¨ OJÎ1®« ÷N´çÈ$ŸZ oÄ<“JMü@J>jÙqbŸ}Kæ[Úe¤¥”#¹33@˰\’Í EUxîY€ë´?˜¶Ž‰‘cVpžì6™Ë³wA? Ë‘[ÌÿÐ)ŒíÝ›ó!E[ø^¨`á… ^¨»ðØ–V `á… ^¨`á… ^¨`ወ”§gì&4±p$LSÙÌ ~àêjMÙ´’5¶e w.ϲgqjY’U¼Â¼=(™¡l-ØÜ(ªÂ3m#/(¾`šÊf²–®Mtvaœeg_¦²ŸÒVwÙ†ê)ªÂsêb˜£Þ²¾+×PÛ±\£Zoi˜•£¥-#*¢¨ ¯YáE~î-vy‡yËê0­ßGGG'9 t2<:è^wøk «åýd±I†t1ÜI t6k¾j¶á¢ ©LÎÔL AÞÌ%u!s©©EVxsWï°ì&´„¡'åÓÉÿ%W›ã@Ž–’÷ª+9¼ðƒ[/˜Ñ¶òˆýØW.¥ƒ‚þPH ßU’···ç{ó#ŸÂOÞsĪ#M©|êºk(ù¾Û–Ó¢Rô;ç‘Oá¾J›e¯Ï÷–v.„#› akXïÃO*0ù>»ä"?Èʸ¤íÖI¥ÇÛB°x\?c™Ì⸥̤ù>öíz'¾·(ñÙëg€ûd˜¸’Y\¼ŸŸAiò)|?¯§“Ükw,¶hË·vÍLEI>…^?«­Ç)¾Wµ·è+1vñ6`ɧð¯?Ýñ—^QÖh9äÙ­ƒª©ˆG l]ö3vñXž| LCbɽô‹[ùi…bÛÿÀ{KIh–%#ŸÂ«à^’¹ûhøi#sÜâ]%†| ÿý¨¯í×öᎱKºMô;’po™&#‰'à’v{å=¨—S¥…ý†5TW™eÈšVr7‹\É :\sZiÉ!ß¿»åU©¥äöÒªÉÐ=W˜‡·˜™ó8Õªüìݦª‘ÓâŽ]]yݪ¢ ì¼÷Ý iã>|9µÿ¿ç 䦠¾u¹Ð˜OFúº¿öo4¨u ~îÂ0Éÿ³Þü›nFðüù^VrBì½̇““ø‚c¬› wÀ®5ë_íeù¾;;hçä¸ù'¾?WÎÜzÖÙÉÔtî‘ „ýÑG1#“7líPr”ò-|îpzžù'ß_ŒôhY+Åg¶l‘ÜÒÊ%ÊÈ缜OágÞ mjÞ„wG«>=«–| ÿ娉Q±]Ü7ùþþk¡ÏFÎvwp?æ­\»€’E>…_rjãµ$hkÊØ93j:ù¾É“ìS]é#ŸÂ—â’C¾…—Güñô]£žÍJѳÌ| m£gÛ»8[«|ÏfŠ©šN>…wh©ð µ”‘Oá…®yÉõqi$ÿÂË—<8Y•]bµh¡šÆc>|à» @þ…—G…%'¨ªä¾«®ð„6î}$„ï- u)Â;z·Û$­mF«'&vè~rF…zCDÝÚÕ¨÷êšÏî&}¦£Ï“mž>D¾É¯0©.Ewm`yúQ\g{êJ8Ý©.uþ ]î$QboÂ̸HËÏAÒc…|ó&VˆŒ“DÌg±Í!ÎþÎŒ ±u„=L™Îec=ýn3ÃC05YC²Ëޮܢ„ÞÇ7û1uþlÆøøK¼5F~['’Ũ;ÈüþY¦²(³MvGÍŒ€D¾âÈüj8c_~òýG—¢¦Ä^E°¿’ïàtÓôµ¡ƒ" <{(>öUWǾœŽjkä—z¾¯O¯ï ÏЬ¿M‰ìB ‘RG4 ¼º¹p~g®ô µWû)¤ÊþÔGq¯ò Ý"¦ÇzëYõ­å<¨NbÀ©Pº´6¼, ‡BÀY‰Íõ %O—!Ì$%+÷NNT²¿<½ç¤ÿžœ¸þ°… { gåNæÏ5à±¥ÊØ4Nÿ%±Ç/ƒÓR—PX,(ø{«;÷#F=kIÔK-ñ4Œh élWëµÃЖ{¸?}3iJ¾ÉìK•7Q÷hºôTÍŸ?}ÃÂ<mé™Ê‚‚óñrù4¥OÐ1w<ÝÍÚFÃÎå\g¹7X*׋åòÃÙv©FÙÀßcôqY =à¦?}{}÷&¿hC's¸qãF… ä’‹­¬Lûém@œEìˆuW² ¤}ù ü•™8˜³U AK«˜:W6ð3oˆÖµ6¯Í{<§W»r‡ßݤK­[·–O,B:—‡¬LÐÑ… r„\úÄ}ÖÉv•\òèºPõ(ø¶uÍüD «Vfü¤¢eÕx¸È–w\‡š’÷ëpñ£\ŽÒ ÷rRòŠ’}FÎÏ‘Ê~RK2ç*ÀYYйT¨ •ª1—Ósw0“€!Q‡/!l'Ub@h•—iCΰ?tXó뿳þ×н֚Í×ùË Á|ùÕ• |‹^óO°¯,UEÒÕ3¦Ë†ÿçÒ ¦Ž/^&STÙ%iY¢g©ÀvÖ\DÔ¨Z¶Že…Õ+Õ¢Ê7²³hdWYW'G…¬’ C9¹ËçsÍcGq9¢Ê¾j¿Aä÷ec¼q=j¸Œµyç{ù)ÏnÏrØ¿†1Ž„AE¦›iÓö= =ÓqnvÒ ÇùÞŽô Š42Ð ”{ß¾ZPø+btmn׿­ý€v&†…¯œÁ êÀñ×M÷¶]¼cc ý%èٽʄjr÷TakÝ~Éeß¹QYòûòOäþE€ôI–=;bÊ¢>þòçË’¨¾4¾l§|΃ʾ7;NW‡^Ùýò'3!J“kÝuÎÈ7ê |àßïi¬ÞòÖ­æß¸IŒ &Ïÿª8¿>ð4ߥVêºÿÉÞo8A§ÁüDõÀEŠñUGJ šø›"q+sæŠQV¿lW æM`7ðÔ`ìõL Ùþ͸Ìîõzá²e‰¡+œsoé¥û%ÉFÓ õ™|l}Å‹"¦#$Ùf™^¬´\v>>êV†Y$dtûº·Üí䙕enÕp)Ͷ¾Ð@4/ð\ÔA®~™«äM`W®4y¼á é[ãnÞaݲ“¹¨ƒ¤ÚZg¹ k¼^¬ä^:wõëJ>´™n±45ê ‰GT^ `à ^ `à ^ `à ^ `à Ž-+PPñ/P0ð/P0ð/P0ð/P0ð/P0ð/P0ð/P0ð/P0ð/P0ð/P4,ðE‘¹|f ŠòÈzÓf›¹»;C ÷Ó£`â"Ë @zeÛšlÓ¢¼Ù¶õò«ózå.5hTà³>.ßeôµAö ˆ¹OY7b 0¹ŸÅAvRä3ÀCšKf`…ôhʶ•EÏ?ãOO&)ùÕÉ6‰Aæu.²? æ÷Áz€Û/Ï“óGY"ѨÀk—ýCyfÄå4íi7Éx ¹0ðPä?b…qY8)gDï~²ªuݹLž­HúRTˆ\²Õ=l‡Çè—3;‡èB¶—§d£Q'Q–̰$ «³òŠË}È‹Æ])ˆO¯UÇ“ŽØÕùÏ…ô\ÆqäÅM¢Ñr]Ø¡‘­îörª=u›U³I0ÿ5£‹$kN† Q×2•Å2§!oo<Æ ÒÎöh"2Dén´4¿ž ï°ìÛꮬ“»ÐϹߜ†F QWeb#ûY”„xD^ ”ÐÀwßÿêì°jÿЙÇ:¸÷÷÷–ÍÏDœ”Ý[Ë©›}Ã-ò³é÷¤òóµÁQt3[jÅCšë´nACŠxÈ9>6ÌËÊa@`,-“ëÒŽYùQät@Uw¦£˜+ÿ„¬„2ÚeZU§nFÑu(*”¦;ÛSWÂéNu©óOh—êÌ6I¶ÌWût,\»ÿzÝðÔîÀÄ7tôùF½W×|v/0é3Mß“–©dQBO¢î^ð«u ÌL ´es~¾Üˆ¾ø™àÎKšë˜ÀEhËò™_ÍÈåšë¿{Öƒ‰´gmÆO¹‹€ÄË\Ô!å~];7ŸHšDý’Ü—þå/‚«áÌv.?a¶Ìm“ m`6´Ñø³¡'ÝO¢Ã~ÚxÒýGÌPÏtØÉJ%4ðïÐCdÞ÷!3$©ü\ö(M2Â{ žý|-·µe©½GÿÌ3$©Ü"·®YGI‚a#ÙÅ`'sè4†é×P†ü=­ ½„öéW:ðˆÙN®ƒA–JnàUBi}àª<¥<ðH^¨:ðâ¿DÃK7¢‡µ7ïÝhjþãY#jAÕ×2$³e žj•Íì;óµhÑB!¢nTxî´jÞ™:Ts~RbP}à@©À§¼±oÖ½i·aÇwù*ˆjQ*ð†U¿,]¯.„ƒR‘_ôáß­zÓ®<ˆŸ„”l” ¼¹«Í¸\ž”!%#f÷†7$‹n#áç?2Ê>õ!8òÈ7ˆ{S;ƒè#”­ŸßÁœí°^=:” |<ð}~"ø³u*4ÖÞyгÀ£üd¡qó,`·«Ý¢#`ÝY8ÅÏ£” ¼y'¾‡eâ ú }é™ïH~‚¸r–fŒ†.ð6¶\Ë ü<%¥ÿõÕ=ûæ=uxjÏz™Sò^¤lRÈýë0³;c4p†oaûµ’iJÞ¨Z“hÞÎ¥~'xušÀË0ØuS#"ÍC©ÀsM@Ç‚Ž á§•2¶Îƒã[Áª&Ä>‡9ÛàÀ#~MC©ÀÓÑAâÄÇôÕ“ü„R@z* ª FÌyºA+Øè “~ãçÑd” <èVî¸üzÍ'Y;ûðS4’ð»°dÄÇ1ö´up<’Ÿ¡¡\àA{Ï4“­£ƒøn âîæ ÇŸáÐc…ÔÒ‹’gX~ôšy¯IÄ=‡‘M%ö®M¼4Så/ºÔºÕD¾ 3›Ãw¢JÜœîÕàëÆX²_˜Á–G¹À›»Ò4wrÏ]gÃí—ü¹87nÜHNN ˽ucQSÉggE_¦õ§vý߬¹œ ¦ï“/|WÑ \àÙŒ1彬m"ay ɜ֭[[yÒÓàÇJŒ¡«çãá— Ä,ÇNˆ åoÈ48ýà7¡‚ëv~Rñsd#ì\Äã–â‘<_” ¼¾=™©9êCê3·Ú„s¯aà ~*’Ê>ÑÄé⌭ò½øIE ¹F#Wj„v}à`(?U`|_G=ÊÞ¬™I =7ÎÀ¢aŒ1~™ æŽãÿzõN¶(¾032œžømWßw0ítüª#g®3ñòô3ÇýØ=]d…eѶE|XpMD¥(xéÅ2ä϶_Á{3cx *Þ2îËw±0¡Ô7ø˜œ5öÀ ½2ÆF•ʤ¦D˜˜ès‰ Zzdö1,ôåÖT6ðìíß©*ft…G7¡akX{&®à§"llïís#Æà‰5™eÑï0¦&\×½K™E® ¨s7c¯uP6ðù9E\ÖPÉ(Ù§†ŽèC†®ÁÚѧ¢l?'$§Þ½3&^”‘™ÅϪRÊ™ÚU+WϦ¢ƒuÅz¶ëÛV²­\†Ÿ©„“[Ká¼P2f:?Ô£ºÿvqÙ€ºòÞ¹ÿë¿rîxí¬T(ë|+üuËÙ¯¶šÔ¨xïÅ{ÙbÛºU’ÓïEÎц,‘y•+ÓŽXU4-c¬ßŸIdºšh Ë\<¤¦gÞüt¿ï§È7¢ §ïÂâ>sþºVåžÄ~’ekT½Âý¨u¨2mêVi_¿jËÚÜ·U ;;;¾+?ÜÜÉÜûÑ&Э–’þUKÏø}Pù6¿ôo“âõáæF€òîrÏ#µÌ;Pnt˜›“““X$¾ }£cÐvS-¸ÌüCÉÅiñÞäI]{ÈãçNüçä+÷£ODø=ÿ’’N<-ª…½’eèêTÃÁªbÿ¶ö-äúË+ c[A”ÜÛ£Ë dNƒNÖroõ‡x*ç[Ô~Ä’— õŒIÒðö‹üo,ÉΟÊžÜÇëÛWø‘„™s”éÿƒóåù%Èž¬µï[j.Ö,Êlï@&~‚"‘¯>ýv8ðØµ°çoùi5ª–ü;—wð×MžC‡üïMMYSÌy´™÷eðçí„©ÍË€–â|n(xÑe0ï(þxª@÷ñI ÐËš1Ü'—šxr1o3™ø …„6è/¦e*À1oö¤qdÆD`ßUö æ7Q.ðæL¯!ùF½Úµ#ð¿.à±@°ñ.t-e&¥#çÏå_0^µh5+».RP.ð©!5.5öžA=ɳ$DSP.ðõÚw™w2›LjÊ^tù´¯7D³P.ðæísí¿W‡_OýûHà$&&FF–æ«*ä;à|ÊÄ¡‘1å ù‘§üWdÉà[y¢dàuËåYÃV;Ï8H @¹À³'ÝÿÝ´QÉí´ÉåϾ¨k"Ê ±].oç’rÏãíRòQ.ðy¼CJ>Ê^t Ì;‰?žro{äxèA~*R‚Q.ðl8Zå{å¿ ón›ªkŶ³AJÊ>éúýÕÿ«ÖÔÖ¢×>y·›Û¯>gf<öZUo,¾„¢\àMÛ|øåêð!¿>R¼S)¶ÞØ ^¤$¡\à!³©¾øÑÙU|7RâQ2ð¹×²EJ>Êïã5å¯_ ïã5åÏTóŠb7‡Ž’Ï$±Ç¨k"J¾Qõº!þÐʵÉVVüý-8üXÉE©Àߊxàû×õÌSº7ä§¥F7?†”@” <¡a×±9bÎbÔ\6ü˜µ5Û€)I(ø‚PµjU¾ Q7Åø’ÆãOÁu’B骥w§QrIy“ô³sÏ-Ÿ›¿Ë ~•R¥‰Ìõ ß=OѰ ðó˜¦Õ#ޝ:®ÕrËÈê'KÄ›¤Ô¬ô¶Ö±—Ó‡“ÿgðs¨šâ|ɉ:a™sYæcì ®?ÆÊ%ãM’¶^ï#!\‹|™Q¤Gà‘^ hRàÝ܇yâº| òtwúóž¹q*@‡yQch:n¨Kí=Oÿ>²Àsà2§Ø9l{Ã3Œv¦þ?EQ^Ä6.›>@§-‘—&Ûó=ºïñ !KC7\!jßëJuûÄàj©WÖ’@?ø]–½7EM £»d¯/Aüñ”eÃtìbo9ç:ÜòͱþMwu¥/xH$'ÍÉ[$̨OU=5Ç2€rXKÓ>2ÿWêPŸk&ؤS”íÍhÚFn-ä¼½8-Ž¢Z\¤k’-½¤lÛÅÐÑ:ì6½˜o’½®ä»™¼¦¬ûÑôMâI _Wýg ú°EþyÎ —ÿFˆ2 à‹Šƒ=-7Ô?rÛëìrv±EÝéAgd‚ž>ó?×Ò5 z¤ÿ¨:KhÚ ëÀ—t1èIVcG9]~9ÞVïSÈEÈJáœeʵ #%lE’ï›l ¥ëSÔa¢ð'q¼$iïä̇–BJžäÜ»–¾ååÙöíë¡_m’šÅnUGšL ÏZY÷88 šü‘}û™ô9€Dÿüó ª_T 97DnñM“y³KôXvQ¿ÎLúc°j'h?f3¸ÒìIzw$·å;næR¼%Àrø)š=çI=’Ux‹'çÔià‡.eD=Ú-1õin-s…µ ÇÖòÜûôËôt6A§*MDz–vS.«YšîÀy^HV·–ìNFAò ª_Ê©5ÍŸžÆw5²£RÒ@Á#ˆ€@Á#ˆ€@ÁŽæÝÿ¹}vk¦ÿ¸úþÙÉUô?vntö8&£ÈÏ\¹×WîîÞaìi©¡Aj|MJhÿË­ í 7ÕÊп®û±'…|Q—/Yoݦxxmö©¬ÍOÉ…"çðg¥'ÿ»¨{»åþ °µ¼6…ä ¾0| ¬5 yFòÛÞké³3ÞϘÐE×ê½¼"ÛÜm`àá@I7á,5r‡ËiX¹žà‰Kä·ém‡éµôÖþèþËyïŒè]*{^pë>çòÙÄÓo:nÚÕ‚ýõ³‹G²óìn^wÍÃ'FìÓ:î™ü”Ù‰rØuϸd§2þ Ïu¯;̬£ÇØWízeëi£ÛÐÖýÁK‰Ò:´ÛîÝgqë‹oø»ÛÔõŽ~’¹ùnÕ)-ÙäOgýtº‡è?óü1ÉÎÒß2óßDÅýŒ^®ùb®¨çâÀ­’צ°"@¡@ÁãzÞ<Ñ‚r媖ó^›ïtký¨J[0ÿ!w[ óRZz†—9sE–SûÊzdÞ£¼IΈü÷|µIžÃÆWMJ?¸âßFG6¤G¤‹Ó7ŒžÖpO„|«ò†Ÿ“3>\˜Ñq{)Ÿâå=ٽȯ+ŸÊí·~Uf¿ z6—ú>]â¸9£Õ…ëÌ«òÅ7.´šq³ZFÂÕW)&¾—l&N™Û¼ñÿ–ößÙ‡îÙ§üç´¬—‡ÆÞz•%++–ÌirÐÉJ=>÷Ç ^’~UÙ*ym )(øÂ eî7»%ùÜçÉ,ÒŒÌ#ü§~Ãf0w•]šË.D®Heärf#u֙ƬReúqfaÀ.2›sd§Àçˆ=ûxã—Ë3ç?öÅvŸí–C¯ð<òëºÊçg¿·_ö«¦k]s“ñå§’—å­62jgѽ¹±•»Ÿsûj†0å_²¼ê6SÑhíuöAýødÖhäév¤Å‘ûž¿¶A¿ÕÌ—”/¦Â*ym ) (xM¦ø~ñzØJ൦€‚G¡ ‚—{6#ºösZÃnÁЩ›Øç’¶['ÖL溺º +"ˆr899ñ]Ž&ž#9˜ªÕ˜ÇZk×4£ð?&9EªU‡WQ ¥mzB·0ø'f*j<ˆSjõõ]WÎP‡Ÿ"#ñú¯+:pÕ >$jÖ?}ï}ãúÏÒY£¢G3#& Wñ&11122’·6Rú0¹wÙ,èœISñž#ÅÎÑ0òaf™J"—ž‰mú¤»ÿdÊ aÿHÔ&øŸÍXzpã­+³] S-ÜÌ{ngÖf¨£Ÿ1 «?î(>k5ªÇª¿YÃé8ä*ÞØÙÙɯŠh*>pí4\:œíiØÜËšÌ)Úmô—´;æ0dçä¤QŽTH8^Ò«Š ‡å¤Î¡[†¯vIÅ›JÞc*œ –Ì@4•˜§pùß@FšÄS·9<¹ ªBÏ1ÐÃ\Ü`Ó<Q-j<ƒ\%ùô[oTšôƒ? ¢ÁžïÍp_2Ø:ÀË0Æh×úŒ‡Ñ¿2R¼¨Uðr­;õjLúA> ÑnùÁÑ?˜ëm‚…ij=UCïqÐo¬;«˜Q3j<{†·?Üáí0ÿf‰‡ÿ3„gøÊë—pxøü å,à#Û½A[zŒ†þSa}ö˜6H G­‚çÎðãü™qŽˆÚùÉH±r Ní‚ë§œõ[ÂàŸá§MÌ„h8j|¢ÿ©ôzé^ çù.§üõzuÄ3|ñ^Ë%×á2\Ü`Ä\X¼OÁ‰”.Ô*x³½˜îN¾WGÅDD$'×28)é”RB£60f!^‡ µ ^ŽÔ'¿?³›S߀ïÏ&%dÃS»1Z{")çð4…Š7CÔcØ2—¹&—ad `ÊïÌ„ ,%EðuçÔçûÑ1†¤ÄÔÌwƵª?©PñFpÜñ‡-s˜WÙ2j:ÂäU°öL¶ArC­‚O íg±§§1þ›þÌiÓ·ÎØzu~jE>–WTÅ›'Á°i&<˜íiܦ­ƒ=·³=R`Ô*xƒú«*ìjXcñ{j;ý®¹}CíBàó;Øø³ÂrG·vH+® ˆÒ¨Uðé6þìé+ÛÂËuõïݬ‹ÿé@'àðøk)ˆ³$‹†ÆÌ6>!GŠõ ´2ÓÈO>íó«G¯ 0ˆ fõÖN…ð»ŒmZ’>C×á0u5 n#MaÂä'™×é$Òˆ«Ôù¿—ž´ŽyæÇ;/ ›\YáwŒsnFwùf§j¼Iëþ&|Ÿ&‘œë§Áv°7Žv½aæŸÐ_Ú§"$<~lÈw}ñ—.õ©WÕz=x|„œÀßé: âÏŽËøq§Ë“}6]çv7jàOÝ6®¬BQ”çÚ%Înp8&j`K‡"ÛÇ¡GA;}E«Ò²!hyç[;Ç··£ÇÂSßV;¨YðÉw6½¨7½¾†<¬‹¸+=™®‘,í .´´ÁsÌ÷b&)Ü|’y¸ov‚´™Ew¶£7ÇáÑôpIRŸÍti[ ä–¬÷½{ÒaVn iz¡lñ¨UðÆÍ¦çóò]ž¯í—…ìkrèý Ö|=ûëÿ¬¦øTêœ#5=ó]Bò»ÏÉ _R‰ñ))åƒèküç/z:Ú!‘o?%¦ç›_ZÕ¥®>Œ‘­ÕÎÑú?¹ÅÖõ,mCývfùÓÛÙ“šU¾¼¡Ýý‚–;耣Q¥‡:ï˜|»¿Âîßj[–÷Q¶nµ ¦¯>$ÉÍ DÉ©ZZPÖİœ™¡¶–Vc=}r¦LÇ ÜÜÜÄ@G[KWGÛ̈©{d §cl(y‚ÉeÈ—”´Œ¯iÙ#m‘’2ó$æ ósRªX,&ÿ Ö“’•%&ÿb¤g0¯9m+›¿|+’å¤*š‘ ²E.•\nÔ·­T׺BC»Êµ©òÄ62PZ ÛçKºÈÉ„`aI~ñ§B˜£˜{½AÞ£ÃÚ-¢éEà{Q¤ðh}Î&“#Ò.¼Ä_ø¦ýØ©›fdºiŒ;"Ÿó;PºJ‘ÙuäÑãÁO«dÅöí2dà_Õ«S“aõd¸z‚©Äò0€ñtÁÔT›¸‚ŸÑ(È]c 2ñ ¹#»p÷Å£¨wäØñèå»°˜÷܃öGaK˜°œïá‘ù¾Mm*©ö€»™îúìÿ]@MöòøóÚ²¾ ß›×êI_L¾Þ¦jö濇ß<™ªæ„*Öð&†Í‚{˜ Aò€Ü#©W«ÚüUwbéýôªübõÞËèÞË$IrÏól<Ï3.£æ²ÑSYL|ƨà–z¯{ÿÅ Ö0;‘};÷ý\< ¿Ÿä;‘¢^Áû[^]†.XuíÀ‹eêò“ ¾Go¢VÁ§…/þÒ'$„òƒö3ÌÇ…§í´×çgAD…¨Uðúö[˜nnØZ?î¬ÀOFDŨUð©×DÙÏ*ði],ºiYo=½uˆWžoùê||m‹|g^ˆü,˜.nüÞç^ñA|Q«àÍ;±X sWÅ·üŠ7‚ä‹Z Hñ¢VÁË-‡ H1 VÁË-‡ H1 VÁªµ‚ J£VÁªµ‚ J£VÁCzËû|‚",áõÀÖN.îw©Æ‰1¶ü\‚¨õ ^»¢±±®ŽŽ–X[ǸJyëV'ØQü\2‡¶¬u\"oýݳ¾U„™KxàQ~A¾…z¯Ó©ÎÛ¶Ž5¹…‹‰†}F)¤+ sàÖ‹#–h‹ü'Þ ÝÀç’:±OøoÜ`šÜ%''‡……ñÖAeÐÒœît ˆZŸú¨ìÊ€§¦Ç6ßVûóÑ«øß.ûg™»1¦¬ëhݺ5™988H}¢TÛ¾$ VÁg¦êedš¸l³'¶Å€üdATŒZ_¸^kQµ ßÃ#Hñ¢VÁã{x)^Ô*x€ÈoĶ/žô²/«æo‚ B@Í2{óöm¦¶®¾NÐ#¢4j|¯é«×Ù= QáëŽNïÝ„ŸŒ ˆJQ›àžÙ6|ÚÆ!K·Íîe¹"äy!ÉŠ.wê&ö¹¤íÆU¼ fÜ"ÑÕ«W³"¹™™©£ƒu „¹yÁ—šÚïØcâƒ9›k$› P Ÿ¡y‡5µ)OÇØ0¶€“ÓùMPPP‹-r"¹ÿ¨‚ÀŽVTŠP›àsR µ3h_y*ßוüüG¾KÃ)A‚W†Ï7—×ë¿mÒ¾û¿v¨ÄO:âñíë\Nk~ó@oI££Ã½ø6Cb'=ª]çÇáûîO3ÜÁýœ&ç04ýVJ?qÜ}š¦ûÕ ¡‡øiBGkÇÕ§±ÇÇÜ}+mtDç0¤ÍΠú}‚¢¢z»n{'ù9M„tž¡é?°R"øC¡Ì(|Ç5<E‡U¿ÝVÓ­SÙ†$IèyùœÌ¯]™Ã-Ê~N9 Í¥”A‚€‚G‚/*Üít÷¿7ÊF±ܹ¡¿¿Å÷uÚ-º<ár-},™§n¼-°‹»Zä¹ë§{†ÿ£ë¹hˆó­½Ó^vØ4¼zAÇüÊïûþHñ‚‚/2ô댲Ñé>ãÂÙ]8‡Döœ4ðŽ~”tù›^  ˜¤ ÏíghסÔÛヴú¾6°QŸc÷u «Iÿ÷v›-‹n¿ ·-&ø‡é@¦c¯Ã÷UìÓ‘U»Œ¬wÃΈ÷÷²xuv;t—ôîÝ_nkÇÜ—èrDï–—c*5·³­ ùHpÊô¨{å7Çdõ=:­fï¨{’oÛ0ÛN#·©Ã-Ç\6ÙÝ¥\öÞ9Ù幩ݚzq_ ¨ |‘¢MÔ~pTùJ)‘`ç Æu˜dÃ2-l%2&˜š1çØŒÔtC-01áηڦ¢l²³•5e®Y™Y2§Ú©)iäCÏÌTæRÜ‹yǵM<{ÜÛÅ.dt_öèìÿ*K’@,&3ÃÒE[aSâæ:º²¢É5ˆÐ–µ|äoêè‹{̇È׺âå@ñƒ‚/*dÒCöž"ç1l¼”œv]¹TéP\Ò¾žŒM 9Næ]÷2õ… ×üg´2"ÇÐÓÙOгÃ’$[«pl óQ©Ç>ÙŽx[ãøO¢v‚îÙÉÇQöÜ_¥ÿ1}v› ßVqS;e™wa÷Î}‡jymJš¹«|‰b                   BWÌö2ˆ ˆÀ3<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚<‚|Q‘ñú´M³‰²Åýb~¨¨#—ž;îåF»šóý ò£vÑ´wNd,N®Kõ¼•۾Ɏ.ÔÒÇM6Ògú3Ëyl<{…œßÑPðEÅĶñú©Ž>±S­´s™JGlågRsWš(1ÇâÛ„l_Á¹øv‡Ñ?æ8LxÓ4ó!âû _T è`æ9uþ(ŸÕåôÀ°ÁÿèƹËÝnq` c•ëA‡n¿4§ÍÈý/¸ü~¸¡dåÌw”u#‰ G³JŒ£(Šù´ŸF_žÃ?Çr‹až”W7 ü)ô¯.åâþîÚñ漈me<2Öñgßœü’žnKÉÖâ]HÏðÀß»”󳜧ÇyDò‹‚,úOåî£ßž Ͼ:0—»ÈŒ§¬s+Vé»íîŸ= ’G¶/DyPðEEçáç}·»ÚS4¸Iýg~Æ™±‹3bhš\Ù·¤¨»_·wúý:ý;“Yüñ”eÃÍ ¼N%š;µô¦¨[‰Ð…1éhšÖ…LÊ:&c޵$«"æ®Î =ºÐÞ è›KV<\pÕ%;OF,QûKšÖx¾µ³S·Ýd_yÞJd“ËÞOMk6ïóôˆCɽ£w_—M÷ŽºWŽÚÙ¥õÙÜ7t¨wãé—^Ìv0 ¶ EÑssÎ`:,Ù#[AœÉ}þ{ntª±Iê~üEjÁ·òP¹=ï_4d¾¢¬›?‰¥Ëh3KåÊCÒ;0® p7 @›=wmzNokÒsaÀËñ¶z/Oi0"6{õ¤ÿV\2cOòYõ(«ät10½Ï®›q ® €¼ZòàèÑþözA'æt)ÃØ (˜M>?¸ìË'æMν;ß}x€¢jx¿^î^U§§#ì<ç6Ü2þÊ~n#]Ow5×{ýçéâ ý®o'Ûè¾äAT ¾hЩæ;¿u]+ö¦—åâ³8ÐÕZà¤mÅÝ WîßÌFõª8ÈÅv©$‹±,3˜›ØÉ=3€ÏƒÄ>Íȧ5·®óbù6Zàá@Œ Û™’süZø`c¿x—B]«u®åm¥ˆ*ÈéòØ»Aø ûf?¸Óן¼õ‹m j.hÙ‹æQåÂ_êµog»ÀÔ™ÜÛ%ž1>·Ú¶¤¸·¢Ÿ+HÙÎåAÁŽÐÙoå$L8ù‚¹Ä—Òfýz½Ü²ì©8˜Ëîá¥(>“‡ìÇòÒU$‹¿…пegbî6ްÉv° òz4HÑ#݈”<6ž3ƒY‹e’,zV’ïÌ>~#Ÿµ¦^ §Ê­"ŸGJAò *_jq§˜gïå»om pA|©…Ò.6x’ A A A A A ¾| œöÙé®j÷‹mÝÍ=N…mìjùóñ_¹ÛÕÙú«îîÞal‘Ÿ¸r E$‹,ƒÄÈJÛÞÇbÂÙ l-= ¸;UáÝ¿ñ ø5ä˜;?!WòúRÿǘW¾ Þþ#?CNòÚRPð…àá¡@'i…÷ûà÷‰:.ã|’àFvõr «m½:îÃo~…Ö&Ì’{רäп#éWë ßvãÙƒê†à³Ø}Ü÷t*ú{r¿~î`á¿J–'½ eû Wú†œéêàöržƒÄ3Ò‘•‹T9캇dùGÚÿ.§n_ô>ühñ’¾§—þ¦±]ÓãÆ¶Pª´Œ•åÖ­ƒ_2À]Wä·qÏñ5k½Üw® ý?w—¿£Glü­õ•uÃÖ_Zw%|P¸µeœÇfŸ>[‚6÷¶ÌþžÒB—·n\ùál€³“Òü6î?¿fÅQn•¼6%ÝR Pð…àЕÄzéYÏöŒ°·?ê³!ñh˜êäeKÊ*Éz…}óݸ4§ë”^ó¤Î̯A±4¤…?ô8HÏÕîæò¿sW†¿ö8L/Ö­g£xvM /Ës¡{ƒ ×ï ¨AÜ#üaëÜ<(õä—۲ü’Öïé ¾áqÌAAdžÄÐ_‚fßfOøcÏ?7¯õé?W…MÖÒqæ›N¿|wÓJï›+ú:ô·åFì¦gjqÛ4ï·…ž·“\ ÛòìïéÛÎÃAòÏqYyS¡¾í2;®¦'m`WY‘û¦zc…_þxNæM½(‚å8õ‹žaž¶sg:û’þ›ÈrÊ£gÁÌSh:äó\ÀJHô¯jî@ìÚFÙ¹˜:´òyV9ü·`òœ÷­–mëÁæ0i•íjŸ½&·®|*—_¿ª,CpSãš­džåCÖýõfcUûó•V뙳\ê;€ÔÆNÃÿØ4;C,†Ô8æûpԭµÐSüž"?Y#üÞõCŸ±òIõ«JWÉkSH!AÁ‚€_ZÕZqã徃í§tµ&ŽOÉŠ9¼ÇfB¿qyØüõ•Á¼ÍáÍ·»±lÖß'äìè=+¯t¥œHtO^ŽkQÿÉ­DÅ<'ZÏ»cºgËAÇš£ ×¸ðè$—±Ùž¡ØÊ­›–Úc„â¾aÅ,ã×_Ó÷ ™ÖôXoÖ‘ôpú-z„±bööJ‚VS¯r;5&²n/øú¸ù¬µ ïÞ%Û4k{ãôSW×rÍ»î½û¯cöæä¿çAI3ßÏ1!IC悹CÎ$†¼6…|!pY{“Ì-¦ì#óŽ3öÔ³LB^6‹ä‰0uN%5Ne‹,CvN9¤NýCSœÈGÈYOÆù¿ÈÜÙüO0︽1-é8¦ ù<{G3«=zÎh{z=2s÷0(®+ŸJ¾ž+·_ø‡Ìæì]ù@rf5=9”³¬G"§â]ÇæéBÈÑij“é¯M3ÍsVô¬Mæwýg»MGþ{rþ²Ö/ýÊ9“¸UòÚR(PðHþ¼:¿¼ù˜m­9Ë^Å#š ^ƒ)¶S\µçÓô|¾W†â R’AÁ#ˆ€@Á#ˆ€@Á#ˆ€(é‚ñ_ç<2Žõžïì<÷êù–í6,¶<ÛaßŶï…z÷âçFä›”tÁ[w˜élÊÔ'[ ¦š´8~-Ôݨœ}øaFð!!!éééÄ‹ÅZZlM,Q:::M›6å{5œ’.x9Ä«'ÎñÛ¶z–ó²b*©Ö¸±d”’€€¹áD9ž?Îwi>#ø™ ,„M§_<ÚW—¢¨qGBù9É <÷¶yÝ#ZV郦WË¥#RP4@ð‚¨ <‚<‚5 ^ì³kùê­ûŸÇ'•©Zg䄟gíŽoÕ¤HQ“àÿÛr‹šì¹ÀÍsÌ~nÓ›6ÓÛ3=) R$¨Iðfí&wæûì»MWè‡AU£&Á³ìëÙÐýÔƒ 5-w<‹j^{TvÇ ‚ êü©gµ†ke\2h¨­gcŒ½”!H‘£NÁo\Y…¢ì.†GÈ¿ßÑ­üd)f’Áï œßÏ’%kËZð÷~ G‚·ì³™î³™5;ŒÆqâ'ê1øî¿”À,–³€Oñ’¤ÚšºV’Ë[:P§à‡Öl°÷ù#¼”GŠ{WÁg/üw ÄYÌ¢¾¤¥J’Z÷€.ƒÀ¥»,oÂóç(xUR»JET;R„deÁ³pÖ î^á'™–סÐu8T¯ËO*Õ¨Sð“–´’ ÉàLÓÅÔ%#Rj!7áÁ¹¿!2GcÊjÕ¡ÛHFáe+ò“„„:?}úš¦ùÞx´ìtèóÍåõúo›´ïþdÜñk‡ÒwÁ…’÷¯àÜ?à»âãøIÍàÇaÐyÈ Ð#xÔ)øuË*í»õrXKÛoÔ¨=|îgÓ¡ÍÒ™ÁäèàÖjÎqgÀÍßA®Ç›ÔÔÔÈÈHÞêH)C÷ý+ó'ͯŸÐIüÄKJvl#réõ¥qoÜ+…ÅÂÀ>É+]¨SðUûìÎ÷ñY½o3f ÀëDftFCQÌk\ùoììì$«!¥†WQpvó¤{.O›žà6 šwä–ŒÙI…`7ª#ñ¿mA–;)è3ÜwÓ›ÖyÖ¥X/R”&z^»ï½•œÁÏ”ÞÑŒÂÏxA¿–´ïË܇7i§àG ƒšoÖnb§¬³;—®Þv 2>ɼrÍaãgÌŸ{]z®Ç›ÞGBÈüÁ‰þÌØe¬” ’“àÌn8ý¼‘\µIàÞc44l£àG”@M‚gÐî>n!™øn¤Ô#ÃÅÃpj„ßå'¹¸A÷Ñà”£e¢"Ô(xDH„Þ‚“;àê ¾¿¾3ô ˜Ç4H1 NÁ»;¸Ë†CìPo”ÿ㽠ɈFóþü»“9“+vy,,¡ïè1 Uûˆ )j¼8ÑÒÒ^ «xÓz¶bDùï_ðÞO‚œºzà6zë: ~D¨IðZfq¨rƒ”t?Á±Íp|+¤$+ø¶÷ÉЪ›‚)¨Ið,؆Fò8o„›çœ†Ðg ˜ e]qµä£NÁcÃ…CphÄD(8íêÃYо·‚)Ù¨SðØFÉ%3ƒ¹P?°–_¿\¨wÂHÑLÔ)xì£d‘š ‡6ÂÑMš’ílÐ ê9Á ŸÀ¬l¶ÑXÔ)øxŸÉÛ¾Ž|¸`õŽ£ƒé‰5ዯ_`ÿjæÁ9¥s84ƒè2úOa°#¥ u ~æÜWû9¹/€Š ÜaÈ0ðÜÏÏéipx\i왼NcFífå`ølè3´uøù‘R„:¿~­õ¼búÕfÝ~2¢ZNï¯eÌ‹4»ùˆñ˜–‘ó˜§ëZßh Œ”*Ô)øJ®›~#C“Y ~"¢ }a×bHùoc%}C¦k§Þž £ÎÐ#êBQw§¨Àì¥|º¸z{iE“‘[§¼?^{¼ù&ob`Ç|¸v*ÛS¯´üFÏÇoˆ:ï-WÙ®}Ûer)¹ðç¢+Ñq1][/z›õP¾ÇD‘M°w¥ä¶¼33'"÷\ ¶ŠùA£NÁË‘þáU8ß§HE“ô¬¬ŒÔ2U_Ç09e=Þq]\¥¤¤„……ɯRê1޼oq|“QLXrÍÆÆÏ™þÒ*Y¾í÷SR}IޝûŸ¨¤$Åf?¥‚"x½üëÕ”OkCzš¬ëÎÝ¢…äö? ÀÁAg3rß½ÀÓû–¶±s'Ø|ôôõ¬r#ßvq¥bïá¼Âh×=ü«Sðã›ôÚqO®G)àM4ü>‘©'£ë˜ºG,«ÆüÀwågwæÄ^¹ÓÈ ID̉C–ì­Ûs ñ ³c%«×ÒTq…¤3GQ3sâ~¼ƒÞMçº{×r!÷ i9P§à?¼½#ë—>ßÖr%šÐ@X5^¿dnËÉ¥;™{,„!?ó³!¥š9[ò]ysè¿wÝzV¢}þj:¸+‘áÁEsHk|e”ÓËLrº‘½ŸˆJ0kÖfÓÝÃîUR-2”øS"ÀÎudêÀÞ`€¶Öd¯“ê¼|k9äêIøgSO†ÃÈ~ÚVçù.½\FQ'ýûhœ€6sè²êRÛÇ®ý<³GãÃÏÊÖ2ªææQÔ¸*-jV¹5FK)jG…§¶7à:€¥<×.q¶ƒ®{o liwCdû8ô279P§à5•c›açBÈÊ”,ZXÂÜí8Â)’æÌ³:`ŸØ‘ùÌidúƒK‘>¢×¹ös¶Xw&dûèž\Øß“;)Z¹-¤iÉà«rÀôÜŠ”Úù NÁÿÚmÀŠsGùÞ’ ¹Pÿk13ƒŒºNŒÎñ!R4Ñ‹:u ~úT›‹c;9Z•ØÕ²ÒRRÿ˜kä»G扪ÕöT«oÒµ¾¤&î{,J%§’‰,~JLù”$×ÁsLô“¾¦É«U0{õ!Q¶X‹*ÿŒ–¾¢hV«Êgod‹­êZÞ|'[lÛÀêÚ#i¯Uí­¯>Ì\½£õr‹mê[]ÍÎÌÛ”cJ_¼“-6®Y%þó—JeŒ+–1*gjHU0#_»Jy‹²&d±jòµ‰!Ëhê¼E×5üþ’6Õæ\èÚnù<+¿û.¶~-Ô»?‘Þ{Qò»ÏÉï˜éƒè+ñ|LLyó)éí§ä÷ Éß–%¡,¤îÉòé-~ªMnÌYÏNíFS´:§ƒ¼x¡8Xb ÑÑÑ276¨cYžY43ÔÖÒªRÎ$=3Ë̈yuJ<†zºF’n¡É¢Ž¶Vf–XOG۔͠¯§“–.½‰0ÐÓžÒ«™Ü¢ÎàÙ­ÈåH׿vé™™ÜñEWG»¶eyrTú’’ž–‘YÎÔ¨®MÅ”´ –%[‘ÿé ý!‘~/9‘ïLV”¥rßJ¶˜+e­*™ÛT.CæÖæ6el«”!srágEŠu  }î×}7*ô®«³êÜa³o6YwD'ô¯š£ÿ ;>2Âݨœ}øaFð!!!\7­—\6/+ †ú:åL Ê›Ô4üºäÕÁ†"i›s²ñF}Ã:Ž37ѯm¨wËX¯Œq¾õŠÞ?H¡?ézfò‹¼Eœö^”ú†4™£ç×÷¢”7Ÿ“ãRâ¾¾ú˜,;Äççä»r—'ߦrY#ëŠ&6f¶dªdjkajcAŽrêýe2$$(ŽºStHjãHªßíïç<ôx`âÍ%å'7úê7üZÓ Óë´ìwàÖñaü :ÿ­+[·úíÆíçî[ÎnÓrÞõ[yÖ[æ¼Q1äÉU÷úCª›1ççÓªœ¿qãÆœ1«{LóFõɹ”œÉ)™Œ Vº¸HXá‘Ý aÌ"î¥ZcvB”!#3+&^õæst|BÔëbD½aæ¯?J:zûù+™n?¯¸^.Ô¬VŽ\ª0U¾žm%{« äj‚ŸIu¨¸Ç‘ߎS×–ÎÝ;uOðÜ.ˆS8×» ÍÂnQ<¤o«³•(Û¼Õ"öñÞP³aƒaú¡-5·¦Ã0å)˜$Š]íLéÕaV¢A…´¬>=Íš¢øEô‹_JQÔ¸#¡¼ ½šVuq±ç9¿Ed(,÷È Q[&ýƌrCQ£jY2ñrƒž¿úôŒþøŒþDŒˆØdNŽ\*±Ét.ø[O¤ÉQÞÁªb=ÛŠõl*±óŠÕ«h×* c¾GÆeæbáßOš^ñÖ{`|Ö‘cº©™Ž½<Ü'7änÊ“£å‡îKO_I>Çô’ •~x¢NÁϾ$…żý>"îããèw±¹K†¯©÷ž¿!9Œ ôT¯ä`]¡¾-™W$sk‹~ŽïæÂ!Ø0C2*°•áì…ºÍò 9˜¸Ô³$?AåíÚÏüû·%²%CïÛQS¡žîÔ—3Ä´æ!-W3^üSì´[Ù‹ß:¿fi¥É;.mÛ>äÄŠ„Ñÿã'g÷Â3³G>¶ªó½°#g¤D Quþ ÿWÚR6Z·Žåû¾ u ¾Zß[Xéÿ¢ükýç‡ËÂn‹(œèœ¨AEÔ)xÕò¶I—Ê3×Ak~‚ RJàŸ÷žQÕŽ ßD½‚/DÕZA”G‚/TÕZA”G‚/TÕZA”G‚/TÕZA”G‚/Öªµ‚¨WðCk6Øûü‘ò-þ) ê|í* ¥ö¹-m~¸èÑì=Þ ò Ô)øIKZ¼_úôçÛzßžp<Ü6×oRSS##¿ÕRA EñõxSŒ¨SðÓ§?¡ Ü5ýàvË™Oç!Ö¹÷x`g‡}È"*CågJêüºe•öÝz9¬¥mAz­eF­`»þj~gv®=Þ ’/ê|Õ>;‡ó}ß„koÔA¹oDÀ¨Sð‚3(xê¼dP{†¬6 Æ_´‹—AÕ¢NÁË¡jGb@‚/¢áòÉ u AbF½‚Ç0¤XQ§à± )fÔ)xìAŠu ;À@bF‚ìAŠu þåž~À´ß‡·º}tÉš/ÃZžòóé{…j! H¡P§àÿ·.ë`h;b´² Ÿs}Ù%tRŸ½‚äŠ:ÿdžê¿¼¹dPóÛÇ–×ùsÓ³cêŒpãg’òáÆZÇf<2ks½þÛ&í»ÿk‡JüL‚|u ¾bçõ+XÃyàgòÑþ¤bºZÿBÓ¿ôm82ÚøMÓn­æÀÍßù™ù&ê|zì™:-'¤J–òéâŠùþòô+{¶·@lCQ ç ⺸JII “Ï Ê”Ä _ÊP§à'÷Üò‚Žì¶ëí9O«FÝÿá'+òöüÜÿ·wpMcÀ_[ʦlAÜ=p°QÁ­œ¨çÀ î‰ ÷¨§ž{qwîó'@\¸Q7œˆ¢ìÕÒþ“–QÂ8(4¿¯ý`òò’–’×_^šqÏz¡›÷³åóäìäèz¿ÊÊmmme¡¡¡3|\⪌™ifSQ»óñžZíõEB1kÈ©Ò~™›t ûHêç½#= O€ÿö3üœKûÛ8 ¾¸¢‘©©éÆð—ÌÉPÖ~fƒŸÙuÌ…°ƒÔ@l¬/s”ƒŸÙàÇ­~îþÛ¶«}ÉUkàûýÌoÔaE;f”£ŸÙàq><Àö3<·øÁ~fƒÇùð?ØÏlð8àû™ ^˜ƒóá~¨ŸÙà•Dÿz·ï`Ü{åÂaØ[ð#üÌÏQ©ºíÜ=B$ø´Üð©ïÓÀÌP¦~fƒÆßíÙÁÓzÜö¹[¯ŒcN€²÷3<ÑÿåØí¼’r„„‡Ë[”¯ŸÙàù<"ÉŽ_8vè©„zÇö¬4æ1+@ÙúI >'q¶wï Iõ¶nZuÿŠÊõ¨UÌ % ôlæºë\K¯+»1§@©~Rƒ'D"‘p¸\÷«Oœ9ü¤†§šnƒ'û ¡|dd¤ìŠ7III!!!ŒÊP”X,ærqóËÎήS§³´’ûI ž§³èϳÔÿ’¬x+çL‡ÞSíZn¤üEßL+“ú™©i"µ¶¶– „††:::æWƒ’àúBÌ¢Êï'5ø<£¹[ƒæéN;òE;íü7455õ9ð9þËOnð¾x½ž«l¬?³”sssfoÔRÈ7ªÂ4øïV­Z5foÔ21Éí6*ÅiððŸ£Á‹;7ªöTËñIøA|—Ïr{ƒsÇ쌩¶UvǞѪ[¸‡ÌŽá­æ„é¼yp´[îê´¿È@¥_Á¢Á§ßêwþmOá_‘¤™s"Ëi5óÒÃÁ}ë‡Ì²;ö$H¢¸‡-ëá›QÁ±[TIúÜÕ)!o½Ê¨ü+˜B4xQ²@•«¤Z%QHH%ÿ{”޲ õsü;‘2‘Þ±ça0ê³TÖ»¨áÇ´ãê9å®NiÉ•Sˆ¯ÙäÏ?×ì°Râ½(Yk¶ƒÃ¼k!ºæ {«…ÈîØ3Ÿ¿„1Àœ‡´ZpÕ>±HY«ÉŸ»¥«Ó -£ÿ.‡ßmí‘wF^³—µÃÍÇBúíù×µ[Ó[ߌÓ{{‡ˆݬªÅ¶»w1€OHlðÒCÖß>§“¹gƒ.IÙwÖFÅ6T¡—ôêóGlõÙ>×9*$ãS­}ccå[šp‹Õ¹Õ,ÄÚùEòK“•\nó~¤ôź×ÎNÈ!úÜÌ^ö–·I³¨ë”“ƒ7º°dÎ ë] w7+´(qRˆ µûDîmžnjêi/ t³—”°¨îUlLk¼'ªKNGl\üç”+4ørÃÑZ®· A¼¢Ø{h=«356vAçê̓^¿¢? ’ Ivêé[%’cÓûÈ}Nã?Œõ‹?Þ?^¼›d¿?÷"6wNáó©Y#bcçÆêõ¯ÆA{=#Wõ±yÂ%9æÕzç‰ãå—f$½ÚÍöК­ófáØS/rƒ«õ¾ëÏy$§q·=÷wÄÙ,ˆõ§>žH„wÁp×ò‹:ÓÕå|T¬ìº%²WBסÕ¦„E¹ú´ÙywEºÿÓ Á—#ÝÖ+Fu™µq3s!n4©ŸªêÕ Š4[Hÿ¼ZT­pßA¦¾ôøŸT»T‘;1;#z°›;õ¿±ÛàsiÅùöݯÑþŸ×‚~©Ô{òKs“&«…Þç¼É¹Bâ,GÓÿóêEŸ dˆSmÕüIÃ…ô¢ö€üJrJ\”À­ÊžŽ¦îM9óÏ+u¹9àAƒ/__vó—_ J^ÒWè"Å\Í+-RL•Ì/l­fÎkoï÷4Îz°èeA»“Ò°úóèûžUâÎ4üµCái´M_åPMˆïä·´ÉÝŽ½u7¡W€ØCCôzt¯óL:cΓšóæ,¢ð¢Üª¿äý.òW',eQ“NM–vø' Ãÿ3 Á—3¥j= þ˯Dàêgjêcl×Dµ˜ÆNS¯×¾‘é¿U»Ý;»‘[¡¶ÀÔtK—9Ç7[®Æ«º\i¶©iÀuWw®f§>mžŒ5n¤¦—[ÄÑ-º4ÕF~V'ýëù¬Í6´8sõ¬)!ýNÜðl^ó6§éãˆ@’œ·¼Â /ªÁÉ NuLÓ-½n_6}˜Ä´¶—=½÷°”EI&v¶9ï±^/W) Á—;½Ù-Ugä©:ôÿü;±yýpBdSs»ßÒÊîONçO¥Të4'6vŽl˜ÑK¯Ù}al÷…E'å ó¶^xœ_({"ù¥å«Ýiê³Ø©rª7_ææ½~Ù2å‡ -Š«{-:÷—²™t8vRnq)‹Z¹:¿üphð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"hð,‚À"J‰„Y•¶è@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à@!à¡R§<=°}_ÐÅ›ÏâREÔŠ¬SÝÊ®­Çï~-«©r˜µ¿Yr°§Åpbè.`N,ޗ̨Sú(!9ÉŽm¿R{ÄÈ_Ô™K*sÙoN.Ÿ¶d÷•W©„h5›ù¿C£ê*ËM.ý¥yåÅø’:PðPÙˆN³œFÌ:Í\ø×rÛzU4¹Ù‰±¯®Û×nÑöؾ®µvÙ¥|Ù¸ÆÆ2 ó1¦&÷–%â°‘…å$åÂ`gŸ‘qÿ7çµ2âåeùt/ªô_~*qûþÕö5tùYŸ^Ü8aôZŸ“‡n¯wÐÈ_T.ÉWÌîíàëµfíe“ÄÐ-“GûuÞã÷Ë¢°ãƒ«]“£ºÈQ̼±œvbÓCÛ‘]{Õ»~ÔËŒZ¢øã©™¿¿ J-Oµ.>ÝEÿîûÍ~r¸ˆ˜u™·!¤WsòîÆ%“¦v˜ªì¸:l_/i_¼Ølûj_ü» ßœÚÝç`ŒVëEgž ªŸ»…$|±±ƒóâ(¢b3zãE{­7§ü½l½*<ë89ÔÑçlš±ûÔ%»–:ÖÓç%½¼~tÍÌΦ©º¬ Ûùk•/Kç²ZÀÆlqW×aÚñ¦ÉF$ÙŸ^ܽvíòö‘3úòˆøÓ¹QŽƒþ—LÔë8»9XY:Ø×ªÛÐìõì.3"©‰ÌÚR_1 O‰Wð¼qýbxÊ%ï§þZ*¹ÝøÀÞ½,÷v9°'èx.R»`Æ‘HÄô ãÊ¿Øï÷%¿{BŠë‰Çó.÷s™q”µÛëK§ÇÕËýâ„£¤B,Q³Iÿ€R\eâÈÉ–Û¨ÊI‰O-£žX$¤§6ZzãÔÓúØÉ²ÿ¾{9*Mç•;zßì{`¡c€¶c& êÒʺ¶‘'ãËÈK×.Üvƒsï©ôîe»~ÖA3#g´îýa³¿·“qöó 'Ùú@däuìÚ²æ_¢>à5[O]µ²_¬77Ž®›4æÐ3úi>½ý,$‚"iù³ÜŸîÒûãÖUC›©½9·v„¿¢ˆ’Ý\?ÛoéêñøT¬äÄݙҮ‰VALæwãç÷}@Åbóù3KYº¦Ýœùög&‡Ïsp¼=ÿw?é.ú›–Nœ÷¿X¢ÔbÖŒRfýz_ò»[ èQ_[P?è©íR·Žë—·nörßÕß[Ò‡FjØÎ^ârrLÈl§öÑkÖLhc’t}Ûä‘ëÈÏ­bfmFÂcnPå:˶0HrvÛœi› ÕÑt˜·Ðá¬o˜_‹—ýü§öq¬-ÈùüêNÐú³_ðš- ;ìeVVËÁç(THX1¡ráh·úýnìŠÄ'vl?0å¯éïRéþ•’n­¦­:/:àÑX'·—¥d>(èmï—§×/ýcrÛÕñYDÅĺ³ß¡ÃŒ¤«½†Ó¦»!mÏø}E·¦ó¨êzµš·é¹õ\»ì€þ#÷-ö=Ôi_Ó‚ç•úŠYì·‡Ì}·pPó~ÏR5ª;^2Õ£®ú7u“­Vm4|ÆÎ5¬1ìºÿÚ&çü}é*–“÷Í>Gï»6»´«Qiû”Lû¾îù1b×Ê5o9÷³ˆ¨W±tî¾âÄè^MõËøcàk~wõ&~W;NnÕgŸÆo–];àUM‰gä±çEÇ·çþ˜·tVë1|s×QËÏný£¥OÞ1€ô/>3ì~› —n_û›Ã2¢lÒÌsüâKÛçµVP‡(UýíЫžŸîì_»æ¯É—ÆgJW“æíúì¾1¬µ©t€R- B*ã– ðCðt¬<|×zø2Ë™8j5;NÙÚq ³\†£V×sÑ!ÏEŒâ•·»®Ì,rØ×ͲéÒMyUä1[ú(áWí´øˆ3Sˆ¦zöÛËF]ÿ”hvØzbŠÅ$’íàeû/c–çc>u¥¿ÔoøÝ ½ÍÖrÕÝØU…ʈJµvSv´ËÿÃ%ÿ!?•ÂÓ·óYsÜg|sÉžžM¿ù;ûÍg–ç+«åT<x€Ê&ãÎ|·ßÆä(éÕwp}‚{µ/w`<@e£á°:âÍjf©B+Úï€ÿ‚€P@x„€P@x„€‡ò"Šù«£íÌG¹W8—¤ÜÝ<~ìêàiJÜM[0ÆÕ˜¾>Ì×–Hö´ÝæQøòé²ÂóÞÛZäßCåËÙÜ(2£†Y³.£Ìõj"(í|óŸF’ùêüÙlç®õT™SÊF±oò7<Ñ¿ÙÈp‡Í7ww1(ãwók_g¡»ìÐ8ZÕíº]0ë·†š%œ»ÿ ¾öU”<”QÜùC‰½Žü³Ú¶ÐMÌsÞíóófιgôMQ„/6¸9ÿ–~ãpgÁW–ëÊ/´dZrG_—ò![tRrð6}@áÊ⸽]‡M p)³Ë¶—”Ë“ÇíôŽ(·€§o{ãÎ,ü&9±{‡ÎSÙ|uãÏ¡{mŽôÿé×-ú‡Ž?ÐÍ~Ðİ¿]µ *<”*ß÷?ÍáÏsª~ÿ½Ø°©×Ü?æzÔP!$=êô‹*½jæÞòŒ¯WCO‰Ãã}}ù&¦¼zyãßiçÙ0®êJm4ò¢×Ö£óìÄï9Õêj½ýsÌÁäÙ]jiJ®/ûm6oõÉYMÔˆðŸ }ü2'­Ù\ãßÓ‹ÚýÊ.à±ô†°òÛ›—Å.'ùB?› Zþ§×ýjÊIÿÀô¡Ûkï¼8ÛªÐK*¡Žü½_Û4ù£¤„y³ò+пï};ßÍ+‡µ¬®wll‡))kîìi#¾þ{°ïûëÆØj}¸à?p莊»+ðÅ–Áëêl¸Ð¾–¦Óú³moq89ª?÷m 7ÿmÃßs;YÖwâËÉ—v{‰¢–»õ}8 `ÕoVº’÷7wŒ´ÍìïëØk_ŒÝ¨1fj—úúÜÏwwM¶·É¾à) èß%|Ô€å›Ww©­ñ^îåÿ~~u$'óÓ«–nÓ™´Õ™N÷æ’¾Úbß„¯|UŒ§(Kx(JfƒÎDÊ¿ßïÑjÂŒ‹kl©‡—¿[–Ãáp•rׯ-ÿ‘8|-ãFwfNÒ²[4·SuêÚz¤\œ3/è¢(h߸¼©Ü%ׯhǹ23|ÕÁD:]—Í~ì´_~L)W‹_ŽÀeõZÇÖ>¶6ûFLëg©järÕJ›z²äæ-©Î—(i^ùåkÙ­Þ1ÉU›4kãQŸ¿MDè<û÷ÌÁ—è_°zûùëï9í‘›G&ëÑŠ¡‡Ûïð4:úñÝÛiŸ¹É9ôÓÍ]“Ð÷Z##z‘QSïÝ‘ÞÒgȤ{äëÿžÖ®ÈË+þýÌ“‘._«J+uÛö<í;»QvÉs•ð&|Ý«(O?á£X!-lLËÖGƒ†šÓë˜$GHÔôÕ©ž·ªeçBú¾,Úô÷èÂÏo’T 4¨rµ¯,¯X8·OS®Ò –•CÀÿFÖ,| €°z ÓŒ·‰B¢C—gÇÜ‹Í.4]FøîQœP:TÒrÏ¨ÓæG±›¥Ãâä›KÜëΟIu¬‰ÜmóJ¨Sr±àyKšW®2õTJE¿žV5³6Î|—$":ô_<óõ7™Ì*é7ç 9ãp®~ޯį?ú·¶ƒç´»´¼•þ1‘1™= éNµ(õC†š!ý‡N½2Ìq©óÉ 13{RåÙQË]:ߤgU6nX…“%”P/&ùù¸EËJz?so(—‹ÃSÖ2ªß²W÷Ú{o§å”>WqoÂ×¾*€ò„€‡ò¡á°.dÅŸÓÜš¢\­Õˆ¥ÛÎyIoòRÅsÃÄ壜j…ÆdUã&îö,µ£wzmyE¥b9óìŽSKú4 'â›Øœ¿zf§jÊ„_gÜ©½··ŽnõûÉhq=±îú²ëË \×,ù_W Ó!Tk¬b;pb{]Y””´"I‹Ú=kÊïG#㥉\Åcý W*¹Å¶ú¬jaš¢êºëîNÛ˜âêÈ+éyKZ~á ,¿ÞÄ“;Î/ïÓtkXœjýÎC] 4Tå#P’2ejâÂãË,å¿)P±œvlžÏ¯SBöt!Ê:üÓ#šw9ÿ/ߴů>ó‚×´ ÿÐŽKV;ŽèRcF¼X¥ªm/ogìCK¥¡ß©µgý³Ý~-&[©J³>3VÍíQG­Ä -ñý¤wž±(¸¯ßÐÂ¥·ïÆ««¥Kž«X_ûªÊÊ GËjÈÆ‹C62Ëy.3FÌ`uybÿ*ZX´$_ÑIEKJR´&ß´ãÜÀŽs •ѸÚMGl½¹Å/‡£aáµ:È‹y©Z®Žãì '³óF‹­SHIÏ[Âò ~Í¿o~yZèH»1ܹû.D×UKþçü†É+;Ï´•;H#pÙpÉ¥`<W¿Ãö˨7d=WÏnÌßs™iž‘ë¬#‘³ |&æðMÜfp›Y0…ÆøsÈû~þçEpKš«ÄgùÊWPnð?ƒâ}Äk8nz™;¬ß Óœ N…&ÿÅ{C~6<€BÀ( <€BÀ—áÛÀជ{ŸÛ'= )'áÚêq“7†¼ÍR®æ:aͺ±v‚ä°%ý}6E~&êæŽƒo˜îjXáÎóÅ‚€ÿ>)W§Ž:Ùd¸MbrîIÄ„ðô&ï¹>™’|:Ñ£õx«s£ƒ|oô;ÿ6Ș+zЭ÷ÊÈ Ë›1/ P–ðßGËÙ—3I>Êœ@r>^˜7)rü¥?[)]Ü–¢,P¥/$¦¤UE5ù\"µ1 ðBaþöIOO‹Åššðrç´ìììªU«6h@_„*,|™½?3kÈ:Þø€y w¶¡ ÄMz[>ÿóðcg/³·;¢ëõ±*œÝ¶¶ôõ[óQy/‰å *ŽèèhêcŠY  ¾,:…W©Jûe'ÛËMåv?Ù]:h9ìȽar“Ê@!à@!àË^’¾ãê Ѻöîî®NÍ­Ô©a¬«©ÊÍNMüôþõ“û‘·BÏŸ<};Óò×áãÇyµ¬¦Š=@%IJ€ç¨˜: \L=˜_ÇHCǨZýæízúø­aN¨\Xð$i·t³àVW8åÁ_Ì>O•WñØpbuw3>³>@eÂÖ€O½2®P‡³G]$ýú$»·½C^í¨Ë'ÉÁž¶.ºJ¯*PY±5àÅ¢ÌÔBß:ê.Ýr5ÇyÁ€:|"ÉxzpóÓÚ]-Ô™ÕXM˜M^E‘èûäùýÀPßô]OúŽP‘±5àmöD?ù®sñÍ+U«?ìè\iX(>&7¿éÇC"Ð%Oî0ëäð”â<Æ1K¡‚akÀa̱‰]G}Ï,§ØDa=(–ôTòü>Õù–vÁï“—Q¤jMòæ)³Z¾_m=’“CêýBj7&u¬Hm+b^ðèÔx-Áµè+<¶|ò%ß™qK¢bÛ#È@1P]ðîÑ*¿©Ÿÿ%:$ñ#³Z>*Ý«×'ʪ¤Nc:¼©¯Õˆhà3Qq°5஫æìõì=;Ó¬[C#us:@Å"ûüŸ»t~SñIFQV!TOZœÃ¬,C¥{SWÂçÓ^· ýÓ¸:³(.¶|æã¿‡ê÷Øf]]W éAR‚´ó}WÚ @bŸ‰„.×1$‰˜•errˆS:æëýBwÁë4Aò±5à9<¾jU§NöÕßwÆ»$óÍ¥ Ó|×^ùØ"à‘ô›{q|`¯ŽGÚïXßÏüÍöþý/û\>`©wá’CÝôq<Š%ÿDÒùýLšâŸòŽªZ“þλXZºÄ©QÓ¢ó›Jq³:„‹N |¶|VÌèèp·Zë™È×dÇQ5wõÝw¹‘§í¶Ü’Ôû‡ŸÔðÚÑDOèyj°ewdœ³$¥[[¹åGDD…ÂüÑôôt.—UP* 屪oŸª¾}¢ú†~ðÒ“eå™fõTcž®›K¨[%µNSQS÷Ìjõ©j">³F¾41yZüB~°ÔÔT---f)T0l x{`l,³°LðµÍ´2?¦æm^NZB¦¦‰@µHIáwÝÖÖV~”Ê{‘Hdaa!_EܺÿýìyIïNOIÌ-·hN¢nª™¯jMÕ®¤}_º N=¨N¹>!…Æ+ƒèèhŽ¢¯ðXðôÍfömø}ãáçzŽ:¸:5³ª_Ë¼Šž¦W˜šø)îõ“қ͜º‘ZßcøÄñ]ª«}í¾tµ¦~‹öhmî—FmF4öÙ¶·™¾vF .¤P‘%%§Òü–ýü—[ÞØÜ+TS†Ã%fµ‰E3Òª;ßu›uônáçcYÀÓ7›´„z0'PoCumC³úÍÚzzO_ÍœúîQîy#\=WÿKÏüå§-€ŸI˜M…þä6ßÔÏ7ψDL—7q"÷®1+Ë›óúÄÎÔ³¡#\S›Y ‚aYÀ{$~ Q·èü–=R“èÂR"\ GôªþSH=kÒÀ†è›0+T*x¨´Äbúj-²ü¦²üåc’#"MɽPfMžKHçA¤¾ iДTo ».€BbëÊMß5n›wãhyqêݼ¢‡ok©)_?0‹>£ŒÊïGôÏøÒØ‰Ü/¡NyýŒØ¸ÐùmÑŒþIuÊX‰­_<®æ/Ó®ÊîA?Õó~þŽðÇ7ȃ0RÅœ<¼Î¬“ïa8ýux+:¿©GÝ&ôõV 0¶¼ÀíïCoÆzúð·º Ä)þš0`ö™xjB 'Vw7û¾Ëß@ b¢é§Â;5‰\9ž{¥¶¢>üK_¾MC‹>÷¬a º/^»v§|¶6˜Ô+ãúu8{ÔU@Ò¯O²ëqÛ;äÕŽº|é®û ]¿øB7PTRÝù˜Dœ¥Of1+Èh¤ô.t*ÂÙ’Föt_\ [×eƒ­/e¦¾¸úÖQ7pé–«9Î ÔáIÆÓƒ›ŸÖîjóÔþ“XL^G‘»×èî8õøø/³Űjnº«k’͈¥-±lA,íèQ(gl xA›=Ѳ/Û}×¹øæ•ªÕvôÁ°üJ =Æíéú/÷Cé ÏH+4ULj$Ò_nÑ8\R£±²'숕12+T~,¶d71¨TØðž7'3+GB ”ŠSß¾LQ©§ŽÏ1Pb1}sñÈúª/oÜ¥¨¦­é^8Õ#ÿʼnÔhH¸Ø… ØðÊ ¦^¹ññÆ®}wú1›D´°kâÜ©ÿèå·/ë"ß¡’Iˆ#wBÈËt¿üCq·I”]ŸUMƒþjügbÝ =r…ÇÖ€§)´¼lïàeÌr€ŠJ$¤oHJ9çQ·è‹Ã䣒ûAÞΨä®Ó˜4u¡/èfiKTð…±9à ÉÉJú÷Á©?ü¶dO8ø{'£oïÚ¤…sža¾ãØøÆêé÷×t÷Ž]2#fPÇ#íw¬ïgþf{ÿþ—}.ê¦ÿµw¡¶ÉJ§Ï@»qŽÜ¾DÞþC—Ô·¡fgÐ3–y+:Ë ª2§‹!àsñT´«5ë³â°®§ýØ ÎܾùB7¶óVÚ´ëÜPzËYã¾{/´ßZû¤†×Ž&zjDÏkTƒ-»#Sºµ•[~dd¤P(ÌMIIár¹ÏŸ?/¨ —š¨uCýQ˜ò»*/2'KIxü4 Ýì.Ã3,l³jYIŠî]OÊ IXgàIJJÒÐÐ`–BÃÖ€Oö´Î,%ô¥jןhýÍéNI¾8züÛÅQ±í©…$žôtyñâ$3­Ì©9D›—“–©i"(ü®[[[ËFDDˆD¢ÚµkË‚‚ˆ{Cnž§;åwBHZ2sj½_èŸjtw¼y[úØ·ª5eS8„àÒ0PqDGGSSÌR¨`Øð÷ÀØâŽEú~‚6Û¼õíRwhtºz.3noc¤f³¸aÖæ~iÔÔÆ>Ûö6ÕòØ@–åÔãÖEúÌ4™ÆŽô‰æùôªÐYÞ‚ÊrW¢cPPðÝØð$'þä„ã"[øÎÕÕ®ž±º89æÞ¹€E3wf šÐ@…YÿËq4Þ|yðf¹"=WÿKÏüå @±$¼£/ºqŽÜ¾H_Mý~ÞÁnòŒÍéN9õXrˆî£”3¶|rÈÄi1sn\-8ÚM¯¦]ï…ÿkÛì×ÖKB‡îtÕ*T WÊgr÷*¹Dn] ‰™S)On³:ĺ%åTïY? [^à²zùáö­<îΙîåÚ¸š.?;áåíÓÛ¯yÚqÏþÖHw ³ésǯŸ!ágHÜë‚róúäÍÓÜaÒ¢iáF𷡝ûPa°5à Ï¨ÓÆ; ÆùFõZ\Ñr`A °Ëó‡$"˜„¢/ÃneWÌnvUúú0ví‰;14eN¨`Xð7›a¯ÄôAìWŽÑ‡¿1î&óà:qêLê4!öíIm+ú†+• [7›a qyNïc§ºæ±r§‰«k‘ìLúÂpª;NuÊ:Ò×pÅÑ*;éGºwD » äáRfQ l xÜlF!%¼£³üZýݹy=úV+ T~Û¸ût×\ߘ9ŒÀ=0ÊYÈl xÜl¦²£ºæo«ÿ#¡'É»WÌ©*Ý»ÓZtœ7l;¤Tg|“À,ý/§÷ªo¦Ï,et͓ÇX¹ûl\7¾qRØ–‰ã5Úqf~ -Æ—pY¯OœVê2¤™²$9ÌÏÍ'º×úUÞvZ1§ýGMØõ,Ç>àq ;a,9T’|}vû!w;¯^;º•QRÄß±ël>³ÄAÀa¾˜/˜Eþe}=¶|ú­™žþFþãf3•CR½=ü4}rZVf^)—>ˆ=1žTQ£OK³s§ãWŒ¨Ìb>&¿xWòÝK-3‹ŠÒl´ä©ft'NÇuÊ¡3Uºv™vu£cîdaLÐ̬·üÕgäiAZØô1FŸð2¥g±ðXvΦ¶{»à¼Å'-|Ƙ»£.ïoBÏ¢ÝjÂþ`Ãn]f„_^ïÀ¬š§”Y¾ï4[¶¼ºõø±ÕGlü«áˆÎÍë™h«*}熔¥çÉ5ªkDŸ¨öæs*Å´6}œCGtÍÏý-ØEe†+ÿ!çH”ø<Ù§røhï}çv-?`²°[—ßÿ82©¡*ýe.ŸGè¯róp”ÕUŠK ‰0S$«Æå«)så«ðLúÝêG¾8õÍò}Øð„gÔaÅ‘ÌRøÑ¨¼Lgyè)òYÚgø¥á+‡Nı#Ñ7aNøri÷§»ôþ¸uÕ0;¸‹Lô m»ç¨º4zö¶ ¨©LHÓ9gwŸŸèÖËfמÕíüwwêݵÛÛE«Çµ1N ßîë³ò^š½tYJ*§G/ĸv3J~rrÕ8ß›©6#©bµæóÖ7kß¶û«•«F·©!~qzÕ˜y¯†ýý›©Ò·Ìò]¾sv€¯‘øÞÓz’¾£yvÞžöª5É¿/s‡µõé~¹c'ú>+8 ¾üuŒá˜‚[huœÔ1o¤ð±x\½¶k¯´• «ÔuìÎ(Ù°¶ËÄÀ[ é³®}Û‘=Ñdåú =–œ÷X"!DÃfÎÕGy# º->ß-wøfù>x(7/Ó{Úï‡Ñ'«†åÎ]H]é]Ô ì°5àÓ® w^ÑöÌÑžFø·,ÈN7¿z‚>EíC‘Ûô5q"n_œˆSâÐá«ö´g sÒ³è³Õ3³EYôí)©QªHËfKR3³…"úøšäô¬1ý¥VRZ&‡p¨ájžz’(G’’ž%[fJF¶º ?Y:J Ë*ú j™Ô€‰¾æ»„Ôü'Ê—’NWn\Ëèþ‹‚o¨g‘-JÆ¡¡YØã˜üQ†–VÕ®ÓU?%lIŸM‘Ÿ‰º¹ã Å¦»þø3ñ$’˜š)‹©”æä¤fÐéHåY–P”ž%¢â“JÄÉiÙB1•ŽTR?eáJç¨(Ç@[ýŸ˜O‰iY‰$+%¥­ð·Œ‡%Ñ’M-ü¹ŠIí¬‚dJ$*Aœ:‡9õÏrj¦~ãtÃû'IHä{²î¯üj2uMuÿ‰ýÌ(Ìg ­ö1)ï^«Eh¨)§eÐ/àkUÕ×ú7!…Yš§nºÞ?±Ÿ˜¥y>$¥QaÉ,- BQNIK¦Ê« J9´ØÁÒ,ìQ‰ÔÆÁͧÅo:P×2¼ÿâ³4Oé *ºï?w5@)ºJþÖµ¹ ¡ª¬£©¢©¦Lm@hJ‡©m-ueM5>5@maPÛô¨ª2µ¡­¡¢­¡*ÐP¦6C /¾[¾üîŸõpqûá ÿÙ’÷rú¾7úd̽ èÖ{eä…åÍJ¾RÞ½×IS÷Þ—¬ˆïæ+½ƒèܨÚÕ‡%v]›‡ÜÃ,ÍãÒÄ<ä^1S HzIôpÍ×ÖÉO”I¡­L G+LÐhp‚k(1¾ª+õ9®«Ä3ÔÖ¨e"¤:‹ÔÇ7‡Ã¡>Ö©IÔÇ7—á>ñ«èªSÝlªoÉçñ¨~.—šD¿u:*TeêóÇ¡»ªÔ羬Ï*›‘® ©Bm”Pÿò»ªð è-¿4z«"9Þÿ!Ûž£7û2E¢qJF¶¬BF–03;G¶Gäs ½ H Sc™B‘¹‘€ÚLÍȦÙ"qbÞ6 •èòë0U'K˜ñ)%wsÍXO#îS‰[fZ1KÜkPMÿÉÛÏ“¶07xû!™Z¨- Uú§¦*µ’P+ õ“Ú"¡WKM5ª\O‹.WUfëÇ È+íšw’䫾íý-öñ®AïÒÆ]\;}Þ¶sÏS¹º :ø,X<ÊÑZ‰„¯¶yôšvf•ó÷ž¿^VX»fçÄŸ×qZL—y35›Ô~§Oľ¦¯¯¿ðYŸ#½kû~ɬwbâ%QŸ,,Å·–ü:SmË¡¦ïR”ªt8)iUQM>—($D.à#""„Bé5S¥$ÂŒ¤ô‚Q†ÏɹŸzê*JJ\Ž€Ž=ކВ ½C•kª£ÔÞÚT ÆWVâª*ó4U骦º2ª§£©<Ä¥º=ÌU•ê¨(q©¼ÅÛðÞiݽ$¸¢öò!‘œWš^ÅZ9™N÷,ãš)¿¸¤üÒ:Ó¬^þÔfÒG9PO˜7˜Aè=ôD˜”[ð>¯7›»ÛÊ‚’ô¡Nm6ÉVQMY±Šôñ#P›éÙ¢Ô QJ¦05S”JýÌ ÐÃ)ôƒêñ«)‘dj“"SD¦K¿»É§Â˶6ŠÝDhZKÿö‹7šÕ6¸õ¼¸»KÙÖ5xõ!M[¯£¡¬­N=¨ •üQ=MªéQÃÔƒWè¤'”ššª¥Å–Ûn _lìé§¾áœ4Ý…ÿüá1U{sà_W&˦fÞ[àêpbÝååMÕjxÚ0«]ÏM;ƒFÕúö )Cl ø”˾Óÿ}åX7=µ±&m‹|+Ïeü_[ͽòÛî6ß¼êjµÞ±ÊV5 %NŠXÄUÕPÖjÒÛòùŸ‡;{™½ Ü]¯Uî‡f.[[[ùQ>?âÚ\GǼK/”«×OÈ•ãôãÅ#ú·WQ…¦R]ç†ÍéëÁ9wU76—•É>éq)¨¼2²DŸR2>§f~JÉüL=R3dT¡º __Wœ!{0ö¢©ª©¿Oüø>±øïŒJ?À¢UóËEöh«Ô©ŸÖµ3²…F:ù%úµ*ô¨º†j…ˆ †èèh‘¨ÐvUYÚ<‹Ä—øN–hÄbbdFPýï†C¹uû¯Ù8­ƒYrø¦ ãN4]±ŽêÚvó}ßoãêáŽz ×6Ms¸Þ–³Ë[ê”x4–äã©i;ªÎ±–mòòëŽ :*?§¦¥¦®$íÁQ=7ë©óŒ[M;Õë`7ƒ °ÇÖ€×jõûÊã;^Ÿ5ÇÇ)KB$Ù‰¯#Nn¿à|½µA.ßœî޶ëÆPWé ŠÓ¢Óô@í‘Ý¥%–ÃŽÜVP÷G{z‡Îò«'Hì æ$JF*iæJœ»§NDÇ9@Q¨©(™ªh™|O;gÊæ|JÎü˜œ.Û2ø”;’»­Àçñ´Ô”S 2ò1)ƒ>Ää-¡6/½.qÏUM£/‹»J„”euƒq‰†ÚêUt5¨ŸÔ6t˜þI=¨#uCõÊt¼ä­‹äeþIc_Ìk*!Ò€§¨7˜w.Ø»:pz®ÓWíÖ;0\NȤ)¯'] ½2 Í´#çMºvžzu«³Ü’ I}pô™y×&ů-—\¦µ9xbݼ=\ZMºVvôAj·ÖÅÏñC±5à ÏÐ}íͼþêᨮôæ‹OõY,W§2ç{¡¹‡µ'ä]Õĉ¾ ‹ŒŠ*iÑŽŽsûöô­Õà;¨ðy&úÔƒ9á›$¦fÆ'Ò›Ôµð)%ãß„ÔÔvCbz|b5‰Ú€ÈÌÎí@?—H ¿‰O¦…C+ý@TUe¥üåéA3Ôµ¡` P3ÐV3ÑÓ¤6¨ajû@¶¡` P§6äP>¶‡1K¾GYM¹ -‹$»øÓÙÑv“[:2ؼ"¿1é©äú:Îoœ#Y…¿40¡¶7‰ž1}©v箤‰#áâ#–ÒRWÖR׫kªÇœð•>Ó{Òd[ ñ‰éñŸÓˆ°Ä3 *}@ÄAÙ1ðšn3N¸åOà꺮¹ý8¯–딣QSòFJ¸þG¿Ã2ïõý–Þlåß‚Žmžžã´Àó¹™Òn,YøiÄ>w½"ÉÿSTä+Oâ¬ô¬¬”Ôl1õç*(å(©ª c¿’ ðÉŸèË»^>F_ÞU”]h7»ŒI Ò²iÕÔ³¦Ó LéJÏ3¬Ÿ÷7)ïƒì*~­Q‡–NèÐgû¾Ãê—r$ƒðõö>…ËÏøHÏ¥«*NŽýXÙqß~ am.»'`aÝëì•þÿ½W¼ù6z®…'óÒߣEå½Ïúëóš ™“ ì/6ÇÑvYæÂ,eâWvâ»(Sl øò»ÐÍwK«ZGûŽܑú6ôç-»Ówdøbl ø ,K×8tÁ©t<((<€bmÀ—Û¥j*¶|ù]ª `kÀ—ߥj*¶¼Â_ªص/Œ96±ëè£ï™åägþP†ØðÉ—|gÆ-‰Šm_^A.þxz„ÓˆOkPÛ âİ%ý}6E~&êæŽƒo˜îjˆëÇ@¹bkÀ \WÍÙëÙ{v¦ÿX·†Fêeœ·÷–u›–æ^G=œK¿µÀ÷F¿óoƒŒ¹¢·Ýz¯Œ¼°¼nø剭ŸùxÇï¡ú=¶YW×U+ÛtÅîØ7lБS¶·zJ^”ü.EY JߣPI«Šjò¹D!!r! óGÓÓÓ¹\nTTÞLjjª–ŽF®èØð_µªS'ûꂲ>ã]É´ÏžË}ˆÜÍ5›ô¶|þçáÇÎ^fowD×ëc¥)WŸ[[[ùQ*ïE"‘………|!@ÅÁª›ÍT^l ø¬˜;ÑÑánµÖ3'2;È®à††ÝDv—Y;ro˜|%€rÁÖ€¯À7›ø~, øä`OÛmÞî$ØÓbˆôò¢Ê¨ðó°,àów›'SÃöÌ ¾ØèÞ冲ôÒµ•Ë^’rg…G×µQéX¸…)c:á9ùþÙ¼ðAp•Ëž£e3õ|ÌTf1€‚aYÀ°@±6àsâOŽë8-¦Ë¼™ƒšÇMj¿Ó'b_Ó×ÇWŒ_ø¬Ï‘ƒÞµËúò7?[>å²ïôg_9ÖMC’ƒ¥GÍó ¬<—òm5÷Êo»Ûà"ŒP™±5àµZý¾òxGÇŽ×gÍñqÊ’Ivâ눓[ç/8_om Ò*9¶<ắ½™w?—ÃQ]éÿÌŸê³X®@eÅÖ€O.åJv|6nƒgÌÜL§lo4ð£°5à•«4430Xpq£»>}×\’¤ .S5.,¶|±´]ëàçfZªL¨,Øð\U-5~’Š㢴.‡¯i¨Á#<ÝjZÉ!…„ à bkÀ+7˜ñéÎþùCöœ¼vÿß4 Q1²lõ«÷¤×/ ¸ÉgûwÞë°yŸó7\³63rVëQY³÷Ïm£ófÿè^›êì¹8ꟴ߱¾Ÿù›íýû_ö¹|¨›>.w剭OãéÙô›Ðo³œÛîÛnÌÂ/¤j½(\öå¾Ä¬¦‘©­©ðþ²'5¼v4ÑS#z^£lÙ™Ò­­ÜMn"##…BaþhJJ —Ë}þüyA €Š$))ICCƒY  [>'þp¿Ök-6š¨³­óXÞ† ñfŸŸ†ü5gú!“µ—6·7ûbþH2ßžÿcɹšÓ—oT&7Í´2?¦æm^NZB¦¦‰ ð»nmm-?!‰j×®-_PqDGGSSÌR¨`Øði÷ZLÛܲЀŒ_dÑaÝí1[]¬=æw´èî6íªÓŽVß°s^&ý†¯­ÇþOÒá{&Ðw—?è·¸aÖæ~i„ûlÛÛL1@ckÀ Z-ù{O¯EdÅèv]ê†Nœxh™»rĆ ÿÔZ[•Yý+¨·Xõ v£ÐÕÿÒ3F@ùakÀ~Ýѧ wúóQ'ÙP»ù!y¿¨ÄØði׆;¯h{æhO£ïû¶ BbkÀç¤%¼¿5ÁºÚæŠ}@T »ÜQî•[^àË,Pl xÜ[÷ƒ…ÆÖ€Çýà@¡±5àq?xPhl ø´°I]fÅÛöî×ß³­¥>¾qÃÖ€×pøýâEéðÃÝ£»¶oÿ+è¥aëÞý½úöhUWÀcÔ¨dØðùÄY©IÉ)©éY¾Šªªšª*W¾€Ê­Ÿrqˆë¼l7¯^=][ œK=˜5*1¶¼–kÀMWù‰ðãã cÇŽŸºo¾üôZ§o¾›@ÀÒ€—dÆÝ½ð¿ãTžŸ{(©U3-ZuæåãÛ ²l7h&³î÷'†-éï³)ò3Q7w´xÃtWC|ËåŠeŸìi9äyËI«üǺvò¶îä=OVh»ÍD_Ez¹›r~kï~çßsEoºõ^yay35f-€2IJ€¸¾¥/A/É|yòÇŽ;qþ1©]35M%.!["P.—Œ%¿KQ¨Òï)iUQM>—($D.à#""„BaþhZZšH$:wî\A €ïMýTVVfNøVÔg”@€[rUt, ø<UëN>Ôc~^‰$ûã³ôwð§Öð?µÆ± ¿ƒ×lÒÛòùŸ‡;{™½ Ü]¯Uá…ÛÚÚÊRyO5GGGùB€o†5 Êœl¥b–BÃÒ€/Š£lØÈm0õ˜Åœòݸ†ÝDv—Z;roc2@ÙCÀW8Œ=ÀwÂe+U¥€€P@x„€¯0Äñ½:i¿c}?ó7Ûû÷¿ìsùP7ýr9¬HÖã¥í¼³ge÷j‰çöôËZseVìàÂ+ÒûK½ÿ«+ȸ;ßmwùÁ)ÖY¦v_ Ø~iüóÿµ -ÁJõ³!à+ŒÔû‡ŸÔðÚÑDOèyj°ewdJ·¶8J§ÒÐïÊ-qüá>]¯¥¨9. n*¹ÿcEŠÓ`®ZEK°²A¾”ðUxvÝW»× ûhè2eÛâ&¢þÿ¹ -ÁJõ³!à+ ¾¶™VæÇÔ¢ÍËIKÈÔ4à_†kÔãÀ“äsPçYW:Mb®HªEV­¢%XÙ OÓHÇÀÎ÷à²%’tÒÓqDË™+LÑU¨h VªŸ  C­©ßâ†=Z›û¥"hì³mo3uf€"ÄŸÃ׎¿&$V¤Û¤ÿ“·5R²a¬HúÚuþ³+äSoábÜÊ‘MÍCß+Õèà·k#u¬T•¾âàê¹ú_zæÏ,( W×~âž勊®H_RgÐrÚÁ;ÓäJŠ®0_R?@!à@!à*•ä`OËaé3/Q‡Ï(·ÝæèþýçÝ–á¢þ›0æØÄ®£¾W²^tõØ`ó¼öXúkÈŸJJ­VѧÈ#[6íùþnÃ!׫÷þ}ÓôÎuɽõý{¯yªÖhવÝëk¾?6¶Ã””5wö´¡g båî³qÝø6ÆIa[&Ž?ÖhÇ™ù-´8D’|}vû!w;¯^;º•QRÄß±ël>³ÄAÀ¡žÅbÈ“Îk-sÊz%©km"×Ή?9ÚÍ÷}¿«‡;ê%\Û4aÌáz[Î.o©Ã-¨S Iê­½÷X»bˆ­öûKë'ŒÜ™mï]¤Z‰¯M¾VfäBŸÓöŸíS½ÐBÔO¿ö$w¸äœL¿‡·-½×¯›èVW]îM+¡\Pʫʉ?6¤Íb ¿€E=êŠîš4lo“}ÁSÈ¿¨BÊoxUúÙLÐZqz‡)'ýýýÓ‡n¯½óâl+ùçPXx¨”8ÇÅÇëOì>Õc×2—/LJzÓ`õÑ•=èêöÃ}[l|`A'zŠYúüm"Y5ÍFK.œhFo:è¸N9t¦J×.Ó®nt$á3ÆÜuáxz’v« ûƒ »u™~y½5®Õ|ñ’ut9D7ïéd’C&My=éBÐSz.A›iGΛtí<5ôêVç‹—î7â¦÷Éã^ÒžµVû™Ç.wh{ºhµ_›†\5áç7©ºž†Œt/¬ô¬Ù|ÍÞ¹¤/ü¦W^Ê«"ás$L;ÐW:©©÷îHÙ6K¦lqE|ëҴñî¦<<`ëÆ¸Ú5›ØÙ÷ý3r0®,‚€‡J‹kà¾öhãÀ‘ÝøìÏœ˜O"ÌIòF¸ªUù®Rñ›\®\¹Dœ#Qâó¨hàðÕ”¹òÁ3ét«=”LMURå—<M•BO#‘ˆÅ„WhA¥àPè ”ÕŠynɯMžV‹‘­cæzÕqDMfÈ ãÃO>4ïäjZú .é×,¶¼”W•*KH‘Jö ¯Š«ë²êöSzH˜ø22t‡Ï¼=Æ[/,´VeVPHx¨Ô”L<·~õç ŸfçwT•T„O^ˆqíf”üääªq¾7SmFÊÏôŸÒîOwéýqëªav:qÿ˜èÚvÏQ;úº›Íç­oÖ¾m÷W+WnSCüâôª1ó^ úû7ÓÒ’†ÃÒUuÛ¶ë¿þwG½O¡[&ŽÞ[wÝ{ú 2fÝb¨ÛûÿÕ¦{§î1þkƺTIŒØ1ÙÛÿ^š=³šÚ¾6M‡5—–¯îÝÈZ³ï”ñýÚ5©®ÃMzs7äà†?B-ü÷ú™R±Ïÿ¾,¯”W¥é¸`–®{O?­¿fw¨–ùhÿÔ¡7ÜsfJž¦¡Ê‡û/’ª&Šäùû†·1;jy»~Qƒ¶-íÕĸ¦u^æûŸ² AÀ;”ö¹PáwÔ¿Æà=çŒ Úì‰~ Ôoè±ä¼Ç’Ü …çe,ª`Ô=0&6¿X«ãì ŽùcDÃfÎÕGy# º->ß-w¸¸–‡«ëºæöãÜ딣QSòFJ˜«p¹ªå¤3÷&åNi9þàÓüý…ª•øÚ8š¿L:ù4wy4ƒÚöž~öž_ü‚åß´âËKyU<ã»îõ ký2ðÏÛ¥ƒÊŽ›î^– 1—[ì7¼*‹i—ïä›yl~à‘? øð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð  €ð èÿ¯^ü4¹4ŽQIEND®B`‚libkqueue-1.0.4/test/benchmark/scalability.ods0000644000175000017500000004403111607445336020775 0ustar mheilymheilyPKºrt<…l9Š..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPKºrt<ObjectReplacements/Object 1í t\ÅyÇ?IزÞÚ•,lÙ–dÉø~b †b  Æ6XEôÀcÀ¼ll¤pRF¤†&„PrBHx¹¥%PB£i ¼Nx¤ R^=„º¼’ þçÎ~šÑH£+¥o–"éÜývvf÷þ¾¹¿½÷îÝ{l[vXÇA4ô_M ûïèEDwïOôr Ñ¿aJ êÎ £ˆ.EóB<ÖÝ}!îU[OÛ†V·˜Mô5Ü›Œ{…É “GÕŸjUÐóhé²e«Wµ¾zIû—.Wõ[“®ûQr»0¹M×"Ñ'M,6ñ-Ì®y®‰êqnGtAwòÇ3-è™ý{Aþ9õô¹g¡õèÑÖ`DÕjfœžh_~ Û×wÜq‡Z|ô Æêö«:ŽXqh{ï¾[ŸÀ¬Kn÷ÄmVAN§‚­Ü¿¢^¥^ˆ½D9-WARÙˬ÷ºAÖôé†:îŒ;nE2¿¥ÉmšGÂtCŽõ†ÁuLoà;fN¿3É—Ä3^3©8l‰Û3É—Ä«ë4SuÝ0&nÏ$_?(«™T¶Äí™äKâçWj&‡-q{&ù’øí¥šIÅaKÜžI¾$þæhͤâ°%nÏdçO_«Ä¼^ļfªÎž4]¥è÷iæ@w&ùàÈç6qµú¥˜T¶ÄÝ™Œ$1q5R)&^¶áОÉHâçñfXwg’‰wØžsg2’xäć}CÖÉHâ‘ö-xw&#‰GN|Øw]¸3IZ›Ç-BœQB4qÞŠÓ[ˈ•Õ"¶U•!.Åà\„¸²Šè¢#«‰þñLïb:uo ž‚þ}ñÌZ¢ß"ž3–èEį"õ7ïªã¥ã°xVå_Büûz¢ßeôÞª×oœHôâ?£O¶#ÞÞ@ô)â½D%˜×Ïšˆê1™h âcÍD{">ÝfĦu þn7¢ÛT¢¯ ¾;è2ÄíÓ‰~ˆøÉ ¢» v'z±x<±rшµ³‰Ê‘Ó,Òæ½‹aïšTaŽ<À,hußìRO]Ÿ#£ŽP¸5™Í?&·û%·ÅTåX¾&,ÇjkyNA<±õ­U)nŸOÐÞÞÕÕWë¡>RaÿFÄ wë£uë- Ÿ>ücßävt£/2;—±ô E;õ¤äÕw˜š¾@´Iílïï°Ô 0 ƒÒhkpÚñXË8‚Ôá¨òR3“/ºìѤN3R‡&%.52«Trò'í<ÄUýsûR‡£ÊKÍŒ¾Èì\Ž&u:˜‘:4)q©`VßÏ˜ÕÆ½±Œq#HŽ*/53ù¢ËMêt0#uhRâR³!û`Ûç¿2fhÄúæö¤G•—š}‘Ù¹Mêt0#uhRâR3ÀblÈÿGÆlз#îeãG:U^jf\â‰ÌÎíÄ¥Ö‡æò‚ö4r‡&'.7\Iô|Æì¥:qâ9s˜ÛG;U^nfôEfçr´;ÌHš”¸Ô °¢ŠèÉŒÙåzx•^Aec\àR‡£ÊKÍL¾è²G“:ÌHš”¸ÔlÈ_Vý»õýA'bc?ÀÜ>‚Ôá¨òR3£/2;—£If¤MJ\j8Óóeر˜Æg1.p©ÃQ廕×€ç'óÝâZÄš¬aæ¸}„n G•+˜Ñ™ËÑÆŠt03V„&%.5¬«!Ú–1_”ŸŠX™5ƸÀ¤G•—š}‘Ù¹Mêt0#uhRâR3À†Z¢Û3樈¥YcŒ AêpTy©™Ñ™ËѤN3R‡&%.5œ=õsÓ¹ˆÅYcŒ AêpTy©mF_|ÑÊ%ÚÓ@{¹C“—›Ôñx·dÌqy*ŽÊjÐâ¬æöäG•—ÛfôŬr´;ÌHš”¸Ô  .åÈ›*SØ8‚Ôá¨òRÛŒ¾ø‚UŽ&u:˜‘:4)q©@)ýãŒ9bZEuÐãG:U^j›Ñc•£If¤MJ\jP‡ýß–1‡ÿ«8&kŒq#HŽ*/µÍ¨â·œh³ßsÝzð€FîÐäÄåf «ë‰î̘ߴ\…XS.Ï™ÃÀÜ>‚Üá¨òr3£/2;—£Øé`FêФĥf€b¶÷fÌ´~€˜Éc\àR‡£ÊKÍŒ¾Èì\Ž&u:˜‘:4)q©উD?Ϙ_Þ€8.kŒq#HŽ*/53ú"³s9šÔé`FêФĥf€[&ý2c~:ûOˆMYcŒ AêpTy©™‘ãÈì\mÝzð€FîÐäÄåf ;ˆ~1¿¿ q&L¹9gsûr‡£ÊËÍŒ¾Èì\Ž6b§ƒ©C“—š~ÒHôrÆœÜànÄ…YcŒ AêpTy©™Ñ™ËѤN3R‡&%.5<ÐDôVÆœ©ã§ˆK,c\àR‡£ÊKÍŒ¾Èì\Ž&u:˜‘:4)q©à—“‰>ʘÓÎ<Œ¸Ò2ÆŽ u8ª¼ÔÌèÆGv~<Úºµ æ4r‡&'.7=Ù¬¨às)=‚¸ñ¡œ9 Ìí#ÈŽ*/73ú"³s9Úˆf¤MJ\jx®…h׬91ØSˆë-c\àR‡£ÊKÍŒ¾Èì\Ž&u:˜‘:4)q©à¥)DÓ³æ,wÏ!^`ãG:U^jfôEfçr4©ÓÁŒÔ¡I‰KͯîF´OÖœ²ñeÄoYƸÀ¤G•—š}‘Ù¹Mêt0#uhRâR3À[S‰Ëšó¾†x£eŒ AêpTy©™Ñ™ËѤN3R‡&%.5¼7èKYs2Ý·ﱌq#HŽ*/53ú"³s9šÔé`FêФĥf€§•5g†þâc–1.p©ÃQå¥fF7~ä°óãÑví¹`~@#whrâr3Ч3ˆþ6kNwþâï•!9s˜ÛG;U^nfòE—=Úˆf¤MJ\j6dÔîD?P ¹s÷ÿy¦>g¿ Ìí#HŽ*/53ú"³÷”cIf¤MJ\j(Ûƒèά¹EÑúlŒ AêpTy©™É]öhR§ƒ©C“—š ÉÎÒ&ðUUJgé«©¸ÀÜ>‚Ôá¨òR3£/2;—£If¤MJ\j7[›À—ªž­/ ÄÆ¸À¤G•—š™|Ñe&u:˜‘:4)q©ÙÆ9Ú¾ÞÕ®ˆËûæö¤G•ïVfœ:W³ðåÃO°˜9n¡[ÃQÑ­ªÉ ¯nW¹¤›Â¯n÷I¾êÒ 5úB5ŠY]Ûã㬾ÂõY}ùåY}²í3úüÄ7fô)]Èè³`vWëÓ6ÞR­Ïtwtµ>9Xyµ>ŸÒO«ô)gÖWé³t4Wé<_© þJýóÙöJý‹Ãî ý#­­Ð¿k9§Bÿ µB5ýA¹>Ðôžr}lÞùåú0¦Êõ‘£Ëõ—åO”éï¿[¦¿’Y]¦÷bÏ(Ó;þ¶—ê}$?/Õ›•—•ê5ñ¿*Õ+/ÓJõx¿½D¿E,ÑËôнÌW—$B„]Ý.Yt;ÿ¶©Är|J½…±ÿÚZžsŸ@œ‰ú)µÔ³¼¹=äîî^²¤²’g>üo›pÔúïÖþˆeF_dv.÷|ÄJu*Ħƒ™ØÐ¤v¶÷wXj˜€7ØkpÚ ñ\Ë8‚Ôá¨òR3£/2;—£If¤MJ\j˜ŒO•'³æ“v?ÄË8‚Ôá¨òR3“/ºìѤN3R‡&%.52½LÀ«">ží Ìí#HŽ*/53ú"³s9šÔé`FêФĥf€Ùåú§¼ tâ¹–1.p©ÃQå¥fF_dv.G“:ÌHš”¸Ô °°B_ƒ…7èGl±Œq#HŽ*/53¶z"³s;q©ùàh_@#whrâr3Ð"tèyÖ^ª#+sWÛÊ™ÃÀÜ>‚Üá¨òr3£/2;—£Øé`FêФĥf€¶*|n[»\A<ß2ÆŽ u8ª¼ÔÌä‹.{4©ÓÁŒÔ¡I‰K͆,­&z¶Ú|°ÓŒ~€¹}©ÃQå¥fF_dv.G“:ÌHš”¸Ô °Kÿ"ë˰SŸ·Œq#HŽ*/53ú"³s9šÔé`FêФĥf€N¬ŒÎ·¾Ù=±Ë2ÆŽ u8ª¼ÔÌè‹ÌÎåhR§ƒ©C“—šŽ©!z¥Ê¦pb«eŒ AêpÔëÔ9F_dv.Ç[§N³Ö©“—šN¨%úv•9ææ«ˆoTc\àR‡£ÊKÍŒ¾Èì\Ž&u:˜‘:4)q©à”±DíÖd›¯¶Œq#HŽ*/53žê‰ÌÎí¢í¯öö4r‡&'.7m¬#ú¨Ò¹qâE9s˜ÛG;U^nfôEfçr´;ÌHš”¸Ô p®ºfY¥9Ä÷rÄË8‚Ôá¨òR3£/2;—£If¤MJ\jø›qøÜ®4Ç«_…xeŒ AêpTy©™É]öhR§ƒ©C“—š ¹x<Ñ„Jóã‹k×÷Ìí#HŽ*/53rì²b½ÅÎõâRŸã€º€~P#yh’â’3ÐßÕ=Sa~Yt=âL<þ}Ôo°€¹}ÉÃQå%gF_dv.G¹ÓÁŒÔ¡I‰KÍßÁl¿iýLîfuM3Ë8‚Ôá¨òR3£/2;—£If¤MJ\jøÞDluY¿ù¼ ñ:Ë8‚Ôá¨òR3£/2;—£If¤MJ\jøÑ$¢² 󿻳Œq#HŽ*/53r¼Î‰ÌÎõÑö_ÐÈšœ¸Ü ô D¿*7¿Ê¿q2ß–3‡¹}¹ÃQååfF_dv.G±ÓÁŒÔ¡I‰KÍ·6beÔ:ÅÄCˆ¯ZƸÀ¤G•—š}‘Ù¹Mêt0#uhRâR3À]MD‡[çKyq«eŒ AêpTy©™Ñ™ËѤN3R‡&%.5Ü7™hl¹9ùÏSˆ§ZƸÀ¤G•—š}‘Ù¹Mêt0#uhRâR3ÀƒÍD/–™3Y=‡8Ï2ÆŽ u8ª¼ÔÌø'2;·‹¶Áèí häMN\nz´…èú2sz¶—?F|6gsûr‡£ÊËÍL¾è²G±ÓÁŒÔ¡I‰K͆<5Ãu®ÁWè˜ÛG:U^jfôEfçr4©ÓÁŒÔ¡I‰KÍÏïF´À:q曈[,c\àR‡£ÊKÍŒ¾Èì\Ž&u:˜‘:4)q©à·S‰>-5g}±Ã2ÆŽ u8ª¼ÔÌè‹ÌÎåhR§ƒ©C“—š^›FôH©9¥ñˆ–1.p©ÃQå¥fF_dv.G“:ÌHš”¸Ô ðöt¢+­ósÿñmË8‚Ôá¨òR3“/ºìѤN3R‡&%.5ò‡DÇ•š“ÍÍ$º»`nAêpTy©™‘ãûNdv®¶koð€FîÐäÄåf aÈl²»¾‚B âfÄœ9 Ìí#ÈŽ*/73ú"³÷”cØé`FêФĥf€ný©Ä\¤ ñË8‚Ôá¨òR3“/ºìѤN3R‡&%.52zVDK̵mêûæö¤G•—š}‘Ù¹Mêt0#uhRâR3@ùl¢«¬ 5MD|×2ÆŽ u8ª¼ÔÌè‹ÌÎåhR§ƒ©C“—šjæXb®:Ö‚xŸeŒ AêpTy©™É]öhR§ƒ©C“—š ©ŸK´ Ä\Bo&â–~€¹}©ÃQÑ­ªIê/¶»»Wߟ„‚:Íßý£‰.o :¯˜èíFý²MutTGûQ«W.^º¼cÕÁ‹W¶›Å¢þ¶àù{!Ñ<ÜŽ¢Å×wš®ªGÏvwSò°þë.ê¡ÎÝý:é+Ð%9ªnüQWâ-^¢¾žÎD€Ñã ÜÚ¨—×Üä)ê޵ɽ¹ ¯ºfTIŽ·}E›ÝC½«–ù«VÚUj¶µ};`‡:vU½îØw&æcǶâþñõºcÕã{¢+ç&Ý»KRVÝ»?f÷¦ó Õ£=-v¤ÃÛ üU…þª"o•ä|n¶^‚ëçåë|}¶Y‚­©K°õs·ÏÂ#âý7CÈ ù¶ÐE¨­jøÌ nÏ"YãuÇ~½>ß:v*ÚA¦šÏOº¶0¹Ïo‹Bý轤û~ÍXÝ÷÷×åkߟW§û~A¿}¿à3Ü÷WgtßoÏækßÿKV÷}k¿}ßúîû'+tßϬÊ×¾£R÷ýÞýöýÞŸÑ¾ß ™*Õ}ÿ¥²|ë{¬ÑAx`R™š(s+BEIÉ^*âºá_’^<‹FëÅsUq¾.žŽb^<óX£¯ˆ›Oƒwë÷ˉöŘþ3Lm9/ĸ~X~ÏØF,•£k‰¾‡uçëÔ\v˜,ê†NÛ.þªQþªÑþªbÕU‰¿ªÔ_Uæ­êôWµ•û«*üU•þyù«ÚªüUÕþªŒ¿*믪ñWÕú«Æú«êüU»z«$Ç,õŽ|rß<ƒè¾Q4cÖæ!Üù—z„Ç¢Å<¾0·k£‰¦ü¯Ä½ôX° ãÄ&<òeÜ®£3c-¦ GÖÐɨ_‚éŒä¾z5õzÏ`ÚšÛq²d÷bz·b~×àzÞŠÕxÏ}ÓÉxƒí«¾LÆûâULÛð&hÛvÅçAÔQèÒŽj}a·?a:®u` Nƒ=Äô$T¹Nœ½«>÷èضžŠ­×O0=>AQ Y†ù:&væýpÙ9Ô#é—’cbgÞ—mãüðþª¶ñþªzÕÕDÕ$Uƒ¿ªÑ_Õ䯚ì¯jöWµx«$?¾=Ÿh®:éø~X¯¬Ì3Z÷$zϘ³hYÆ4êjO¾»,ÀpH4¶Ïwg=Ÿe+Hµ[šÜªä é@¼âµx馅:æ>©Lìýwäæ+Fuß@';ÿE¨âÙ”ãÙdq©ø+DUÏñÚ\ܤ/BFÔÞÞÕÅ3þ/BÃQUÏÈ|ª„)ÉjaÖÔ˜FÉ×ÖÁÂlËê,U fˆ¾9W<[r<[,._Éê^ç¸-·$£¼ø7çá¨r¿P_‡é’EØŽÍÐP¬!oº5äcq÷˨™úÝn/IjN!½¾û`nû}snû½¯=«WÅŪåÈÖu¿U’‚J¸g°8§íCtG å™pjvJ8µY£ºrÞЧá:Í'lCâ1õè“ÞÓwP¡Þá´óhÆËlÝEµpž“¯‚ ù¦J§¿JR°ûqÓÕDô&6×6Sž öŸ¸ÿ8‹A¦VÄ—c:cÒZló>À¥tôY‹{mxä ü¯ÅVïY»Ti“zµä¹j‡só\gubZ€Ñ O/Åô¿¹}–‡a¼Ó¹müÃrû,Õçá+˜žÊí³¬Ä[óœÜ¶ý°¤*jÕÜ‚(óUü!·ûs¸>²KsÛèlˆKŠ ðþª‚‹5xÇÔ Í1DýŠ›‹zîÒ ÅOqÿ.´2µN»˜ÎN†šãÀ¯5¨œžÜÛ‚zï4lÂgí鸧>¡'%¯Ð¸1½î jh¼&·:x&¦GÆèaqrnXT_ç\ˆ7ËÛ˜6äv}ª‹ß5å†Fõ•Ρðñ®Üðx,[ Þ«æ¼Üÿ¿ËFÄÎüøþg€±3?vhv~þË!ß¡Ù9Ôû:;ýUC7œ_–Û7qinôl¢¯ážÚóY˜ì(ìÙóòp ïÇØ¶¿z¼ç©ÿPK`üçî/PKºrt< content.xmlÝXMoã6½÷W*P´Z–?[½hÚí)Af[ôVÐesC‰*IÅv}‡¤DQŽ(H|éÅ‘†oæ=‡C*7Ÿö9<!)/–A4R$<¥ÅfüñåW4>­¾¹áYF§<©rR(”ðBÁßx2¶£Ë E̱¤2.pNd¬’˜—¤h¼b.k‘êÀz»°ï­È^õuÖØŽ/^÷g6`ß;x××Yc!©¾{Æû:ï%C‡¬ç%VôHÅžÑâql•*ã0ÜívÃÝdÈÅ&Œ‹EhFàÄáÊJ0ƒJ“0¢Éd £°ÁæDá¾ú4Ö—TTùšˆÞ©Á ?[ÕR ˜®.Ì~|ŸN}=mzW×ÓæLš“-½ëÌ€»¥2Iû—Ê$õ}s¬¶gÖwÞàù¹¿këJä}¹4¶“ªDв÷4-Ú÷çœ;©ÚÁnv#w<MCûî¡w/Âw‚*"à…ržB$&Z÷—ºϤn.·4qZêw_¾ºÅê„eIHRô– ý ÷NA²Óyꛆ 3yb]³۠XóôÐö>¸$áTn Q«K`~k2+øACª¬Í„CÞb[s) ù6ª:±êsÂÝlÀšÉÜk¨Dp ¬œ&W "µe‚Âò£4õvY¡„OPŠg”€ëò‰º×5êe8"ï«æ9]ØËä5Ì*‚Ô¡„xãXØýšB锫¨´O]¹:Þ;&£‘G¢ß.ÃsÕá¹z+ÏêÆìÝL@îëÅ€ûŒ-(œ¦ð¹ ›bÞ¯³7Mt:Zè.ÚšÆ<šN´Ùÿî )ÑðÚà/·m8p2Ö§ÂÕp2éSÁ›~;^¦×Õ0ƒ3¬!Ò†zF|ýšå+¸¢ÙñU%|JAKÏÀņ´s»½ŽÝÓ ~úÙÙ>;ÛíÜáæçlŸíváp ‡s¶Ï ¸ÐéÏÎx =o Ã߬ܨ±Û—4/õ1amr«¿l¸´1áDU0¡eÀ‹;ŽS½çÌŒáÀÚ“¿h•Iý5ûq¬a[F'ëî¢àÔf‘ šï¦ÝŒ®ÿ©HE.µ ýMx!ޱÇ1¾ ÇlÖrÌfoâ¸t¿ï³Ê%gìûñÊMäå&º Ç|Úŗ—áˆF×- ¼¼•}Ë+w¦ñeïLw#úø©Oþ×SO]ŸÃλy;þ'ùê?PK/­Þ(ePKºrt< styles.xmlÝYmoÛ6þ¾_!(CÑ“%ÙI»±ƒŰKQôeßi‰’¹R¤@R±Ý_¿#)Ê’%«ZÛ¡ë …Èç^øÜñî¤ÞÝ ê=b! gk?žE¾‡YÂSÂòµÿáýoÁ­¿ùéŽgIð*åIU`¦©ŽK„™\Ù͵_ ¶âH¹b¨Àr¥’/1sB«6zeLÙ£lª¸·¥>¨©ÂÛ‘EÛé– ¸- ´Ÿ*¬±Ài[<ãS…’^”H‘3/”°k§T¹ Ãý~?Û/f\äa¼\.C³Û8œ4¸²Ô Ò$Äkc2Œgqè°VhªÛv‰UÅ‹ÉÔ …zQ-–ã꼜¦¨-ÓɯÇ|rv=æhNvHLÎ3î¦Ê"ž*‹´-[ µ»ßÛð6ÍŸ‡?Ny%Š©¶4¶CU"H9ù˜Ý–çœ7®j{Ù»ó(ºís ½…ïQX´àÉ(n«Öèvg¸¯Y—tŠçQmÔ6ûŠ›=£Zt RSÂg'I»ú¹M¥žVr‡R¾ =$VÁAóÇ·1„`hÿØÚŸ_=T).K¤ƒKRÌ-Ñr×PSV,Q•i¥Á¶!ۉ桡Ž0lF0zH×A¹]³aÒ ž‚úèíă°ë*§§H£Dûa¦Í Q‰Û™mòPž:ÕxRV0viºŒñ„Ss‰6é8P€Þ¨æÞk¼÷ÞB¦° EHhÜhhLIò 8‰ç¥š|=(VЃX0Ã’õ½eLëtÙ?¿.Õ7¸3Fe“÷ƒJ¿Á±Sb=,vnŒ-#¯#ÿ äÕOaG ç —’œ(¨n±14 ³Ñ‘TBÀ»ÍqÈT]¿‰Ü99…Ä×/†ìÇbË©ó¦E‡mErósãØ™Š çëWîñ“»Ý\ðªÃ¬%gþEìt‰Õo¯½pÅåwŒôוo˜‘ñÏ6zJrH^ Ýë3ÒÔjx¡ÁH“ W)×|Ç ü¤tí' /g糦÷¼nzC™óuÁ¬¿ˆRŽ)” n?.P¯´û˨ïdx6Ö×:¹áEƽ磀¢#¯TçLeû ¾SÝy‹Š@mO1ÙÁéN°³–q®‹nÀtuÜÕAˆfóåÍ b'“‰ö(ÎôNwQÔøîê–+¥ßÁ£Y´¼½¶ÓixÙ«Úïá)L™ƒnv] {±˜Ãùÿ,†zoËEª¿îÁââú9až©íÞUd~ ¤D©ý‚ ˜èEìQòQ7X–º‚~•Dú·!©…€öã^ÊôücäÔ—¶Ïçxx±‚Õ’ y˜Ì¢Ö46Ò´oÅ@á³îoîÌwû²þWî0¶èÍýýý]x¾X¯”g$œ%€Ž¥káDœxì5AËMcý>Ký Ý¶ÃÜ&vöZk=œªé£.„=?GíÛúå³ó³nÈu[ÓýC²½§§ˆ¢mˆ}~Ö#¢c©³dîÑ™u®ðÅ9 ¦·˜Yæü(Ž‚hÌárD‹p…z¥öB7¿xÎað>ŽVóxu³hœJŸ®ß-§¼° 4oG›å² ´kÿRî…÷=þŸÁÍßPK+_%qYPKºrt<Object 1/content.xmlÕZmoÛ6þ¾_a¸(Ð~Лe;¶‘¸h‹ ÐÚûJK´Í†5’ŽíýúIQ¢lY‘ÓèÀ‰xw¼ãs/¼rûîÑÁæ‚°ünùáp€ó„¥$ßÜ ÿúö›7¾[þrËÖk’àEÊ’]†sé%,—ð{Ò¹XêÝpÇóC‚ˆEŽ2,2Y°çVjár/´.³"ä‘ö×Ì®´ÄÙWXñ6dѪ¿fÍìJ§íû +^Õ_³¾ÂA½5Ô³IrbÅ’üþn¸•²XÁ~¿÷÷±Ïø&ˆæóy ©•ÁIÅWì8Õ\i`Š•2D~XÞ KÔ×>Åëš”ï²潡AyUÒ­Wk“至ÙÅFq<™MÛ®¦šó*NUcÄv¹yÝmSO?wõ*­´ÊÍÓëÜ=Òaÿ$x¯×ãQØÚ Ô”ÿÞô°×£×Ñ>•Ìm ©ÏUv?ÒÒ>g×wÑìDÎÍ.ް%aÅÒcõ  _Þš@ÖßÚ®2 £©N&6 í$5÷£ÉZ,¥(¢„`áöinß¡¦W«F© …!dÆ~N­5MùÑl\oÞÜCM躡(–_a\B+Ba*„Q~@ÉêþŸÞáÛ d¸ mVµØ­ÚcÇŽöÈÃø’ö¸Ö>Éxðû·o8µBŠÁN@š ÞàºÅpžlÏ,±Ê­1opžÜ玄§¡53ùóiäØûóptÉα C((ƒþ¯VFèÐÍ[fÕðz.`Èó4åXùóëcùnå_¿Î­:ÓUêvFvaZEY1¹­lýñ8j@[ŸÀ†×È×Íç°hO :Ī3Ípn^Ö¬)æ(‡±›C›ÝÈôbè9öÍüøbÜÝÔžÿbÞí@Èý‘ÂVYž›ŽWtG^‚$Þ05(<ŽùÇóà5ݰÛa9^KèÇ£‘Ì?5WQÛ³™÷0  H°guϦÆàM&Þvƒ³á$mÝ~~Rd2ôñ ˜1¬½ö„v3†y]àÏ*ðgÕà£ÜåGÅþav¹"ZuæèCÕà¤ÞáT“õ±ÌQú,ºâ`óóLî:XÅþáÔ Ïu°=¢´ýX£º„­)c¼Ëݹªt:¿ôw™kF‚2¸3<½2lpy[Œ`¤WWë.ËE“f•¢ë$Z­=æoÉèbõèf¥võ¦¡I€•µàZ›¡g[ÚÉJõb\TÙÔB›—[•êf¨WïhÛ Ö2t’ýYô-c¸nŸ}Óéc›gȶ…@«ú¸å)@7ªÍ²­éy6dêE¸Kn>ìð¸[%ʲÀiø¦£—Ñ1™8mÝäÊ€x'»•wY0Jߌ޾Ð᣻÷År½Œ³q ÿlü2:¢ÐiààáG²Þ$·»T]Aå,4G« 1vÙ§ÓÿmYþPKÙ–%s#PKºrt<Object 1/styles.xml”Írƒ €ï} ‡žMÚ™†‰æÖ'h€ ¦Ê:€?}û V‡´I†£ú}»Ë.îñ4µM4p¥ÈeIŠ".”BÖ9úüxßЩx:BU ÆI ¬o¹4±6ß ×Ñ,KM–9ê•$@µÐDÒ–kbŽËU">M\ªå ª;Ø· ŸL¨lÙ+—žÃ3;Ø·KEÇPÙ²sO}½‚PyÒM\AÌ í¨ª˜!¿rt1¦#㘌ûT³Ãá€Ý×­`¶q]¯G• ó†ÛdgI†WVö홫àãQCÿMFuðT‡úÎñØ…ªàù:øzDû2|Dûòª|¦Dœz¡}¶v[a¹ø®é»4}Á˳GñQ Õ‡³‡8£ Ûºí­ 2sž‰˜vü+­ì¡ïF~ÅŠw ÌVHþãÏÝÙmYÊŠnifR'.BlE—Ë€[n(¶Ê3Š~ˆ·´v¨X7Ô²˜pqÄ·WVñPKÓp:ªiòPKºrt<Object 1/meta.xml‘Aoƒ0 …ïû(êL ‡•vص—õ<¡àvÙÀFIüüvØ1öç÷žü0¶Mð‰Ö¦BÈ(’æÚÐ¥§—çðQʇœÏg£QÕ¬ûɇ-ú*¸Ž’SK«½%Å•3NQÕ¢S^+îÖµ¥Õl´TÆÆÐG!Þ¼ïÀ0 ÑFl/ ³,ƒ¹»¢µ¾q]o›™ª5`ƒ“ƒIXÙ)áCMì63ߌ&| =Û%q¼‡å-‚ïe6çKD¹Þj-óYú‚„¶òlËãUíø£–Frw"3÷õ×Îò;j©Œ[™ížzÓÔa¶Oâ~ æpg}TùPK~,ºùæPKºrt Mark Heily2010-03-20T09:51:542010-03-20T10:21:53Mark HeilyPT00H14M28S3OpenOffice.org/3.1$Unix OpenOffice.org_project/310m19$Build-9420PKºrt<Thumbnails/thumbnail.pngí–é7ˆŵÕeÔÖ–*­ejÉkè (FjKMIDÑNçI ©¶„H-/<íX[¥tAl•…ðhb ÒÅ ¥AH¤¢±Ö–´‚I¢^ßûðþ†÷aÎùsïÇ{î¹n¦¬¦¢§¢¤¤¤æ}Ñ#à»¶*)íQ:tà»SuD))©8{{\€ãKV„?Ü,”ê÷¬_ß÷ãÛüi×ÖAÁºëJU'ãZi¼rsðQmí¿)3Aäsí´`Í=оŠt¢Þ!M-ˬó«êx#0] 0õ3¿d€^=MHXïÙ Ï÷5`“tÑ“‡ç‹ÇVÙËW»t#Í)šqP‰ UVú'ïö뼫ë¨b l`t ’äô †¯ g«‡¼Iá»EW'’Q¸ÁO8s ó8ÛÝìM¬45°kS=`p)0Ù`mO¬ú8ÁìKþªêÒ<ÿ›öÓlûÜç‚!  ¢…¦&ÆSÆ«·øØïµ9’rKh‰K"ƒb£î-ˆÝ^èÖ \¹öüé›…ãWÊ­6M(Ÿ׳4€°*ÔjÄoÛƒ*iX'Zd­©“„:QVë…¯oxÁô±ÀÄ㪶ï}§Ì‘6þ £“tÎx6¢Ž#Dµd­ ¾hÎsHÎŒg¶:þµóðxrX]ÝöZè:”DŸ ÇûÿMZZ$N‘’ÅŠ*5•Õsll~°×슷ǻy£–)‰ iâœÛYÝžó}%Ï{³å;k%àUäãg¯ü<#žE 9­#BãèñqÛÂüºŽ½íådçgî· ÿ+˜o³–¶ÿ‚81%&fšTî?+2÷= 6•jfìÏÂ,€þ…åjŠâäøÎöΙus«¿î'³<Åü}9Áö˜aT‹žXüº-غ¹`€2Í‚)2{Âø tà •õ\Îø¶4€ažà±·C’ײ¿8óE&¾ŠmŠŽûˆ?¦@·ŠlVxÑ.³8Êlrmøô±Ü3ø‘y±ò6Ûze9|ɉÙÁLjƮ•Ã#7tê™÷‚Óæ^ù &!Æ_Ò®®^1„ÝkÑ Æ¼õÅH>üD EcFsÝ,½/k8Ùl ºq·†ýä~=n('dÒasU;…²~ìùÎ4‡9R¦ºëdñˆ$ïR¦ŸxBX7›#;&nb¹àÊÔšÖ(› ¿Wc›‰LòA-:棵ò¯é³¸À.—Ú Åª­Û†Ãî¤Ë” RŽôRlš).ŸO‰nv£9A ¨”e‘ä¬e~¨›æ5ÖÙR3“%ˆœÅÍ]îg¬ƒ¶];>\²Ée¸Y{^›÷jß *¨ÿó@rP´{é$%ÿ¨G:vŸ}Š3Z7±ŽøµbÁÁr )ž]åöâ©ÕP%2‹Ä‹Hð2YvlÎ^!$О(y~é Šœ“_ ¸óV»È…nÅïzÙ‡séû¦‹qn}›«„èÐ:õ¥NDËÄœ^‰ÁpºkÞLDŽ­Îç_kÓI{ù ½Xòã/6‰\4~'K”Ðk·o^j9lÉ ]tÌ7FÿŒZ9 >TÛå%–º-1äȶ© ¾èÖ”=8ÜdL¶YY›-cüüÌŸžšõ¬C¾®Ð¨ 8cç;¨†õt™Þ‚©Çˆ¨ôÃÕ’ªàН6±  ‚,£ü}¾ '„KU©þÓžDX/õ˜ÄûûŒËˆà‰³xþðÛ{¹ú— b9•åºuß]‡ÏõÓG}ÜrìT¼§Ÿ·]NŸM€3C3_Î áÍ ¥hú{¸äL,ï:g†ÖÈrÁÇâíÃsCÆÝ®‹Y6v1pš^ϲ (ÃQ†üÅД_;0Yé±÷ÆO  #nXHÙó­2_.²À†>”-óáÄõSØ–²Í™N™Qû³ž²'ä7Ã&|µý–!ªa.Гµ7Æt"ƺÊ8ø~ÒÛ‚Jó ’=´»s2pú««¿(f4bx1S¸†C(ö*¸F@ Æàuãi§˜—Ê(÷€GÖ!;òÁ@nú•*ØeßýCv~½·¶0UªìfHIMxÃ=&™úúµj<åŸç”Ï?ŽžVÉÌô'%„dÌRâ´òè³SÓTÖª,ü)É9 b£J+wöyzs ssØ!¤©ËFº¨g Σ¡».Âý+;Íz?ƒX¬È3Rÿ’‡àžË]ÞH n5µHª µSï¸{Í`?ÝÛÕ2ágÞôáX½Ã¡¼8Q½P2rw饊„„`ŸÐ*·}É=³ j7Ã+¸'÷åªÇ¥8Ú/~$`¶oà‘ òùFQ¿tè“ðiõâR….Fp:¤ß&F{øp—Y1-*‡ùæ‚ÿƒhU(HÍÙÄ‘ÈÕ6¸Ñß‹Äo³zþ°E¶1.tP´i«;©&AÑÚ{}†mR¡¤+{] š X’D²ÄðÎ;àÂhÕš-vØeWîćÖt~“þÒN+ ŠIa•a üÑRLu”Ácj ër¹©Ó‚á×ìx¾ýZká&âO‡í ¤p„ƒktП›éVº$íî±àNrš¼@«EØ¥¶K_É žÚw<‚ˆ¶&m\¸'y»óLâp ðF8F5þ”ª~)¿ ¬½è„–ˆu·P *îE^h*þ¶BÄ›ª•Ô¶°ïqý˜ðÁWÄÝ£Ä.½¿ø b—0UéVŽèˆ¡ÕÁÿÜIoOˆG½ÛÕ´PK©[þú u PKºrt<'Configurations2/accelerator/current.xmlPKPKºrt<Configurations2/progressbar/PKºrt<Configurations2/floater/PKºrt<Configurations2/popupmenu/PKºrt<Configurations2/menubar/PKºrt<Configurations2/toolbar/PKºrt<Configurations2/images/Bitmaps/PKºrt<Configurations2/statusbar/PKºrt< settings.xmlí™_s›8ÀßïSxxwü'9·fb:Ž{¹øšk3ÆI{}“amk"´Œ$LÜOL'!` 7Ó?yŒ¤ß.«Õîj¹üðä±Ö„¤ÈGFï¬k´€;èR¾÷óëö{ãƒõÛ%.—ÔÓE'ð€«¶¥ôÙÒ˹4“á‘n"‘Tšœx Må˜èO—™Ïg›±°äÉ£üqd¬•òÍN' óðü ŪÓ‡x4ê _ÒUYQÉìç¢ñ§ hA¢L,¬ßí^t’ÿFk§ä3Óô +µCúúÖåN@òÓ¦ ¼È6­ÝãHµ‘¡Eš áO«yë^®y ’.Œ9úF:¨¶¾¤\V0^v^sÞľ…¥Ê…÷4ü+uÕ:—þ®;|w4þèj«ýðâ¢4¾í¿M¹ OàfEA˜¿Uñídb[Fa§nFK©„öÊ{oÒ4‚fôœmCо\b¯T¯„N!QÜ¡¤JŸ‚oyÆTÛÈ—äòÈçÕÈ7(èäŠ0ÛgTý.dÍ¿F¡ùÝŠ.BQ§)zFûÔ@yö©CÿøcGÑ Äôá«óô«ÁS}‹WESì¬(¤É-ˆáGR¯P)ôjGôæšR«GGЂ,5V´×­j²‚(|î¥*Âí5† š Û D„–ä“s>ÏEÃqX/{´â€_Q’È~tèq2¶ b_ ?¹Ø±. ¸Ò•Ì〨@,r·%a²ÀßʈùcýåÛý¹ ÿ3ª¦ÐÕá!rD C‘A3ŒŽP¯?8×õoźæÅ¾6`•"µêÇgÞqõý¤!q Ñ‘¦úT~ ”¾Ó½õȤ Ù”[‹›Ž3"d7ºŽ“•€§rw#iL ¤ÞïÂò»W9gñ¹5ø±x;X¸tCe¡ú5Áó•¯ê: ~üD¥½åÎZ §?à¿«v×Ñü Tù¶Cò $Úà·ôNyã–7n)¼÷]¢ øÊZõÂ}JI§”tJI¿FJ:$i(œæL‡Šúñwúšªßã³þ“ƒí”fè3d»* "apqE9Û2¨±ï³í½ñ‘(RÇÛ·“¯Q,¨ëŸ¬‰ ŽŠ¢ä±Íå[ÂWyÝb+mÊ ‘¤Ê눠„gceùõW°¢\§¦êoðw÷®?P‚5i xÅÞø¾]±îíŠ}ß_}¯–Ëíšž¼ z¾EéÚ{QSù Km뻀;* 9ýõZZ;dÉçÉ/|ÂP6‘ð“’ñZ 7Ïo&'ŒÓµoÔÿÂÅ„pXõ/w¦‹Sä,{°ê°ÓT~Ü}y¶µ“'æ=" /{W_;Eßã­PK‚ß%ÑPKºrt<META-INF/manifest.xmlµ–MoÜ †ïù–½Ù49UÎz#µUªªJUz®fñØK…2C²ûï I÷#íJÝµÌ Ðð¼ï,î6£.Ñ“²¦-¯ë·eFÚN™¡-¿?ÜWïÊ»åÕb£z$nv"Î3´ï¶eð¦±@Š#Rò±MgeÑpó:¾IJË«âî•Æ*úmqÃNAÅ[‡m Îi%£OñhºúY«>–¨Éy„ŽÖˆ\ GÉÝ ÷AëʯÛR”b²•M•,ؾW«¡S#2¤ù·O*{¢½õ#pZ“öͯ`ùöÓÇÏ_bÐ} z8mêëê'Jþ†NƒÄ”‰—¡âúB»gó/ä2nXÄm=Í—ÖpÚ”0'—x«‘fÇîÖVäò½È@:ègÔ—\ƒçÿ¨ÏšÓÄTNÃÖa\Pšïšµ3ÃÜðYx¬éÕüó~Ð)QcìZ/dð~Ú ½\+‹€óvðH´‚L½¶À˜Ë½uÁÅzyð‰œmaØZ ®FÄ{Å#¸LuðG# ›8L9”_L*Î:¨ZkœgcÞ×™ãWnÿÜ,Ä??¹åoPKÚ"Éæ° PKºrt<…l9Š..mimetypePKºrt<`üçî/TObjectReplacements/Object 1PKºrt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "common.h" static int sigusr1_caught = 0; static pid_t pid; static int __thread kqfd; static void sig_handler(int signum) { sigusr1_caught = 1; } static void test_kevent_proc_add(void) { struct kevent kev; test_no_kevents(kqfd); kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); test_no_kevents(kqfd); } static void test_kevent_proc_delete(void) { struct kevent kev; test_no_kevents(kqfd); kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL); if (kill(pid, SIGKILL) < 0) die("kill"); sleep(1); test_no_kevents(kqfd); } static void test_kevent_proc_get(void) { struct kevent kev; /* Create a child that waits to be killed and then exits */ pid = fork(); if (pid == 0) { pause(); printf(" -- child caught signal, exiting\n"); exit(2); } printf(" -- child created (pid %d)\n", (int) pid); test_no_kevents(kqfd); kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); /* Cause the child to exit, then retrieve the event */ printf(" -- killing process %d\n", (int) pid); if (kill(pid, SIGUSR1) < 0) die("kill"); kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); } #ifdef TODO void test_kevent_signal_disable(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) die("sigprocmask"); if (kill(getpid(), SIGKILL) < 0) die("kill"); test_no_kevents(); success(); } void test_kevent_signal_enable(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) die("sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) die("kill"); kev.flags = EV_ADD | EV_CLEAR; #if LIBKQUEUE kev.data = 1; /* WORKAROUND */ #else kev.data = 2; // one extra time from test_kevent_signal_disable() #endif kevent_cmp(&kev, kevent_get(kqfd)); /* Delete the watch */ kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("%s", test_id); success(); } void test_kevent_signal_del(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)"; struct kevent kev; test_begin(test_id); /* Delete the kevent */ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) die("sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) die("kill"); test_no_kevents(); success(); } void test_kevent_signal_oneshot(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) die("sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) die("kill"); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Send another one and make sure we get no events */ if (kill(getpid(), SIGUSR1) < 0) die("kill"); test_no_kevents(); success(); } #endif void test_evfilt_proc(int _kqfd) { kqfd = _kqfd; signal(SIGUSR1, sig_handler); /* Create a child that waits to be killed and then exits */ pid = fork(); if (pid == 0) { pause(); exit(2); } printf(" -- child created (pid %d)\n", (int) pid); test(kevent_proc_add); test(kevent_proc_delete); test(kevent_proc_get); signal(SIGUSR1, SIG_DFL); #if TODO test_kevent_signal_add(); test_kevent_signal_del(); test_kevent_signal_get(); test_kevent_signal_disable(); test_kevent_signal_enable(); test_kevent_signal_oneshot(); #endif } libkqueue-1.0.4/test/read.c0000644000175000017500000002160211607445336015112 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "common.h" static int __thread kqfd; static int __thread sockfd[2]; static void kevent_socket_drain(void) { char buf[1]; /* Drain the read buffer, then make sure there are no more events. */ if (read(sockfd[0], &buf[0], 1) < 1) die("read(2)"); } static void kevent_socket_fill(void) { if (write(sockfd[1], ".", 1) < 1) die("write(2)"); } void test_kevent_socket_add(void) { struct kevent kev; kevent_add(kqfd, &kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); } void test_kevent_socket_add_without_ev_add(void) { struct kevent kev; /* Try to add a kevent without specifying EV_ADD */ EV_SET(&kev, sockfd[0], EVFILT_READ, 0, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == 0) die("kevent should have failed"); kevent_socket_fill(); test_no_kevents(kqfd); kevent_socket_drain(); /* Try to delete a kevent which does not exist */ kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == 0) die("kevent should have failed"); } void test_kevent_socket_get(void) { struct kevent kev; EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); kevent_socket_fill(); kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); kevent_socket_drain(); test_no_kevents(kqfd); kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); } void test_kevent_socket_clear(void) { struct kevent kev; test_no_kevents(kqfd); kevent_socket_drain(); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); kevent_socket_fill(); kevent_socket_fill(); /* Solaris does not offer a way to get the amount of data pending */ #if defined(__sun__) kev.data = 1; #else kev.data = 2; #endif kevent_cmp(&kev, kevent_get(kqfd)); /* We filled twice, but drain once. Edge-triggered would not generate additional events. */ kevent_socket_drain(); test_no_kevents(kqfd); kevent_socket_drain(); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); } void test_kevent_socket_disable_and_enable(void) { struct kevent kev; /* Add an event, then disable it. */ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DISABLE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); kevent_socket_fill(); test_no_kevents(kqfd); /* Re-enable the knote, then see if an event is generated */ kev.flags = EV_ENABLE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); kev.flags = EV_ADD; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); kevent_socket_drain(); kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); } void test_kevent_socket_del(void) { struct kevent kev; EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); kevent_socket_fill(); test_no_kevents(kqfd); kevent_socket_drain(); } void test_kevent_socket_oneshot(void) { struct kevent kev; /* Re-add the watch and make sure no events are pending */ kevent_add(kqfd, &kev, sockfd[0], EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &sockfd[0]); test_no_kevents(kqfd); kevent_socket_fill(); kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); /* Verify that the event has been deleted */ kevent_socket_fill(); test_no_kevents(kqfd); kevent_socket_drain(); } /* * Test if the data field returns 1 when a listen(2) socket has * a pending connection. */ void test_kevent_socket_listen_backlog(void) { struct kevent kev; struct sockaddr_in sain; socklen_t sa_len = sizeof(sain); int one = 1; const short port = 14973; int clnt, srvr; /* Create a passive socket */ memset(&sain, 0, sizeof(sain)); sain.sin_family = AF_INET; sain.sin_port = htons(port); if ((srvr = socket(PF_INET, SOCK_STREAM, 0)) < 0) abort(); if (setsockopt(srvr, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) != 0) abort(); if (bind(srvr, (struct sockaddr *) &sain, sa_len) < 0) abort(); if (listen(srvr, 100) < 0) abort(); /* Watch for events on the socket */ test_no_kevents(kqfd); kevent_add(kqfd, &kev, srvr, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, NULL); test_no_kevents(kqfd); /* Simulate a client connecting to the server */ sain.sin_family = AF_INET; sain.sin_port = htons(port); sain.sin_addr.s_addr = inet_addr("127.0.0.1"); if ((clnt = socket(AF_INET, SOCK_STREAM, 0)) < 0) abort(); if (connect(clnt, (struct sockaddr *) &sain, sa_len) < 0) abort(); /* Verify that data=1 */ kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); } #if HAVE_EV_DISPATCH void test_kevent_socket_dispatch(void) { struct kevent kev; /* Re-add the watch and make sure no events are pending */ kevent_add(kqfd, &kev, sockfd[0], EVFILT_READ, EV_ADD | EV_DISPATCH, 0, 0, &sockfd[0]); test_no_kevents(kqfd); /* The event will occur only once, even though EV_CLEAR is not specified. */ kevent_socket_fill(); kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); /* Re-enable the kevent */ /* FIXME- is EV_DISPATCH needed when rearming ? */ kevent_add(kqfd, &kev, sockfd[0], EVFILT_READ, EV_ENABLE | EV_DISPATCH, 0, 0, &sockfd[0]); kev.data = 1; kev.flags = EV_ADD | EV_DISPATCH; /* FIXME: may not be portable */ kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); /* Since the knote is disabled, the EV_DELETE operation succeeds. */ kevent_add(kqfd, &kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); kevent_socket_drain(); } #endif /* HAVE_EV_DISPATCH */ #if BROKEN_ON_LINUX void test_kevent_socket_lowat(void) { struct kevent kev; test_begin(test_id); /* Re-add the watch and make sure no events are pending */ puts("-- re-adding knote, setting low watermark to 2 bytes"); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_ONESHOT, NOTE_LOWAT, 2, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("%s", test_id); test_no_kevents(); puts("-- checking that one byte does not trigger an event.."); kevent_socket_fill(); test_no_kevents(); puts("-- checking that two bytes triggers an event.."); kevent_socket_fill(); if (kevent(kqfd, NULL, 0, &kev, 1, NULL) != 1) die("%s", test_id); KEV_CMP(kev, sockfd[0], EVFILT_READ, 0); test_no_kevents(); kevent_socket_drain(); kevent_socket_drain(); } #endif void test_kevent_socket_eof(void) { struct kevent kev; /* Re-add the watch and make sure no events are pending */ kevent_add(kqfd, &kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); test_no_kevents(kqfd); if (close(sockfd[1]) < 0) die("close(2)"); kev.flags |= EV_EOF; kevent_cmp(&kev, kevent_get(kqfd)); /* Delete the watch */ kevent_add(kqfd, &kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); } void test_evfilt_read(int _kqfd) { /* Create a connected pair of full-duplex sockets for testing socket events */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) die("socketpair"); kqfd = _kqfd; test(kevent_socket_add); test(kevent_socket_del); test(kevent_socket_add_without_ev_add); test(kevent_socket_get); test(kevent_socket_disable_and_enable); test(kevent_socket_oneshot); test(kevent_socket_clear); #if HAVE_EV_DISPATCH test(kevent_socket_dispatch); #endif test(kevent_socket_listen_backlog); test(kevent_socket_eof); close(sockfd[0]); close(sockfd[1]); } libkqueue-1.0.4/test/vnode.c0000644000175000017500000001532311607445336015315 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "common.h" static int __thread kqfd; static int __thread vnode_fd; static char __thread testfile[1024]; /* Create an empty file */ static void testfile_create(void) { int fd; if ((fd = open(testfile, O_CREAT | O_WRONLY, 0600)) < 0) die("open"); close(fd); } static void testfile_touch(void) { char buf[1024]; snprintf(&buf[0], sizeof(buf), "touch %s", testfile); if (system(buf) != 0) die("system"); } static void testfile_write(void) { char buf[1024]; snprintf(&buf[0], sizeof(buf), "echo hi >> %s", testfile); if (system(buf) != 0) die("system"); } static void testfile_rename(int step) { char buf[1024]; snprintf(&buf[0], sizeof(buf), "%s.tmp", testfile); /* XXX-FIXME use of 'step' conceals a major memory corruption when the file is renamed twice. To replicate, remove "if step" conditional so two renames occur in this function. */ if (step == 0) { if (rename(testfile,buf) != 0) err(1,"rename"); } else { if (rename(buf, testfile) != 0) err(1,"rename"); } } void test_kevent_vnode_add(void) { struct kevent kev; testfile_create(); vnode_fd = open(testfile, O_RDWR); if (vnode_fd < 0) err(1, "open of %s", testfile); kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD, NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE, 0, NULL); } void test_kevent_vnode_note_delete(void) { struct kevent kev; kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL); if (unlink(testfile) < 0) die("unlink"); kevent_cmp(&kev, kevent_get(kqfd)); } void test_kevent_vnode_note_write(void) { struct kevent kev; kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_WRITE, 0, NULL); testfile_write(); /* BSD kqueue adds NOTE_EXTEND even though it was not requested */ /* BSD kqueue removes EV_ENABLE */ kev.flags &= ~EV_ENABLE; // XXX-FIXME compatibility issue kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue kevent_cmp(&kev, kevent_get(kqfd)); } void test_kevent_vnode_note_attrib(void) { struct kevent kev; int nfds; kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL); testfile_touch(); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) die("kevent"); if (kev.ident != vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); } void test_kevent_vnode_note_rename(void) { struct kevent kev; int nfds; kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_RENAME, 0, NULL); testfile_rename(0); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) die("kevent"); if (kev.ident != vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_RENAME) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); testfile_rename(1); test_no_kevents(kqfd); } void test_kevent_vnode_del(void) { struct kevent kev; kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL); } void test_kevent_vnode_disable_and_enable(void) { struct kevent kev; int nfds; test_no_kevents(kqfd); /* Add the watch and immediately disable it */ kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL); kev.flags = EV_DISABLE; kevent_update(kqfd, &kev); /* Confirm that the watch is disabled */ testfile_touch(); test_no_kevents(kqfd); /* Re-enable and check again */ kev.flags = EV_ENABLE; kevent_update(kqfd, &kev); testfile_touch(); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) die("kevent"); if (kev.ident != vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); } #if HAVE_EV_DISPATCH void test_kevent_vnode_dispatch(void) { struct kevent kev; int nfds; test_no_kevents(kqfd); kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_DISPATCH, NOTE_ATTRIB, 0, NULL); testfile_touch(); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) die("kevent"); if (kev.ident != vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); /* Confirm that the watch is disabled automatically */ testfile_touch(); test_no_kevents(kqfd); /* Re-enable the kevent */ /* FIXME- is EV_DISPATCH needed when rearming ? */ kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ENABLE | EV_DISPATCH, 0, 0, NULL); kev.flags = EV_ADD | EV_DISPATCH; /* FIXME: may not be portable */ kev.fflags = NOTE_ATTRIB; testfile_touch(); kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); /* Delete the watch */ kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_DELETE, NOTE_ATTRIB, 0, NULL); } #endif /* HAVE_EV_DISPATCH */ void test_evfilt_vnode(int _kqfd) { snprintf(testfile, sizeof(testfile), "/tmp/kqueue-test%d.tmp", testing_make_uid()); kqfd = _kqfd; test(kevent_vnode_add); test(kevent_vnode_del); test(kevent_vnode_disable_and_enable); #if HAVE_EV_DISPATCH test(kevent_vnode_dispatch); #endif test(kevent_vnode_note_write); test(kevent_vnode_note_attrib); test(kevent_vnode_note_rename); test(kevent_vnode_note_delete); unlink(testfile); } libkqueue-1.0.4/test/signal.c0000644000175000017500000001101111607445336015445 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "common.h" static int __thread kqfd; void test_kevent_signal_add(void) { struct kevent kev; kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); } void test_kevent_signal_get(void) { struct kevent kev; kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); if (kill(getpid(), SIGUSR1) < 0) die("kill"); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); } void test_kevent_signal_disable(void) { struct kevent kev; kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL); if (kill(getpid(), SIGUSR1) < 0) die("kill"); test_no_kevents(kqfd); } void test_kevent_signal_enable(void) { struct kevent kev; kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL); if (kill(getpid(), SIGUSR1) < 0) die("kill"); kev.flags = EV_ADD | EV_CLEAR; #if LIBKQUEUE kev.data = 1; /* WORKAROUND */ #else kev.data = 2; // one extra time from test_kevent_signal_disable() #endif kevent_cmp(&kev, kevent_get(kqfd)); /* Delete the watch */ kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) die("kevent"); } void test_kevent_signal_del(void) { struct kevent kev; /* Delete the kevent */ kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); signal(SIGUSR1, SIG_IGN); if (kill(getpid(), SIGUSR1) < 0) die("kill"); test_no_kevents(kqfd); } void test_kevent_signal_oneshot(void) { struct kevent kev; kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL); if (kill(getpid(), SIGUSR1) < 0) die("kill"); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Send another one and make sure we get no events */ test_no_kevents(kqfd); if (kill(getpid(), SIGUSR1) < 0) die("kill"); test_no_kevents(kqfd); } void test_kevent_signal_modify(void) { struct kevent kev; kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, ((void *)-1)); if (kill(getpid(), SIGUSR1) < 0) die("kill"); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); } #if HAVE_EV_DISPATCH void test_kevent_signal_dispatch(void) { struct kevent kev; test_no_kevents(kqfd); kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_CLEAR | EV_DISPATCH, 0, 0, NULL); /* Get one event */ if (kill(getpid(), SIGUSR1) < 0) die("kill"); kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Confirm that the knote is disabled */ if (kill(getpid(), SIGUSR1) < 0) die("kill"); test_no_kevents(kqfd); /* Enable the knote and make sure no events are pending */ kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE | EV_DISPATCH, 0, 0, NULL); test_no_kevents(kqfd); /* Get the next event */ if (kill(getpid(), SIGUSR1) < 0) die("kill"); kev.flags = EV_ADD | EV_CLEAR | EV_DISPATCH; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Remove the knote and ensure the event no longer fires */ kevent_add(kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); if (kill(getpid(), SIGUSR1) < 0) die("kill"); test_no_kevents(kqfd); } #endif /* HAVE_EV_DISPATCH */ void test_evfilt_signal(int _kqfd) { signal(SIGUSR1, SIG_IGN); kqfd = _kqfd; test(kevent_signal_add); test(kevent_signal_del); test(kevent_signal_get); test(kevent_signal_disable); test(kevent_signal_enable); test(kevent_signal_oneshot); test(kevent_signal_modify); #if HAVE_EV_DISPATCH test(kevent_signal_dispatch); #endif } libkqueue-1.0.4/test/user.c0000644000175000017500000001013711607445336015156 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "common.h" static int __thread kqfd; static void test_kevent_user_add_and_delete(void) { struct kevent kev; kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL); test_no_kevents(kqfd); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL); test_no_kevents(kqfd); } static void test_kevent_user_get(void) { struct kevent kev; test_no_kevents(kqfd); /* Add the event, and then trigger it */ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL); kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); kev.fflags &= ~NOTE_FFCTRLMASK; kev.fflags &= ~NOTE_TRIGGER; kev.flags = EV_CLEAR; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); } static void test_kevent_user_disable_and_enable(void) { struct kevent kev; test_no_kevents(kqfd); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DISABLE, 0, 0, NULL); /* Trigger the event, but since it is disabled, nothing will happen. */ kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); test_no_kevents(kqfd); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ENABLE, 0, 0, NULL); kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); kev.flags = EV_CLEAR; kev.fflags &= ~NOTE_FFCTRLMASK; kev.fflags &= ~NOTE_TRIGGER; kevent_cmp(&kev, kevent_get(kqfd)); } static void test_kevent_user_oneshot(void) { struct kevent kev; test_no_kevents(kqfd); kevent_add(kqfd, &kev, 2, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL); puts(" -- event 1"); kevent_add(kqfd, &kev, 2, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); kev.flags = EV_ONESHOT; kev.fflags &= ~NOTE_FFCTRLMASK; kev.fflags &= ~NOTE_TRIGGER; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); } #if HAVE_EV_DISPATCH void test_kevent_user_dispatch(void) { struct kevent kev; test_no_kevents(kqfd); /* Add the event, and then trigger it */ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR | EV_DISPATCH, 0, 0, NULL); kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); /* Retrieve one event */ kev.fflags &= ~NOTE_FFCTRLMASK; kev.fflags &= ~NOTE_TRIGGER; kev.flags = EV_CLEAR; kevent_cmp(&kev, kevent_get(kqfd)); /* Confirm that the knote is disabled automatically */ test_no_kevents(kqfd); /* Re-enable the kevent */ /* FIXME- is EV_DISPATCH needed when rearming ? */ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ENABLE | EV_CLEAR | EV_DISPATCH, 0, 0, NULL); test_no_kevents(kqfd); /* Trigger the event */ kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); kev.fflags &= ~NOTE_FFCTRLMASK; kev.fflags &= ~NOTE_TRIGGER; kev.flags = EV_CLEAR; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(kqfd); /* Delete the watch */ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL); test_no_kevents(kqfd); } #endif /* HAVE_EV_DISPATCH */ void test_evfilt_user(int _kqfd) { kqfd = _kqfd; test(kevent_user_add_and_delete); test(kevent_user_get); test(kevent_user_disable_and_enable); test(kevent_user_oneshot); #if HAVE_EV_DISPATCH test(kevent_user_dispatch); #endif /* TODO: try different fflags operations */ } libkqueue-1.0.4/test/test.c0000644000175000017500000000507311607445336015162 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef __linux__ #include #endif #include #include #include #include "common.h" static int __thread testnum = 1; static int __thread error_flag = 1; static char __thread * cur_test_id = NULL; /* FIXME: not portable beyond linux */ static void error_handler(int signum) { #ifdef __linux__ void *buf[32]; /* FIXME: the symbols aren't printing */ printf("***** ERROR: Program received signal %d *****\n", signum); backtrace_symbols_fd(buf, sizeof(buf) / sizeof(void *), 2); #else printf("***** ERROR: Program received signal %d *****\n", signum); #endif exit(1); } static void testing_atexit(void) { if (error_flag) { printf(" *** TEST FAILED: %s\n", cur_test_id); //TODO: print detailed log } else { printf("\n---\n" "+OK All %d tests completed.\n", testnum - 1); } } void test_begin(const char *func) { if (cur_test_id) free(cur_test_id); cur_test_id = strdup(func); printf("%d: %s\n", testnum++, cur_test_id); //TODO: redirect stdout/err to logfile } void test_end(void) { free(cur_test_id); cur_test_id = NULL; } void testing_begin(void) { struct sigaction sa; atexit(testing_atexit); /* Install a signal handler for crashes and hangs */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = error_handler; sigemptyset(&sa.sa_mask); sigaction(SIGSEGV, &sa, NULL); sigaction(SIGABRT, &sa, NULL); sigaction(SIGINT, &sa, NULL); } void testing_end(void) { error_flag = 0; } /* Generate a unique ID */ int testing_make_uid(void) { static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; static int id = 0; pthread_mutex_lock(&mtx); if (id == INT_MAX) abort(); id++; pthread_mutex_unlock(&mtx); return (id); } libkqueue-1.0.4/test/timer.c0000644000175000017500000000775111607445336015330 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "common.h" static int __thread kqfd; void test_kevent_timer_add(void) { struct kevent kev; kevent_add(kqfd, &kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); } void test_kevent_timer_del(void) { struct kevent kev; kevent_add(kqfd, &kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); test_no_kevents(kqfd); } void test_kevent_timer_get(void) { struct kevent kev; kevent_add(kqfd, &kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); kevent_add(kqfd, &kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); } static void test_kevent_timer_oneshot(void) { struct kevent kev; test_no_kevents(kqfd); kevent_add(kqfd, &kev, 2, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL); /* Retrieve the event */ kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Check if the event occurs again */ sleep(3); test_no_kevents(kqfd); } static void test_kevent_timer_periodic(void) { struct kevent kev; test_no_kevents(kqfd); kevent_add(kqfd, &kev, 3, EVFILT_TIMER, EV_ADD, 0, 1000,NULL); /* Retrieve the event */ kev.flags = EV_ADD | EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Check if the event occurs again */ sleep(1); kevent_cmp(&kev, kevent_get(kqfd)); /* Delete the event */ kev.flags = EV_DELETE; kevent_update(kqfd, &kev); } static void test_kevent_timer_disable_and_enable(void) { struct kevent kev; test_no_kevents(kqfd); /* Add the watch and immediately disable it */ kevent_add(kqfd, &kev, 4, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,NULL); kev.flags = EV_DISABLE; kevent_update(kqfd, &kev); test_no_kevents(kqfd); /* Re-enable and check again */ kev.flags = EV_ENABLE; kevent_update(kqfd, &kev); kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); } #if HAVE_EV_DISPATCH void test_kevent_timer_dispatch(void) { struct kevent kev; test_no_kevents(kqfd); kevent_add(kqfd, &kev, 4, EVFILT_TIMER, EV_ADD | EV_DISPATCH, 0, 800, NULL); /* Get one event */ kev.flags = EV_ADD | EV_CLEAR | EV_DISPATCH; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Confirm that the knote is disabled */ sleep(1); test_no_kevents(kqfd); /* Enable the knote and make sure no events are pending */ kevent_add(kqfd, &kev, 4, EVFILT_TIMER, EV_ENABLE | EV_DISPATCH, 0, 800, NULL); test_no_kevents(kqfd); /* Get the next event */ sleep(1); kev.flags = EV_ADD | EV_CLEAR | EV_DISPATCH; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Remove the knote and ensure the event no longer fires */ kevent_add(kqfd, &kev, 4, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); sleep(1); test_no_kevents(kqfd); } #endif /* HAVE_EV_DISPATCH */ void test_evfilt_timer(int _kqfd) { kqfd = _kqfd; test(kevent_timer_add); test(kevent_timer_del); test(kevent_timer_get); test(kevent_timer_oneshot); test(kevent_timer_periodic); test(kevent_timer_disable_and_enable); #if HAVE_EV_DISPATCH test(kevent_timer_dispatch); #endif } libkqueue-1.0.4/test/main.c0000644000175000017500000000726511607445336015134 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "common.h" struct unit_test { const char *ut_name; int ut_enabled; void (*ut_func)(int); }; /* * Test the method for detecting when one end of a socketpair * has been closed. This technique is used in kqueue_validate() */ static void test_peer_close_detection(void) { int sockfd[2]; char buf[1]; struct pollfd pfd; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) die("socketpair"); pfd.fd = sockfd[0]; pfd.events = POLLIN | POLLHUP; pfd.revents = 0; if (poll(&pfd, 1, 0) > 0) die("unexpected data"); if (close(sockfd[1]) < 0) die("close"); if (poll(&pfd, 1, 0) > 0) { if (recv(sockfd[0], buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT) != 0) die("failed to detect peer shutdown"); } } void test_kqueue(void) { int kqfd; if ((kqfd = kqueue()) < 0) die("kqueue()"); test_no_kevents(kqfd); if (close(kqfd) < 0) die("close()"); } void test_ev_receipt(void) { int kq; struct kevent kev; if ((kq = kqueue()) < 0) die("kqueue()"); #if HAVE_EV_RECEIPT EV_SET(&kev, SIGUSR2, EVFILT_SIGNAL, EV_ADD | EV_RECEIPT, 0, 0, NULL); if (kevent(kq, &kev, 1, &kev, 1, NULL) < 0) die("kevent"); /* TODO: check the receipt */ close(kq); #else memset(&kev, 0, sizeof(kev)); puts("Skipped -- EV_RECEIPT is not available"); #endif } int main(int argc, char **argv) { struct unit_test tests[] = { { "socket", 1, test_evfilt_read }, { "signal", 1, test_evfilt_signal }, #if FIXME { "proc", 1, test_evfilt_proc }, #endif { "vnode", 1, test_evfilt_vnode }, { "timer", 1, test_evfilt_timer }, #if HAVE_EVFILT_USER { "user", 1, test_evfilt_user }, #endif { NULL, 0, NULL }, }; struct unit_test *test; char *arg; int match, kqfd; /* If specific tests are requested, disable all tests by default */ if (argc > 1) { for (test = &tests[0]; test->ut_name != NULL; test++) { test->ut_enabled = 0; } } while (argc > 1) { match = 0; arg = argv[1]; for (test = &tests[0]; test->ut_name != NULL; test++) { if (strcmp(arg, test->ut_name) == 0) { test->ut_enabled = 1; match = 1; break; } } if (!match) { printf("ERROR: invalid option: %s\n", arg); exit(1); } else { printf("enabled test: %s\n", arg); } argv++; argc--; } testing_begin(); test(peer_close_detection); test(kqueue); if ((kqfd = kqueue()) < 0) die("kqueue()"); for (test = &tests[0]; test->ut_name != NULL; test++) { if (test->ut_enabled) test->ut_func(kqfd); } test(ev_receipt); testing_end(); return (0); } libkqueue-1.0.4/test/Makefile0000644000175000017500000000267011607445336015477 0ustar mheilymheily# # Copyright (c) 2009 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # include config.mk all: kqtest kqtest: $(SOURCES) $(CC) -o kqtest $(CFLAGS) $(SOURCES) $(LDADD) check: kqtest ./kqtest valgrind: kqtest valgrind --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=20 --track-fds=yes ./kqtest check-installed: $(CC) -o kqtest $(CFLAGS) $(SOURCES) $(LDADD) -lkqueue ./kqtest check-libtool: gcc $(CFLAGS) -c *.c libtool --mode=link gcc -g -O0 -o kqtest *.o $(LIBDIR)/libkqueue.la ./kqtest distclean: clean rm -f config.mk config.h for x in libdispatch stress; do cd $$x && make distclean && cd .. ; done edit: $(EDITOR) *.[ch] clean: rm -f *.o *.a kqtest for x in libdispatch stress; do cd $$x && make clean && cd ..; done libkqueue-1.0.4/test/config.inc0000755000175000017500000000144511607445336016001 0ustar mheilymheily#!/bin/sh program=libkqueue-test version=0.1 cflags="-g -O0 -Wall -Werror" sources="main.c kevent.c test.c proc.c read.c signal.c timer.c vnode.c" pre_configure_hook() { check_header "err.h" check_header "sys/event.h" \ && sys_event_h="sys/event.h" \ || { sys_event_h="../include/sys/event.h" cflags="$cflags -I../include" ldadd="$ldadd ../libkqueue.a -lpthread -lrt" } test "$target" = "linux" && cflags="$cflags -rdynamic" test "$target" = "solaris" && ldadd="$ldadd -lsocket -lnsl -m64" check_symbol $sys_event_h EV_DISPATCH check_symbol $sys_event_h EV_RECEIPT check_symbol $sys_event_h NOTE_TRUNCATE check_symbol $sys_event_h EVFILT_TIMER check_symbol $sys_event_h EVFILT_USER && \ sources="$sources user.c" } libkqueue-1.0.4/test/kevent.c0000644000175000017500000001053611607445336015477 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "common.h" extern int kqfd; /* Checks if any events are pending, which is an error. */ void test_no_kevents(int kqfd) { int nfds; struct timespec timeo; struct kevent kev; memset(&timeo, 0, sizeof(timeo)); nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo); if (nfds < 0) die("kevent(2)"); if (nfds > 0) { puts("\nUnexpected event:"); die(kevent_to_str(&kev)); } } /* Retrieve a single kevent */ struct kevent * kevent_get(int kqfd) { int nfds; static struct kevent __thread kev; nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) die("kevent(2)"); return (&kev); } char * kevent_fflags_dump(struct kevent *kev) { char *buf; #define KEVFFL_DUMP(attrib) \ if (kev->fflags & attrib) \ strncat(buf, #attrib" ", 64); if ((buf = calloc(1, 1024)) == NULL) abort(); /* Not every filter has meaningful fflags */ if (kev->filter != EVFILT_VNODE) { snprintf(buf, 1024, "fflags = %d", kev->fflags); return (buf); } snprintf(buf, 1024, "fflags = %d (", kev->fflags); KEVFFL_DUMP(NOTE_DELETE); KEVFFL_DUMP(NOTE_WRITE); KEVFFL_DUMP(NOTE_EXTEND); #if HAVE_NOTE_TRUNCATE KEVFFL_DUMP(NOTE_TRUNCATE); #endif KEVFFL_DUMP(NOTE_ATTRIB); KEVFFL_DUMP(NOTE_LINK); KEVFFL_DUMP(NOTE_RENAME); #if HAVE_NOTE_REVOKE KEVFFL_DUMP(NOTE_REVOKE); #endif buf[strlen(buf) - 1] = ')'; return (buf); } char * kevent_flags_dump(struct kevent *kev) { char *buf; #define KEVFL_DUMP(attrib) \ if (kev->flags & attrib) \ strncat(buf, #attrib" ", 64); if ((buf = calloc(1, 1024)) == NULL) abort(); snprintf(buf, 1024, "flags = %d (", kev->flags); KEVFL_DUMP(EV_ADD); KEVFL_DUMP(EV_ENABLE); KEVFL_DUMP(EV_DISABLE); KEVFL_DUMP(EV_DELETE); KEVFL_DUMP(EV_ONESHOT); KEVFL_DUMP(EV_CLEAR); KEVFL_DUMP(EV_EOF); KEVFL_DUMP(EV_ERROR); #if HAVE_EV_DISPATCH KEVFL_DUMP(EV_DISPATCH); #endif #if HAVE_EV_RECEIPT KEVFL_DUMP(EV_RECEIPT); #endif buf[strlen(buf) - 1] = ')'; return (buf); } /* TODO - backport changes from src/common/kevent.c kevent_dump() */ const char * kevent_to_str(struct kevent *kev) { char buf[512]; snprintf(&buf[0], sizeof(buf), "[ident=%d, filter=%d, %s, %s, data=%d, udata=%p]", (u_int) kev->ident, kev->filter, kevent_flags_dump(kev), kevent_fflags_dump(kev), (int) kev->data, kev->udata); return (strdup(buf)); } void kevent_update(int kqfd, struct kevent *kev) { if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) { printf("Unable to add the following kevent:\n%s\n", kevent_to_str(kev)); die("kevent"); } } void kevent_add(int kqfd, struct kevent *kev, uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata) { EV_SET(kev, ident, filter, flags, fflags, data, NULL); if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) { printf("Unable to add the following kevent:\n%s\n", kevent_to_str(kev)); die("kevent"); } } void kevent_cmp(struct kevent *k1, struct kevent *k2) { /* XXX- Workaround for inconsistent implementation of kevent(2) */ #if defined (__FreeBSD_kernel__) || defined (__FreeBSD__) if (k1->flags & EV_ADD) k2->flags |= EV_ADD; #endif if (memcmp(k1, k2, sizeof(*k1)) != 0) { printf("kevent_cmp: mismatch:\n expected %s\n but got %s\n", kevent_to_str(k1), kevent_to_str(k2)); abort(); } } libkqueue-1.0.4/test/common.h0000644000175000017500000000572311607445336015502 0ustar mheilymheily/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _COMMON_H #define _COMMON_H #if HAVE_ERR_H # include #else # define err(rc,msg,...) do { perror(msg); exit(rc); } while (0) # define errx(rc,msg,...) do { puts(msg); exit(rc); } while (0) #endif #define die(str) do { \ fprintf(stderr, "%s(): %s: %s\n", __func__,str, strerror(errno));\ abort();\ } while (0) #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" void test_evfilt_read(int); void test_evfilt_signal(int); void test_evfilt_vnode(int); void test_evfilt_timer(int); void test_evfilt_proc(int); #if HAVE_EVFILT_USER void test_evfilt_user(int); #endif #define test(f,...) do { \ test_begin("test_"#f"()\t"__VA_ARGS__); \ test_##f();\ test_end(); \ } while (/*CONSTCOND*/0) extern const char * kevent_to_str(struct kevent *); struct kevent * kevent_get(int); void kevent_update(int kqfd, struct kevent *kev); void kevent_cmp(struct kevent *, struct kevent *); void kevent_add(int kqfd, struct kevent *kev, uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata); /* DEPRECATED: */ #define KEV_CMP(kev,_ident,_filter,_flags) do { \ if (kev.ident != (_ident) || \ kev.filter != (_filter) || \ kev.flags != (_flags)) \ err(1, "kevent mismatch: got [%d,%d,%d] but expecting [%d,%d,%d]", \ (int)_ident, (int)_filter, (int)_flags,\ (int)kev.ident, kev.filter, kev.flags);\ } while (0); /* Checks if any events are pending, which is an error. */ void test_no_kevents(int); /* From test.c */ void test_begin(const char *); void test_end(void); void test_atexit(void); void testing_begin(void); void testing_end(void); int testing_make_uid(void); #endif /* _COMMON_H */ libkqueue-1.0.4/test/libkqueue-test.pc0000644000175000017500000000005111607445336017315 0ustar mheilymheily# AUTOMATICALLY GENERATED -- DO NOT EDIT