mirror of
https://github.com/darlinghq/darling-libkqueue.git
synced 2024-11-26 21:20:38 +00:00
v0.5
git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/tags/REL_0_4@201 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
This commit is contained in:
parent
443e157e1f
commit
b6d85c3ec8
13
ChangeLog
13
ChangeLog
@ -1,3 +1,16 @@
|
||||
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
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
56
Makefile
56
Makefile
@ -16,19 +16,28 @@
|
||||
|
||||
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
|
||||
|
||||
build:
|
||||
$(CC) $(CFLAGS) -c $(SOURCES)
|
||||
ar rcs libkqueue.a *.o
|
||||
gcc -shared -Wl,-soname,libkqueue.so -o libkqueue.so *.o
|
||||
.PHONY :: install uninstall check dist dist-upload publish-www clean merge distclean fresh-build rpm
|
||||
|
||||
install:
|
||||
%.o: %.c $(DEPS)
|
||||
$(CC) -c -o $@ $(CFLAGS) $<
|
||||
|
||||
$(PROGRAM).so: $(OBJS)
|
||||
$(AR) rcs $(PROGRAM).a $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $(PROGRAM).so $(OBJS) $(LDADD)
|
||||
|
||||
all: $(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 libkqueue.so $(LIBDIR)/libkqueue.so
|
||||
$(INSTALL) -m 644 $(PROGRAM).so $(LIBDIR)
|
||||
$(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
|
||||
@ -46,17 +55,22 @@ uninstall:
|
||||
check:
|
||||
cd test && ./configure && make check
|
||||
|
||||
dist:
|
||||
$(DISTFILE): $(OBJS)
|
||||
cd test && make distclean || true
|
||||
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`
|
||||
rm -rf `find $(PROGRAM)-$(VERSION) -type d -name .svn -o -name .libs`
|
||||
cd $(PROGRAM)-$(VERSION) && rm $(OBJS)
|
||||
tar zcf $(PROGRAM)-$(VERSION).tar.gz $(PROGRAM)-$(VERSION)
|
||||
rm -rf $(PROGRAM)-$(VERSION)
|
||||
|
||||
dist:
|
||||
rm -f $(DISTFILE)
|
||||
make $(DISTFILE)
|
||||
|
||||
dist-upload: dist
|
||||
scp $(PROGRAM)-$(VERSION).tar.gz $(DIST)
|
||||
|
||||
@ -64,7 +78,14 @@ publish-www:
|
||||
rm ~/public_html/libkqueue/*.html ; cp -R www/*.html ~/public_html/libkqueue/
|
||||
|
||||
clean:
|
||||
rm -f a.out *.a *.o *.so
|
||||
rm -f *.a $(OBJS) *.so
|
||||
cd test && make clean || 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 -
|
||||
@ -73,4 +94,19 @@ merge:
|
||||
echo "ok"
|
||||
|
||||
distclean: clean
|
||||
rm -f *.tar.gz config.mk config.h libkqueue.pc
|
||||
rm -f *.tar.gz config.mk config.h $(PROGRAM).pc $(PROGRAM).la rpm.spec
|
||||
|
||||
rpm: clean $(DISTFILE)
|
||||
rm -rf rpm *.rpm *.deb
|
||||
mkdir -p rpm/BUILD rpm/RPMS rpm/SOURCES rpm/SPECS rpm/SRPMS
|
||||
mkdir -p rpm/RPMS/i386 rpm/RPMS/x86_64
|
||||
cp $(DISTFILE) rpm/SOURCES
|
||||
rpmbuild -bb rpm.spec
|
||||
mv ./rpm/RPMS/* .
|
||||
rm -rf rpm
|
||||
rmdir i386 x86_64 # WORKAROUND: These aren't supposed to exist
|
||||
fakeroot alien --scripts *.rpm
|
||||
|
||||
debug-install:
|
||||
./configure --prefix=/usr --debug=yes
|
||||
make clean && make && sudo make install
|
||||
|
42
config.inc
42
config.inc
@ -1,18 +1,31 @@
|
||||
program="libkqueue"
|
||||
version="0.3"
|
||||
version="0.4"
|
||||
cflags="-fPIC -I./include -I./src/common -Wall -Werror"
|
||||
sources="src/common/*.c"
|
||||
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() {
|
||||
if [ "$debug" = "yes" ] ; then
|
||||
cflags="$cflags -DKQUEUE_DEBUG"
|
||||
cflags="$cflags -g3 -O0 -DKQUEUE_DEBUG"
|
||||
else
|
||||
cflags="$cflags -g -O2"
|
||||
fi
|
||||
|
||||
optional_headers="err.h"
|
||||
|
||||
libdepends=" -L$libdir"
|
||||
if [ $target = "linux" ] ; then
|
||||
libdepends="$libdepends -lpthread -lrt"
|
||||
required_headers="sys/epoll.h sys/inotify.h
|
||||
@ -22,5 +35,26 @@ pre_configure_hook() {
|
||||
|
||||
post_configure_hook() {
|
||||
finalize target "$target"
|
||||
sources="$sources src/$target/*.c"
|
||||
|
||||
evfilt_signal="src/$target/signal.c"
|
||||
evfilt_proc="src/$target/proc.c"
|
||||
evfilt_socket="src/$target/socket.c"
|
||||
evfilt_timer="src/$target/timer.c"
|
||||
evfilt_user="src/$target/user.c"
|
||||
evfilt_vnode="src/$target/vnode.c"
|
||||
|
||||
if [ $target = "linux" ] ; then
|
||||
if [ "$have_sys_signalfd_h" != "yes" ] ; then
|
||||
evfilt_signal="src/posix/signal.c"
|
||||
fi
|
||||
if [ "$have_sys_timerfd_h" != "yes" ] ; then
|
||||
evfilt_timer="" # TODO: "src/posix/timer.c"
|
||||
fi
|
||||
if [ "$have_sys_eventfd_h" != "yes" ] ; then
|
||||
evfilt_user="" # TODO: "src/posix/user.c"
|
||||
fi
|
||||
fi
|
||||
|
||||
sources="$sources src/$target/hook.c $evfilt_signal $evfilt_proc
|
||||
$evfilt_socket $evfilt_timer $evfilt_user $evfilt_vnode"
|
||||
}
|
||||
|
150
configure
vendored
150
configure
vendored
@ -5,8 +5,8 @@ c_exports="program version target cflags"
|
||||
make_exports="program version target \
|
||||
prefix libdir includedir mandir \
|
||||
cflags ldflags ldadd libdepends \
|
||||
sources mans headers extra_dist subdirs \
|
||||
install"
|
||||
sources objs deps mans headers extra_dist subdirs \
|
||||
cc cpp ld ar install"
|
||||
|
||||
required_headers=
|
||||
optional_headers=
|
||||
@ -19,12 +19,17 @@ post_configure_hook() {
|
||||
return
|
||||
}
|
||||
|
||||
. ./config.inc
|
||||
|
||||
export_to_make() {
|
||||
for id in $*
|
||||
do
|
||||
uc_id=`echo $id | tr 'a-z' 'A-Z'`;
|
||||
|
||||
# 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
|
||||
}
|
||||
@ -32,12 +37,13 @@ export_to_make() {
|
||||
export_to_c() {
|
||||
for id in $*
|
||||
do
|
||||
uc_id=`echo $id | tr 'a-z' 'A-Z'`;
|
||||
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"
|
||||
}
|
||||
|
||||
@ -51,10 +57,19 @@ process_argv() {
|
||||
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 "$LDFLAGS" && ldflags="$LDFLAGS"
|
||||
test -n "$AR" && ar="$AR"
|
||||
}
|
||||
|
||||
check_header() {
|
||||
sym=`echo "have_$1" | sed 's,[./],_,g'`
|
||||
uc_sym=`echo "$sym" | tr a-z A-Z`
|
||||
uc_sym=`echo "$sym" | $tr '[:lower:]' '[:upper:]'`;
|
||||
path=$1
|
||||
|
||||
printf "checking for $path.. "
|
||||
@ -82,8 +97,8 @@ check_symbol() {
|
||||
header=$1
|
||||
symbol=$2
|
||||
|
||||
uc_symbol=`echo "HAVE_$symbol" | tr a-z A-Z | sed 's,[./],_,g'`
|
||||
lc_symbol=`echo "have_$symbol" | tr A-Z a-z | sed 's,[./],_,g'`
|
||||
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"
|
||||
@ -119,17 +134,101 @@ check_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
|
||||
finalize target "$default_target"
|
||||
echo "$target"
|
||||
}
|
||||
|
||||
check_compiler() {
|
||||
printf "checking for a C compiler.. "
|
||||
if [ "`uname -s`" = "SunOS" ] ; then
|
||||
default_cc="/usr/sfw/bin/gcc"
|
||||
else
|
||||
default_cc="/usr/bin/cc"
|
||||
fi
|
||||
finalize cc "$default_cc"
|
||||
echo "$cc"
|
||||
}
|
||||
|
||||
check_linker() {
|
||||
printf "checking for a suitable linker.. "
|
||||
|
||||
# Disabled due to problems with ld(1) under Linux
|
||||
#if [ "`uname -s`" = "SunOS" ] ; then
|
||||
# default_ld="/usr/sfw/bin/gld"
|
||||
#else
|
||||
# default_ld="/usr/bin/ld"
|
||||
#fi
|
||||
#ldflags="-shared -export-dynamic -soname $program.so $ldflags"
|
||||
|
||||
# Workaround for "hidden symbol <foo> 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 -Wl,-soname,$program.so $ldflags"
|
||||
|
||||
finalize ld "$default_ld"
|
||||
echo "$ld"
|
||||
}
|
||||
|
||||
check_archiver() {
|
||||
printf "checking for a suitable archiver.. "
|
||||
if [ "`uname -s`" = "SunOS" ] ; then
|
||||
default_ar="/usr/sfw/bin/gar"
|
||||
else
|
||||
default_ar="/usr/bin/ar"
|
||||
fi
|
||||
finalize ar "$default_ar"
|
||||
echo "$ar"
|
||||
}
|
||||
err() {
|
||||
echo "*** ERROR *** $*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
subst_vars() {
|
||||
outfile=$1
|
||||
|
||||
if [ ! -f "${outfile}.in" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Creating $outfile"
|
||||
rm -f $outfile
|
||||
sed -e "
|
||||
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
|
||||
@ -141,10 +240,16 @@ 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
|
||||
|
||||
finalize program "$program"
|
||||
finalize version "$version"
|
||||
finalize target `uname -s | tr A-Z a-z`
|
||||
finalize prefix "/usr/local"
|
||||
finalize libdir "${prefix}/lib"
|
||||
finalize includedir "${prefix}/include"
|
||||
@ -153,10 +258,7 @@ finalize cflags "$cflags"
|
||||
finalize libdepends "$libdepends"
|
||||
finalize ldadd ""
|
||||
finalize ldflags ""
|
||||
finalize cc "/usr/bin/cc"
|
||||
|
||||
echo "checking operating system type.. $target"
|
||||
check_install
|
||||
finalize deps ""
|
||||
|
||||
pre_configure_hook
|
||||
|
||||
@ -168,22 +270,16 @@ check_headers $optional_headers
|
||||
|
||||
post_configure_hook
|
||||
|
||||
if [ -f "$program.pc.in" ] ; then
|
||||
echo "Creating $program.pc"
|
||||
sed -e "
|
||||
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;
|
||||
" < $program.pc.in >> $program.pc
|
||||
chmod 400 $program.pc
|
||||
fi
|
||||
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"
|
||||
|
||||
make clean >/dev/null 2>&1
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
struct timespec;
|
||||
|
||||
@ -41,17 +40,15 @@ 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_USER (-11) /* User events */
|
||||
#if !LIBKQUEUE
|
||||
#define EVFILT_AIO (-3) /* attached to aio requests */
|
||||
#define EVFILT_NETDEV (-8) /* network devices */
|
||||
#define EVFILT_FS (-9) /* filesystem events */
|
||||
#define EVFILT_LIO (-10) /* attached to lio requests */
|
||||
#endif /* ! LIBKQUEUE */
|
||||
#define EVFILT_USER (-11) /* User events */
|
||||
#define EVFILT_SYSCOUNT 11
|
||||
|
||||
#define EV_SET(kevp_, a, b, c, d, e, f) do { \
|
||||
@ -133,7 +130,6 @@ struct kevent {
|
||||
* data/hint flags for EVFILT_PROC
|
||||
*/
|
||||
#define NOTE_EXIT 0x80000000 /* process exited */
|
||||
#if ! LIBKQUEUE
|
||||
#define NOTE_FORK 0x40000000 /* process forked */
|
||||
#define NOTE_EXEC 0x20000000 /* process exec'd */
|
||||
#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */
|
||||
@ -143,23 +139,39 @@ struct kevent {
|
||||
#define NOTE_TRACK 0x00000001 /* follow across forks */
|
||||
#define NOTE_TRACKERR 0x00000002 /* could not track child */
|
||||
#define NOTE_CHILD 0x00000004 /* am a child process */
|
||||
#endif /* ! LIBKQUEUE */
|
||||
|
||||
#if ! LIBKQUEUE
|
||||
/*
|
||||
* 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 */
|
||||
#endif /* ! LIBKQUEUE */
|
||||
|
||||
__BEGIN_DECLS
|
||||
/* KLUDGE: This is from <sys/mount.h> 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 */
|
||||
|
||||
|
||||
#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);
|
||||
__END_DECLS
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_SYS_EVENT_H_ */
|
||||
|
||||
|
35
libkqueue.la.in
Normal file
35
libkqueue.la.in
Normal file
@ -0,0 +1,35 @@
|
||||
# @@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@@'
|
@ -7,7 +7,7 @@ Name: @@PROGRAM@@
|
||||
Description: Emulates FreeBSD kqueue(2) on other platforms
|
||||
Version: @@VERSION@@
|
||||
Requires:
|
||||
Libs: -L${libdir} -lkqueue
|
||||
Libs: @@LIBDEPENDS@@ -lkqueue
|
||||
Libs.private: @@LIBDEPENDS@@
|
||||
Cflags: -I${includedir}/kqueue
|
||||
|
||||
|
64
rpm.spec.in
Normal file
64
rpm.spec.in
Normal file
@ -0,0 +1,64 @@
|
||||
#
|
||||
# Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
%define _topdir ./rpm
|
||||
%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
|
||||
|
||||
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}-%{release}-build
|
||||
|
||||
%description
|
||||
@@PKG_DESCRIPTION@@
|
||||
|
||||
%prep
|
||||
tar zxvf ../SOURCES/@@PROGRAM@@-@@VERSION@@.tar.gz
|
||||
mv @@PROGRAM@@-@@VERSION@@/* .
|
||||
|
||||
%build
|
||||
./configure --prefix=/usr
|
||||
make
|
||||
|
||||
%install
|
||||
make DESTDIR=%buildroot install
|
||||
|
||||
%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.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
|
@ -46,7 +46,13 @@ filter_register(struct kqueue *kq, short filter, const struct filter *src)
|
||||
dst = &kq->kq_filt[filt];
|
||||
memcpy(dst, src, sizeof(*src));
|
||||
dst->kf_kqueue = kq;
|
||||
KNOTELIST_INIT(&dst->knl);
|
||||
pthread_mutex_init(&dst->kf_mtx, NULL);
|
||||
KNOTELIST_INIT(&dst->kf_watchlist);
|
||||
KNOTELIST_INIT(&dst->kf_eventlist);
|
||||
if (src->kf_id == 0) {
|
||||
dbg_puts("filter is not implemented");
|
||||
return (0);
|
||||
}
|
||||
if (src->kf_init == NULL) {
|
||||
dbg_puts("filter has no initializer");
|
||||
return (-1);
|
||||
@ -60,15 +66,12 @@ filter_register(struct kqueue *kq, short filter, const struct filter *src)
|
||||
}
|
||||
|
||||
/* Add the filter's event descriptor to the main fdset */
|
||||
if (dst->kf_pfd <= 0) {
|
||||
dbg_printf("FIXME - filter %s did not return a fd to poll!",
|
||||
filter_name(filter));
|
||||
return (-1);
|
||||
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);
|
||||
}
|
||||
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("%s registered", filter_name(filter));
|
||||
|
||||
return (0);
|
||||
@ -111,7 +114,11 @@ filter_unregister_all(struct kqueue *kq)
|
||||
kq->kq_filt[i].kf_destroy(&kq->kq_filt[i]);
|
||||
|
||||
/* Destroy all knotes associated with this filter */
|
||||
for (n1 = LIST_FIRST(&kq->kq_filt[i].knl); n1 != NULL; n1 = n2) {
|
||||
for (n1 = LIST_FIRST(&kq->kq_filt[i].kf_watchlist); n1 != NULL; n1 = n2) {
|
||||
n2 = LIST_NEXT(n1, entries);
|
||||
free(n1);
|
||||
}
|
||||
for (n1 = LIST_FIRST(&kq->kq_filt[i].kf_eventlist); n1 != NULL; n1 = n2) {
|
||||
n2 = LIST_NEXT(n1, entries);
|
||||
free(n1);
|
||||
}
|
||||
@ -124,7 +131,7 @@ filter_socketpair(struct filter *filt)
|
||||
{
|
||||
int sockfd[2];
|
||||
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0)
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
|
||||
return (-1);
|
||||
|
||||
fcntl(sockfd[0], F_SETFL, O_NONBLOCK);
|
||||
@ -133,25 +140,29 @@ filter_socketpair(struct filter *filt)
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct filter *
|
||||
filter_lookup(struct kqueue *kq, short id)
|
||||
int
|
||||
filter_lookup(struct filter **filt, struct kqueue *kq, short id)
|
||||
{
|
||||
id = (-1 * id) - 1;
|
||||
if (id < 0 || id >= EVFILT_SYSCOUNT) {
|
||||
if (~id < 0 || ~id >= EVFILT_SYSCOUNT) {
|
||||
dbg_printf("invalid id: id %d ~id %d", id, (~id));
|
||||
errno = EINVAL;
|
||||
return (NULL);
|
||||
*filt = NULL;
|
||||
return (-1);
|
||||
}
|
||||
if (kq->kq_filt[id].kf_copyin == NULL) {
|
||||
errno = ENOTSUP;
|
||||
return (NULL);
|
||||
*filt = &kq->kq_filt[~id];
|
||||
if ((*filt)->kf_copyin == NULL) {
|
||||
errno = ENOSYS;
|
||||
*filt = NULL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (&kq->kq_filt[id]);
|
||||
return (0);
|
||||
}
|
||||
|
||||
const char *
|
||||
filter_name(short filt)
|
||||
{
|
||||
unsigned int id;
|
||||
const char *fname[EVFILT_SYSCOUNT] = {
|
||||
"EVFILT_READ",
|
||||
"EVFILT_WRITE",
|
||||
@ -166,8 +177,52 @@ filter_name(short filt)
|
||||
"EVFILT_USER"
|
||||
};
|
||||
|
||||
if (~filt >= EVFILT_SYSCOUNT)
|
||||
return "EVFILT_BAD_RANGE";
|
||||
id = ~filt;
|
||||
if (id < 0 || id >= EVFILT_SYSCOUNT)
|
||||
return "EVFILT_INVALID";
|
||||
else
|
||||
return fname[~filt];
|
||||
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);
|
||||
}
|
||||
|
@ -32,16 +32,99 @@
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
static char *
|
||||
kevent_filter_dump(const struct kevent *kev)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if ((buf = calloc(1, 64)) == NULL)
|
||||
abort();
|
||||
|
||||
snprintf(buf, 64, "%d (%s)", kev->filter, filter_name(kev->filter));
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static char *
|
||||
kevent_fflags_dump(const struct kevent *kev)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
#define KEVFFL_DUMP(attrib) \
|
||||
if (kev->fflags & attrib) \
|
||||
strncat(buf, #attrib" ", 64);
|
||||
|
||||
if ((buf = calloc(1, 1024)) == NULL)
|
||||
abort();
|
||||
|
||||
snprintf(buf, 1024, "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)
|
||||
{
|
||||
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=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(struct kevent *kev)
|
||||
kevent_dump(const struct kevent *kev)
|
||||
{
|
||||
char buf[512];
|
||||
snprintf(&buf[0], sizeof(buf), "[filter=%d,flags=%d,ident=%u,udata=%p]",
|
||||
kev->filter,
|
||||
kev->flags,
|
||||
|
||||
snprintf(&buf[0], 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 (strdup(buf));
|
||||
|
||||
return (strdup(buf)); /* FIXME: memory leak */
|
||||
}
|
||||
|
||||
static void
|
||||
@ -51,21 +134,31 @@ kevent_error(struct kevent *dst, const struct kevent *src, int data)
|
||||
dst->data = data;
|
||||
}
|
||||
|
||||
/** @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)
|
||||
{
|
||||
struct knote *dst;
|
||||
struct knote *dst = NULL;
|
||||
struct filter *filt;
|
||||
int kn_alloc,status;
|
||||
int kn_alloc;
|
||||
int status, rv;
|
||||
|
||||
kn_alloc = 0;
|
||||
status = 0;
|
||||
rv = 0;
|
||||
|
||||
dbg_printf("nchanges=%d nevents=%d", nchanges, nevents);
|
||||
|
||||
for (; nchanges > 0; src++, nchanges--) {
|
||||
|
||||
if ((filt = filter_lookup(kq, src->filter)) == NULL) {
|
||||
status = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
status = filter_lookup(&filt, kq, src->filter);
|
||||
if (status < 0)
|
||||
goto err_out_unlocked;
|
||||
|
||||
dbg_printf("%d %s\n", nchanges, kevent_dump(src));
|
||||
|
||||
filter_lock(filt);
|
||||
/*
|
||||
* Retrieve an existing knote object, or create a new one.
|
||||
*/
|
||||
@ -77,7 +170,6 @@ kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
|
||||
status = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
dbg_puts(kevent_dump(&dst->kev));
|
||||
kn_alloc = 1;
|
||||
} else if (src->flags & EV_ENABLE
|
||||
|| src->flags & EV_DISABLE
|
||||
@ -85,6 +177,14 @@ kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
|
||||
status = -ENOENT;
|
||||
goto err_out;
|
||||
} else {
|
||||
|
||||
/* Special case for EVFILT_USER:
|
||||
Ignore user-generated events that are not of interest */
|
||||
if (src->fflags & NOTE_TRIGGER) {
|
||||
filter_unlock(filt);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* flags == 0 or no action */
|
||||
status = -EINVAL;
|
||||
goto err_out;
|
||||
@ -110,20 +210,24 @@ kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
filter_unlock(filt);
|
||||
continue;
|
||||
|
||||
err_out:
|
||||
filter_unlock(filt);
|
||||
err_out_unlocked:
|
||||
if (status != 0 && kn_alloc)
|
||||
knote_free(dst);
|
||||
if (nevents > 0) {
|
||||
kevent_error(eventlist++, src, status);
|
||||
nevents--;
|
||||
rv++;
|
||||
} else {
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
@ -132,9 +236,7 @@ kevent(int kqfd, const struct kevent *changelist, int nchanges,
|
||||
const struct timespec *timeout)
|
||||
{
|
||||
struct kqueue *kq;
|
||||
struct filter *filt;
|
||||
fd_set fds;
|
||||
int i, rv, n, nret;
|
||||
int rv, n, nret;
|
||||
|
||||
errno = 0;
|
||||
|
||||
@ -149,11 +251,13 @@ kevent(int kqfd, const struct kevent *changelist, int nchanges,
|
||||
* Process each kevent on the changelist.
|
||||
*/
|
||||
if (nchanges) {
|
||||
kqueue_lock(kq);
|
||||
rv = kevent_copyin(kq, changelist, nchanges, eventlist, nevents);
|
||||
kqueue_unlock(kq);
|
||||
if (rv < 0)
|
||||
return (-1);
|
||||
if (rv > 0) {
|
||||
eventlist += rv;
|
||||
nevents -= rv;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if we need to wait for events. */
|
||||
@ -162,52 +266,24 @@ kevent(int kqfd, const struct kevent *changelist, int nchanges,
|
||||
if (nevents > MAX_KEVENT)
|
||||
nevents = MAX_KEVENT;
|
||||
|
||||
/*
|
||||
* Wait for one or more filters to have events.
|
||||
*/
|
||||
wait_for_events:
|
||||
fds = kq->kq_fds;
|
||||
n = pselect(kq->kq_nfds, &fds, NULL , NULL, timeout, NULL);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
dbg_puts("signal caught");
|
||||
return (-1);
|
||||
}
|
||||
dbg_perror("pselect(2)");
|
||||
return (-1);
|
||||
}
|
||||
dbg_printf("pselect(2): %d bits set", n);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Process each event and place it on the eventlist
|
||||
*/
|
||||
nret = 0;
|
||||
kqueue_lock(kq);
|
||||
for (i = 0; (i < EVFILT_SYSCOUNT && n > 0 && nevents > 0); i++) {
|
||||
dbg_printf("eventlist: n = %d nevents = %d", n, nevents);
|
||||
filt = &kq->kq_filt[i];
|
||||
dbg_printf("pfd[%d] = %d", i, filt->kf_pfd);
|
||||
if (FD_ISSET(filt->kf_pfd, &fds)) {
|
||||
dbg_printf("event(s) for filter #%d", i);
|
||||
rv = filt->kf_copyout(filt, eventlist, nevents);
|
||||
if (rv < 0) {
|
||||
kqueue_unlock(kq);
|
||||
dbg_puts("kevent_copyout failed");
|
||||
return (-1);
|
||||
}
|
||||
nret += rv;
|
||||
eventlist += rv;
|
||||
nevents -= rv;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
kqueue_unlock(kq);
|
||||
|
||||
/* Handle spurious wakeups where no events are generated. */
|
||||
if (nret == 0)
|
||||
goto wait_for_events;
|
||||
for (nret = 0; nret == 0;
|
||||
nret = kevent_copyout(kq, n, eventlist, nevents))
|
||||
{
|
||||
/* Wait for one or more events. */
|
||||
n = kevent_wait(kq, timeout);
|
||||
if (n < 0)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0); /* Timeout */
|
||||
}
|
||||
|
||||
#ifdef KQUEUE_DEBUG
|
||||
dbg_printf("returning %d events", nret);
|
||||
for (n = 0; n < nret; n++) {
|
||||
dbg_printf("eventlist[%d] = %s", n, kevent_dump(&eventlist[n]));
|
||||
}
|
||||
#endif /* KQUEUE_DEBUG */
|
||||
|
||||
return (nret);
|
||||
}
|
||||
|
@ -30,15 +30,15 @@ knote_new(struct filter *filt)
|
||||
|
||||
if ((dst = calloc(1, sizeof(*dst))) == NULL)
|
||||
return (NULL);
|
||||
KNOTE_INSERT(&filt->knl, dst);
|
||||
KNOTE_INSERT(&filt->kf_watchlist, dst);
|
||||
return (dst);
|
||||
}
|
||||
|
||||
void
|
||||
knote_free(struct knote *kn)
|
||||
{
|
||||
dbg_printf("knote_free(): filt=%d, ident=%u",
|
||||
kn->kev.filter, (u_int) kn->kev.ident);
|
||||
dbg_printf("filter=%s, ident=%u",
|
||||
filter_name(kn->kev.filter), (u_int) kn->kev.ident);
|
||||
LIST_REMOVE(kn, entries);
|
||||
free(kn);
|
||||
}
|
||||
@ -48,20 +48,33 @@ struct knote *
|
||||
knote_lookup(struct filter *filt, short ident)
|
||||
{
|
||||
struct knote *kn;
|
||||
LIST_FOREACH(kn, &filt->knl, entries) {
|
||||
|
||||
/* TODO: Use rbtree for faster searching */
|
||||
LIST_FOREACH(kn, &filt->kf_watchlist, entries) {
|
||||
if (ident == kn->kev.ident)
|
||||
break;
|
||||
return (kn);
|
||||
}
|
||||
return (kn);
|
||||
LIST_FOREACH(kn, &filt->kf_eventlist, entries) {
|
||||
if (ident == kn->kev.ident)
|
||||
return (kn);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
struct knote *
|
||||
knote_lookup_data(struct filter *filt, intptr_t data)
|
||||
{
|
||||
struct knote *kn;
|
||||
LIST_FOREACH(kn, &filt->knl, entries) {
|
||||
|
||||
LIST_FOREACH(kn, &filt->kf_watchlist, entries) {
|
||||
if (data == kn->kev.data)
|
||||
break;
|
||||
return (kn);
|
||||
}
|
||||
return (kn);
|
||||
LIST_FOREACH(kn, &filt->kf_eventlist, entries) {
|
||||
if (data == kn->kev.data)
|
||||
return (kn);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
@ -30,258 +30,84 @@
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
static int kqueue_initialized; /* Set to 1 by kqueue_init() */
|
||||
|
||||
static LIST_HEAD(,kqueue) kqlist = LIST_HEAD_INITIALIZER(&kqlist);
|
||||
static size_t kqcount = 0;
|
||||
static size_t kqcount;
|
||||
|
||||
#define kqlist_lock() pthread_mutex_lock(&kqlist_mtx)
|
||||
#define kqlist_unlock() pthread_mutex_unlock(&kqlist_mtx)
|
||||
static pthread_mutex_t kqlist_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static sigset_t saved_sigmask;
|
||||
|
||||
static int kqgc_pfd[2];
|
||||
static pthread_once_t kqueue_init_ctl = PTHREAD_ONCE_INIT;
|
||||
static pthread_cond_t kqueue_init_cond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t kqueue_init_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int kqueue_init_status = 0;
|
||||
|
||||
static void
|
||||
mask_signals(void)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
sigemptyset (&mask);
|
||||
if (pthread_sigmask(SIG_BLOCK, &mask, &saved_sigmask) != 0)
|
||||
sigemptyset (&saved_sigmask);
|
||||
}
|
||||
|
||||
static void
|
||||
unmask_signals(void)
|
||||
{
|
||||
pthread_sigmask(SIG_SETMASK, &saved_sigmask, NULL);
|
||||
}
|
||||
static pthread_rwlock_t kqlist_mtx = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
struct kqueue *
|
||||
kqueue_lookup(int kq)
|
||||
{
|
||||
struct kqueue *ent = NULL;
|
||||
|
||||
kqlist_lock();
|
||||
pthread_rwlock_rdlock(&kqlist_mtx);
|
||||
LIST_FOREACH(ent, &kqlist, entries) {
|
||||
if (ent->kq_sockfd[1] == kq)
|
||||
break;
|
||||
}
|
||||
kqlist_unlock();
|
||||
pthread_rwlock_unlock(&kqlist_mtx);
|
||||
|
||||
return (ent);
|
||||
}
|
||||
|
||||
/* SEE ALSO: kqueue_shutdown */
|
||||
static void
|
||||
kqueue_destroy_unlocked(struct kqueue *kq)
|
||||
/* Must hold the kqlist_mtx when calling this */
|
||||
void
|
||||
kqueue_free(struct kqueue *kq)
|
||||
{
|
||||
dbg_printf("fd=%d", kq->kq_sockfd[1]);
|
||||
LIST_REMOVE(kq, entries);
|
||||
filter_unregister_all(kq);
|
||||
free(kq);
|
||||
}
|
||||
|
||||
static void *
|
||||
kqueue_close_wait(void *unused)
|
||||
{
|
||||
struct kqueue *kq;
|
||||
char buf[1];
|
||||
struct pollfd fds[MAX_KQUEUE + 1];
|
||||
int wake_flag;
|
||||
int i, n;
|
||||
|
||||
pthread_mutex_lock(&kqueue_init_mtx);
|
||||
|
||||
/* Block all signals in this thread */
|
||||
sigset_t mask;
|
||||
sigfillset(&mask);
|
||||
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
|
||||
wake_flag = 0;
|
||||
|
||||
pthread_cond_signal(&kqueue_init_cond);
|
||||
pthread_mutex_unlock(&kqueue_init_mtx);
|
||||
|
||||
for (;;) {
|
||||
|
||||
/* This thread is woken up by writing to kqgc_pfd[1] */
|
||||
fds[0].fd = kqgc_pfd[0];
|
||||
fds[0].events = POLLIN;
|
||||
n = 1;
|
||||
|
||||
kqlist_lock();
|
||||
LIST_FOREACH(kq, &kqlist, entries) {
|
||||
fds[n].fd = kq->kq_sockfd[0];
|
||||
fds[n].events = POLLIN;
|
||||
n++;
|
||||
}
|
||||
kqlist_unlock();
|
||||
|
||||
/* Inform the main thread that it can continue */
|
||||
if (wake_flag) {
|
||||
if (write (kqgc_pfd[0], ".", 1) < 1) {
|
||||
/* TODO: error checking */
|
||||
abort();
|
||||
}
|
||||
wake_flag = 0;
|
||||
}
|
||||
|
||||
|
||||
dbg_printf("gc: waiting for events on %d kqfds", n - 1);
|
||||
|
||||
n = poll(&fds[0], n, -1);
|
||||
if (n == 0)
|
||||
continue; /* Spurious wakeup */
|
||||
if (n < 0) {
|
||||
dbg_printf("poll(2): %s", strerror(errno));
|
||||
abort(); //FIXME
|
||||
}
|
||||
|
||||
dbg_printf("watcher: revents=%d", fds[0].revents);
|
||||
if (fds[0].revents == POLLIN) {
|
||||
dbg_puts("gc: waking up at user request");
|
||||
if (read(kqgc_pfd[0], &buf, 1) < 0) {
|
||||
abort();//TODO: err handling
|
||||
}
|
||||
|
||||
wake_flag = 1;
|
||||
} else {
|
||||
/* Destroy any kqueue that close(2) has been called on */
|
||||
kqlist_lock();
|
||||
for (i = 1; i <= n; i++) {
|
||||
dbg_printf("i=%d revents=%d", i, fds[n].revents);
|
||||
if (fds[n].revents & POLLHUP) {
|
||||
LIST_FOREACH(kq, &kqlist, entries) {
|
||||
if (kq->kq_sockfd[0] == fds[n].fd) {
|
||||
dbg_printf("gc: destroying kqueue; kqfd=%d",
|
||||
kq->kq_sockfd[1]);
|
||||
kqueue_destroy_unlocked(kq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
kqlist_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTREACHED */
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
kqgc_wakeup(void)
|
||||
{
|
||||
char buf[1];
|
||||
ssize_t n;
|
||||
|
||||
dbg_puts("main: requesting wakeup");
|
||||
n = write (kqgc_pfd[1], ".", 1);
|
||||
if (n < 1) {
|
||||
/* TODO: error checking */
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* There is a race condition as follows:
|
||||
* main_thread GC_thread
|
||||
* ----------- ---------
|
||||
* kqueue() called
|
||||
* create kqueue
|
||||
* wake GC_thread ... (sleeping)
|
||||
* kqueue() returns
|
||||
* .. ..
|
||||
* close(2) kqueue fd .
|
||||
* wake and update pollset
|
||||
*
|
||||
* This would prevent the resources for the kqueue from
|
||||
* ever being deallocated.
|
||||
*
|
||||
* To prevent this, the main_thread will wait for the
|
||||
* gc_thread to update it's pollset before continuing.
|
||||
*
|
||||
*/
|
||||
dbg_puts("main: waiting for gc to refresh it's pollset");
|
||||
n = read(kqgc_pfd[1], &buf, 1);
|
||||
if (n < 1) {
|
||||
/* TODO: error checking */
|
||||
abort();
|
||||
}
|
||||
dbg_puts("main: synchronization complete");
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
kqueue_init(void)
|
||||
{
|
||||
pthread_t tid;
|
||||
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, kqgc_pfd) < 0)
|
||||
goto errout;
|
||||
if (kqueue_init_hook() < 0)
|
||||
return (-1);
|
||||
|
||||
pthread_mutex_lock(&kqueue_init_mtx);
|
||||
pthread_create(&tid, NULL, kqueue_close_wait, NULL);
|
||||
pthread_cond_wait(&kqueue_init_cond, &kqueue_init_mtx);
|
||||
pthread_mutex_unlock(&kqueue_init_mtx);
|
||||
|
||||
errout:
|
||||
kqueue_init_status = -1;
|
||||
}
|
||||
|
||||
void
|
||||
kqueue_lock(struct kqueue *kq)
|
||||
{
|
||||
dbg_puts("kqueue_lock()");
|
||||
pthread_mutex_lock(&kq->kq_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
kqueue_unlock(struct kqueue *kq)
|
||||
{
|
||||
dbg_puts("kqueue_unlock()");
|
||||
pthread_mutex_unlock(&kq->kq_mtx);
|
||||
kqueue_initialized = 1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kqueue(void)
|
||||
{
|
||||
struct kqueue *kq;
|
||||
int rv;
|
||||
|
||||
kq = calloc(1, sizeof(*kq));
|
||||
if (kq == NULL)
|
||||
return (-1);
|
||||
pthread_mutex_init(&kq->kq_mtx, NULL);
|
||||
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, kq->kq_sockfd) < 0)
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, kq->kq_sockfd) < 0)
|
||||
goto errout_unlocked;
|
||||
|
||||
pthread_rwlock_wrlock(&kqlist_mtx);
|
||||
if (!kqueue_initialized && kqueue_init() < 0)
|
||||
goto errout;
|
||||
|
||||
(void) pthread_once(&kqueue_init_ctl, kqueue_init);
|
||||
|
||||
kqlist_lock();
|
||||
mask_signals();
|
||||
rv = filter_register_all(kq);
|
||||
if (rv == 0) {
|
||||
LIST_INSERT_HEAD(&kqlist, kq, entries);
|
||||
kqcount++;
|
||||
}
|
||||
unmask_signals();
|
||||
kqlist_unlock();
|
||||
|
||||
if (rv != 0)
|
||||
if (filter_register_all(kq) < 0)
|
||||
goto errout;
|
||||
if (kqlist_insert_hook(kq) < 0)
|
||||
goto errout;
|
||||
if (kqueue_gc() < 0)
|
||||
goto errout;
|
||||
LIST_INSERT_HEAD(&kqlist, kq, entries);
|
||||
kqcount++;
|
||||
pthread_rwlock_unlock(&kqlist_mtx);
|
||||
|
||||
/* Force the GC thread to add the fd to it's pollset */
|
||||
kqgc_wakeup();
|
||||
|
||||
dbg_printf("created kqueue: fd=%d", kq->kq_sockfd[1]);
|
||||
dbg_printf("created kqueue, fd=%d", kq->kq_sockfd[1]);
|
||||
return (kq->kq_sockfd[1]);
|
||||
|
||||
errout:
|
||||
pthread_rwlock_unlock(&kqlist_mtx);
|
||||
|
||||
errout_unlocked:
|
||||
//FIXME: unregister_all filters
|
||||
if (kq->kq_sockfd[0] != kq->kq_sockfd[1]) {
|
||||
// FIXME: close() may clobber errno in the case of EINTR
|
||||
close(kq->kq_sockfd[0]);
|
||||
close(kq->kq_sockfd[1]);
|
||||
}
|
||||
|
@ -17,8 +17,15 @@
|
||||
#ifndef _KQUEUE_PRIVATE_H
|
||||
#define _KQUEUE_PRIVATE_H
|
||||
|
||||
#if defined (__SVR4) && defined (__sun)
|
||||
#define SOLARIS
|
||||
#include <port.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include "sys/event.h"
|
||||
|
||||
@ -29,9 +36,9 @@
|
||||
#define MAX_KEVENT 512
|
||||
|
||||
#ifdef KQUEUE_DEBUG
|
||||
# define dbg_puts(str) fprintf(stderr, "%s\n", str)
|
||||
# define dbg_printf(fmt,...) fprintf(stderr, fmt"\n", __VA_ARGS__)
|
||||
# define dbg_perror(str) fprintf(stderr, "%s: %s\n", str, strerror(errno))
|
||||
# define dbg_puts(str) fprintf(stderr, "%s(): %s\n", __func__,str)
|
||||
# define dbg_printf(fmt,...) fprintf(stderr, "%s(): "fmt"\n", __func__,__VA_ARGS__)
|
||||
# define dbg_perror(str) fprintf(stderr, "%s(): %s: %s\n", __func__,str, strerror(errno))
|
||||
#else
|
||||
# define dbg_puts(str) ;
|
||||
# define dbg_printf(fmt,...) ;
|
||||
@ -81,17 +88,22 @@ struct filter {
|
||||
int kf_wfd; /* fd to write when an event occurs */
|
||||
u_int kf_timeres; /* timer resolution, in miliseconds */
|
||||
sigset_t kf_sigmask;
|
||||
pthread_mutex_t kf_mtx;
|
||||
struct evfilt_data *kf_data; /* filter-specific data */
|
||||
struct knotelist knl;
|
||||
struct knotelist kf_watchlist; /* events that have not occurred */
|
||||
struct knotelist kf_eventlist; /* events that have occurred */
|
||||
struct kqueue *kf_kqueue;
|
||||
};
|
||||
|
||||
struct kqueue {
|
||||
int kq_sockfd[2];
|
||||
struct filter kq_filt[EVFILT_SYSCOUNT];
|
||||
fd_set kq_fds;
|
||||
fd_set kq_fds, kq_rfds;
|
||||
int kq_nfds;
|
||||
pthread_mutex_t kq_mtx;
|
||||
#ifdef SOLARIS
|
||||
int kq_port;
|
||||
#endif
|
||||
LIST_ENTRY(kqueue) entries;
|
||||
};
|
||||
|
||||
@ -100,22 +112,27 @@ struct knote * knote_lookup_data(struct filter *filt, intptr_t);
|
||||
struct knote * knote_new(struct filter *);
|
||||
void knote_free(struct knote *);
|
||||
|
||||
struct filter * filter_lookup(struct kqueue *, short);
|
||||
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 *);
|
||||
#define filter_lock(f) pthread_mutex_lock(&(f)->kf_mtx)
|
||||
#define filter_unlock(f) pthread_mutex_unlock(&(f)->kf_mtx)
|
||||
|
||||
int kevent_init(struct kqueue *);
|
||||
const char * kevent_dump(struct kevent *);
|
||||
int kevent_wait(struct kqueue *kq,
|
||||
struct kevent *kevent,
|
||||
int nevents,
|
||||
const struct timespec *timeout);
|
||||
const char * kevent_dump(const struct kevent *);
|
||||
int kevent_wait(struct kqueue *, const struct timespec *);
|
||||
int kevent_copyout(struct kqueue *, int, struct kevent *, int);
|
||||
void kevent_free(struct kqueue *);
|
||||
|
||||
struct kqueue * kqueue_lookup(int kq);
|
||||
void kqueue_lock(struct kqueue *kq);
|
||||
void kqueue_unlock(struct kqueue *kq);
|
||||
/* TODO: make a kqops struct */
|
||||
int kqueue_init_hook(void); // in hook.c
|
||||
int kqlist_insert_hook(struct kqueue *); // in hook.c
|
||||
int kqueue_gc(void); // in hook.c
|
||||
void kqueue_free(struct kqueue *);
|
||||
|
||||
#endif /* ! _KQUEUE_PRIVATE_H */
|
||||
|
128
src/linux/hook.c
Normal file
128
src/linux/hook.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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/epoll.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
static int gcfd;
|
||||
|
||||
static int
|
||||
gcfd_ctl(int op, struct kqueue *kq)
|
||||
{
|
||||
struct epoll_event evt;
|
||||
|
||||
evt.data.ptr = kq;
|
||||
evt.events = 0;
|
||||
return (epoll_ctl(gcfd, op, kq->kq_sockfd[0], &evt));
|
||||
}
|
||||
|
||||
int
|
||||
kqueue_init_hook(void)
|
||||
{
|
||||
int epfd;
|
||||
|
||||
if ((epfd = epoll_create(1)) < 0) {
|
||||
dbg_perror("epoll_create(2)");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
gcfd = epfd;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kqueue_gc(void)
|
||||
{
|
||||
struct epoll_event evt;
|
||||
struct kqueue *kq;
|
||||
int rv;
|
||||
|
||||
do {
|
||||
rv = epoll_wait(gcfd, &evt, 1, 0);
|
||||
if (rv > 0) {
|
||||
kq = (struct kqueue *)evt.data.ptr;
|
||||
rv = gcfd_ctl(EPOLL_CTL_DEL, kq);
|
||||
kqueue_free(kq);
|
||||
} else if (rv < 0) {
|
||||
dbg_perror("epoll_wait(2)");
|
||||
}
|
||||
} while (rv > 0);
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
kqlist_insert_hook(struct kqueue *kq)
|
||||
{
|
||||
return (gcfd_ctl(EPOLL_CTL_ADD, kq));
|
||||
}
|
||||
|
||||
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 %s",
|
||||
filter_name(filt->kf_id));
|
||||
filter_lock(filt);
|
||||
rv = filt->kf_copyout(filt, eventlist, nevents);
|
||||
if (rv < 0) {
|
||||
filter_unlock(filt);
|
||||
dbg_puts("kevent_copyout failed");
|
||||
return (-1);
|
||||
}
|
||||
nret += rv;
|
||||
eventlist += rv;
|
||||
nevents -= rv;
|
||||
nready--;
|
||||
filter_unlock(filt);
|
||||
}
|
||||
}
|
||||
|
||||
return (nret);
|
||||
}
|
116
src/linux/proc.c
116
src/linux/proc.c
@ -35,20 +35,14 @@
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
LIST_HEAD(proc_eventhead, proc_event);
|
||||
|
||||
struct proc_event {
|
||||
uint64_t pe_kqid;
|
||||
pid_t pe_pid;
|
||||
int pe_status;
|
||||
LIST_ENTRY(proc_event) entries;
|
||||
};
|
||||
|
||||
/* 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_mutex_t wthr_mtx;
|
||||
struct proc_eventhead wthr_waitq;
|
||||
struct proc_eventhead wthr_eventq;
|
||||
pthread_cond_t wait_cond;
|
||||
pthread_mutex_t wait_mtx;
|
||||
};
|
||||
|
||||
static void *
|
||||
@ -57,7 +51,7 @@ wait_thread(void *arg)
|
||||
struct filter *filt = (struct filter *) arg;
|
||||
uint64_t counter = 1;
|
||||
const int options = WEXITED | WNOWAIT;
|
||||
struct proc_event *evt;
|
||||
struct knote *kn;
|
||||
siginfo_t si;
|
||||
sigset_t sigmask;
|
||||
|
||||
@ -68,36 +62,48 @@ wait_thread(void *arg)
|
||||
for (;;) {
|
||||
|
||||
/* Wait for a child process to exit(2) */
|
||||
if (waitid(P_ALL, 0, &si, options) != 0)
|
||||
break;
|
||||
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 */
|
||||
pthread_mutex_lock(&filt->kf_data->wthr_mtx);
|
||||
LIST_FOREACH(evt, &filt->kf_data->wthr_waitq, entries) {
|
||||
if (evt->pe_pid == si.si_pid)
|
||||
break;
|
||||
}
|
||||
if (evt == NULL) {
|
||||
pthread_mutex_unlock(&filt->kf_data->wthr_mtx);
|
||||
pthread_mutex_lock(&filt->kf_mtx);
|
||||
kn = knote_lookup(filt, si.si_pid);
|
||||
if (kn == NULL) {
|
||||
pthread_mutex_unlock(&filt->kf_mtx);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Create a proc_event */
|
||||
if (si.si_code == CLD_EXITED) {
|
||||
evt->pe_status = si.si_status;
|
||||
kn->kev.data = si.si_status;
|
||||
} else if (si.si_code == CLD_KILLED) {
|
||||
/* FIXME: probably not true on BSD */
|
||||
/* FIXME: arbitrary non-zero number */
|
||||
evt->pe_status = 254;
|
||||
kn->kev.data = 254;
|
||||
} else {
|
||||
/* Should never happen. */
|
||||
evt->pe_status = 255;
|
||||
/* FIXME: arbitrary non-zero number */
|
||||
kn->kev.data = 1;
|
||||
}
|
||||
|
||||
/* Add the event to the eventlist */
|
||||
LIST_REMOVE(evt, entries);
|
||||
LIST_INSERT_HEAD(&filt->kf_data->wthr_eventq, evt, entries);
|
||||
pthread_mutex_unlock(&filt->kf_data->wthr_mtx);
|
||||
/* Move the knote from the watchlist to the eventlist */
|
||||
LIST_REMOVE(kn, entries);
|
||||
LIST_INSERT_HEAD(&filt->kf_eventlist, kn, entries);
|
||||
pthread_mutex_unlock(&filt->kf_mtx);
|
||||
|
||||
/* Indicate read(2) readiness */
|
||||
if (write(filt->kf_pfd, &counter, sizeof(counter)) < 0) {
|
||||
@ -114,14 +120,6 @@ wait_thread(void *arg)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
watch_add(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
pthread_mutex_lock(&filt->kf_data->wthr_mtx);
|
||||
abort(); /*XXX-TODO*/
|
||||
pthread_mutex_unlock(&filt->kf_data->wthr_mtx);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_proc_init(struct filter *filt)
|
||||
{
|
||||
@ -130,18 +128,18 @@ evfilt_proc_init(struct filter *filt)
|
||||
|
||||
if ((ed = calloc(1, sizeof(*ed))) == NULL)
|
||||
return (-1);
|
||||
pthread_mutex_init(&ed->wthr_mtx, NULL);
|
||||
LIST_INIT(&ed->wthr_eventq);
|
||||
LIST_INIT(&ed->wthr_waitq);
|
||||
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;
|
||||
|
||||
filt->kf_pfd = efd;
|
||||
|
||||
return (0);
|
||||
|
||||
@ -166,7 +164,8 @@ evfilt_proc_copyin(struct filter *filt,
|
||||
{
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
watch_add(filt, dst);
|
||||
/* TODO: Consider holding the mutex here.. */
|
||||
pthread_cond_signal(&filt->kf_data->wait_cond);
|
||||
}
|
||||
|
||||
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
|
||||
@ -181,8 +180,7 @@ evfilt_proc_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int maxevents)
|
||||
{
|
||||
struct proc_event *elm;
|
||||
struct knote *kn;
|
||||
struct knote *kn, *kn_nxt;
|
||||
int nevents = 0;
|
||||
uint64_t cur;
|
||||
|
||||
@ -193,38 +191,32 @@ evfilt_proc_copyout(struct filter *filt,
|
||||
}
|
||||
dbg_printf(" counter=%llu", (unsigned long long) cur);
|
||||
|
||||
pthread_mutex_lock(&filt->kf_data->wthr_mtx);
|
||||
LIST_FOREACH(elm, &filt->kf_data->wthr_eventq, entries) {
|
||||
kn = knote_lookup(filt, elm->pe_pid);
|
||||
if (kn == NULL) {
|
||||
LIST_REMOVE(elm, entries);
|
||||
free(elm);
|
||||
continue;
|
||||
}
|
||||
pthread_mutex_lock(&filt->kf_mtx);
|
||||
for (kn = LIST_FIRST(&filt->kf_eventlist); kn != NULL; kn = kn_nxt) {
|
||||
kn_nxt = LIST_NEXT(kn, entries);
|
||||
|
||||
kevent_dump(&kn->kev);
|
||||
dst->ident = kn->kev.ident;
|
||||
dst->filter = kn->kev.filter;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = kn->kev.flags;
|
||||
dst->fflags = NOTE_EXIT;
|
||||
dst->data = elm->pe_status;
|
||||
memcpy(dst, &kn->kev, sizeof(*dst));
|
||||
|
||||
if (kn->kev.flags & EV_DISPATCH) {
|
||||
KNOTE_DISABLE(kn);
|
||||
}
|
||||
if (kn->kev.flags & EV_ONESHOT)
|
||||
if (kn->kev.flags & EV_ONESHOT) {
|
||||
knote_free(kn);
|
||||
} else {
|
||||
kn->kev.data = 0;
|
||||
LIST_REMOVE(kn, entries);
|
||||
LIST_INSERT_HEAD(&filt->kf_watchlist, kn, entries);
|
||||
}
|
||||
|
||||
LIST_REMOVE(elm, entries);
|
||||
free(elm);
|
||||
|
||||
if (++nevents > maxevents)
|
||||
break;
|
||||
dst++;
|
||||
}
|
||||
pthread_mutex_unlock(&filt->kf_mtx);
|
||||
|
||||
if (!LIST_EMPTY(&filt->kf_data->wthr_eventq)) {
|
||||
if (!LIST_EMPTY(&filt->kf_eventlist)) {
|
||||
/* XXX-FIXME: If there are leftover events on the waitq,
|
||||
re-arm the eventfd. list */
|
||||
abort();
|
||||
@ -234,7 +226,7 @@ evfilt_proc_copyout(struct filter *filt,
|
||||
}
|
||||
|
||||
const struct filter evfilt_proc = {
|
||||
EVFILT_PROC,
|
||||
0, //XXX-FIXME broken: EVFILT_PROC,
|
||||
evfilt_proc_init,
|
||||
evfilt_proc_destroy,
|
||||
evfilt_proc_copyin,
|
||||
|
@ -115,12 +115,8 @@ evfilt_signal_copyout(struct filter *filt,
|
||||
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->ident = sig[i].ssi_signo;
|
||||
dst->filter = EVFILT_SIGNAL;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = EV_ADD | EV_CLEAR;
|
||||
dst->fflags = 0;
|
||||
dst->data = 1;
|
||||
|
||||
if (kn->kev.flags & EV_DISPATCH || kn->kev.flags & EV_ONESHOT) {
|
||||
@ -129,10 +125,8 @@ evfilt_signal_copyout(struct filter *filt,
|
||||
}
|
||||
if (kn->kev.flags & EV_DISPATCH)
|
||||
KNOTE_DISABLE(kn);
|
||||
if (kn->kev.flags & EV_ONESHOT) {
|
||||
dst->flags |= EV_ONESHOT;
|
||||
if (kn->kev.flags & EV_ONESHOT)
|
||||
knote_free(kn);
|
||||
}
|
||||
|
||||
dst++;
|
||||
nevents++;
|
||||
|
@ -89,6 +89,7 @@ evfilt_socket_copyin(struct filter *filt,
|
||||
op = EPOLL_CTL_ADD;
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
}
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
if (src->flags & EV_DELETE)
|
||||
op = EPOLL_CTL_DEL;
|
||||
if (src->flags & EV_ENABLE || src->flags & EV_DISABLE)
|
||||
@ -175,15 +176,7 @@ evfilt_socket_copyout(struct filter *filt,
|
||||
epoll_event_dump(ev);
|
||||
kn = knote_lookup(filt, ev->data.fd);
|
||||
if (kn != NULL) {
|
||||
dst->ident = kn->kev.ident;
|
||||
dst->filter = kn->kev.filter;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = EV_ADD;
|
||||
dst->fflags = 0;
|
||||
if (kn->kev.flags & EV_ONESHOT) /*TODO: move elsewhere */
|
||||
dst->flags |= EV_ONESHOT;
|
||||
if (kn->kev.flags & EV_CLEAR) /*TODO: move elsewhere */
|
||||
dst->flags |= EV_CLEAR;
|
||||
memcpy(dst, &kn->kev, sizeof(*dst));
|
||||
if (ev->events & EPOLLRDHUP || ev->events & EPOLLHUP)
|
||||
dst->flags |= EV_EOF;
|
||||
if (ev->events & EPOLLERR)
|
||||
|
@ -149,9 +149,10 @@ evfilt_timer_destroy(struct filter *filt)
|
||||
struct knote *kn;
|
||||
|
||||
/* Destroy all timerfds */
|
||||
KNOTELIST_FOREACH(kn, &filt->knl) {
|
||||
KNOTELIST_FOREACH(kn, &filt->kf_watchlist) {
|
||||
close(kn->kn_pfd);
|
||||
}
|
||||
/* TODO: use eventlist, close these also */
|
||||
|
||||
close (filt->kf_pfd);
|
||||
}
|
||||
@ -212,15 +213,7 @@ evfilt_timer_copyout(struct filter *filt,
|
||||
ev = &epevt[i];
|
||||
/* TODO: put in generic debug.c: epoll_event_dump(ev); */
|
||||
kn = ev->data.ptr;
|
||||
dst->ident = kn->kev.ident;
|
||||
dst->filter = kn->kev.filter;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = EV_ADD;
|
||||
dst->fflags = 0;
|
||||
if (kn->kev.flags & EV_ONESHOT) /*TODO: move elsewhere */
|
||||
dst->flags |= EV_ONESHOT;
|
||||
if (kn->kev.flags & EV_CLEAR) /*TODO: move elsewhere */
|
||||
dst->flags |= EV_CLEAR;
|
||||
memcpy(dst, &kn->kev, sizeof(*dst));
|
||||
if (ev->events & EPOLLERR)
|
||||
dst->fflags = 1; /* FIXME: Return the actual timer error */
|
||||
|
||||
|
@ -30,6 +30,38 @@
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
/* TODO: make common linux function */
|
||||
static int
|
||||
evfd_raise(int evfd)
|
||||
{
|
||||
uint64_t counter;
|
||||
|
||||
dbg_puts("efd_raise(): raising event level");
|
||||
counter = 1;
|
||||
if (write(evfd, &counter, sizeof(counter)) < 0) {
|
||||
/* FIXME: handle EAGAIN */
|
||||
dbg_printf("write(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
evfd_lower(int evfd)
|
||||
{
|
||||
uint64_t cur;
|
||||
|
||||
/* Reset the counter */
|
||||
dbg_puts("efd_lower(): lowering event level");
|
||||
if (read(evfd, &cur, sizeof(cur)) < sizeof(cur)) {
|
||||
/* FIXME: handle EAGAIN */
|
||||
dbg_printf("read(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
dbg_printf(" counter=%llu", (unsigned long long) cur);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_user_init(struct filter *filt)
|
||||
{
|
||||
@ -54,11 +86,9 @@ evfilt_user_copyin(struct filter *filt,
|
||||
{
|
||||
u_int ffctrl;
|
||||
struct kevent *kev;
|
||||
uint64_t counter;
|
||||
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst))
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
}
|
||||
kev = &dst->kev;
|
||||
if (src->flags & EV_ENABLE) {
|
||||
dst->kev.flags &= ~EV_DISABLE;
|
||||
@ -97,15 +127,8 @@ evfilt_user_copyin(struct filter *filt,
|
||||
}
|
||||
|
||||
if ((!(dst->kev.flags & EV_DISABLE)) && src->fflags & NOTE_TRIGGER) {
|
||||
counter = 1;
|
||||
if (write(filt->kf_pfd, &counter, sizeof(counter)) < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
dbg_printf("write(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
dst->kev.fflags |= NOTE_TRIGGER;
|
||||
dbg_puts("knote triggered");
|
||||
evfd_raise(filt->kf_pfd);
|
||||
}
|
||||
|
||||
return (0);
|
||||
@ -116,42 +139,44 @@ evfilt_user_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int maxevents)
|
||||
{
|
||||
struct knote *kn;
|
||||
struct knote *kn, *kn_next;
|
||||
int nevents = 0;
|
||||
uint64_t cur;
|
||||
|
||||
for (kn = LIST_FIRST(&filt->kf_watchlist); kn != NULL; kn = kn_next) {
|
||||
kn_next = LIST_NEXT(kn, entries);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Scan for events that have been triggered */
|
||||
KNOTELIST_FOREACH(kn, &filt->knl) {
|
||||
/* Skip knotes that have not been triggered */
|
||||
if (!(kn->kev.fflags & NOTE_TRIGGER))
|
||||
continue;
|
||||
continue;
|
||||
|
||||
dst->ident = kn->kev.ident;
|
||||
dst->filter = kn->kev.filter;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = EV_ADD;
|
||||
dst->fflags = kn->kev.fflags;
|
||||
dst->data = 0;
|
||||
if (kn->kev.flags & EV_ONESHOT) /*TODO: move elsewhere */
|
||||
dst->flags |= EV_ONESHOT;
|
||||
if (kn->kev.flags & EV_CLEAR) /*TODO: move elsewhere */
|
||||
dst->flags |= EV_CLEAR;
|
||||
memcpy(dst, &kn->kev, sizeof(*dst));
|
||||
dst->fflags &= ~NOTE_FFCTRLMASK; //FIXME: Not sure if needed
|
||||
dst->fflags &= ~NOTE_TRIGGER;
|
||||
if (kn->kev.flags & EV_DISPATCH)
|
||||
KNOTE_DISABLE(kn);
|
||||
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_ONESHOT)
|
||||
knote_free(kn);
|
||||
if (kn->kev.flags & EV_CLEAR)
|
||||
kn->kev.fflags &= ~NOTE_TRIGGER;
|
||||
if (kn->kev.flags & (EV_DISPATCH | EV_CLEAR | EV_ONESHOT))
|
||||
evfd_lower(filt->kf_pfd);
|
||||
|
||||
dst++;
|
||||
if (++nevents == maxevents)
|
||||
break;
|
||||
}
|
||||
|
||||
/* This should normally never happen but is here for debugging */
|
||||
if (nevents == 0) {
|
||||
dbg_puts("spurious wakeup");
|
||||
evfd_lower(filt->kf_pfd);
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
}
|
||||
|
||||
|
@ -229,12 +229,8 @@ evfilt_vnode_copyout(struct filter *filt,
|
||||
return (-1);
|
||||
}
|
||||
|
||||
kevent_dump(&kn->kev);
|
||||
dst->ident = kn->kev.ident;
|
||||
dst->filter = kn->kev.filter;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = kn->kev.flags;
|
||||
dst->fflags = 0;
|
||||
memcpy(dst, &kn->kev, sizeof(*dst));
|
||||
dst->data = 0;
|
||||
|
||||
/* No error checking because fstat(2) should rarely fail */
|
||||
if ((evt.mask & IN_ATTRIB || evt.mask & IN_MODIFY)
|
||||
|
190
src/posix/proc.c
Normal file
190
src/posix/proc.c
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#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,
|
||||
};
|
97
src/solaris/hook.c
Normal file
97
src/solaris/hook.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
#define PORTEV_DUMP(pe) dbg_printf("port_event: events=%d source=%hu", \
|
||||
(pe)->portev_events, (pe)->portev_source)
|
||||
int
|
||||
kqueue_init_hook(void)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kqueue_hook(struct kqueue *kq)
|
||||
{
|
||||
if ((kq->kq_port = port_create()) < 0) {
|
||||
dbg_perror("port_create(2)");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kevent_wait(struct kqueue *kq, const struct timespec *timeout)
|
||||
{
|
||||
port_event_t pe[1];
|
||||
int rv, nget;
|
||||
|
||||
dbg_printf("port_get: %d %lu %lu",
|
||||
kq->kq_port, timeout->tv_sec, timeout->tv_nsec);
|
||||
nget = 1;
|
||||
rv = port_getn(kq->kq_port, pe, 1, &nget, (struct timespec *) timeout);
|
||||
if (rv < 0) {
|
||||
if (errno == EINTR) {
|
||||
dbg_puts("signal caught");
|
||||
return (-1);
|
||||
}
|
||||
dbg_perror("port_getn(2)");
|
||||
return (-1);
|
||||
}
|
||||
if (nget > 0) {
|
||||
dbg_printf(" -- event(s) pending: nget = %d", nget);
|
||||
PORTEV_DUMP(&pe[0]);
|
||||
} else {
|
||||
dbg_puts(" -- no events --");
|
||||
}
|
||||
|
||||
return (nget);
|
||||
}
|
||||
|
||||
int
|
||||
kevent_copyout(struct kqueue *kq, int nready,
|
||||
struct kevent *eventlist, int nevents)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
struct filter *filt;
|
||||
int i, rv, nret;
|
||||
|
||||
if (FD_ISSET(filt->kf_pfd, &kq->kq_rfds)) {
|
||||
dbg_printf("event(s) for filter #%d", i);
|
||||
filter_lock(filt);
|
||||
rv = filt->kf_copyout(filt, eventlist, nevents);
|
||||
if (rv < 0) {
|
||||
filter_unlock(filt);
|
||||
dbg_puts("kevent_copyout failed");
|
||||
return (-1);
|
||||
}
|
||||
nret += rv;
|
||||
eventlist += rv;
|
||||
nevents -= rv;
|
||||
nready--;
|
||||
filter_unlock(filt);
|
||||
}
|
||||
|
||||
return (nret);
|
||||
#endif
|
||||
}
|
111
src/solaris/proc.c
Normal file
111
src/solaris/proc.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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_proc_init(struct filter *filt)
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_proc_destroy(struct filter *filt)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_proc_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
/* TODO: Consider holding the mutex here.. */
|
||||
pthread_cond_signal(&filt->kf_data->wait_cond);
|
||||
}
|
||||
|
||||
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
|
||||
/* Nothing to do.. */
|
||||
}
|
||||
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_proc_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int maxevents)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
struct knote *kn, *kn_nxt;
|
||||
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);
|
||||
|
||||
pthread_mutex_lock(&filt->kf_mtx);
|
||||
for (kn = LIST_FIRST(&filt->kf_eventlist); kn != NULL; kn = kn_nxt) {
|
||||
kn_nxt = LIST_NEXT(kn, entries);
|
||||
|
||||
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(kn);
|
||||
} else {
|
||||
kn->kev.data = 0;
|
||||
LIST_REMOVE(kn, entries);
|
||||
LIST_INSERT_HEAD(&filt->kf_watchlist, kn, entries);
|
||||
}
|
||||
|
||||
|
||||
if (++nevents > maxevents)
|
||||
break;
|
||||
dst++;
|
||||
}
|
||||
pthread_mutex_unlock(&filt->kf_mtx);
|
||||
|
||||
if (!LIST_EMPTY(&filt->kf_eventlist)) {
|
||||
/* XXX-FIXME: If there are leftover events on the waitq,
|
||||
re-arm the eventfd. list */
|
||||
abort();
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct filter evfilt_proc = {
|
||||
0, //EVFILT_PROC,
|
||||
evfilt_proc_init,
|
||||
evfilt_proc_destroy,
|
||||
evfilt_proc_copyin,
|
||||
evfilt_proc_copyout,
|
||||
};
|
117
src/solaris/signal.c
Normal file
117
src/solaris/signal.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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"
|
||||
|
||||
/* Highest signal number supported. POSIX standard signals are < 32 */
|
||||
#define SIGNAL_MAX 32
|
||||
|
||||
int
|
||||
evfilt_signal_init(struct filter *filt)
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_signal_destroy(struct filter *filt)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
if (src->ident >= SIGNAL_MAX) {
|
||||
dbg_printf("unsupported signal number %u", (u_int) src->ident);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
#if TODO
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
dst->kev.flags |= EV_CLEAR;
|
||||
}
|
||||
if (src->flags & EV_ADD || src->flags & EV_ENABLE)
|
||||
sigaddset(&filt->kf_sigmask, src->ident);
|
||||
if (src->flags & EV_DISABLE || src->flags & EV_DELETE)
|
||||
sigdelset(&filt->kf_sigmask, src->ident);
|
||||
|
||||
return (update_sigmask(filt));
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
|
||||
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(kn);
|
||||
|
||||
dst++;
|
||||
nevents++;
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct filter evfilt_signal = {
|
||||
0, //EVFILT_SIGNAL,
|
||||
evfilt_signal_init,
|
||||
evfilt_signal_destroy,
|
||||
evfilt_signal_copyin,
|
||||
evfilt_signal_copyout,
|
||||
};
|
110
src/solaris/socket.c
Normal file
110
src/solaris/socket.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <port.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
int
|
||||
evfilt_socket_init(struct filter *filt)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_socket_destroy(struct filter *filt)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
int port, events;
|
||||
int rv;
|
||||
|
||||
port = filt->kf_kqueue->kq_port;
|
||||
|
||||
/* Not supported or not implemented */
|
||||
if (src->flags & EV_CLEAR) {
|
||||
dbg_puts("attempt to use unsupported mechanism");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (src->filter == EVFILT_READ)
|
||||
events = POLLIN;
|
||||
else
|
||||
events = POLLOUT;
|
||||
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst))
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
|
||||
if (src->flags & EV_DELETE || src->flags & EV_DISABLE) {
|
||||
rv = port_dissociate(port, PORT_SOURCE_FD, src->ident);
|
||||
if (rv < 0) {
|
||||
dbg_perror("port_disassociate(2)");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
if (src->flags & EV_ENABLE || src->flags & EV_ADD) {
|
||||
rv = port_associate(port, PORT_SOURCE_FD,
|
||||
src->ident, events, src->udata);
|
||||
if (rv < 0) {
|
||||
dbg_perror("port_associate(2)");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
/* XXX-TODO support modifying an existing watch */
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
const struct filter evfilt_read = {
|
||||
EVFILT_READ,
|
||||
evfilt_socket_init,
|
||||
evfilt_socket_destroy,
|
||||
evfilt_socket_copyin,
|
||||
evfilt_socket_copyout,
|
||||
};
|
||||
|
||||
const struct filter evfilt_write = {
|
||||
EVFILT_WRITE,
|
||||
evfilt_socket_init,
|
||||
evfilt_socket_destroy,
|
||||
evfilt_socket_copyin,
|
||||
evfilt_socket_copyout,
|
||||
};
|
125
src/solaris/timer.c
Normal file
125
src/solaris/timer.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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 <port.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
int
|
||||
evfilt_timer_init(struct filter *filt)
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_timer_destroy(struct filter *filt)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
dst->kev.flags |= EV_CLEAR;
|
||||
}
|
||||
if (src->flags & EV_ADD)
|
||||
return ktimer_create(filt, dst);
|
||||
if (src->flags & EV_DELETE)
|
||||
return ktimer_delete(filt, dst);
|
||||
if (src->flags & EV_ENABLE)
|
||||
return ktimer_create(filt, dst);
|
||||
if (src->flags & EV_DISABLE) {
|
||||
// TODO: err checking
|
||||
(void) ktimer_delete(filt, dst);
|
||||
KNOTE_DISABLE(dst);
|
||||
}
|
||||
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
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->kn_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);
|
||||
if (kn->kev.flags & EV_ONESHOT) {
|
||||
ktimer_delete(filt, kn);
|
||||
knote_free(kn);
|
||||
}
|
||||
|
||||
nevents++;
|
||||
dst++;
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct filter evfilt_timer = {
|
||||
0, //EVFILT_TIMER,
|
||||
evfilt_timer_init,
|
||||
evfilt_timer_destroy,
|
||||
evfilt_timer_copyin,
|
||||
evfilt_timer_copyout,
|
||||
};
|
151
src/solaris/user.c
Normal file
151
src/solaris/user.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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 (-1);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_user_destroy(struct filter *filt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_user_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
u_int ffctrl;
|
||||
struct kevent *kev;
|
||||
uint64_t counter;
|
||||
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst))
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
kev = &dst->kev;
|
||||
if (src->flags & EV_ENABLE) {
|
||||
dst->kev.flags &= ~EV_DISABLE;
|
||||
/* FIXME: what happens if NOTE_TRIGGER is in fflags?
|
||||
should the event fire? */
|
||||
}
|
||||
if (src->flags & EV_DISABLE)
|
||||
dst->kev.flags |= EV_DISABLE;
|
||||
|
||||
/* FIXME: can oneshot be added after the knote is already created? */
|
||||
if (src->flags & EV_ONESHOT)
|
||||
dst->kev.flags |= EV_ONESHOT;
|
||||
|
||||
/* Excerpted from sys/kern/kern_event.c in FreeBSD HEAD */
|
||||
ffctrl = kev->fflags & NOTE_FFCTRLMASK;
|
||||
kev->fflags &= NOTE_FFLAGSMASK;
|
||||
switch (ffctrl) {
|
||||
case NOTE_FFNOP:
|
||||
break;
|
||||
|
||||
case NOTE_FFAND:
|
||||
kev->fflags &= src->fflags;
|
||||
break;
|
||||
|
||||
case NOTE_FFOR:
|
||||
kev->fflags |= src->fflags;
|
||||
break;
|
||||
|
||||
case NOTE_FFCOPY:
|
||||
kev->fflags = kev->fflags;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* XXX Return error? */
|
||||
break;
|
||||
}
|
||||
|
||||
if ((!(dst->kev.flags & EV_DISABLE)) && src->fflags & NOTE_TRIGGER) {
|
||||
counter = 1;
|
||||
if (write(filt->kf_pfd, &counter, sizeof(counter)) < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
dbg_printf("write(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
dst->kev.fflags |= NOTE_TRIGGER;
|
||||
dbg_puts("knote triggered");
|
||||
}
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_user_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int maxevents)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
struct knote *kn, *kn_next;
|
||||
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 = LIST_FIRST(&filt->kf_watchlist); kn != NULL; kn = kn_next) {
|
||||
kn_next = LIST_NEXT(kn, entries);
|
||||
|
||||
memcpy(dst, &kn->kev, sizeof(*dst));
|
||||
dst->fflags &= ~NOTE_FFCTRLMASK; //FIXME: Not sure if needed
|
||||
dst->fflags &= ~NOTE_TRIGGER;
|
||||
if (kn->kev.flags & EV_DISPATCH)
|
||||
KNOTE_DISABLE(kn);
|
||||
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_ONESHOT) {
|
||||
|
||||
/* NOTE: True on FreeBSD but not consistent behavior with
|
||||
other filters. */
|
||||
dst->flags &= ~EV_ONESHOT;
|
||||
|
||||
knote_free(kn);
|
||||
}
|
||||
|
||||
dst++;
|
||||
if (++nevents == maxevents)
|
||||
break;
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct filter evfilt_user = {
|
||||
0, //EVFILT_USER,
|
||||
evfilt_user_init,
|
||||
evfilt_user_destroy,
|
||||
evfilt_user_copyin,
|
||||
evfilt_user_copyout,
|
||||
};
|
172
src/solaris/vnode.c
Normal file
172
src/solaris/vnode.c
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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_vnode_init(struct filter *filt)
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_vnode_destroy(struct filter *filt)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
#if TODO
|
||||
char path[PATH_MAX];
|
||||
struct stat sb;
|
||||
uint32_t mask;
|
||||
|
||||
if (src->flags & EV_DELETE || src->flags & EV_DISABLE)
|
||||
return delete_watch(filt->kf_pfd, dst);
|
||||
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
if (fstat(src->ident, &sb) < 0) {
|
||||
dbg_puts("fstat failed");
|
||||
return (-1);
|
||||
}
|
||||
dst->kn_st_nlink = sb.st_nlink;
|
||||
dst->kn_st_size = sb.st_size;
|
||||
dst->kev.data = -1;
|
||||
}
|
||||
|
||||
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
|
||||
|
||||
/* Convert the fd to a pathname */
|
||||
if (fd_to_path(&path[0], sizeof(path), src->ident) < 0)
|
||||
return (-1);
|
||||
|
||||
/* Convert the fflags to the inotify mask */
|
||||
mask = 0;
|
||||
if (dst->kev.fflags & NOTE_DELETE)
|
||||
mask |= IN_ATTRIB | IN_DELETE_SELF;
|
||||
if (dst->kev.fflags & NOTE_WRITE)
|
||||
mask |= IN_MODIFY | IN_ATTRIB;
|
||||
if (dst->kev.fflags & NOTE_EXTEND)
|
||||
mask |= IN_MODIFY | IN_ATTRIB;
|
||||
if ((dst->kev.fflags & NOTE_ATTRIB) ||
|
||||
(dst->kev.fflags & NOTE_LINK))
|
||||
mask |= IN_ATTRIB;
|
||||
if (dst->kev.fflags & NOTE_RENAME)
|
||||
mask |= IN_MOVE_SELF;
|
||||
if (dst->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);
|
||||
dst->kev.data = inotify_add_watch(filt->kf_pfd, path, mask);
|
||||
if (dst->kev.data < 0) {
|
||||
dbg_printf("inotify_add_watch(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
#endif
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
#if TODO
|
||||
struct inotify_event evt;
|
||||
struct stat sb;
|
||||
struct knote *kn;
|
||||
|
||||
if (get_one_event(&evt, filt->kf_pfd) < 0)
|
||||
return (-1);
|
||||
|
||||
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 */
|
||||
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->kn_st_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->kn_st_size && kn->kev.fflags & NOTE_WRITE)
|
||||
dst->fflags |= NOTE_EXTEND;
|
||||
kn->kn_st_nlink = sb.st_nlink;
|
||||
kn->kn_st_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->kf_pfd, kn); /* TODO: error checking */
|
||||
KNOTE_DISABLE(kn);
|
||||
}
|
||||
if (kn->kev.flags & EV_ONESHOT)
|
||||
knote_free(kn);
|
||||
|
||||
return (1);
|
||||
#endif
|
||||
return (-1);
|
||||
}
|
||||
|
||||
const struct filter evfilt_vnode = {
|
||||
0, //EVFILT_VNODE,
|
||||
evfilt_vnode_init,
|
||||
evfilt_vnode_destroy,
|
||||
evfilt_vnode_copyin,
|
||||
evfilt_vnode_copyout,
|
||||
};
|
@ -16,27 +16,31 @@
|
||||
|
||||
include config.mk
|
||||
|
||||
check:
|
||||
if [ ! -f /usr/include/sys/event.h ] ; then \
|
||||
cd .. && ./configure --debug=yes && make build ; \
|
||||
fi
|
||||
$(CC) $(CFLAGS) $(SOURCES) $(LDADD)
|
||||
./a.out
|
||||
all: kqtest
|
||||
|
||||
kqtest: $(SOURCES)
|
||||
test -L libkqueue.a || ln -s ../libkqueue.a
|
||||
$(CC) -o kqtest $(CFLAGS) $(SOURCES) $(LDADD)
|
||||
|
||||
check: kqtest
|
||||
./kqtest
|
||||
|
||||
# NOTE: copy+paste of 'make check'
|
||||
valgrind:
|
||||
cd .. && make build CFLAGS="$(CFLAGS) -g -O0 -DKQUEUE_DEBUG -DUNIT_TEST"
|
||||
gcc -c $(CFLAGS) test.c
|
||||
gcc -g -O0 $(CFLAGS) test.c libkqueue.a $(LDADD)
|
||||
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=20 --track-fds=yes ./a.out
|
||||
|
||||
valgrind: kqtest
|
||||
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=20 --track-fds=yes ./kqtest
|
||||
|
||||
check-installed:
|
||||
gcc $(CFLAGS) -I$(PREFIX)/kqueue test.c -lkqueue
|
||||
./a.out
|
||||
$(CC) -o kqtest $(CFLAGS) $(SOURCES) $(LDADD) -lkqueue
|
||||
./kqtest
|
||||
|
||||
distclean: clean
|
||||
rm -f config.mk config.h a.out
|
||||
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 ; do cd $$x && make distclean ; done
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f *.o *.a kqtest
|
||||
|
@ -18,7 +18,12 @@
|
||||
#define _COMMON_H
|
||||
|
||||
|
||||
#include <err.h>
|
||||
#if HAVE_ERR_H
|
||||
# include <err.h>
|
||||
#else
|
||||
# define err(rc,msg,...) do { perror(msg); exit(rc); } while (0)
|
||||
# define errx(rc,msg,...) do { puts(msg); exit(rc); } while (0)
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
@ -43,6 +48,15 @@ struct kevent * kevent_get(int);
|
||||
|
||||
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) || \
|
||||
@ -57,6 +71,6 @@ void kevent_cmp(struct kevent *, struct kevent *);
|
||||
extern void test_no_kevents(void);
|
||||
|
||||
extern void test_begin(const char *);
|
||||
extern void success(const char *);
|
||||
extern void success(void);
|
||||
|
||||
#endif /* _COMMON_H */
|
||||
|
@ -7,14 +7,17 @@ sources="main.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"
|
||||
ldadd="$ldadd libkqueue.a -lpthread -lrt"
|
||||
}
|
||||
|
||||
test "$target" = "solaris" && ldadd="$ldadd -lsocket"
|
||||
|
||||
check_symbol $sys_event_h EV_DISPATCH
|
||||
check_symbol $sys_event_h EV_RECEIPT
|
||||
check_symbol $sys_event_h NOTE_TRUNCATE
|
||||
|
16
test/libdispatch/Makefile
Normal file
16
test/libdispatch/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
CFLAGS=`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
|
99
test/libdispatch/main.c
Normal file
99
test/libdispatch/main.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/event.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
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);
|
||||
}
|
96
test/main.c
96
test/main.c
@ -19,7 +19,7 @@
|
||||
#include "common.h"
|
||||
|
||||
int testnum = 1;
|
||||
char *cur_test_id = "undef";
|
||||
char *cur_test_id = NULL;
|
||||
int kqfd;
|
||||
|
||||
extern void test_evfilt_read();
|
||||
@ -39,7 +39,6 @@ test_no_kevents(void)
|
||||
struct timespec timeo;
|
||||
struct kevent kev;
|
||||
|
||||
puts("confirming that there are no events pending");
|
||||
memset(&timeo, 0, sizeof(timeo));
|
||||
nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
|
||||
if (nfds != 0) {
|
||||
@ -80,7 +79,7 @@ kevent_fflags_dump(struct kevent *kev)
|
||||
|
||||
/* Not every filter has meaningful fflags */
|
||||
if (kev->filter != EVFILT_VNODE) {
|
||||
snprintf(buf, 1024, "fflags = %d", kev->flags);
|
||||
snprintf(buf, 1024, "fflags = %d", kev->fflags);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
@ -134,24 +133,51 @@ kevent_flags_dump(struct kevent *kev)
|
||||
return (buf);
|
||||
}
|
||||
|
||||
/* Copied from ../kevent.c kevent_dump() and improved */
|
||||
/* 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), "[filter=%d,%s,%s,ident=%u,data=%d,udata=%p]",
|
||||
|
||||
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),
|
||||
(u_int) kev->ident,
|
||||
(int) kev->data,
|
||||
kev->udata);
|
||||
|
||||
return (strdup(buf));
|
||||
}
|
||||
|
||||
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));
|
||||
err(1, "kevent(): %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kevent_cmp(struct kevent *k1, struct kevent *k2)
|
||||
{
|
||||
/* XXX-
|
||||
Workaround for inconsistent implementation of kevent(2)
|
||||
*/
|
||||
#ifdef __FreeBSD__
|
||||
if (k1->flags & EV_ADD)
|
||||
k2->flags |= EV_ADD;
|
||||
#endif
|
||||
if (memcmp(k1, k2, sizeof(*k1)) != 0) {
|
||||
printf("kevent_cmp: mismatch:\n %s !=\n %s\n",
|
||||
kevent_to_str(k1), kevent_to_str(k2));
|
||||
@ -162,14 +188,21 @@ kevent_cmp(struct kevent *k1, struct kevent *k2)
|
||||
void
|
||||
test_begin(const char *func)
|
||||
{
|
||||
cur_test_id = (char *) func;
|
||||
if (cur_test_id)
|
||||
free(cur_test_id);
|
||||
cur_test_id = strdup(func);
|
||||
if (!cur_test_id)
|
||||
err(1, "strdup failed");
|
||||
|
||||
printf("\n\nTest %d: %s\n", testnum++, func);
|
||||
}
|
||||
|
||||
void
|
||||
success(const char *func)
|
||||
success(void)
|
||||
{
|
||||
printf("%-70s %s\n", func, "passed");
|
||||
printf("%-70s %s\n", cur_test_id, "passed");
|
||||
free(cur_test_id);
|
||||
cur_test_id = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
@ -179,7 +212,7 @@ test_kqueue(void)
|
||||
if ((kqfd = kqueue()) < 0)
|
||||
err(1, "kqueue()");
|
||||
test_no_kevents();
|
||||
success("kqueue()");
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -188,13 +221,41 @@ test_kqueue_close(void)
|
||||
test_begin("close(kq)");
|
||||
if (close(kqfd) < 0)
|
||||
err(1, "close()");
|
||||
success("kqueue_close()");
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
test_ev_receipt(void)
|
||||
{
|
||||
int kq;
|
||||
struct kevent kev;
|
||||
|
||||
test_begin("EV_RECEIPT");
|
||||
|
||||
#if HAVE_EV_RECEIPT
|
||||
|
||||
if ((kq = kqueue()) < 0)
|
||||
err(1, "kqueue");
|
||||
|
||||
EV_SET(&kev, SIGUSR2, EVFILT_SIGNAL, EV_ADD | EV_RECEIPT, 0, 0, NULL);
|
||||
if (kevent(kq, &kev, 1, &kev, 1, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
/* TODO: check the receipt */
|
||||
|
||||
close(kq);
|
||||
|
||||
#else
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
puts("Skipped -- EV_RECEIPT is not available");
|
||||
#endif
|
||||
success();
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int test_proc = 1;
|
||||
int test_proc = 0; /* XXX-FIXME */
|
||||
int test_socket = 1;
|
||||
int test_signal = 1;
|
||||
int test_vnode = 1;
|
||||
@ -221,13 +282,6 @@ main(int argc, char **argv)
|
||||
test_kqueue();
|
||||
test_kqueue_close();
|
||||
|
||||
#if FIXME
|
||||
if (test_proc)
|
||||
test_evfilt_proc();
|
||||
puts("All proc tests OK");
|
||||
exit(0);
|
||||
#endif
|
||||
|
||||
if (test_socket)
|
||||
test_evfilt_read();
|
||||
if (test_signal)
|
||||
@ -240,6 +294,10 @@ main(int argc, char **argv)
|
||||
#endif
|
||||
if (test_timer)
|
||||
test_evfilt_timer();
|
||||
if (test_proc)
|
||||
test_evfilt_proc();
|
||||
|
||||
test_ev_receipt();
|
||||
|
||||
printf("\n---\n"
|
||||
"+OK All %d tests completed.\n", testnum - 1);
|
||||
|
120
test/proc.c
120
test/proc.c
@ -16,63 +16,82 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static int sigusr1_caught = 0;
|
||||
|
||||
int kqfd;
|
||||
pid_t child;
|
||||
|
||||
static void
|
||||
sig_handler(int signum)
|
||||
{
|
||||
sigusr1_caught = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
add_and_delete(void)
|
||||
{
|
||||
const char *test_id1 = "kevent(EVFILT_PROC, EV_ADD)";
|
||||
const char *test_id2 = "kevent(EVFILT_PROC, EV_ADD)";
|
||||
struct kevent kev;
|
||||
pid_t pid;
|
||||
|
||||
test_begin(test_id1);
|
||||
/* 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);
|
||||
|
||||
EV_SET(&kev, child, EVFILT_PROC, EV_ADD, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id1);
|
||||
test_begin("kevent(EVFILT_PROC, EV_ADD)");
|
||||
|
||||
success(test_id1);
|
||||
test_no_kevents();
|
||||
kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
|
||||
test_no_kevents();
|
||||
|
||||
test_begin(test_id2);
|
||||
success();
|
||||
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id2);
|
||||
test_begin("kevent(EVFILT_PROC, EV_DELETE)");
|
||||
|
||||
success(test_id2);
|
||||
|
||||
}
|
||||
|
||||
#if TODO
|
||||
void
|
||||
test_kevent_signal_get(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, wait)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%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)
|
||||
err(1, "sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
test_no_kevents();
|
||||
kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
|
||||
if (kill(pid, SIGKILL) < 0)
|
||||
err(1, "kill");
|
||||
sleep(1);
|
||||
test_no_kevents();
|
||||
|
||||
kev.flags |= EV_CLEAR;
|
||||
kev.data = 1;
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
success();
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
static void
|
||||
event_trigger(void)
|
||||
{
|
||||
struct kevent kev;
|
||||
pid_t pid;
|
||||
|
||||
test_begin("kevent(EVFILT_PROC, wait)");
|
||||
|
||||
/* 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();
|
||||
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)
|
||||
err(1, "kill");
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
test_no_kevents();
|
||||
|
||||
success();
|
||||
}
|
||||
|
||||
#ifdef TODO
|
||||
void
|
||||
test_kevent_signal_disable(void)
|
||||
{
|
||||
@ -91,12 +110,12 @@ test_kevent_signal_disable(void)
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
err(1, "sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
if (kill(getpid(), SIGKILL) < 0)
|
||||
err(1, "kill");
|
||||
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -133,7 +152,7 @@ test_kevent_signal_enable(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -159,7 +178,7 @@ test_kevent_signal_del(void)
|
||||
err(1, "kill");
|
||||
|
||||
test_no_kevents();
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -192,7 +211,7 @@ test_kevent_signal_oneshot(void)
|
||||
err(1, "kill");
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -201,14 +220,13 @@ test_evfilt_proc()
|
||||
{
|
||||
kqfd = kqueue();
|
||||
|
||||
/* Create a child that sleeps for a few seconds and then exits */
|
||||
child = fork();
|
||||
if (child == 0) {
|
||||
sleep(3);
|
||||
exit(123);
|
||||
}
|
||||
signal(SIGUSR1, sig_handler);
|
||||
|
||||
add_and_delete();
|
||||
event_trigger();
|
||||
|
||||
signal(SIGUSR1, SIG_DFL);
|
||||
|
||||
#if TODO
|
||||
test_kevent_signal_add();
|
||||
test_kevent_signal_del();
|
||||
|
21
test/read.c
21
test/read.c
@ -50,7 +50,7 @@ test_kevent_socket_add(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -77,7 +77,7 @@ test_kevent_socket_get(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -111,7 +111,7 @@ test_kevent_socket_clear(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -147,7 +147,7 @@ test_kevent_socket_disable_and_enable(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -166,7 +166,7 @@ test_kevent_socket_del(void)
|
||||
test_no_kevents();
|
||||
kevent_socket_drain();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -199,7 +199,7 @@ test_kevent_socket_oneshot(void)
|
||||
|
||||
kevent_socket_drain();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
|
||||
@ -223,7 +223,6 @@ test_kevent_socket_dispatch(void)
|
||||
/* The event will occur only once, even though EV_CLEAR is not
|
||||
specified. */
|
||||
kevent_socket_fill();
|
||||
kev.flags = EV_ADD; /* FIXME: not sure what the proper behavior is */
|
||||
kev.data = 1;
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
test_no_kevents();
|
||||
@ -235,7 +234,7 @@ test_kevent_socket_dispatch(void)
|
||||
|
||||
kevent_socket_drain();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
#endif /* HAVE_EV_DISPATCH */
|
||||
|
||||
@ -269,7 +268,7 @@ test_kevent_socket_lowat(void)
|
||||
kevent_socket_drain();
|
||||
kevent_socket_drain();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -298,14 +297,14 @@ test_kevent_socket_eof(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
test_evfilt_read()
|
||||
{
|
||||
/* Create a connected pair of full-duplex sockets for testing socket events */
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0)
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
|
||||
abort();
|
||||
|
||||
kqfd = kqueue();
|
||||
|
@ -30,7 +30,7 @@ test_kevent_signal_add(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -58,7 +58,7 @@ test_kevent_signal_get(void)
|
||||
kev.data = 1;
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -84,7 +84,7 @@ test_kevent_signal_disable(void)
|
||||
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -121,7 +121,7 @@ test_kevent_signal_enable(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -147,7 +147,7 @@ test_kevent_signal_del(void)
|
||||
err(1, "kill");
|
||||
|
||||
test_no_kevents();
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -180,7 +180,7 @@ test_kevent_signal_oneshot(void)
|
||||
err(1, "kill");
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
|
12
test/timer.c
12
test/timer.c
@ -30,7 +30,7 @@ test_kevent_timer_add(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -47,7 +47,7 @@ test_kevent_timer_del(void)
|
||||
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -70,7 +70,7 @@ test_kevent_timer_get(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -97,7 +97,7 @@ test_oneshot(void)
|
||||
test_no_kevents();
|
||||
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -128,7 +128,7 @@ test_periodic(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -159,7 +159,7 @@ disable_and_enable(void)
|
||||
kev.data = 1;
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
|
81
test/user.c
81
test/user.c
@ -26,17 +26,13 @@ add_and_delete(void)
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
|
||||
test_no_kevents();
|
||||
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL);
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -47,17 +43,20 @@ event_wait(void)
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
test_no_kevents();
|
||||
|
||||
kev.fflags |= NOTE_TRIGGER;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
/* 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));
|
||||
|
||||
success(test_id);
|
||||
test_no_kevents();
|
||||
|
||||
success();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -68,25 +67,24 @@ disable_and_enable(void)
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
kev.flags = EV_DISABLE;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
kev.fflags |= NOTE_TRIGGER;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
test_no_kevents();
|
||||
|
||||
kev.flags = EV_ENABLE;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
kev.flags = EV_ADD;
|
||||
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();
|
||||
|
||||
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));
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -97,26 +95,21 @@ oneshot(void)
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, 1, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
test_no_kevents();
|
||||
|
||||
kevent_add(kqfd, &kev, 2, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL);
|
||||
|
||||
puts(" -- event 1");
|
||||
kev.fflags |= NOTE_TRIGGER;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
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));
|
||||
|
||||
/* Try to trigger the event again. It is deleted, so that
|
||||
should be an error.
|
||||
*/
|
||||
puts(" -- event 2 (should fail)");
|
||||
kev.flags = 0;
|
||||
kev.fflags |= NOTE_TRIGGER;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == 0)
|
||||
err(1, "%s", test_id);
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
|
16
test/vnode.c
16
test/vnode.c
@ -40,7 +40,7 @@ test_kevent_vnode_add(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -60,7 +60,7 @@ test_kevent_vnode_note_delete(void)
|
||||
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -84,7 +84,7 @@ test_kevent_vnode_note_write(void)
|
||||
kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -112,7 +112,7 @@ test_kevent_vnode_note_attrib(void)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -143,7 +143,7 @@ test_kevent_vnode_note_rename(void)
|
||||
if (system("mv /tmp/kqueue-test2.tmp /tmp/kqueue-test.tmp") < 0)
|
||||
err(1, "system");
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -158,7 +158,7 @@ test_kevent_vnode_del(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
@ -200,7 +200,7 @@ test_kevent_vnode_disable_and_enable(void)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
|
||||
#if HAVE_EV_DISPATCH
|
||||
@ -242,7 +242,7 @@ test_kevent_vnode_dispatch(void)
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "remove watch failed: %s", test_id);
|
||||
|
||||
success(test_id);
|
||||
success();
|
||||
}
|
||||
#endif /* HAVE_EV_DISPATCH */
|
||||
|
||||
|
164
www/index.html
164
www/index.html
@ -7,7 +7,33 @@
|
||||
|
||||
<title>libkqueue</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin-left: 2em;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
.blockquote { margin-right: 2em }
|
||||
code.block {
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
background: beige;
|
||||
margin: 3em;
|
||||
}
|
||||
h1 {
|
||||
margin-left: -0.5em;
|
||||
}
|
||||
h2 {
|
||||
margin-left: -0.5em;
|
||||
padding-top: 1em;
|
||||
}
|
||||
ol {
|
||||
margin: 2em;
|
||||
}
|
||||
li {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
p {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@ -15,21 +41,37 @@
|
||||
|
||||
<h1>libkqueue</h1>
|
||||
|
||||
libkqueue is a portable userspace implementation of the <a href="http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2">kqueue(2)</a> kernel event notification mechanism. Initial efforts are focused on porting the kqueue API to the Linux 2.6 kernel.
|
||||
libkqueue is a portable userspace implementation of the <a href="http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2">kqueue(2)</a> kernel event notification mechanism. Initial efforts are focused on porting the kqueue API to the Linux 2.6 kernel. There is also an experimental Solaris port (currently broken).
|
||||
|
||||
<h2>Download</h2>
|
||||
|
||||
Source code releases can be found <a href="dist">here</a>.
|
||||
Source code releases can be found <a href="dist">here</a>. There are no binary packages at the moment.
|
||||
<p>
|
||||
To checkout the SVN repository, run the following command:
|
||||
To checkout the entire SVN repository, run the following command:
|
||||
<p>
|
||||
<code>svn checkout svn://mark.heily.com/libkqueue</code>
|
||||
<code class=block>svn co svn://mark.heily.com/libkqueue</code>
|
||||
<p>
|
||||
You can checkout the trunk by using the following command:
|
||||
<p>
|
||||
<code class=block>svn co svn://mark.heily.com/libkqueue/trunk libkqueue</code>
|
||||
|
||||
<p>
|
||||
There is heavy development on the trunk, and it may not always be in a usable state. If you want to be current, but not bleeding edge, there is also a stable branch. You can checkout the stable branch by using the following command:
|
||||
<p>
|
||||
<code class=block>svn co svn://mark.heily.com/libkqueue/branches/stable libkqueue</code>
|
||||
|
||||
|
||||
<h2>Mailing Lists</h2>
|
||||
|
||||
There are two mailing lists: one for
|
||||
<a href="http://groups.google.com/group/libkqueue">general discussion</a>, and one for
|
||||
<a href="http://groups.google.com/group/libkqueue-announce">announcements only</a>.
|
||||
There are two mailing lists: one for general discussion, and one for announcements. If you subscribe to a mailing list through the web interface, you will need to sign up for a free Google account. If you prefer not to create a Google account, you can subscribe via email instead.
|
||||
<p>
|
||||
To subscribe to the general discussion group, visit the group's <a href="http://groups.google.com/group/libkqueue">web interface</a>, or send an email to <a href="mailto:libkqueue+subscribe@googlegroups.com">libkqueue+subscribe@googlegroups.com</a>.
|
||||
<p>
|
||||
To subscribe to the low-traffic annoucements-only group, visit the group's <a href="http://groups.google.com/group/libkqueue-announce">web interface</a>, or send an email to <a href="mailto:libkqueue-announce+subscribe@googlegroups.com">libkqueue-announce+subscribe@googlegroups.com</a>.
|
||||
<p>
|
||||
Please note that if you use multiple email addresses, Google Groups does not allow you to choose which email address will receive messages. The address on the <code>From:</code> line of the subscribe request will be used as the destination address.
|
||||
<p>
|
||||
To unsubscribe from either group, send an email to <code>[group name]+unsubscribe@googlegroups.com</code>. If you have any problems subscribing or unsubscribing, send a note to <a href="mailto:devel+libkqueue@heily.com">Mark Heily</a>.
|
||||
|
||||
<h2>Status</h2>
|
||||
|
||||
@ -38,15 +80,17 @@ The kqueue and kevent functions are implemented, and most of the filters are wor
|
||||
<ul>
|
||||
<li><code>EVFILT_PROC</code>
|
||||
<li><code>EVFILT_AIO</code>
|
||||
<li><code>EVFILT_FS</code>
|
||||
<li><code>EVFILT_NETDEV</code>
|
||||
</ul>
|
||||
|
||||
More details about the current implementation status can be found <a href="status.html">here</a>.
|
||||
<p>
|
||||
There are several compatibility issues to be aware of when using this library under Linux:<p>
|
||||
There are several compatibility issues to be aware of when using this library under Linux:<br>
|
||||
|
||||
<ol>
|
||||
|
||||
<li>The NOTE_LOWAT flag is not supported. According to <i>socket(7)</i>,<p>
|
||||
<li>The NOTE_LOWAT flag is not supported. According to <i>socket(7)</i>,<br>
|
||||
<pre style="blockquote">
|
||||
"The select(2) and poll(2) system calls currently do not respect the
|
||||
SO_RCVLOWAT setting on Linux, and mark a socket readable when even a
|
||||
@ -72,6 +116,46 @@ The EVFILT_PROC filter only supports the NOTE_EXIT flag, and the <code>ident</co
|
||||
|
||||
</ol>
|
||||
|
||||
Here are the issues to be aware of when using this library under any platform:<br>
|
||||
|
||||
<ol>
|
||||
<li>When a kqueue descriptor is closed, it's resources are not immediately reclaimed. Instead,
|
||||
they will be freed the next time that the <code>kqueue(2)</code> function is called.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2>Future Plans</h2>
|
||||
|
||||
In the future, I plan to add three more backends. One will be for Solaris and take advantage of the event port mechanism described in <a href="http://docs.sun.com/app/docs/doc/816-5168/port-create-3c?l=en&a=view&q=port_create">port_create(3C)</a>. The second will be a generic POSIX backend that does not use any non-portable system calls. The third will be a Microsoft Windows port that uses <code>WaitForMultipleObjects()</code>.
|
||||
|
||||
<h2>Portability of kevent(2) across BSD systems</h2>
|
||||
|
||||
There are some differences in the behavior of the <code>kevent(2)</code> system call across the various BSD-based operating systems. Here are some of the differences to be aware of:
|
||||
|
||||
<ol>
|
||||
|
||||
<li>FreeBSD 8 does not set the <i>EV_ADD</i> flag for kevents on the eventlist, but OpenBSD and Darwin do. This means you should never use the equality operator (==) to test the flags; use the logical AND operator instead.
|
||||
</li>
|
||||
|
||||
<li>The <code>EVFILT_USER</code> filter behaves differently from other filters with respect to the <i>EV_ONESHOT</i> flag. All other filters will preserve the flag when the event is triggered and placed on the eventlist. The <code>EVFILT_USER</code> filter does not preserve this flag.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
OpenBSD has the <i>NOTE_TRUNCATE</i> fflag, while FreeBSD and Darwin do not.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
EVFILT_FS is undocumented and only available on FreeBSD and Darwin. Here is the <a href="http://adam.kungfoohampster.com/lists/cvs-all/msg71399.shtml">CVS commit log</a> which could be helpful to document this filter.
|
||||
</li>
|
||||
<!--
|
||||
<li>
|
||||
[TODO - Verify and check against 10.6] OS X 10.5 and earlier do not implement the <code>EVFILT_AIO</code> or <code>EVFILT_TIMER</code> filters.
|
||||
</li>
|
||||
-->
|
||||
|
||||
</ol>
|
||||
|
||||
|
||||
<h2>Requirements</h2>
|
||||
|
||||
libkqueue currently requires the following:
|
||||
@ -82,14 +166,49 @@ libkqueue currently requires the following:
|
||||
<li>glibc 2.8 or higher
|
||||
</ul>
|
||||
|
||||
The Solaris port requires Solaris 10 or higher, and uses the GNU compiler and toolchain.
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
Here are the steps to use libkqueue in your program:
|
||||
|
||||
<h3>Ordinary Makefile</h3>
|
||||
|
||||
Here are the steps to use libkqueue in your program if you use an ordinary Makefile:
|
||||
|
||||
<ol>
|
||||
<li>Add <code>`pkg-config libkqueue --cflags`</code> to the CFLAGS variable.
|
||||
<li>Add <code>`pkg-config libkqueue --libs`</code> to the LDADD variable.
|
||||
<li>Add <code>#include <sys/event.h></code> to the source code.
|
||||
</ol>
|
||||
<p>
|
||||
|
||||
<h3>Autoconf/Automake/Libtool</h3>
|
||||
|
||||
If your program uses the GNU Autoconf/Automake/Libtool build system, the following steps will allow you to use libkqueue:
|
||||
|
||||
<ol>
|
||||
|
||||
<li>Add the following to <code>configure.ac</code>:
|
||||
<pre>
|
||||
#
|
||||
# Prefer native kqueue(2); otherwise use libkqueue if present.
|
||||
#
|
||||
AC_CHECK_HEADER(sys/event.h, [],
|
||||
[PKG_CHECK_MODULES(KQUEUE, libkqueue)]
|
||||
)
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
Add the following to <code>Makefile.am</code> (assuming your program is named "foo"):
|
||||
<pre>
|
||||
foo_CFLAGS+=$(KQUEUE_CFLAGS)
|
||||
foo_LDADD+=$(KQUEUE_LIBS)
|
||||
</pre>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>Instead of using the <code>$(KQUEUE_LIBS)</code> variable, you could just add <code>libkqueue.la</code> to your programs LDADD variable.
|
||||
|
||||
<h3>Threads</h3>
|
||||
|
||||
@ -106,8 +225,31 @@ libkqueue uses one or more helper threads, so all programs that link with libkqu
|
||||
<li><a href="http://doc.geoffgarside.co.uk/kqueue/">Kqueues for Fun and Profit</a> [Werner]</li>
|
||||
</ul>
|
||||
|
||||
<li>
|
||||
<a href="http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/tools/tests/xnu_quick_test/kqueue_tests.c">XNU kqueue test program</a> - Demonstrates use of EVFILT_USER
|
||||
</li>
|
||||
|
||||
<h3>Solaris Event Ports</h3>
|
||||
I do plan to add Solaris event port support to libkqueue once the Linux backend is complete and the internal API is stabilized. I checked the port_create(3C) manpage in Solaris 10 and OpenSolaris, and did not see any way to wait for signals. There is also no equivalent to EVFILT_VNODE in Solaris 10, although OpenSolaris has the PORT_SOURCE_FILE functionality.
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://docs.sun.com/app/docs/doc/816-5168/port-create-3c?l=en&a=view&q=port_create">Solaris 10 port_create(3C) manual page</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="http://docs.sun.com/app/docs/doc/819-2243/port-create-3c?a=view">
|
||||
OpenSolaris port_create(3C) manual page</a>
|
||||
</li>
|
||||
|
||||
|
||||
<h2>Contact</h2>
|
||||
For more information, contact <a href="mailto:devel@heily.com">Mark Heily</a>.
|
||||
For more information, contact <a href="mailto:devel+libkqueue@heily.com">Mark Heily</a>.
|
||||
|
||||
<p>
|
||||
<hr>
|
||||
<center>
|
||||
© 2009 Mark Heily. All rights reserved.
|
||||
</center>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user