mirror of
https://github.com/darlinghq/darling.git
synced 2026-05-03 09:19:19 -05:00
Adding libdispatch, libkqueue and libpwq code
This commit is contained in:
+1
Submodule external/libdispatch added at 617a4e1a31
+1
@@ -0,0 +1 @@
|
||||
libkqueue-2.0.1
|
||||
Vendored
+279
@@ -0,0 +1,279 @@
|
||||
# AUTOMATICALLY GENERATED -- DO NOT EDIT
|
||||
AR = ar
|
||||
BINDIR = $(EPREFIX)/bin
|
||||
CC = cc
|
||||
DATADIR = $(DATAROOTDIR)
|
||||
DATAROOTDIR = $(PREFIX)/share
|
||||
DOCDIR = $(DATAROOTDIR)/doc/$(PACKAGE)
|
||||
EPREFIX = $(PREFIX)
|
||||
INCLUDEDIR = $(PREFIX)/include
|
||||
INFODIR = $(DATAROOTDIR)/info
|
||||
INSTALL ?= /usr/bin/install
|
||||
LD = cc
|
||||
LIBDIR = $(EPREFIX)/lib
|
||||
LIBEXECDIR = $(EPREFIX)/libexec
|
||||
LOCALEDIR = $(DATAROOTDIR)/locale
|
||||
LOCALSTATEDIR = $(PREFIX)/var
|
||||
MANDIR = $(DATAROOTDIR)/man
|
||||
OLDINCLUDEDIR = /usr/include
|
||||
PKGCONFIGDIR = $(LIBDIR)/pkgconfig
|
||||
PKGDATADIR = $(DATADIR)/$(PACKAGE)
|
||||
PKGINCLUDEDIR = $(INCLUDEDIR)/$(PACKAGE)
|
||||
PKGLIBDIR = $(LIBDIR)/$(PACKAGE)
|
||||
PREFIX = /usr/local
|
||||
RANLIB = ranlib
|
||||
SBINDIR = $(EPREFIX)/sbin
|
||||
SHAREDSTATEDIR = $(PREFIX)/com
|
||||
SYSCONFDIR = $(PREFIX)/etc
|
||||
|
||||
|
||||
#
|
||||
# Detect the canonical system type of the system we are building on
|
||||
# (build) and the system the package runs on (host)
|
||||
#
|
||||
BUILD_CPU=$(shell uname -m)
|
||||
HOST_CPU=$(BUILD_CPU)
|
||||
BUILD_VENDOR=unknown
|
||||
HOST_VENDOR=$(BUILD_VENDOR)
|
||||
BUILD_KERNEL=$(shell uname | tr '[A-Z]' '[a-z]')
|
||||
HOST_KERNEL=$(BUILD_KERNEL)
|
||||
BUILD_SYSTEM=gnu
|
||||
HOST_SYSTEM=$(BUILD_SYSTEM)
|
||||
BUILD_TYPE=$(BUILD_CPU)-$(BUILD_VENDOR)-$(BUILD_KERNEL)-$(BUILD_SYSTEM)
|
||||
HOST_TYPE=$(HOST_CPU)-$(HOST_VENDOR)-$(HOST_KERNEL)-$(HOST_SYSTEM)
|
||||
|
||||
# Allow variables to be overridden via a ./configure script that outputs config.mk
|
||||
# FIXME -- requires GNU Make
|
||||
-include config.mk
|
||||
|
||||
default: all
|
||||
|
||||
all: libkqueue.so libkqueue.a kqtest libkqueue.pc
|
||||
|
||||
check: kqtest
|
||||
./kqtest
|
||||
|
||||
clean:
|
||||
rm -f *.rpm
|
||||
rm -f libkqueue-2.0.1.tar.gz
|
||||
rm -f src/common/filter.o
|
||||
rm -f src/common/knote.o
|
||||
rm -f src/common/map.o
|
||||
rm -f src/common/kevent.o
|
||||
rm -f src/common/kqueue.o
|
||||
rm -f src/posix/platform.o
|
||||
rm -f src/linux/platform.o
|
||||
rm -f src/linux/read.o
|
||||
rm -f src/linux/write.o
|
||||
rm -f src/linux/user.o
|
||||
rm -f src/linux/vnode.o
|
||||
rm -f src/linux/signal.o
|
||||
rm -f src/linux/timer.o
|
||||
rm -f libkqueue.so
|
||||
rm -f libkqueue.a
|
||||
rm -f test/main.o
|
||||
rm -f test/kevent.o
|
||||
rm -f test/test.o
|
||||
rm -f test/proc.o
|
||||
rm -f test/read.o
|
||||
rm -f test/signal.o
|
||||
rm -f test/timer.o
|
||||
rm -f test/vnode.o
|
||||
rm -f test/user.o
|
||||
rm -f kqtest
|
||||
|
||||
config.h:
|
||||
@echo "checking build system type... $(BUILD_TYPE)"
|
||||
@echo "checking host system type... $(HOST_TYPE)"
|
||||
@echo "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */" > config.h.tmp
|
||||
@date > config.log
|
||||
@printf "checking whether EPOLLRDHUP is declared in sys/epoll.h... " | tee -a config.log
|
||||
@( printf '#define _GNU_SOURCE\n#include <sys/epoll.h>\nint main() { EPOLLRDHUP; }' | $(CC) $(CFLAGS) -E -x c - ) >/dev/null 2>>config.log && ( echo '#define HAVE_DECL_EPOLLRDHUP 1' >> config.h.tmp ; echo 'yes' ) || (echo '#define HAVE_DECL_EPOLLRDHUP 0' >> config.h.tmp ; echo 'no' )
|
||||
@printf "checking whether ppoll is declared in poll.h... " | tee -a config.log
|
||||
@( printf '#define _GNU_SOURCE\n#include <poll.h>\nint main() { ppoll; }' | $(CC) $(CFLAGS) -E -x c - ) >/dev/null 2>>config.log && ( echo '#define HAVE_DECL_PPOLL 1' >> config.h.tmp ; echo 'yes' ) || (echo '#define HAVE_DECL_PPOLL 0' >> config.h.tmp ; echo 'no' )
|
||||
@printf "checking for sys/epoll.h... "
|
||||
@( echo '#include <sys/epoll.h>' | $(CC) $(CFLAGS) -E -x c - ) >/dev/null 2>&1 && ( echo '#define HAVE_SYS_EPOLL_H 1' >> config.h.tmp ; echo 'yes' ) || (echo '/* #undef HAVE_SYS_EPOLL_H */' >> config.h.tmp ; echo 'no' )
|
||||
@printf "checking for sys/event.h... "
|
||||
@( echo '#include <sys/event.h>' | $(CC) $(CFLAGS) -E -x c - ) >/dev/null 2>&1 && ( echo '#define HAVE_SYS_EVENT_H 1' >> config.h.tmp ; echo 'yes' ) || (echo '/* #undef HAVE_SYS_EVENT_H */' >> config.h.tmp ; echo 'no' )
|
||||
@printf "checking for sys/eventfd.h... "
|
||||
@( echo '#include <sys/eventfd.h>' | $(CC) $(CFLAGS) -E -x c - ) >/dev/null 2>&1 && ( echo '#define HAVE_SYS_EVENTFD_H 1' >> config.h.tmp ; echo 'yes' ) || (echo '/* #undef HAVE_SYS_EVENTFD_H */' >> config.h.tmp ; echo 'no' )
|
||||
@printf "checking for sys/inotify.h... "
|
||||
@( echo '#include <sys/inotify.h>' | $(CC) $(CFLAGS) -E -x c - ) >/dev/null 2>&1 && ( echo '#define HAVE_SYS_INOTIFY_H 1' >> config.h.tmp ; echo 'yes' ) || (echo '/* #undef HAVE_SYS_INOTIFY_H */' >> config.h.tmp ; echo 'no' )
|
||||
@printf "checking for sys/signalfd.h... "
|
||||
@( echo '#include <sys/signalfd.h>' | $(CC) $(CFLAGS) -E -x c - ) >/dev/null 2>&1 && ( echo '#define HAVE_SYS_SIGNALFD_H 1' >> config.h.tmp ; echo 'yes' ) || (echo '/* #undef HAVE_SYS_SIGNALFD_H */' >> config.h.tmp ; echo 'no' )
|
||||
@printf "checking for sys/timerfd.h... "
|
||||
@( echo '#include <sys/timerfd.h>' | $(CC) $(CFLAGS) -E -x c - ) >/dev/null 2>&1 && ( echo '#define HAVE_SYS_TIMERFD_H 1' >> config.h.tmp ; echo 'yes' ) || (echo '/* #undef HAVE_SYS_TIMERFD_H */' >> config.h.tmp ; echo 'no' )
|
||||
@rm -f conftest.c conftest.o
|
||||
@echo "creating config.h"
|
||||
@mv config.h.tmp config.h
|
||||
|
||||
dist: libkqueue-2.0.1.tar.gz
|
||||
|
||||
distclean: clean
|
||||
rm -f GNUmakefile
|
||||
rm -f libkqueue-2.0.1.tar.gz
|
||||
rm -f config.h
|
||||
rm -f config.yaml
|
||||
|
||||
distdir: config.h
|
||||
umask 22 ; mkdir -p '$(distdir)/src/common'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/common/../posix'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/common/../posix/../../include/sys'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/common/../linux'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/posix'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/posix/../common'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/posix/../common/../posix'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/posix/../common/../posix/../../include/sys'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/posix/../common/../linux'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/linux'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/linux/../common'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/linux/../common/../posix'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/linux/../common/../posix/../../include/sys'
|
||||
umask 22 ; mkdir -p '$(distdir)/src/linux/../common/../linux'
|
||||
umask 22 ; mkdir -p '$(distdir)/include/sys'
|
||||
umask 22 ; mkdir -p '$(distdir)/test'
|
||||
umask 22 ; mkdir -p '$(distdir)/test/..'
|
||||
cp -RL libkqueue.spec config.h GNUmakefile kqueue.2 libkqueue.pc.in configure configure.rb LICENSE $(distdir)
|
||||
cp -RL src/common/filter.c src/common/private.h src/common/tree.h src/common/debug.h src/common/knote.c src/common/alloc.h src/common/map.c src/common/kevent.c src/common/kqueue.c $(distdir)/src/common
|
||||
cp -RL src/common/../posix/platform.h $(distdir)/src/common/../posix
|
||||
cp -RL src/common/../posix/../../include/sys/event.h $(distdir)/src/common/../posix/../../include/sys
|
||||
cp -RL src/common/../linux/platform.h $(distdir)/src/common/../linux
|
||||
cp -RL src/posix/platform.c $(distdir)/src/posix
|
||||
cp -RL src/posix/../common/private.h src/posix/../common/tree.h src/posix/../common/debug.h $(distdir)/src/posix/../common
|
||||
cp -RL src/posix/../common/../posix/platform.h $(distdir)/src/posix/../common/../posix
|
||||
cp -RL src/posix/../common/../posix/../../include/sys/event.h $(distdir)/src/posix/../common/../posix/../../include/sys
|
||||
cp -RL src/posix/../common/../linux/platform.h $(distdir)/src/posix/../common/../linux
|
||||
cp -RL src/linux/platform.c src/linux/read.c src/linux/write.c src/linux/user.c src/linux/vnode.c src/linux/signal.c src/linux/timer.c $(distdir)/src/linux
|
||||
cp -RL src/linux/../common/private.h src/linux/../common/tree.h src/linux/../common/debug.h $(distdir)/src/linux/../common
|
||||
cp -RL src/linux/../common/../posix/platform.h $(distdir)/src/linux/../common/../posix
|
||||
cp -RL src/linux/../common/../posix/../../include/sys/event.h $(distdir)/src/linux/../common/../posix/../../include/sys
|
||||
cp -RL src/linux/../common/../linux/platform.h $(distdir)/src/linux/../common/../linux
|
||||
cp -RL include/sys/event.h $(distdir)/include/sys
|
||||
cp -RL test/main.c test/common.h test/kevent.c test/test.c test/proc.c test/read.c test/signal.c test/timer.c test/vnode.c test/user.c $(distdir)/test
|
||||
cp -RL test/../config.h $(distdir)/test/..
|
||||
|
||||
install:
|
||||
/usr/bin/test -e $(DESTDIR)$(LIBDIR) || $(INSTALL) -d -m 755 $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL) -m 0644 libkqueue.so $(DESTDIR)$(LIBDIR)/libkqueue.so.0.0
|
||||
/usr/bin/test -e $(DESTDIR)$(INCLUDEDIR)/kqueue/sys || $(INSTALL) -d -m 755 $(DESTDIR)$(INCLUDEDIR)/kqueue/sys
|
||||
$(INSTALL) -m 644 include/sys/event.h $(DESTDIR)$(INCLUDEDIR)/kqueue/sys
|
||||
/usr/bin/test -e $(DESTDIR)$(MANDIR)/man2 || $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man2
|
||||
$(INSTALL) -m 644 kqueue.2 $(DESTDIR)$(MANDIR)/man2
|
||||
/usr/bin/test -e $(DESTDIR)$(PKGCONFIGDIR) || $(INSTALL) -d -m 755 $(DESTDIR)$(PKGCONFIGDIR)
|
||||
$(INSTALL) -m 644 libkqueue.pc $(DESTDIR)$(PKGCONFIGDIR)
|
||||
rm -f $(DESTDIR)$(LIBDIR)/libkqueue.so
|
||||
ln -s libkqueue.so.0.0 $(DESTDIR)$(LIBDIR)/libkqueue.so
|
||||
rm -f $(DESTDIR)$(LIBDIR)/libkqueue.so.0
|
||||
ln -s libkqueue.so.0.0 $(DESTDIR)$(LIBDIR)/libkqueue.so.0
|
||||
rm -f $(DESTDIR)$(MANDIR)/man2/kevent.2
|
||||
ln -s kqueue.2 $(DESTDIR)$(MANDIR)/man2/kevent.2
|
||||
|
||||
kqtest: test/main.o test/kevent.o test/test.o test/proc.o test/read.o test/signal.o test/timer.o test/vnode.o test/user.o
|
||||
$(LD) -o kqtest -L . -Wl,-rpath,. -L . $(LDFLAGS) test/main.o test/kevent.o test/test.o test/proc.o test/read.o test/signal.o test/timer.o test/vnode.o test/user.o libkqueue.a -lpthread -lrt $(LDADD)
|
||||
|
||||
libkqueue-2.0.1.tar.gz:
|
||||
rm -rf libkqueue-2.0.1
|
||||
mkdir libkqueue-2.0.1
|
||||
$(MAKE) distdir distdir=libkqueue-2.0.1
|
||||
rm -rf libkqueue-2.0.1.tar libkqueue-2.0.1.tar.gz
|
||||
tar cf libkqueue-2.0.1.tar libkqueue-2.0.1
|
||||
gzip libkqueue-2.0.1.tar
|
||||
rm -rf libkqueue-2.0.1
|
||||
|
||||
libkqueue.a: src/common/filter.o src/common/knote.o src/common/map.o src/common/kevent.o src/common/kqueue.o src/posix/platform.o src/linux/platform.o src/linux/read.o src/linux/write.o src/linux/user.o src/linux/vnode.o src/linux/signal.o src/linux/timer.o
|
||||
ifneq ($(DISABLE_STATIC),1)
|
||||
$(AR) cru libkqueue.a src/common/filter.o src/common/knote.o src/common/map.o src/common/kevent.o src/common/kqueue.o src/posix/platform.o src/linux/platform.o src/linux/read.o src/linux/write.o src/linux/user.o src/linux/vnode.o src/linux/signal.o src/linux/timer.o
|
||||
$(RANLIB) libkqueue.a
|
||||
endif
|
||||
|
||||
libkqueue.pc: config.h
|
||||
@echo 'creating libkqueue.pc'
|
||||
@printf "prefix=$(PREFIX)\nexec_prefix=$(EPREFIX)\nlibdir=$(LIBDIR)\nincludedir=$(INCLUDEDIR)\n" > libkqueue.pc
|
||||
@cat libkqueue.pc.in >> libkqueue.pc
|
||||
|
||||
libkqueue.so: src/common/filter.o src/common/knote.o src/common/map.o src/common/kevent.o src/common/kqueue.o src/posix/platform.o src/linux/platform.o src/linux/read.o src/linux/write.o src/linux/user.o src/linux/vnode.o src/linux/signal.o src/linux/timer.o
|
||||
$(LD) -o libkqueue.so -shared -fPIC -L . -Wl,-soname,libkqueue.so.0 $(LDFLAGS) src/common/filter.o src/common/knote.o src/common/map.o src/common/kevent.o src/common/kqueue.o src/posix/platform.o src/linux/platform.o src/linux/read.o src/linux/write.o src/linux/user.o src/linux/vnode.o src/linux/signal.o src/linux/timer.o -lpthread -lrt $(LDADD)
|
||||
|
||||
package: clean libkqueue-2.0.1.tar.gz
|
||||
rm -rf rpm *.rpm
|
||||
mkdir -p rpm/BUILD rpm/RPMS rpm/SOURCES rpm/SPECS rpm/SRPMS
|
||||
mkdir -p rpm/RPMS/`uname -m`
|
||||
cp libkqueue-2.0.1.tar.gz rpm/SOURCES
|
||||
cp libkqueue.spec rpm/SPECS/libkqueue.spec
|
||||
perl -pi -e 's/^Version:.*/Version: 2.0.1/' rpm/SPECS/libkqueue.spec
|
||||
rpmbuild --define "_topdir `pwd`/rpm" -bs rpm/SPECS/libkqueue.spec
|
||||
rpmbuild --define "_topdir `pwd`/rpm" -bb rpm/SPECS/libkqueue.spec
|
||||
mv ./rpm/SRPMS/* ./rpm/RPMS/*/*.rpm .
|
||||
rm -rf rpm
|
||||
|
||||
src/common/filter.o: src/common/filter.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/common/filter.o -fPIC -DPIC $(CFLAGS) -c src/common/filter.c
|
||||
|
||||
src/common/kevent.o: src/common/kevent.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/common/kevent.o -fPIC -DPIC $(CFLAGS) -c src/common/kevent.c
|
||||
|
||||
src/common/knote.o: src/common/knote.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h src/common/alloc.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/common/knote.o -fPIC -DPIC $(CFLAGS) -c src/common/knote.c
|
||||
|
||||
src/common/kqueue.o: src/common/kqueue.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/common/kqueue.o -fPIC -DPIC $(CFLAGS) -c src/common/kqueue.c
|
||||
|
||||
src/common/map.o: src/common/map.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/common/map.o -fPIC -DPIC $(CFLAGS) -c src/common/map.c
|
||||
|
||||
src/linux/platform.o: src/linux/platform.c src/linux/../common/private.h config.h src/linux/../common/tree.h src/linux/../common/../posix/platform.h src/linux/../common/../posix/../../include/sys/event.h src/linux/../common/../linux/platform.h src/linux/../common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/linux/platform.o -fPIC -DPIC $(CFLAGS) -c src/linux/platform.c
|
||||
|
||||
src/linux/read.o: src/linux/read.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/linux/read.o -fPIC -DPIC $(CFLAGS) -c src/linux/read.c
|
||||
|
||||
src/linux/signal.o: src/linux/signal.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/linux/signal.o -fPIC -DPIC $(CFLAGS) -c src/linux/signal.c
|
||||
|
||||
src/linux/timer.o: src/linux/timer.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/linux/timer.o -fPIC -DPIC $(CFLAGS) -c src/linux/timer.c
|
||||
|
||||
src/linux/user.o: src/linux/user.c include/sys/event.h src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/linux/user.o -fPIC -DPIC $(CFLAGS) -c src/linux/user.c
|
||||
|
||||
src/linux/vnode.o: src/linux/vnode.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/linux/vnode.o -fPIC -DPIC $(CFLAGS) -c src/linux/vnode.c
|
||||
|
||||
src/linux/write.o: src/linux/write.c src/common/private.h config.h src/common/tree.h src/common/../posix/platform.h src/common/../posix/../../include/sys/event.h src/common/../linux/platform.h src/common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/linux/write.o -fPIC -DPIC $(CFLAGS) -c src/linux/write.c
|
||||
|
||||
src/posix/platform.o: src/posix/platform.c src/posix/../common/private.h config.h src/posix/../common/tree.h src/posix/../common/../posix/platform.h src/posix/../common/../posix/../../include/sys/event.h src/posix/../common/../linux/platform.h src/posix/../common/debug.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -fvisibility=hidden -o src/posix/platform.o -fPIC -DPIC $(CFLAGS) -c src/posix/platform.c
|
||||
|
||||
test/kevent.o: test/kevent.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/kevent.o $(CFLAGS) -c test/kevent.c
|
||||
|
||||
test/main.o: test/main.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/main.o $(CFLAGS) -c test/main.c
|
||||
|
||||
test/proc.o: test/proc.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/proc.o $(CFLAGS) -c test/proc.c
|
||||
|
||||
test/read.o: test/read.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/read.o $(CFLAGS) -c test/read.c
|
||||
|
||||
test/signal.o: test/signal.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/signal.o $(CFLAGS) -c test/signal.c
|
||||
|
||||
test/test.o: test/test.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/test.o $(CFLAGS) -c test/test.c
|
||||
|
||||
test/timer.o: test/timer.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/timer.o $(CFLAGS) -c test/timer.c
|
||||
|
||||
test/user.o: test/user.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/user.o $(CFLAGS) -c test/user.c
|
||||
|
||||
test/vnode.o: test/vnode.c test/common.h include/sys/event.h test/../config.h GNUmakefile
|
||||
$(CC) -DHAVE_CONFIG_H -I. -g -O0 -Wall -Werror -I./include -I./test -g -O0 -o test/vnode.o $(CFLAGS) -c test/vnode.c
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(LIBDIR)/libkqueue.so
|
||||
rm -f $(DESTDIR)$(INCLUDEDIR)/kqueue/sys/include/sys/event.h
|
||||
rm -f $(DESTDIR)$(MANDIR)/man2/kqueue.2
|
||||
rm -f $(DESTDIR)$(PKGCONFIGDIR)/libkqueue.pc
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
== all source ==
|
||||
|
||||
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.
|
||||
|
||||
== event.h ==
|
||||
|
||||
Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
/* AUTOMATICALLY GENERATED -- DO NOT EDIT */
|
||||
#define HAVE_DECL_EPOLLRDHUP 1
|
||||
#define HAVE_DECL_PPOLL 1
|
||||
#define HAVE_SYS_EPOLL_H 1
|
||||
/* #undef HAVE_SYS_EVENT_H */
|
||||
#define HAVE_SYS_EVENTFD_H 1
|
||||
#define HAVE_SYS_INOTIFY_H 1
|
||||
#define HAVE_SYS_SIGNALFD_H 1
|
||||
#define HAVE_SYS_TIMERFD_H 1
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2013 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.
|
||||
#
|
||||
|
||||
usage() {
|
||||
cat << "EOF"
|
||||
Usage: configure [options]
|
||||
|
||||
Installation options:
|
||||
--bindir [DIRECTORY] TODO describe this [$(EPREFIX)/bin]
|
||||
--datadir [DIRECTORY] TODO describe this [$(DATAROOTDIR)]
|
||||
--datarootdir [DIRECTORY] TODO describe this [$(PREFIX)/share]
|
||||
--docdir [DIRECTORY] TODO describe this [$(DATAROOTDIR)/doc/$(PACKAGE)]
|
||||
--eprefix [DIRECTORY] TODO describe this [$(PREFIX)]
|
||||
--includedir [DIRECTORY] TODO describe this [$(PREFIX)/include]
|
||||
--infodir [DIRECTORY] TODO describe this [$(DATAROOTDIR)/info]
|
||||
--libdir [DIRECTORY] TODO describe this [$(EPREFIX)/lib]
|
||||
--libexecdir [DIRECTORY] TODO describe this [$(EPREFIX)/libexec]
|
||||
--localedir [DIRECTORY] TODO describe this [$(DATAROOTDIR)/locale]
|
||||
--localstatedir [DIRECTORY] TODO describe this [$(PREFIX)/var]
|
||||
--mandir [DIRECTORY] TODO describe this [$(DATAROOTDIR)/man]
|
||||
--oldincludedir [DIRECTORY] TODO describe this [/usr/include]
|
||||
--pkgconfigdir [DIRECTORY] where to install pkg-config files [$(LIBDIR)/pkgconfig]
|
||||
--pkgdatadir [DIRECTORY] TODO describe this [$(DATADIR)/$(PACKAGE)]
|
||||
--pkgincludedir [DIRECTORY] TODO describe this [$(INCLUDEDIR)/$(PACKAGE)]
|
||||
--pkglibdir [DIRECTORY] TODO describe this [$(LIBDIR)/$(PACKAGE)]
|
||||
--prefix [DIRECTORY] TODO describe this [/usr/local]
|
||||
--sbindir [DIRECTORY] TODO describe this [$(EPREFIX)/sbin]
|
||||
--sharedstatedir [DIRECTORY] TODO describe this [$(PREFIX)/com]
|
||||
--sysconfdir [DIRECTORY] TODO describe this [$(PREFIX)/etc]
|
||||
|
||||
Project options:
|
||||
|
||||
System types:
|
||||
--build BUILD set the system type for building
|
||||
--host HOST cross-compile programs to run on a different system type
|
||||
--target TARGET build a compiler for cross-compiling
|
||||
|
||||
Optional Features:
|
||||
|
||||
Common options:
|
||||
--disable-static Disable generation of static libraries
|
||||
--disable-option-checking
|
||||
-h, --help Show this message
|
||||
-V, --version Display version information and exit
|
||||
EOF
|
||||
}
|
||||
|
||||
err() {
|
||||
echo "*** ERROR *** $*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_var() {
|
||||
key=$1
|
||||
eval "val=\$$key"
|
||||
if [ "$val" = "" ] ; then
|
||||
echo "ERROR: you must provide --$2 as a command line option"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
echo "# Automatically generated by ./configure -- do not edit" > config.mk
|
||||
for arg in $*
|
||||
do
|
||||
if [ `echo "$arg" | grep "^--"` = "$arg" ] ; then
|
||||
key=`echo $arg | sed "s/^--//; s/=.*//; s/^with-//;"`
|
||||
val=`echo $arg | sed "s/.*=//"`
|
||||
uc_key=`echo "$key" | tr "a-z" "A-Z" | tr "-" "_"`
|
||||
case $key in
|
||||
"help")
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
bindir|datadir|datarootdir|docdir|includedir|infodir|libdir|libexecdir|localedir|localstatedir|mandir|pkgconfigdir|pkgdatadir|pkgincludedir|pkglibdir|prefix|sbindir|sharedstatedir|sysconfdir)
|
||||
echo "$uc_key=$val" >> config.mk
|
||||
;;
|
||||
exec-prefix)
|
||||
echo "EPREFIX=$val" >> config.mk
|
||||
;;
|
||||
program-prefix)
|
||||
if [ "$val" != "" ] ; then err "FIXME - not implemented" ; fi
|
||||
;;
|
||||
disable-static)
|
||||
echo "$uc_key=1" >> config.mk
|
||||
;;
|
||||
build|host)
|
||||
# TODO: we should split this up, and override the other Makeconf
|
||||
# variables like *_VENDOR, *_ARCH, *_CPU, *_KERNEL, *_SYSTEM
|
||||
echo "$uc_key=$val" >> config.mk
|
||||
eval "${key}_system_type=\"$val\""
|
||||
;;
|
||||
disable-option-checking)
|
||||
# Not implemented, this behavior is the default (for now)
|
||||
;;
|
||||
disable-dependency-tracking)
|
||||
# Not implemented, dependency tracking is done in Ruby (for now)
|
||||
;;
|
||||
# Android-specific variables
|
||||
ndk|sdk)
|
||||
echo "$uc_key=$val" >> config.mk
|
||||
eval "$key=\"$val\""
|
||||
;;
|
||||
*)
|
||||
echo "Warning: unrecognized option: $arg"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
# Android-specific options
|
||||
if [ "`echo ${host_system_type} | grep androideabi`" != "" ] ; then
|
||||
exec ./configure.rb $*
|
||||
# require_var ndk with-ndk
|
||||
# require_var sdk with-sdk
|
||||
fi
|
||||
|
||||
printf "checking for a C compiler... "
|
||||
for cmd in ${host_system_type}-cc cc gcc gcc4 clang
|
||||
do
|
||||
$cmd --version >/dev/null 2>&1
|
||||
if [ $? -eq 0 ] ; then cc="$cmd" ; break ; fi
|
||||
done
|
||||
if [ -n "$CC" ] ; then cc="$CC" ; fi
|
||||
if [ -n "$cc" ] ; then
|
||||
echo "$cc"
|
||||
echo "CC=$cc" >> config.mk
|
||||
if [ "$cc" != "cc" ] ; then echo "LD=$cc" >> config.mk ; fi
|
||||
if [ -n "$CFLAGS" ] ; then echo "CFLAGS=$CFLAGS" >> config.mk ; fi
|
||||
else
|
||||
echo "not found"
|
||||
err "Please install a compiler and add it to your PATH"
|
||||
fi
|
||||
|
||||
printf "checking for ar.. "
|
||||
for ar in ${host_system_type}-ar ar gar
|
||||
do
|
||||
$ar --version >/dev/null 2>&1
|
||||
if [ $? -eq 0 ] ; then ar="$ar" ; break ; fi
|
||||
done
|
||||
if [ -n "$ar" ] ; then
|
||||
echo "$ar"
|
||||
echo "AR=$ar" >> config.mk
|
||||
else
|
||||
echo "not found"
|
||||
err "Please install an archiver and add it to your PATH"
|
||||
fi
|
||||
|
||||
printf "checking for ranlib.. "
|
||||
for ranlib in ${host_system_type}-ranlib ranlib
|
||||
do
|
||||
$ar --version >/dev/null 2>&1
|
||||
if [ $? -eq 0 ] ; then ranlib="$ranlib" ; break ; fi
|
||||
done
|
||||
if [ -n "$ranlib" ] ; then
|
||||
echo "$ranlib"
|
||||
echo "RANLIB=$ranlib" >> config.mk
|
||||
else
|
||||
echo "not found"
|
||||
err "Please install ranlib and add it to your PATH"
|
||||
fi
|
||||
|
||||
printf "checking for a usable make command... "
|
||||
for cmd in make gmake
|
||||
do
|
||||
$cmd --version >/dev/null 2>&1
|
||||
if [ $? -eq 0 ] ; then make="$cmd" ; break ; fi
|
||||
done
|
||||
if [ -n "$make" ] ; then
|
||||
echo "yes"
|
||||
echo "MAKE=$make" >> config.mk
|
||||
else
|
||||
echo "not found"
|
||||
err "Please install GNU Make and add it to your PATH as either make or gmake"
|
||||
fi
|
||||
|
||||
# Allow additional variables from the environment to override the defaults
|
||||
#
|
||||
test -n "$LD" && echo "LD=$LD" >> config.mk
|
||||
test -n "$LDFLAGS" && echo "LDFLAGS=$LDFLAGS" >> config.mk
|
||||
# TODO: need to add Makefile support for: LIBS, CPP, CPPFLAGS
|
||||
|
||||
rm -f config.h
|
||||
$make config.h
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
#!/usr/bin/env ruby
|
||||
#
|
||||
# Copyright (c) 2012 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.
|
||||
#
|
||||
|
||||
begin
|
||||
require 'makeconf'
|
||||
rescue LoadError
|
||||
$LOAD_PATH << "makeconf"
|
||||
require 'makeconf'
|
||||
end
|
||||
|
||||
# Determine the list of compiler flags
|
||||
def get_cflags
|
||||
cflags='-I./src/common -I./include -Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600'.split(/ /)
|
||||
|
||||
if Platform.is_linux?
|
||||
# TODO - note this as a GCC 4.X dependency
|
||||
cflags.push ' -fvisibility=hidden'
|
||||
end
|
||||
if Platform.is_solaris?
|
||||
cflags.push "-D__EXTENSIONS__"
|
||||
end
|
||||
cflags
|
||||
end
|
||||
|
||||
# Determine the list of source code files for libkqueue
|
||||
def get_source_list(project)
|
||||
src = %w{
|
||||
src/common/filter.c
|
||||
src/common/knote.c
|
||||
src/common/map.c
|
||||
src/common/kevent.c
|
||||
src/common/kqueue.c
|
||||
}
|
||||
|
||||
if Platform.is_solaris?
|
||||
src.push 'src/solaris/signal.c',
|
||||
'src/solaris/timer.c',
|
||||
'src/solaris/platform.c',
|
||||
'src/solaris/user.c'
|
||||
end
|
||||
|
||||
if Platform.is_linux? or Platform.is_solaris?
|
||||
src.push 'src/posix/platform.c'
|
||||
end
|
||||
|
||||
if Platform.is_linux?
|
||||
src.push 'src/linux/platform.c',
|
||||
'src/linux/read.c',
|
||||
'src/linux/write.c',
|
||||
'src/linux/user.c',
|
||||
'src/linux/vnode.c'
|
||||
|
||||
# FIXME: needed for RHEL5
|
||||
#src.push 'src/posix/user.c'
|
||||
|
||||
if project.check_header('sys/signalfd.h')
|
||||
src.push 'src/linux/signal.c'
|
||||
else
|
||||
src.push 'src/posix/signal.c'
|
||||
end
|
||||
|
||||
src.push 'src/linux/timer.c'
|
||||
end
|
||||
|
||||
if Platform.is_windows?
|
||||
src.push 'src/windows/timer.c',
|
||||
'src/windows/platform.c',
|
||||
'src/windows/read.c',
|
||||
'src/windows/user.c'
|
||||
end
|
||||
|
||||
src
|
||||
end
|
||||
|
||||
# Generate the linker flags
|
||||
def get_ldadd
|
||||
ldadd = ''
|
||||
|
||||
if Platform.is_linux? or Platform.is_solaris?
|
||||
ldadd += ' -lpthread'
|
||||
end
|
||||
|
||||
if Platform.is_linux?
|
||||
ldadd += ' -lrt'
|
||||
end
|
||||
|
||||
if Platform.is_windows?
|
||||
ldadd += ' ws2_32.lib'
|
||||
end
|
||||
|
||||
ldadd
|
||||
end
|
||||
|
||||
#
|
||||
# MAIN()
|
||||
#
|
||||
|
||||
project = Project.new \
|
||||
:id => 'libkqueue',
|
||||
:version => '2.0.1'
|
||||
|
||||
kq = Library.new(
|
||||
:id => 'libkqueue',
|
||||
:cflags => get_cflags(),
|
||||
:ldadd => get_ldadd(),
|
||||
:sources => get_source_list(project)
|
||||
)
|
||||
|
||||
|
||||
project.check_header 'sys/event.h'
|
||||
if Platform.is_linux?
|
||||
project.check_decl 'EPOLLRDHUP', :include => 'sys/epoll.h'
|
||||
project.check_decl 'ppoll', :cflags => '#define _GNU_SOURCE', :include => 'poll.h'
|
||||
|
||||
|
||||
project.check_header('sys/epoll.h') or throw 'epoll is required'
|
||||
project.check_header('sys/inotify.h') or throw 'inotify is required'
|
||||
project.check_header %w{ sys/signalfd.h sys/timerfd.h sys/eventfd.h }
|
||||
end
|
||||
|
||||
project.add(kq)
|
||||
|
||||
project.add(Header.new(
|
||||
:id => 'event.h',
|
||||
:sources => 'include/sys/event.h',
|
||||
:namespace => 'kqueue/sys'
|
||||
))
|
||||
|
||||
project.add(Manual.new('kqueue.2', :alias => 'kevent.2'))
|
||||
|
||||
test_ldadd = get_ldadd()
|
||||
test_ldadd += ' libkqueue.a'
|
||||
if Platform.is_windows?
|
||||
project.add(
|
||||
Test.new(
|
||||
:id => 'kqtest',
|
||||
:cflags => '-g -O0 -Wall -Werror -Iinclude -Itest',
|
||||
:sources => %w{
|
||||
test/main.c
|
||||
test/kevent.c
|
||||
test/test.c
|
||||
test/read.c
|
||||
test/timer.c
|
||||
test/vnode.c
|
||||
test/user.c
|
||||
}, # NOTE: signal.c and proc.c are removed
|
||||
:ldadd => test_ldadd.split(' ')
|
||||
)
|
||||
)
|
||||
else
|
||||
project.add(
|
||||
Test.new(
|
||||
:id => 'kqtest',
|
||||
:cflags => '-g -O0 -Wall -Werror -I./include -I./test',
|
||||
:sources => %w{
|
||||
test/main.c
|
||||
test/kevent.c
|
||||
test/test.c
|
||||
test/proc.c
|
||||
test/read.c
|
||||
test/signal.c
|
||||
test/timer.c
|
||||
test/vnode.c
|
||||
test/user.c
|
||||
},
|
||||
:ldadd => test_ldadd.split(' ')
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
project.add(PkgConfig.new(
|
||||
:name => 'libkqueue',
|
||||
:description => 'Emulates FreeBSD kqueue(2) on other platforms',
|
||||
:requires => '',
|
||||
:libs => '-lkqueue',
|
||||
:libs_private => '-lpthread',
|
||||
:export_cflags => '-I${includedir}/kqueue'
|
||||
))
|
||||
|
||||
project.packager.add 'libkqueue.spec'
|
||||
|
||||
mc = Makeconf.new()
|
||||
mc.configure(project)
|
||||
|
||||
__END__
|
||||
#
|
||||
# BEGIN: old config.inc contents
|
||||
#
|
||||
program="libkqueue"
|
||||
version="2.0a"
|
||||
abi_major="0"
|
||||
abi_minor="0"
|
||||
abi_version="$abi_major.$abi_minor"
|
||||
cflags="-Wall -Wextra -Wno-missing-field-initializers -Werror -g -O2 -std=c99 -D_XOPEN_SOURCE=600"
|
||||
ldflags=""
|
||||
sources="src/common/filter.c src/common/knote.c src/common/map.c
|
||||
src/common/kevent.c src/common/kqueue.c"
|
||||
libdepends=""
|
||||
deps="src/common/private.h src/common/debug.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 -g3 -O0 -rdynamic"
|
||||
fi
|
||||
|
||||
if [ "$target" != "windows" ] ; then
|
||||
cflags="$cflags -fpic"
|
||||
fi
|
||||
|
||||
optional_headers="err.h"
|
||||
|
||||
libdepends=" -L$libdir"
|
||||
if [ $target = "linux" ] ; then
|
||||
|
||||
check_symbol sys/epoll.h EPOLLRDHUP
|
||||
|
||||
# TODO - note this as a GCC 4.X dependency
|
||||
cflags="$cflags -fvisibility=hidden"
|
||||
|
||||
libdepends="$libdepends -lpthread -lrt"
|
||||
required_headers="sys/epoll.h sys/inotify.h"
|
||||
optional_headers="sys/signalfd.h sys/timerfd.h sys/eventfd.h"
|
||||
fi
|
||||
|
||||
if [ $target = "solaris" ] ; then
|
||||
cflags="$cflags -m64"
|
||||
ldflags="$ldflags -m64"
|
||||
libdepends="$libdepends -lsocket -lnsl"
|
||||
fi
|
||||
}
|
||||
|
||||
post_configure_hook() {
|
||||
finalize target "$target"
|
||||
|
||||
platform="src/posix/platform.c"
|
||||
evfilt_signal="src/posix/signal.c"
|
||||
evfilt_proc="src/$target/proc.c"
|
||||
evfilt_socket="src/$target/socket.c"
|
||||
evfilt_timer="src/posix/timer.c"
|
||||
evfilt_user="src/posix/user.c"
|
||||
evfilt_vnode="src/$target/vnode.c"
|
||||
|
||||
if [ $target = "linux" ] ; then
|
||||
evfilt_user="src/linux/user.c"
|
||||
evfilt_socket="src/linux/read.c src/linux/write.c"
|
||||
|
||||
#XXX-FIXME disabled
|
||||
evfilt_proc=""
|
||||
|
||||
if [ "$have_sys_signalfd_h" = "yes" ] ; then
|
||||
evfilt_signal="src/linux/signal.c"
|
||||
fi
|
||||
if [ "$have_sys_timerfd_h" = "yes" ] ; then
|
||||
evfilt_timer="src/linux/timer.c"
|
||||
fi
|
||||
platform="$platform src/linux/platform.c"
|
||||
fi
|
||||
|
||||
if [ $target = "solaris" ] ; then
|
||||
cflags="$cflags -D__EXTENSIONS__"
|
||||
platform="$platform src/solaris/platform.c"
|
||||
evfilt_timer="src/solaris/timer.c"
|
||||
evfilt_user="src/solaris/user.c"
|
||||
evfilt_signal="src/solaris/signal.c"
|
||||
evfilt_proc=""
|
||||
evfilt_vnode=""
|
||||
fi
|
||||
|
||||
# FIXME: This will compile but not actually work
|
||||
if [ $target = "freebsd" ] ; then
|
||||
evfilt_signal="src/posix/signal.c"
|
||||
evfilt_proc=""
|
||||
evfilt_socket=""
|
||||
evfilt_timer=""
|
||||
evfilt_vnode=""
|
||||
fi
|
||||
|
||||
if [ $target = "windows" ] ; then
|
||||
platform="src/windows/platform.c"
|
||||
cflags="$cflags -march=i686 -lws2_32"
|
||||
ldflags="$ldflags -march=i686"
|
||||
ldadd="-lws2_32"
|
||||
evfilt_proc=""
|
||||
evfilt_signal=""
|
||||
#evfilt_socket="src/windows/read.c src/linux/write.c"
|
||||
evfilt_socket="src/windows/read.c"
|
||||
evfilt_timer="src/windows/timer.c"
|
||||
evfilt_user="src/windows/user.c"
|
||||
evfilt_vnode=""
|
||||
fi
|
||||
|
||||
sources="$sources $platform
|
||||
$evfilt_signal $evfilt_proc
|
||||
$evfilt_socket $evfilt_timer $evfilt_user $evfilt_vnode"
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD SVN Revision 197533$
|
||||
*/
|
||||
|
||||
#ifndef _SYS_EVENT_H_
|
||||
#define _SYS_EVENT_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define intptr_t long
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#if defined(_WIN32) && _MSC_VER < 1600 && !defined(__MINGW32__)
|
||||
# include "../../src/windows/stdint.h"
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
#define LIBKQUEUE 1
|
||||
#endif
|
||||
|
||||
struct timespec;
|
||||
|
||||
#define EVFILT_READ (-1)
|
||||
#define EVFILT_WRITE (-2)
|
||||
#define EVFILT_AIO (-3) /* attached to aio requests */
|
||||
#define EVFILT_VNODE (-4) /* attached to vnodes */
|
||||
#define EVFILT_PROC (-5) /* attached to struct proc */
|
||||
#define EVFILT_SIGNAL (-6) /* attached to struct proc */
|
||||
#define EVFILT_TIMER (-7) /* timers */
|
||||
#define EVFILT_NETDEV (-8) /* network devices */
|
||||
#define EVFILT_FS (-9) /* filesystem events */
|
||||
#define EVFILT_LIO (-10) /* attached to lio requests */
|
||||
#define EVFILT_USER (-11) /* User events */
|
||||
#define EVFILT_SYSCOUNT 11
|
||||
|
||||
#define EV_SET(kevp_, a, b, c, d, e, f) do { \
|
||||
struct kevent *kevp = (kevp_); \
|
||||
(kevp)->ident = (a); \
|
||||
(kevp)->filter = (b); \
|
||||
(kevp)->flags = (c); \
|
||||
(kevp)->fflags = (d); \
|
||||
(kevp)->data = (e); \
|
||||
(kevp)->udata = (f); \
|
||||
} while(0)
|
||||
|
||||
struct kevent {
|
||||
uintptr_t ident; /* identifier for this event */
|
||||
short filter; /* filter for event */
|
||||
unsigned short flags;
|
||||
unsigned int fflags;
|
||||
intptr_t data;
|
||||
void *udata; /* opaque user data identifier */
|
||||
};
|
||||
|
||||
/* actions */
|
||||
#define EV_ADD 0x0001 /* add event to kq (implies enable) */
|
||||
#define EV_DELETE 0x0002 /* delete event from kq */
|
||||
#define EV_ENABLE 0x0004 /* enable event */
|
||||
#define EV_DISABLE 0x0008 /* disable event (not reported) */
|
||||
|
||||
/* flags */
|
||||
#define EV_ONESHOT 0x0010 /* only report one occurrence */
|
||||
#define EV_CLEAR 0x0020 /* clear event state after reporting */
|
||||
#define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
|
||||
#define EV_DISPATCH 0x0080 /* disable event after reporting */
|
||||
|
||||
#define EV_SYSFLAGS 0xF000 /* reserved by system */
|
||||
#define EV_FLAG1 0x2000 /* filter-specific flag */
|
||||
|
||||
/* returned values */
|
||||
#define EV_EOF 0x8000 /* EOF detected */
|
||||
#define EV_ERROR 0x4000 /* error, data contains errno */
|
||||
|
||||
/*
|
||||
* data/hint flags/masks for EVFILT_USER
|
||||
*
|
||||
* On input, the top two bits of fflags specifies how the lower twenty four
|
||||
* bits should be applied to the stored value of fflags.
|
||||
*
|
||||
* On output, the top two bits will always be set to NOTE_FFNOP and the
|
||||
* remaining twenty four bits will contain the stored fflags value.
|
||||
*/
|
||||
#define NOTE_FFNOP 0x00000000 /* ignore input fflags */
|
||||
#define NOTE_FFAND 0x40000000 /* AND fflags */
|
||||
#define NOTE_FFOR 0x80000000 /* OR fflags */
|
||||
#define NOTE_FFCOPY 0xc0000000 /* copy fflags */
|
||||
#define NOTE_FFCTRLMASK 0xc0000000 /* masks for operations */
|
||||
#define NOTE_FFLAGSMASK 0x00ffffff
|
||||
|
||||
#define NOTE_TRIGGER 0x01000000 /* Cause the event to be
|
||||
triggered for output. */
|
||||
|
||||
/*
|
||||
* data/hint flags for EVFILT_{READ|WRITE}
|
||||
*/
|
||||
#define NOTE_LOWAT 0x0001 /* low water mark */
|
||||
#undef NOTE_LOWAT /* Not supported on Linux */
|
||||
|
||||
/*
|
||||
* data/hint flags for EVFILT_VNODE
|
||||
*/
|
||||
#define NOTE_DELETE 0x0001 /* vnode was removed */
|
||||
#define NOTE_WRITE 0x0002 /* data contents changed */
|
||||
#define NOTE_EXTEND 0x0004 /* size increased */
|
||||
#define NOTE_ATTRIB 0x0008 /* attributes changed */
|
||||
#define NOTE_LINK 0x0010 /* link count changed */
|
||||
#define NOTE_RENAME 0x0020 /* vnode was renamed */
|
||||
#define NOTE_REVOKE 0x0040 /* vnode access was revoked */
|
||||
#undef NOTE_REVOKE /* Not supported on Linux */
|
||||
|
||||
/*
|
||||
* data/hint flags for EVFILT_PROC
|
||||
*/
|
||||
#define NOTE_EXIT 0x80000000 /* process exited */
|
||||
#define NOTE_FORK 0x40000000 /* process forked */
|
||||
#define NOTE_EXEC 0x20000000 /* process exec'd */
|
||||
#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */
|
||||
#define NOTE_PDATAMASK 0x000fffff /* mask for pid */
|
||||
|
||||
/* additional flags for EVFILT_PROC */
|
||||
#define NOTE_TRACK 0x00000001 /* follow across forks */
|
||||
#define NOTE_TRACKERR 0x00000002 /* could not track child */
|
||||
#define NOTE_CHILD 0x00000004 /* am a child process */
|
||||
|
||||
/*
|
||||
* data/hint flags for EVFILT_NETDEV
|
||||
*/
|
||||
#define NOTE_LINKUP 0x0001 /* link is up */
|
||||
#define NOTE_LINKDOWN 0x0002 /* link is down */
|
||||
#define NOTE_LINKINV 0x0004 /* link state is invalid */
|
||||
|
||||
/* KLUDGE: This is from <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 */
|
||||
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
struct timespec {
|
||||
time_t tv_sec;
|
||||
long tv_nsec;
|
||||
};
|
||||
|
||||
__declspec(dllexport) int
|
||||
kqueue(void);
|
||||
|
||||
__declspec(dllexport) int
|
||||
kevent(int kq, const struct kevent *changelist, int nchanges,
|
||||
struct kevent *eventlist, int nevents,
|
||||
const struct timespec *timeout);
|
||||
|
||||
#ifdef MAKE_STATIC
|
||||
__declspec(dllexport) int
|
||||
libkqueue_init();
|
||||
#endif
|
||||
|
||||
#else
|
||||
int kqueue(void);
|
||||
int kevent(int kq, const struct kevent *changelist, int nchanges,
|
||||
struct kevent *eventlist, int nevents,
|
||||
const struct timespec *timeout);
|
||||
#ifdef MAKE_STATIC
|
||||
int libkqueue_init();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !__KERNEL__* */
|
||||
|
||||
#endif /* !_SYS_EVENT_H_ */
|
||||
Vendored
+515
@@ -0,0 +1,515 @@
|
||||
.\" $FreeBSD: Revision: 197243$
|
||||
.\" Copyright (c) 2010 Mark Heily
|
||||
.\" Copyright (c) 2000 Jonathan Lemon
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd September 17, 2010
|
||||
.Dt KQUEUE 2
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm kqueue ,
|
||||
.Nm kevent
|
||||
.Nd kernel event notification mechanism
|
||||
.Sh SYNOPSIS
|
||||
.In sys/types.h
|
||||
.In sys/event.h
|
||||
.In sys/time.h
|
||||
.Ft int
|
||||
.Fn kqueue "void"
|
||||
.Ft int
|
||||
.Fn kevent "int kq" "const struct kevent *changelist" "int nchanges" "struct kevent *eventlist" "int nevents" "const struct timespec *timeout"
|
||||
.Fn EV_SET "&kev" ident filter flags fflags data udata
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn kqueue
|
||||
system call
|
||||
provides a generic method of notifying the user when an event
|
||||
happens or a condition holds, based on the results of small
|
||||
pieces of kernel code termed filters.
|
||||
A kevent is identified by the (ident, filter) pair; there may only
|
||||
be one unique kevent per kqueue.
|
||||
.Pp
|
||||
The filter is executed upon the initial registration of a kevent
|
||||
in order to detect whether a preexisting condition is present, and is also
|
||||
executed whenever an event is passed to the filter for evaluation.
|
||||
If the filter determines that the condition should be reported,
|
||||
then the kevent is placed on the kqueue for the user to retrieve.
|
||||
.Pp
|
||||
The filter is also run when the user attempts to retrieve the kevent
|
||||
from the kqueue.
|
||||
If the filter indicates that the condition that triggered
|
||||
the event no longer holds, the kevent is removed from the kqueue and
|
||||
is not returned.
|
||||
.Pp
|
||||
Multiple events which trigger the filter do not result in multiple
|
||||
kevents being placed on the kqueue; instead, the filter will aggregate
|
||||
the events into a single struct kevent.
|
||||
Calling
|
||||
.Fn close
|
||||
on a file descriptor will remove any kevents that reference the descriptor.
|
||||
.Pp
|
||||
The
|
||||
.Fn kqueue
|
||||
system call
|
||||
creates a new kernel event queue and returns a descriptor.
|
||||
The queue is not inherited by a child created with
|
||||
.Xr fork 2 .
|
||||
However, if
|
||||
.Xr rfork 2
|
||||
is called without the
|
||||
.Dv RFFDG
|
||||
flag, then the descriptor table is shared,
|
||||
which will allow sharing of the kqueue between two processes.
|
||||
.Pp
|
||||
The
|
||||
.Fn kevent
|
||||
system call
|
||||
is used to register events with the queue, and return any pending
|
||||
events to the user.
|
||||
The
|
||||
.Fa changelist
|
||||
argument
|
||||
is a pointer to an array of
|
||||
.Va kevent
|
||||
structures, as defined in
|
||||
.In sys/event.h .
|
||||
All changes contained in the
|
||||
.Fa changelist
|
||||
are applied before any pending events are read from the queue.
|
||||
The
|
||||
.Fa nchanges
|
||||
argument
|
||||
gives the size of
|
||||
.Fa changelist .
|
||||
The
|
||||
.Fa eventlist
|
||||
argument
|
||||
is a pointer to an array of kevent structures.
|
||||
The
|
||||
.Fa nevents
|
||||
argument
|
||||
determines the size of
|
||||
.Fa eventlist .
|
||||
When
|
||||
.Fa nevents
|
||||
is zero,
|
||||
.Fn kevent
|
||||
will return immediately even if there is a
|
||||
.Fa timeout
|
||||
specified unlike
|
||||
.Xr select 2 .
|
||||
If
|
||||
.Fa timeout
|
||||
is a non-NULL pointer, it specifies a maximum interval to wait
|
||||
for an event, which will be interpreted as a struct timespec.
|
||||
If
|
||||
.Fa timeout
|
||||
is a NULL pointer,
|
||||
.Fn kevent
|
||||
waits indefinitely.
|
||||
To effect a poll, the
|
||||
.Fa timeout
|
||||
argument should be non-NULL, pointing to a zero-valued
|
||||
.Va timespec
|
||||
structure.
|
||||
The same array may be used for the
|
||||
.Fa changelist
|
||||
and
|
||||
.Fa eventlist .
|
||||
.Pp
|
||||
The
|
||||
.Fn EV_SET
|
||||
macro is provided for ease of initializing a
|
||||
kevent structure.
|
||||
.Pp
|
||||
The
|
||||
.Va kevent
|
||||
structure is defined as:
|
||||
.Bd -literal
|
||||
struct kevent {
|
||||
uintptr_t ident; /* identifier for this event */
|
||||
short filter; /* filter for event */
|
||||
u_short flags; /* action flags for kqueue */
|
||||
u_int fflags; /* filter flag value */
|
||||
intptr_t data; /* filter data value */
|
||||
void *udata; /* opaque user data identifier */
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The fields of
|
||||
.Fa struct kevent
|
||||
are:
|
||||
.Bl -tag -width XXXfilter
|
||||
.It ident
|
||||
Value used to identify this event.
|
||||
The exact interpretation is determined by the attached filter,
|
||||
but often is a file descriptor.
|
||||
.It filter
|
||||
Identifies the kernel filter used to process this event.
|
||||
The pre-defined
|
||||
system filters are described below.
|
||||
.It flags
|
||||
Actions to perform on the event.
|
||||
.It fflags
|
||||
Filter-specific flags.
|
||||
.It data
|
||||
Filter-specific data value.
|
||||
.It udata
|
||||
Opaque user-defined value passed through the kernel unchanged.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Va flags
|
||||
field can contain the following values:
|
||||
.Bl -tag -width XXXEV_ONESHOT
|
||||
.It EV_ADD
|
||||
Adds the event to the kqueue.
|
||||
Re-adding an existing event
|
||||
will modify the parameters of the original event, and not result
|
||||
in a duplicate entry.
|
||||
Adding an event automatically enables it,
|
||||
unless overridden by the EV_DISABLE flag.
|
||||
.It EV_ENABLE
|
||||
Permit
|
||||
.Fn kevent
|
||||
to return the event if it is triggered.
|
||||
.It EV_DISABLE
|
||||
Disable the event so
|
||||
.Fn kevent
|
||||
will not return it.
|
||||
The filter itself is not disabled.
|
||||
.It EV_DISPATCH
|
||||
Disable the event source immediately after delivery of an event.
|
||||
See
|
||||
.Dv EV_DISABLE
|
||||
above.
|
||||
.It EV_DELETE
|
||||
Removes the event from the kqueue.
|
||||
Events which are attached to
|
||||
file descriptors are automatically deleted on the last close of
|
||||
the descriptor.
|
||||
.It EV_RECEIPT
|
||||
This flag is useful for making bulk changes to a kqueue without draining
|
||||
any pending events.
|
||||
When passed as input, it forces
|
||||
.Dv EV_ERROR
|
||||
to always be returned.
|
||||
When a filter is successfully added the
|
||||
.Va data
|
||||
field will be zero.
|
||||
.It EV_ONESHOT
|
||||
Causes the event to return only the first occurrence of the filter
|
||||
being triggered.
|
||||
After the user retrieves the event from the kqueue,
|
||||
it is deleted.
|
||||
.It EV_CLEAR
|
||||
After the event is retrieved by the user, its state is reset.
|
||||
This is useful for filters which report state transitions
|
||||
instead of the current state.
|
||||
Note that some filters may automatically
|
||||
set this flag internally.
|
||||
.It EV_EOF
|
||||
Filters may set this flag to indicate filter-specific EOF condition.
|
||||
.It EV_ERROR
|
||||
See
|
||||
.Sx RETURN VALUES
|
||||
below.
|
||||
.El
|
||||
.Pp
|
||||
The predefined system filters are listed below.
|
||||
Arguments may be passed to and from the filter via the
|
||||
.Va fflags
|
||||
and
|
||||
.Va data
|
||||
fields in the kevent structure.
|
||||
.Bl -tag -width EVFILT_SIGNAL
|
||||
.It EVFILT_READ
|
||||
Takes a descriptor as the identifier, and returns whenever
|
||||
there is data available to read.
|
||||
The behavior of the filter is slightly different depending
|
||||
on the descriptor type.
|
||||
.Pp
|
||||
.Bl -tag -width 2n
|
||||
.It Sockets
|
||||
Sockets which have previously been passed to
|
||||
.Fn listen
|
||||
return when there is an incoming connection pending.
|
||||
.Va data
|
||||
contains the size of the listen backlog.
|
||||
.Pp
|
||||
Other socket descriptors return when there is data to be read,
|
||||
subject to the
|
||||
.Dv SO_RCVLOWAT
|
||||
value of the socket buffer.
|
||||
This may be overridden with a per-filter low water mark at the
|
||||
time the filter is added by setting the
|
||||
NOTE_LOWAT
|
||||
flag in
|
||||
.Va fflags ,
|
||||
and specifying the new low water mark in
|
||||
.Va data .
|
||||
On return,
|
||||
.Va data
|
||||
contains the number of bytes of protocol data available to read.
|
||||
.Pp
|
||||
If the read direction of the socket has shutdown, then the filter
|
||||
also sets EV_EOF in
|
||||
.Va flags ,
|
||||
and returns the socket error (if any) in
|
||||
.Va fflags .
|
||||
It is possible for EOF to be returned (indicating the connection is gone)
|
||||
while there is still data pending in the socket buffer.
|
||||
.It Vnodes
|
||||
Returns when the file pointer is not at the end of file.
|
||||
.Va data
|
||||
contains the offset from current position to end of file,
|
||||
and may be negative.
|
||||
.It "Fifos, Pipes"
|
||||
Returns when the there is data to read;
|
||||
.Va data
|
||||
contains the number of bytes available.
|
||||
.Pp
|
||||
When the last writer disconnects, the filter will set EV_EOF in
|
||||
.Va flags .
|
||||
This may be cleared by passing in EV_CLEAR, at which point the
|
||||
filter will resume waiting for data to become available before
|
||||
returning.
|
||||
.It "BPF devices"
|
||||
Returns when the BPF buffer is full, the BPF timeout has expired, or
|
||||
when the BPF has
|
||||
.Dq immediate mode
|
||||
enabled and there is any data to read;
|
||||
.Va data
|
||||
contains the number of bytes available.
|
||||
.El
|
||||
.It EVFILT_WRITE
|
||||
Takes a descriptor as the identifier, and returns whenever
|
||||
it is possible to write to the descriptor.
|
||||
For sockets, pipes
|
||||
and fifos,
|
||||
.Va data
|
||||
will contain the amount of space remaining in the write buffer.
|
||||
The filter will set EV_EOF when the reader disconnects, and for
|
||||
the fifo case, this may be cleared by use of EV_CLEAR.
|
||||
Note that this filter is not supported for vnodes or BPF devices.
|
||||
.Pp
|
||||
For sockets, the low water mark and socket error handling is
|
||||
identical to the EVFILT_READ case.
|
||||
.It EVFILT_VNODE
|
||||
Takes a file descriptor as the identifier and the events to watch for in
|
||||
.Va fflags ,
|
||||
and returns when one or more of the requested events occurs on the descriptor.
|
||||
The events to monitor are:
|
||||
.Bl -tag -width XXNOTE_RENAME
|
||||
.It NOTE_DELETE
|
||||
The
|
||||
.Fn unlink
|
||||
system call
|
||||
was called on the file referenced by the descriptor.
|
||||
.It NOTE_WRITE
|
||||
A write occurred on the file referenced by the descriptor.
|
||||
.It NOTE_EXTEND
|
||||
The file referenced by the descriptor was extended.
|
||||
.It NOTE_ATTRIB
|
||||
The file referenced by the descriptor had its attributes changed.
|
||||
.It NOTE_LINK
|
||||
The link count on the file changed.
|
||||
.It NOTE_RENAME
|
||||
The file referenced by the descriptor was renamed.
|
||||
.El
|
||||
.Pp
|
||||
On return,
|
||||
.Va fflags
|
||||
contains the events which triggered the filter.
|
||||
.It EVFILT_SIGNAL
|
||||
Takes the signal number to monitor as the identifier and returns
|
||||
when the given signal is delivered to the process.
|
||||
This overrides the
|
||||
.Fn signal
|
||||
and
|
||||
.Fn sigaction
|
||||
facilities, and has a higher precedence.
|
||||
The filter will record
|
||||
all attempts to deliver a signal to a process, even if the signal has
|
||||
been marked as SIG_IGN.
|
||||
.Va data
|
||||
returns the number of times the signal has occurred since the last call to
|
||||
.Fn kevent .
|
||||
This filter automatically sets the EV_CLEAR flag internally.
|
||||
.It EVFILT_TIMER
|
||||
Establishes an arbitrary timer identified by
|
||||
.Va ident .
|
||||
When adding a timer,
|
||||
.Va data
|
||||
specifies the timeout period in milliseconds.
|
||||
The timer will be periodic unless EV_ONESHOT is specified.
|
||||
On return,
|
||||
.Va data
|
||||
contains the number of times the timeout has expired since the last call to
|
||||
.Fn kevent .
|
||||
This filter automatically sets the EV_CLEAR flag internally.
|
||||
There is a system wide limit on the number of timers
|
||||
which is controlled by the
|
||||
.Va kern.kq_calloutmax
|
||||
sysctl.
|
||||
.It Dv EVFILT_USER
|
||||
Establishes a user event identified by
|
||||
.Va ident
|
||||
which is not assosicated with any kernel mechanism but is triggered by
|
||||
user level code.
|
||||
The lower 24 bits of the
|
||||
.Va fflags
|
||||
may be used for user defined flags and manipulated using the following:
|
||||
.Bl -tag -width XXNOTE_FFLAGSMASK
|
||||
.It Dv NOTE_FFNOP
|
||||
Ignore the input
|
||||
.Va fflags .
|
||||
.It Dv NOTE_FFAND
|
||||
Bitwise AND
|
||||
.Va fflags .
|
||||
.It Dv NOTE_FFOR
|
||||
Bitwise OR
|
||||
.Va fflags .
|
||||
.It Dv NOTE_COPY
|
||||
Copy
|
||||
.Va fflags .
|
||||
.It Dv NOTE_FFCTRLMASK
|
||||
Control mask for
|
||||
.Va fflags .
|
||||
.It Dv NOTE_FFLAGSMASK
|
||||
User defined flag mask for
|
||||
.Va fflags .
|
||||
.El
|
||||
.Pp
|
||||
A user event is triggered for output with the following:
|
||||
.Bl -tag -width XXNOTE_FFLAGSMASK
|
||||
.It Dv NOTE_TRIGGER
|
||||
Cause the event to be triggered.
|
||||
.El
|
||||
.Pp
|
||||
On return,
|
||||
.Va fflags
|
||||
contains the users defined flags in the lower 24 bits.
|
||||
.El
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
.Fn kqueue
|
||||
system call
|
||||
creates a new kernel event queue and returns a file descriptor.
|
||||
If there was an error creating the kernel event queue, a value of -1 is
|
||||
returned and errno set.
|
||||
.Pp
|
||||
The
|
||||
.Fn kevent
|
||||
system call
|
||||
returns the number of events placed in the
|
||||
.Fa eventlist ,
|
||||
up to the value given by
|
||||
.Fa nevents .
|
||||
If an error occurs while processing an element of the
|
||||
.Fa changelist
|
||||
and there is enough room in the
|
||||
.Fa eventlist ,
|
||||
then the event will be placed in the
|
||||
.Fa eventlist
|
||||
with
|
||||
.Dv EV_ERROR
|
||||
set in
|
||||
.Va flags
|
||||
and the system error in
|
||||
.Va data .
|
||||
Otherwise,
|
||||
.Dv -1
|
||||
will be returned, and
|
||||
.Dv errno
|
||||
will be set to indicate the error condition.
|
||||
If the time limit expires, then
|
||||
.Fn kevent
|
||||
returns 0.
|
||||
.Sh ERRORS
|
||||
The
|
||||
.Fn kqueue
|
||||
system call fails if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ENOMEM
|
||||
The kernel failed to allocate enough memory for the kernel queue.
|
||||
.It Bq Er EMFILE
|
||||
The per-process descriptor table is full.
|
||||
.It Bq Er ENFILE
|
||||
The system file table is full.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn kevent
|
||||
system call fails if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EACCES
|
||||
The process does not have permission to register a filter.
|
||||
.It Bq Er EFAULT
|
||||
There was an error reading or writing the
|
||||
.Va kevent
|
||||
structure.
|
||||
.It Bq Er EBADF
|
||||
The specified descriptor is invalid.
|
||||
.It Bq Er EINTR
|
||||
A signal was delivered before the timeout expired and before any
|
||||
events were placed on the kqueue for return.
|
||||
.It Bq Er EINVAL
|
||||
The specified time limit or filter is invalid.
|
||||
.It Bq Er ENOENT
|
||||
The event could not be found to be modified or deleted.
|
||||
.It Bq Er ENOMEM
|
||||
No memory was available to register the event
|
||||
or, in the special case of a timer, the maximum number of
|
||||
timers has been exceeded.
|
||||
This maximum is configurable via the
|
||||
.Va kern.kq_calloutmax
|
||||
sysctl.
|
||||
.It Bq Er ESRCH
|
||||
The specified process to attach to does not exist.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr aio_error 2 ,
|
||||
.Xr aio_read 2 ,
|
||||
.Xr aio_return 2 ,
|
||||
.Xr poll 2 ,
|
||||
.Xr read 2 ,
|
||||
.Xr select 2 ,
|
||||
.Xr sigaction 2 ,
|
||||
.Xr write 2 ,
|
||||
.Xr signal 3
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Fn kqueue
|
||||
and
|
||||
.Fn kevent
|
||||
system calls first appeared in
|
||||
.Fx 4.1 .
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Fn kqueue
|
||||
system and this manual page were written by
|
||||
.An Jonathan Lemon Aq jlemon@FreeBSD.org .
|
||||
+1
@@ -0,0 +1 @@
|
||||
libkqueue
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
Name: libkqueue
|
||||
Description: Emulates FreeBSD kqueue(2) on other platforms
|
||||
Version: 0.1
|
||||
Requires:
|
||||
Libs: -lkqueue
|
||||
Libs.private: -lpthread
|
||||
Cflags: -I${includedir}/kqueue
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
Name: libkqueue
|
||||
Summary: Userspace implementation of the kqueue event notification mechanism
|
||||
Version: 2.0.1
|
||||
Release: 1
|
||||
# The entire source code is MIT, event.h which is BSD (2-clause)
|
||||
License: MIT and BSD
|
||||
Group: System Environment/Libraries
|
||||
URL: http://sourceforge.net/p/libkqueue/wiki/Home/
|
||||
Source0: http://downloads.sourceforge.net/%{name}/%{name}-%{version}.tar.gz
|
||||
Provides: libkqueue.so.0
|
||||
|
||||
%description
|
||||
A user space implementation of the kqueue(2) kernel event notification
|
||||
mechanism. libkqueue acts as a translator between the kevent structure and the
|
||||
native kernel facilities.
|
||||
|
||||
%package devel
|
||||
Group: Development/Libraries
|
||||
Summary: Development files for %{name}-%{version}
|
||||
Requires: %{name}%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description devel
|
||||
%{summary}
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
# %%{configure} sets --target 'x86_64-redhat-linux-gnu' instead of 'linux'
|
||||
%build
|
||||
CFLAGS="%{optflags} -fpic" %{_configure} --prefix=%{_prefix} --libdir=%{_libdir}
|
||||
make
|
||||
|
||||
%install
|
||||
%{make_install}
|
||||
|
||||
%check
|
||||
make check
|
||||
|
||||
%post -p /sbin/ldconfig
|
||||
|
||||
%postun -p /sbin/ldconfig
|
||||
|
||||
%files
|
||||
%doc LICENSE
|
||||
%{_libdir}/libkqueue.so.*
|
||||
|
||||
%files devel
|
||||
%dir %{_includedir}/kqueue
|
||||
%dir %{_includedir}/kqueue/sys
|
||||
%{_libdir}/libkqueue.so
|
||||
%{_includedir}/kqueue/sys/event.h
|
||||
%{_libdir}/pkgconfig/libkqueue.pc
|
||||
%{_mandir}/man2/kqueue.2.*
|
||||
%{_mandir}/man2/kevent.2.*
|
||||
|
||||
%changelog
|
||||
* Wed May 08 2013 Mark Heily <mark@heily.com> - 2.0.1-1
|
||||
- New upstream release
|
||||
|
||||
* Wed May 01 2013 Eric Radman <ericshane@eradman.com> - 2.0-1
|
||||
- Adapt to libkqueue 2.0
|
||||
|
||||
* Thu Apr 04 2013 Eric Radman <ericshane@eradman.com> - 1.0.1-6
|
||||
- Set CFLAGS using optflags since we're using a custom configure
|
||||
- Shorten summary
|
||||
|
||||
* Wed Apr 03 2013 Eric Radman <ericshane@eradman.com> - 1.0.1-5
|
||||
- Use %% to avoid warning about special character in comment
|
||||
- Remove defattr
|
||||
|
||||
* Fri Jan 18 2013 Eric Radman <ericshane@eradman.com> - 1.0.1-4
|
||||
- Configure respects CFLAGS and LDFLAGS. Also pass --libdir to the configure script
|
||||
|
||||
* Sat Jan 12 2013 Eric Radman <ericshane@eradman.com> - 1.0.1-3
|
||||
- Add license text for event.h
|
||||
- Use the dir macro for the include directories
|
||||
- Remove static package, exclude .a file, and make sure new directories are owned by the RPM
|
||||
|
||||
* Thu Jan 03 2013 Eric Radman <ericshane@eradman.com> - 1.0.1-3
|
||||
- Configure respects CFLAGS and LDFLAGS. Also pass --libdir= to the configure script instead of setting LIB
|
||||
|
||||
* Mon Dec 31 2012 Eric Radman <ericshane@eradman.com> - 1.0.1-2
|
||||
- Do not set the buildroot (F-10 does not require it)
|
||||
- Do not manually clean (F-13)
|
||||
|
||||
* Thu Dec 27 2012 Eric Radman <ericshane@eradman.com> - 1.0.1-1
|
||||
- Initial build with separate -devel package. Based on work done by Mark Heily,
|
||||
Aditya Patawari and John Haxby
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* A simple fixed-size memory allocator.
|
||||
*
|
||||
* Each translation unit in a program can include this header and
|
||||
* have access to it's own private memory allocator. This can be useful
|
||||
* for improving the performance of programs which frequently allocate
|
||||
* and deallocate objects with a fixed size.
|
||||
*
|
||||
* The allocator must be initialized by calling mem_init(). This
|
||||
* function takes two arguments: the object size, and the maximum
|
||||
* number of objects in the cache.
|
||||
*
|
||||
* The functions mem_alloc() and mem_free() have similar semantics
|
||||
* to the traditional malloc() and free() calls. The main difference
|
||||
* is that mem_alloc() does not allow you to specify a specific size.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
static __thread struct {
|
||||
void **ac_cache; /* An array of reusable memory objects */
|
||||
size_t ac_count; /* The number of objects in the cache */
|
||||
size_t ac_max; /* The maximum number of cached objects */
|
||||
size_t ac_size; /* The size, in bytes, of each object */
|
||||
} _ma;
|
||||
|
||||
static inline int
|
||||
mem_init(size_t objsize, size_t cachesize)
|
||||
{
|
||||
_ma.ac_size = objsize;
|
||||
_ma.ac_cache = malloc(cachesize * sizeof(void *));
|
||||
return (_ma.ac_cache == NULL ? -1 : 0);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
mem_alloc(void)
|
||||
{
|
||||
if (_ma.ac_count > 0)
|
||||
return (_ma.ac_cache[_ma.ac_count--]);
|
||||
else
|
||||
return (malloc(_ma.ac_size));
|
||||
}
|
||||
|
||||
static inline void *
|
||||
mem_calloc(void)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = mem_alloc();
|
||||
if (p != NULL)
|
||||
memset(p, 0, _ma.ac_size);
|
||||
return (p);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mem_free(void *ptr)
|
||||
{
|
||||
if (_ma.ac_count < _ma.ac_max)
|
||||
_ma.ac_cache[_ma.ac_count++] = ptr;
|
||||
else
|
||||
free(ptr);
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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.
|
||||
*/
|
||||
|
||||
#ifndef _DEBUG_H
|
||||
#define _DEBUG_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
extern int DEBUG_KQUEUE;
|
||||
extern char *KQUEUE_DEBUG_IDENT;
|
||||
|
||||
#if defined(__linux__)
|
||||
# include <sys/syscall.h>
|
||||
# define THREAD_ID ((pid_t) syscall(__NR_gettid))
|
||||
#elif defined(__sun)
|
||||
# define THREAD_ID ((int) pthread_self())
|
||||
#elif defined(_WIN32)
|
||||
# define THREAD_ID (int)(GetCurrentThreadId())
|
||||
#else
|
||||
# error Unsupported platform
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define dbg_puts(str) do { \
|
||||
if (DEBUG_KQUEUE) \
|
||||
fprintf(stderr, "%s [%d]: %s(): %s\n", \
|
||||
KQUEUE_DEBUG_IDENT, THREAD_ID, __func__, str); \
|
||||
} while (0)
|
||||
|
||||
#define dbg_printf(fmt,...) do { \
|
||||
if (DEBUG_KQUEUE) \
|
||||
fprintf(stderr, "%s [%d]: %s(): "fmt"\n", \
|
||||
KQUEUE_DEBUG_IDENT, THREAD_ID, __func__, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define dbg_perror(str) do { \
|
||||
if (DEBUG_KQUEUE) \
|
||||
fprintf(stderr, "%s [%d]: %s(): %s: %s (errno=%d)\n", \
|
||||
KQUEUE_DEBUG_IDENT, THREAD_ID, __func__, str, \
|
||||
strerror(errno), errno); \
|
||||
} while (0)
|
||||
|
||||
# define reset_errno() do { errno = 0; } while (0)
|
||||
|
||||
# if defined(_WIN32)
|
||||
# define dbg_lasterror(str) do { \
|
||||
if (DEBUG_KQUEUE) \
|
||||
fprintf(stderr, "%s: [%d] %s(): %s: (LastError=%d)\n", \
|
||||
KQUEUE_DEBUG_IDENT, THREAD_ID, __func__, str, (int)GetLastError()); \
|
||||
} while (0)
|
||||
|
||||
# define dbg_wsalasterror(str) do { \
|
||||
if (DEBUG_KQUEUE) \
|
||||
fprintf(stderr, "%s: [%d] %s(): %s: (WSALastError=%d)\n", \
|
||||
KQUEUE_DEBUG_IDENT, THREAD_ID, __func__, str, (int)WSAGetLastError()); \
|
||||
} while (0)
|
||||
|
||||
# else
|
||||
# define dbg_lasterror(str) ;
|
||||
# define dbg_wsalasterror(str) ;
|
||||
# endif
|
||||
|
||||
/*
|
||||
* Tracing mutexes are a thin wrapper around the pthread_mutex_t
|
||||
* datatype that tracks and reports when a mutex is locked or unlocked.
|
||||
* It also allows you to assert that a mutex has (or has not) been locked
|
||||
* by calling tracing_mutex_assert().
|
||||
*/
|
||||
|
||||
# define MTX_UNLOCKED 0
|
||||
# define MTX_LOCKED 1
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t mtx_lock;
|
||||
int mtx_status;
|
||||
int mtx_owner;
|
||||
} tracing_mutex_t;
|
||||
|
||||
# define tracing_mutex_init(mtx, attr) do { \
|
||||
pthread_mutex_init(&(mtx)->mtx_lock, (attr)); \
|
||||
(mtx)->mtx_status = MTX_UNLOCKED; \
|
||||
(mtx)->mtx_owner = -1; \
|
||||
} while (0)
|
||||
|
||||
# define tracing_mutex_destroy(mtx) pthread_mutex_destroy(&(mtx)->mtx_lock)
|
||||
|
||||
# define tracing_mutex_assert(x,y) do { \
|
||||
if ((y) == MTX_UNLOCKED) \
|
||||
assert((x)->mtx_status == MTX_UNLOCKED || (x)->mtx_owner != THREAD_ID); \
|
||||
else if ((y) == MTX_LOCKED) \
|
||||
assert((x)->mtx_status == MTX_LOCKED && (x)->mtx_owner == THREAD_ID); \
|
||||
else \
|
||||
abort(); \
|
||||
} while (0)
|
||||
|
||||
# define tracing_mutex_lock(x) do { \
|
||||
dbg_printf("waiting for %s", #x); \
|
||||
pthread_mutex_lock(&((x)->mtx_lock)); \
|
||||
dbg_printf("locked %s", #x); \
|
||||
(x)->mtx_owner = THREAD_ID; \
|
||||
(x)->mtx_status = MTX_LOCKED; \
|
||||
} while (0)
|
||||
|
||||
# define tracing_mutex_unlock(x) do { \
|
||||
(x)->mtx_status = MTX_UNLOCKED; \
|
||||
(x)->mtx_owner = -1; \
|
||||
pthread_mutex_unlock(&((x)->mtx_lock)); \
|
||||
dbg_printf("unlocked %s", # x); \
|
||||
} while (0)
|
||||
|
||||
#else /* NDEBUG */
|
||||
# define dbg_puts(str) do {} while (0)
|
||||
# define dbg_printf(fmt,...) do {} while (0)
|
||||
# define dbg_perror(str) do {} while (0)
|
||||
# define dbg_lasterror(str) do {} while (0)
|
||||
# define dbg_wsalasterror(str) do {} while (0)
|
||||
# define reset_errno() do {} while (0)
|
||||
# define MTX_UNLOCKED
|
||||
# define MTX_LOCKED
|
||||
# define tracing_mutex_t pthread_mutex_t
|
||||
# define tracing_mutex_init pthread_mutex_init
|
||||
# define tracing_mutex_destroy pthread_mutex_destroy
|
||||
# define tracing_mutex_assert(x,y) do {} while (0)
|
||||
# define tracing_mutex_lock pthread_mutex_lock
|
||||
# define tracing_mutex_unlock pthread_mutex_unlock
|
||||
#endif
|
||||
|
||||
#endif /* ! _DEBUG_H */
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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 <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
extern const struct filter evfilt_read;
|
||||
extern const struct filter evfilt_write;
|
||||
extern const struct filter evfilt_signal;
|
||||
extern const struct filter evfilt_vnode;
|
||||
extern const struct filter evfilt_proc;
|
||||
extern const struct filter evfilt_timer;
|
||||
extern const struct filter evfilt_user;
|
||||
|
||||
static int
|
||||
filter_register(struct kqueue *kq, short filter, const struct filter *src)
|
||||
{
|
||||
struct filter *dst;
|
||||
unsigned int filt;
|
||||
int rv = 0;
|
||||
|
||||
filt = (-1 * filter) - 1;
|
||||
if (filt >= EVFILT_SYSCOUNT)
|
||||
return (-1);
|
||||
|
||||
dst = &kq->kq_filt[filt];
|
||||
memcpy(dst, src, sizeof(*src));
|
||||
dst->kf_kqueue = kq;
|
||||
RB_INIT(&dst->kf_knote);
|
||||
pthread_rwlock_init(&dst->kf_knote_mtx, NULL);
|
||||
if (src->kf_id == 0) {
|
||||
dbg_puts("filter is not implemented");
|
||||
return (0);
|
||||
}
|
||||
|
||||
assert(src->kf_copyout);
|
||||
assert(src->kn_create);
|
||||
assert(src->kn_modify);
|
||||
assert(src->kn_delete);
|
||||
assert(src->kn_enable);
|
||||
assert(src->kn_disable);
|
||||
|
||||
/* Perform (optional) per-filter initialization */
|
||||
if (src->kf_init != NULL) {
|
||||
rv = src->kf_init(dst);
|
||||
if (rv < 0) {
|
||||
dbg_puts("filter failed to initialize");
|
||||
dst->kf_id = 0;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
#if DEADWOOD
|
||||
/* Add the filter's event descriptor to the main fdset */
|
||||
if (dst->kf_pfd > 0) {
|
||||
FD_SET(dst->kf_pfd, &kq->kq_fds);
|
||||
if (dst->kf_pfd > kq->kq_nfds)
|
||||
kq->kq_nfds = dst->kf_pfd;
|
||||
dbg_printf("fds: added %d (nfds=%d)", dst->kf_pfd, kq->kq_nfds);
|
||||
}
|
||||
dbg_printf("filter %d (%s) registered", filter, filter_name(filter));
|
||||
#endif
|
||||
|
||||
/* FIXME: should totally remove const from src */
|
||||
if (kqops.filter_init != NULL
|
||||
&& kqops.filter_init(kq, dst) < 0)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
filter_register_all(struct kqueue *kq)
|
||||
{
|
||||
int rv;
|
||||
|
||||
FD_ZERO(&kq->kq_fds);
|
||||
rv = 0;
|
||||
rv += filter_register(kq, EVFILT_READ, &evfilt_read);
|
||||
rv += filter_register(kq, EVFILT_WRITE, &evfilt_write);
|
||||
rv += filter_register(kq, EVFILT_SIGNAL, &evfilt_signal);
|
||||
rv += filter_register(kq, EVFILT_VNODE, &evfilt_vnode);
|
||||
rv += filter_register(kq, EVFILT_PROC, &evfilt_proc);
|
||||
rv += filter_register(kq, EVFILT_TIMER, &evfilt_timer);
|
||||
rv += filter_register(kq, EVFILT_USER, &evfilt_user);
|
||||
kq->kq_nfds++;
|
||||
if (rv != 0) {
|
||||
filter_unregister_all(kq);
|
||||
return (-1);
|
||||
} else {
|
||||
dbg_puts("complete");
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
filter_unregister_all(struct kqueue *kq)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < EVFILT_SYSCOUNT; i++) {
|
||||
if (kq->kq_filt[i].kf_id == 0)
|
||||
continue;
|
||||
|
||||
if (kq->kq_filt[i].kf_destroy != NULL)
|
||||
kq->kq_filt[i].kf_destroy(&kq->kq_filt[i]);
|
||||
|
||||
//XXX-FIXME
|
||||
//knote_free_all(&kq->kq_filt[i]);
|
||||
|
||||
if (kqops.filter_free != NULL)
|
||||
kqops.filter_free(kq, &kq->kq_filt[i]);
|
||||
}
|
||||
memset(&kq->kq_filt[0], 0, sizeof(kq->kq_filt));
|
||||
}
|
||||
|
||||
int
|
||||
filter_lookup(struct filter **filt, struct kqueue *kq, short id)
|
||||
{
|
||||
if (~id < 0 || ~id >= EVFILT_SYSCOUNT) {
|
||||
dbg_printf("invalid id: id %d ~id %d", id, (~id));
|
||||
errno = EINVAL;
|
||||
*filt = NULL;
|
||||
return (-1);
|
||||
}
|
||||
*filt = &kq->kq_filt[~id];
|
||||
if ((*filt)->kf_copyout == NULL) {
|
||||
dbg_printf("filter %s is not implemented", filter_name(id));
|
||||
errno = ENOSYS;
|
||||
*filt = NULL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
const char *
|
||||
filter_name(short filt)
|
||||
{
|
||||
int id;
|
||||
const char *fname[EVFILT_SYSCOUNT] = {
|
||||
"EVFILT_READ",
|
||||
"EVFILT_WRITE",
|
||||
"EVFILT_AIO",
|
||||
"EVFILT_VNODE",
|
||||
"EVFILT_PROC",
|
||||
"EVFILT_SIGNAL",
|
||||
"EVFILT_TIMER",
|
||||
"EVFILT_NETDEV",
|
||||
"EVFILT_FS",
|
||||
"EVFILT_LIO",
|
||||
"EVFILT_USER"
|
||||
};
|
||||
|
||||
id = ~filt;
|
||||
if (id < 0 || id >= EVFILT_SYSCOUNT)
|
||||
return "EVFILT_INVALID";
|
||||
else
|
||||
return fname[id];
|
||||
}
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* To get asprintf(3) */
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
static const char *
|
||||
kevent_filter_dump(const struct kevent *kev)
|
||||
{
|
||||
static __thread char buf[64];
|
||||
|
||||
snprintf(&buf[0], sizeof(buf), "%d (%s)",
|
||||
kev->filter, filter_name(kev->filter));
|
||||
return ((const char *) &buf[0]);
|
||||
}
|
||||
|
||||
static const char *
|
||||
kevent_fflags_dump(const struct kevent *kev)
|
||||
{
|
||||
static __thread char buf[1024];
|
||||
|
||||
#define KEVFFL_DUMP(attrib) \
|
||||
if (kev->fflags & attrib) \
|
||||
strncat((char *) &buf[0], #attrib" ", 64);
|
||||
|
||||
snprintf(buf, sizeof(buf), "fflags=0x%04x (", kev->fflags);
|
||||
if (kev->filter == EVFILT_VNODE) {
|
||||
KEVFFL_DUMP(NOTE_DELETE);
|
||||
KEVFFL_DUMP(NOTE_WRITE);
|
||||
KEVFFL_DUMP(NOTE_EXTEND);
|
||||
KEVFFL_DUMP(NOTE_ATTRIB);
|
||||
KEVFFL_DUMP(NOTE_LINK);
|
||||
KEVFFL_DUMP(NOTE_RENAME);
|
||||
} else if (kev->filter == EVFILT_USER) {
|
||||
KEVFFL_DUMP(NOTE_FFNOP);
|
||||
KEVFFL_DUMP(NOTE_FFAND);
|
||||
KEVFFL_DUMP(NOTE_FFOR);
|
||||
KEVFFL_DUMP(NOTE_FFCOPY);
|
||||
KEVFFL_DUMP(NOTE_TRIGGER);
|
||||
} else {
|
||||
strncat((char *) &buf[0], " ", 1);
|
||||
}
|
||||
buf[strlen(buf) - 1] = ')';
|
||||
|
||||
#undef KEVFFL_DUMP
|
||||
|
||||
return ((const char *) &buf[0]);
|
||||
}
|
||||
|
||||
static const char *
|
||||
kevent_flags_dump(const struct kevent *kev)
|
||||
{
|
||||
static __thread char buf[1024];
|
||||
|
||||
#define KEVFL_DUMP(attrib) \
|
||||
if (kev->flags & attrib) \
|
||||
strncat((char *) &buf[0], #attrib" ", 64);
|
||||
|
||||
snprintf(buf, sizeof(buf), "flags=0x%04x (", kev->flags);
|
||||
KEVFL_DUMP(EV_ADD);
|
||||
KEVFL_DUMP(EV_ENABLE);
|
||||
KEVFL_DUMP(EV_DISABLE);
|
||||
KEVFL_DUMP(EV_DELETE);
|
||||
KEVFL_DUMP(EV_ONESHOT);
|
||||
KEVFL_DUMP(EV_CLEAR);
|
||||
KEVFL_DUMP(EV_EOF);
|
||||
KEVFL_DUMP(EV_ERROR);
|
||||
KEVFL_DUMP(EV_DISPATCH);
|
||||
KEVFL_DUMP(EV_RECEIPT);
|
||||
buf[strlen(buf) - 1] = ')';
|
||||
|
||||
#undef KEVFL_DUMP
|
||||
|
||||
return ((const char *) &buf[0]);
|
||||
}
|
||||
|
||||
const char *
|
||||
kevent_dump(const struct kevent *kev)
|
||||
{
|
||||
static __thread char buf[1024];
|
||||
|
||||
snprintf((char *) &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 ((const char *) &buf[0]);
|
||||
}
|
||||
|
||||
static int
|
||||
kevent_copyin_one(struct kqueue *kq, const struct kevent *src)
|
||||
{
|
||||
struct knote *kn = NULL;
|
||||
struct filter *filt;
|
||||
int rv = 0;
|
||||
|
||||
if (src->flags & EV_DISPATCH && src->flags & EV_ONESHOT) {
|
||||
dbg_puts("Error: EV_DISPATCH and EV_ONESHOT are mutually exclusive");
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (filter_lookup(&filt, kq, src->filter) < 0)
|
||||
return (-1);
|
||||
|
||||
dbg_printf("src=%s", kevent_dump(src));
|
||||
|
||||
kn = knote_lookup(filt, src->ident);
|
||||
dbg_printf("knote_lookup: ident %d == %p", (int)src->ident, kn);
|
||||
if (kn == NULL) {
|
||||
if (src->flags & EV_ADD) {
|
||||
if ((kn = knote_new()) == NULL) {
|
||||
errno = ENOENT;
|
||||
return (-1);
|
||||
}
|
||||
memcpy(&kn->kev, src, sizeof(kn->kev));
|
||||
kn->kev.flags &= ~EV_ENABLE;
|
||||
kn->kev.flags |= EV_ADD;//FIXME why?
|
||||
kn->kn_kq = kq;
|
||||
assert(filt->kn_create);
|
||||
if (filt->kn_create(filt, kn) < 0) {
|
||||
knote_release(kn);
|
||||
errno = EFAULT;
|
||||
return (-1);
|
||||
}
|
||||
knote_insert(filt, kn);
|
||||
dbg_printf("created kevent %s", kevent_dump(src));
|
||||
|
||||
/* XXX- FIXME Needs to be handled in kn_create() to prevent races */
|
||||
if (src->flags & EV_DISABLE) {
|
||||
kn->kev.flags |= EV_DISABLE;
|
||||
return (filt->kn_disable(filt, kn));
|
||||
}
|
||||
//........................................
|
||||
|
||||
return (0);
|
||||
} else {
|
||||
dbg_printf("no entry found for ident=%u", (unsigned int)src->ident);
|
||||
errno = ENOENT;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (src->flags & EV_DELETE) {
|
||||
rv = knote_delete(filt, kn);
|
||||
dbg_printf("knote_delete returned %d", rv);
|
||||
} else if (src->flags & EV_DISABLE) {
|
||||
kn->kev.flags |= EV_DISABLE;
|
||||
rv = filt->kn_disable(filt, kn);
|
||||
dbg_printf("kn_disable returned %d", rv);
|
||||
} else if (src->flags & EV_ENABLE) {
|
||||
kn->kev.flags &= ~EV_DISABLE;
|
||||
rv = filt->kn_enable(filt, kn);
|
||||
dbg_printf("kn_enable returned %d", rv);
|
||||
} else if (src->flags & EV_ADD || src->flags == 0 || src->flags & EV_RECEIPT) {
|
||||
kn->kev.udata = src->udata;
|
||||
rv = filt->kn_modify(filt, kn, src);
|
||||
dbg_printf("kn_modify returned %d", rv);
|
||||
}
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/** @return number of events added to the eventlist */
|
||||
static int
|
||||
kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
|
||||
struct kevent *eventlist, int nevents)
|
||||
{
|
||||
int status, nret;
|
||||
|
||||
dbg_printf("nchanges=%d nevents=%d", nchanges, nevents);
|
||||
|
||||
/* TODO: refactor, this has become convoluted to support EV_RECEIPT */
|
||||
for (nret = 0; nchanges > 0; src++, nchanges--) {
|
||||
|
||||
if (kevent_copyin_one(kq, src) < 0) {
|
||||
dbg_printf("errno=%s",strerror(errno));
|
||||
status = errno;
|
||||
goto err_path;
|
||||
} else {
|
||||
if (src->flags & EV_RECEIPT) {
|
||||
status = 0;
|
||||
goto err_path;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
err_path:
|
||||
if (nevents > 0) {
|
||||
memcpy(eventlist, src, sizeof(*src));
|
||||
eventlist->data = status;
|
||||
nevents--;
|
||||
eventlist++;
|
||||
nret++;
|
||||
} else {
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return (nret);
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
kevent(int kqfd, const struct kevent *changelist, int nchanges,
|
||||
struct kevent *eventlist, int nevents,
|
||||
const struct timespec *timeout)
|
||||
{
|
||||
struct kqueue *kq;
|
||||
int rv = 0;
|
||||
#ifndef NDEBUG
|
||||
static unsigned int _kevent_counter = 0;
|
||||
unsigned int myid = 0;
|
||||
|
||||
(void) myid;
|
||||
#endif
|
||||
|
||||
/* Convert the descriptor into an object pointer */
|
||||
kq = kqueue_lookup(kqfd);
|
||||
if (kq == NULL) {
|
||||
errno = ENOENT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (DEBUG_KQUEUE) {
|
||||
myid = atomic_inc(&_kevent_counter);
|
||||
dbg_printf("--- kevent %u --- (nchanges = %d, nevents = %d)", myid, nchanges, nevents);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Process each kevent on the changelist.
|
||||
*/
|
||||
if (nchanges > 0) {
|
||||
kqueue_lock(kq);
|
||||
rv = kevent_copyin(kq, changelist, nchanges, eventlist, nevents);
|
||||
kqueue_unlock(kq);
|
||||
dbg_printf("(%u) changelist: rv=%d", myid, rv);
|
||||
if (rv < 0)
|
||||
goto out;
|
||||
if (rv > 0) {
|
||||
eventlist += rv;
|
||||
nevents -= rv;
|
||||
}
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
|
||||
/*
|
||||
* Wait for events and copy them to the eventlist
|
||||
*/
|
||||
if (nevents > MAX_KEVENT)
|
||||
nevents = MAX_KEVENT;
|
||||
if (nevents > 0) {
|
||||
rv = kqops.kevent_wait(kq, nevents, timeout);
|
||||
dbg_printf("kqops.kevent_wait returned %d", rv);
|
||||
if (fastpath(rv > 0)) {
|
||||
kqueue_lock(kq);
|
||||
rv = kqops.kevent_copyout(kq, rv, eventlist, nevents);
|
||||
kqueue_unlock(kq);
|
||||
} else if (rv == 0) {
|
||||
/* Timeout reached */
|
||||
} else {
|
||||
dbg_printf("(%u) kevent_wait failed", myid);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (DEBUG_KQUEUE) {
|
||||
int n;
|
||||
|
||||
dbg_printf("(%u) returning %d events", myid, rv);
|
||||
for (n = 0; n < rv; n++) {
|
||||
dbg_printf("(%u) eventlist[%d] = %s", myid, n, kevent_dump(&eventlist[n]));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
dbg_printf("--- END kevent %u ret %d ---", myid, rv);
|
||||
return (rv);
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
#include "alloc.h"
|
||||
|
||||
int
|
||||
knote_init(void)
|
||||
{
|
||||
return 0;
|
||||
// return (mem_init(sizeof(struct knote), 1024));
|
||||
}
|
||||
|
||||
static int
|
||||
knote_cmp(struct knote *a, struct knote *b)
|
||||
{
|
||||
return memcmp(&a->kev.ident, &b->kev.ident, sizeof(a->kev.ident));
|
||||
}
|
||||
|
||||
RB_GENERATE(knt, knote, kn_entries, knote_cmp)
|
||||
|
||||
struct knote *
|
||||
knote_new(void)
|
||||
{
|
||||
struct knote *res;
|
||||
|
||||
res = calloc(1, sizeof(struct knote));
|
||||
if (res == NULL)
|
||||
return (NULL);
|
||||
|
||||
res->kn_ref = 1;
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
void
|
||||
knote_release(struct knote *kn)
|
||||
{
|
||||
assert (kn->kn_ref > 0);
|
||||
|
||||
if (atomic_dec(&kn->kn_ref) == 0) {
|
||||
if (kn->kn_flags & KNFL_KNOTE_DELETED) {
|
||||
dbg_printf("freeing knote at %p", kn);
|
||||
free(kn);
|
||||
} else {
|
||||
dbg_puts("this should never happen");
|
||||
}
|
||||
} else {
|
||||
dbg_printf("decrementing refcount of knote %p rc=%d", kn, kn->kn_ref);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
knote_insert(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
pthread_rwlock_wrlock(&filt->kf_knote_mtx);
|
||||
RB_INSERT(knt, &filt->kf_knote, kn);
|
||||
pthread_rwlock_unlock(&filt->kf_knote_mtx);
|
||||
}
|
||||
|
||||
int
|
||||
knote_delete(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct knote query;
|
||||
struct knote *tmp;
|
||||
|
||||
if (kn->kn_flags & KNFL_KNOTE_DELETED) {
|
||||
dbg_puts("ERROR: double deletion detected");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that the knote wasn't removed by another
|
||||
* thread before we acquired the knotelist lock.
|
||||
*/
|
||||
query.kev.ident = kn->kev.ident;
|
||||
pthread_rwlock_wrlock(&filt->kf_knote_mtx);
|
||||
tmp = RB_FIND(knt, &filt->kf_knote, &query);
|
||||
if (tmp == kn) {
|
||||
RB_REMOVE(knt, &filt->kf_knote, kn);
|
||||
}
|
||||
pthread_rwlock_unlock(&filt->kf_knote_mtx);
|
||||
|
||||
filt->kn_delete(filt, kn); //XXX-FIXME check return value
|
||||
|
||||
kn->kn_flags |= KNFL_KNOTE_DELETED;
|
||||
|
||||
knote_release(kn);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct knote *
|
||||
knote_lookup(struct filter *filt, uintptr_t ident)
|
||||
{
|
||||
struct knote query;
|
||||
struct knote *ent = NULL;
|
||||
|
||||
query.kev.ident = ident;
|
||||
|
||||
pthread_rwlock_rdlock(&filt->kf_knote_mtx);
|
||||
ent = RB_FIND(knt, &filt->kf_knote, &query);
|
||||
pthread_rwlock_unlock(&filt->kf_knote_mtx);
|
||||
|
||||
#ifdef __x86_64__
|
||||
dbg_printf("id=%lu ent=%p", ident, ent);
|
||||
#else
|
||||
dbg_printf("id=%u ent=%p", ident, ent);
|
||||
#endif
|
||||
|
||||
return (ent);
|
||||
}
|
||||
|
||||
#if DEADWOOD
|
||||
struct knote *
|
||||
knote_get_by_data(struct filter *filt, intptr_t data)
|
||||
{
|
||||
struct knote *kn;
|
||||
|
||||
pthread_rwlock_rdlock(&filt->kf_knote_mtx);
|
||||
RB_FOREACH(kn, knt, &filt->kf_knote) {
|
||||
if (data == kn->kev.data)
|
||||
break;
|
||||
}
|
||||
if (kn != NULL) {
|
||||
knote_retain(kn);
|
||||
}
|
||||
pthread_rwlock_unlock(&filt->kf_knote_mtx);
|
||||
|
||||
return (kn);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
knote_disable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
assert(!(kn->kev.flags & EV_DISABLE));
|
||||
|
||||
filt->kn_disable(filt, kn); //TODO: Error checking
|
||||
KNOTE_DISABLE(kn);
|
||||
return (0);
|
||||
}
|
||||
|
||||
//TODO: knote_enable()
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
int DEBUG_KQUEUE = 0;
|
||||
char *KQUEUE_DEBUG_IDENT = "KQ";
|
||||
|
||||
#ifdef _WIN32
|
||||
static LONG kq_init_begin = 0;
|
||||
static int kq_init_complete = 0;
|
||||
#else
|
||||
pthread_mutex_t kq_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_once_t kq_is_initialized = PTHREAD_ONCE_INIT;
|
||||
#endif
|
||||
|
||||
static unsigned int
|
||||
get_fd_limit(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/* actually windows should be able to hold
|
||||
way more, as they use HANDLEs for everything.
|
||||
Still this number should still be sufficient for
|
||||
the provided number of kqueue fds.
|
||||
*/
|
||||
return 65536;
|
||||
#else
|
||||
struct rlimit rlim;
|
||||
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
|
||||
dbg_perror("getrlimit(2)");
|
||||
return (65536);
|
||||
} else {
|
||||
return (rlim.rlim_max);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct map *kqmap;
|
||||
|
||||
void
|
||||
libkqueue_init(void)
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
DEBUG_KQUEUE = 0;
|
||||
#else
|
||||
char *s = getenv("KQUEUE_DEBUG");
|
||||
if (s != NULL && strlen(s) > 0) {
|
||||
DEBUG_KQUEUE = 1;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Initialize the Winsock library */
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
|
||||
abort();
|
||||
#endif
|
||||
|
||||
# if defined(_WIN32) && !defined(__GNUC__)
|
||||
/* Enable heap surveillance */
|
||||
{
|
||||
int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
|
||||
tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
|
||||
_CrtSetDbgFlag(tmpFlag);
|
||||
}
|
||||
# endif /* _WIN32 */
|
||||
}
|
||||
#endif
|
||||
|
||||
kqmap = map_new(get_fd_limit()); // INT_MAX
|
||||
if (kqmap == NULL)
|
||||
abort();
|
||||
if (knote_init() < 0)
|
||||
abort();
|
||||
dbg_puts("library initialization complete");
|
||||
#ifdef _WIN32
|
||||
kq_init_complete = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEADWOOD
|
||||
static int
|
||||
kqueue_cmp(struct kqueue *a, struct kqueue *b)
|
||||
{
|
||||
return memcmp(&a->kq_id, &b->kq_id, sizeof(int));
|
||||
}
|
||||
|
||||
/* Must hold the kqtree_mtx when calling this */
|
||||
void
|
||||
kqueue_free(struct kqueue *kq)
|
||||
{
|
||||
RB_REMOVE(kqt, &kqtree, kq);
|
||||
filter_unregister_all(kq);
|
||||
kqops.kqueue_free(kq);
|
||||
free(kq);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct kqueue *
|
||||
kqueue_lookup(int kq)
|
||||
{
|
||||
return ((struct kqueue *) map_lookup(kqmap, kq));
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
kqueue(void)
|
||||
{
|
||||
struct kqueue *kq;
|
||||
struct kqueue *tmp;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (InterlockedCompareExchange(&kq_init_begin, 0, 1) == 0) {
|
||||
libkqueue_init();
|
||||
} else {
|
||||
while (kq_init_complete == 0) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void) pthread_mutex_lock(&kq_mtx);
|
||||
(void) pthread_once(&kq_is_initialized, libkqueue_init);
|
||||
(void) pthread_mutex_unlock(&kq_mtx);
|
||||
#endif
|
||||
|
||||
kq = calloc(1, sizeof(*kq));
|
||||
if (kq == NULL)
|
||||
return (-1);
|
||||
|
||||
tracing_mutex_init(&kq->kq_mtx, NULL);
|
||||
|
||||
if (kqops.kqueue_init(kq) < 0) {
|
||||
free(kq);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
dbg_printf("created kqueue, fd=%d", kq->kq_id);
|
||||
|
||||
tmp = map_delete(kqmap, kq->kq_id);
|
||||
if (tmp != NULL) {
|
||||
dbg_puts("FIXME -- memory leak here");
|
||||
// TODO: kqops.kqueue_free(tmp), or (better yet) decrease it's refcount
|
||||
}
|
||||
if (map_insert(kqmap, kq->kq_id, kq) < 0) {
|
||||
dbg_puts("map insertion failed");
|
||||
kqops.kqueue_free(kq);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (kq->kq_id);
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "private.h"
|
||||
|
||||
struct map {
|
||||
size_t len;
|
||||
void **data;
|
||||
};
|
||||
|
||||
struct map *
|
||||
map_new(size_t len)
|
||||
{
|
||||
struct map *dst;
|
||||
|
||||
dst = calloc(1, sizeof(struct map));
|
||||
if (dst == NULL)
|
||||
return (NULL);
|
||||
#ifdef _WIN32
|
||||
dst->data = calloc(len, sizeof(void*));
|
||||
if(dst->data == NULL) {
|
||||
dbg_perror("calloc()");
|
||||
free(dst);
|
||||
return NULL;
|
||||
}
|
||||
dst->len = len;
|
||||
#else
|
||||
dst->data = mmap(NULL, len * sizeof(void *), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_NORESERVE | MAP_ANON, -1, 0);
|
||||
if (dst->data == MAP_FAILED) {
|
||||
dbg_perror("mmap(2)");
|
||||
free(dst);
|
||||
return (NULL);
|
||||
}
|
||||
dst->len = len;
|
||||
#endif
|
||||
|
||||
return (dst);
|
||||
}
|
||||
|
||||
int
|
||||
map_insert(struct map *m, int idx, void *ptr)
|
||||
{
|
||||
if (slowpath(idx < 0 || idx > (int)m->len))
|
||||
return (-1);
|
||||
|
||||
if (atomic_ptr_cas(&(m->data[idx]), 0, ptr) == NULL) {
|
||||
dbg_printf("inserted %p in location %d", ptr, idx);
|
||||
return (0);
|
||||
} else {
|
||||
dbg_printf("tried to insert a value into a non-empty location %d (value=%p)",
|
||||
idx,
|
||||
m->data[idx]);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
map_remove(struct map *m, int idx, void *ptr)
|
||||
{
|
||||
if (slowpath(idx < 0 || idx > (int)m->len))
|
||||
return (-1);
|
||||
|
||||
if (atomic_ptr_cas(&(m->data[idx]), ptr, 0) == NULL) {
|
||||
dbg_printf("removed %p from location %d", ptr, idx);
|
||||
return (0);
|
||||
} else {
|
||||
dbg_printf("removal failed: location %d does not contain value %p", idx, m->data[idx]);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
map_replace(struct map *m, int idx, void *oldp, void *newp)
|
||||
{
|
||||
void *tmp;
|
||||
|
||||
if (slowpath(idx < 0 || idx > (int)m->len))
|
||||
return (-1);
|
||||
|
||||
tmp = atomic_ptr_cas(&(m->data[idx]), oldp, newp);
|
||||
if (tmp == oldp) {
|
||||
dbg_printf("replaced value %p in location %d with value %p",
|
||||
oldp, idx, newp);
|
||||
return (0);
|
||||
} else {
|
||||
dbg_printf("item in location %d does not match expected value %p",
|
||||
idx, oldp);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
map_lookup(struct map *m, int idx)
|
||||
{
|
||||
if (slowpath(idx < 0 || idx > (int)m->len))
|
||||
return (NULL);
|
||||
|
||||
return m->data[idx];
|
||||
}
|
||||
|
||||
void *
|
||||
map_delete(struct map *m, int idx)
|
||||
{
|
||||
void *oval;
|
||||
void *nval;
|
||||
|
||||
if (slowpath(idx < 0 || idx > (int)m->len))
|
||||
return ((void *)-1);
|
||||
|
||||
/* Hopefully we aren't racing with another thread, but you never know.. */
|
||||
do {
|
||||
oval = m->data[idx];
|
||||
nval = atomic_ptr_cas(&(m->data[idx]), oval, NULL);
|
||||
} while (nval != oval);
|
||||
|
||||
m->data[idx] = NULL;
|
||||
|
||||
return ((void *) oval);
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _KQUEUE_PRIVATE_H
|
||||
#define _KQUEUE_PRIVATE_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "tree.h"
|
||||
|
||||
/* Maximum events returnable in a single kevent() call */
|
||||
#define MAX_KEVENT 512
|
||||
|
||||
struct kqueue;
|
||||
struct kevent;
|
||||
struct knote;
|
||||
struct map;
|
||||
struct eventfd;
|
||||
struct evfilt_data;
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include "../windows/platform.h"
|
||||
# include "../common/queue.h"
|
||||
# if !defined(NDEBUG) && !defined(__GNUC__)
|
||||
# include <crtdbg.h>
|
||||
# endif
|
||||
#elif defined(__linux__)
|
||||
# include "../posix/platform.h"
|
||||
# include "../linux/platform.h"
|
||||
#elif defined(__sun)
|
||||
# include "../posix/platform.h"
|
||||
# include "../solaris/platform.h"
|
||||
#else
|
||||
# error Unknown platform
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/* Workaround for Android */
|
||||
#ifndef EPOLLONESHOT
|
||||
# define EPOLLONESHOT (1 << 30)
|
||||
#endif
|
||||
|
||||
struct eventfd {
|
||||
int ef_id;
|
||||
#if defined(EVENTFD_PLATFORM_SPECIFIC)
|
||||
EVENTFD_PLATFORM_SPECIFIC;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Flags used by knote->kn_flags
|
||||
*/
|
||||
#define KNFL_PASSIVE_SOCKET (0x01) /* Socket is in listen(2) mode */
|
||||
#define KNFL_REGULAR_FILE (0x02) /* File descriptor is a regular file */
|
||||
#define KNFL_KNOTE_DELETED (0x10) /* The knote object is no longer valid */
|
||||
|
||||
struct knote {
|
||||
struct kevent kev;
|
||||
int kn_flags;
|
||||
union {
|
||||
/* OLD */
|
||||
int pfd; /* Used by timerfd */
|
||||
int events; /* Used by socket */
|
||||
struct {
|
||||
nlink_t nlink; /* Used by vnode */
|
||||
off_t size; /* Used by vnode */
|
||||
} vnode;
|
||||
timer_t timerid;
|
||||
struct sleepreq *sleepreq; /* Used by posix/timer.c */
|
||||
void *handle; /* Used by win32 filters */
|
||||
} data;
|
||||
struct kqueue* kn_kq;
|
||||
volatile uint32_t kn_ref;
|
||||
#if defined(KNOTE_PLATFORM_SPECIFIC)
|
||||
KNOTE_PLATFORM_SPECIFIC;
|
||||
#endif
|
||||
RB_ENTRY(knote) kn_entries;
|
||||
};
|
||||
|
||||
#define KNOTE_ENABLE(ent) do { \
|
||||
(ent)->kev.flags &= ~EV_DISABLE; \
|
||||
} while (0/*CONSTCOND*/)
|
||||
|
||||
#define KNOTE_DISABLE(ent) do { \
|
||||
(ent)->kev.flags |= EV_DISABLE; \
|
||||
} while (0/*CONSTCOND*/)
|
||||
|
||||
struct filter {
|
||||
short kf_id;
|
||||
|
||||
/* filter operations */
|
||||
|
||||
int (*kf_init)(struct filter *);
|
||||
void (*kf_destroy)(struct filter *);
|
||||
int (*kf_copyout)(struct kevent *, struct knote *, void *);
|
||||
|
||||
/* knote operations */
|
||||
|
||||
int (*kn_create)(struct filter *, struct knote *);
|
||||
int (*kn_modify)(struct filter *, struct knote *,
|
||||
const struct kevent *);
|
||||
int (*kn_delete)(struct filter *, struct knote *);
|
||||
int (*kn_enable)(struct filter *, struct knote *);
|
||||
int (*kn_disable)(struct filter *, struct knote *);
|
||||
|
||||
struct eventfd kf_efd; /* Used by user.c */
|
||||
|
||||
//MOVE TO POSIX?
|
||||
int kf_pfd; /* fd to poll(2) for readiness */
|
||||
int kf_wfd; /* fd to write when an event occurs */
|
||||
//----?
|
||||
|
||||
struct evfilt_data *kf_data; /* filter-specific data */
|
||||
RB_HEAD(knt, knote) kf_knote;
|
||||
pthread_rwlock_t kf_knote_mtx;
|
||||
struct kqueue *kf_kqueue;
|
||||
#if defined(FILTER_PLATFORM_SPECIFIC)
|
||||
FILTER_PLATFORM_SPECIFIC;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Use this to declare a filter that is not implemented */
|
||||
#define EVFILT_NOTIMPL { 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
||||
|
||||
struct kqueue {
|
||||
int kq_id;
|
||||
struct filter kq_filt[EVFILT_SYSCOUNT];
|
||||
fd_set kq_fds, kq_rfds;
|
||||
int kq_nfds;
|
||||
tracing_mutex_t kq_mtx;
|
||||
volatile uint32_t kq_ref;
|
||||
#if defined(KQUEUE_PLATFORM_SPECIFIC)
|
||||
KQUEUE_PLATFORM_SPECIFIC;
|
||||
#endif
|
||||
RB_ENTRY(kqueue) entries;
|
||||
};
|
||||
|
||||
struct kqueue_vtable {
|
||||
int (*kqueue_init)(struct kqueue *);
|
||||
void (*kqueue_free)(struct kqueue *);
|
||||
// @param timespec can be given as timeout
|
||||
// @param int the number of events to wait for
|
||||
// @param kqueue the queue to wait on
|
||||
int (*kevent_wait)(struct kqueue *, int, const struct timespec *);
|
||||
// @param kqueue the queue to look at
|
||||
// @param int The number of events that should be ready
|
||||
// @param kevent the structure to copy the events into
|
||||
// @param int The number of events to copy
|
||||
// @return the actual number of events copied
|
||||
int (*kevent_copyout)(struct kqueue *, int, struct kevent *, int);
|
||||
int (*filter_init)(struct kqueue *, struct filter *);
|
||||
void (*filter_free)(struct kqueue *, struct filter *);
|
||||
int (*eventfd_init)(struct eventfd *);
|
||||
void (*eventfd_close)(struct eventfd *);
|
||||
int (*eventfd_raise)(struct eventfd *);
|
||||
int (*eventfd_lower)(struct eventfd *);
|
||||
int (*eventfd_descriptor)(struct eventfd *);
|
||||
};
|
||||
extern const struct kqueue_vtable kqops;
|
||||
|
||||
/*
|
||||
* kqueue internal API
|
||||
*/
|
||||
#define kqueue_lock(kq) tracing_mutex_lock(&(kq)->kq_mtx)
|
||||
#define kqueue_unlock(kq) tracing_mutex_unlock(&(kq)->kq_mtx)
|
||||
|
||||
/*
|
||||
* knote internal API
|
||||
*/
|
||||
struct knote * knote_lookup(struct filter *, uintptr_t);
|
||||
//DEADWOOD: struct knote * knote_get_by_data(struct filter *filt, intptr_t);
|
||||
struct knote * knote_new(void);
|
||||
#define knote_retain(kn) atomic_inc(&kn->kn_ref)
|
||||
void knote_release(struct knote *);
|
||||
void knote_insert(struct filter *, struct knote *);
|
||||
int knote_delete(struct filter *, struct knote *);
|
||||
int knote_init(void);
|
||||
int knote_disable(struct filter *, struct knote *);
|
||||
#define knote_get_filter(knt) &((knt)->kn_kq->kq_filt[(knt)->kev.filter])
|
||||
|
||||
int filter_lookup(struct filter **, struct kqueue *, short);
|
||||
int filter_register_all(struct kqueue *);
|
||||
void filter_unregister_all(struct kqueue *);
|
||||
const char *filter_name(short);
|
||||
|
||||
int kevent_wait(struct kqueue *, const struct timespec *);
|
||||
int kevent_copyout(struct kqueue *, int, struct kevent *, int);
|
||||
void kevent_free(struct kqueue *);
|
||||
const char *kevent_dump(const struct kevent *);
|
||||
struct kqueue * kqueue_lookup(int);
|
||||
int kqueue_validate(struct kqueue *);
|
||||
|
||||
struct map *map_new(size_t);
|
||||
int map_insert(struct map *, int, void *);
|
||||
int map_remove(struct map *, int, void *);
|
||||
int map_replace(struct map *, int, void *, void *);
|
||||
void *map_lookup(struct map *, int);
|
||||
void *map_delete(struct map *, int);
|
||||
void map_free(struct map *);
|
||||
|
||||
/* DEADWOOD: No longer needed due to the un-smerging of POSIX and Linux
|
||||
|
||||
int posix_evfilt_user_init(struct filter *);
|
||||
void posix_evfilt_user_destroy(struct filter *);
|
||||
int posix_evfilt_user_copyout(struct kevent *, struct knote *, void *ptr UNUSED);
|
||||
int posix_evfilt_user_knote_create(struct filter *, struct knote *);
|
||||
int posix_evfilt_user_knote_modify(struct filter *, struct knote *, const struct kevent *);
|
||||
int posix_evfilt_user_knote_delete(struct filter *, struct knote *);
|
||||
int posix_evfilt_user_knote_enable(struct filter *, struct knote *);
|
||||
int posix_evfilt_user_knote_disable(struct filter *, struct knote *);
|
||||
|
||||
*/
|
||||
|
||||
#endif /* ! _KQUEUE_PRIVATE_H */
|
||||
+763
@@ -0,0 +1,763 @@
|
||||
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
|
||||
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
|
||||
/* $FreeBSD: src/sys/sys/tree.h,v 1.9.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_TREE_H_
|
||||
#define _SYS_TREE_H_
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
* same number of black nodes,
|
||||
* - each red node (except for the root) has a black parent,
|
||||
* - each leaf node is black.
|
||||
*
|
||||
* Every operation on a red-black tree is bounded as O(lg n).
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return(NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} else { \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if(__comp < 0) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} else if (__comp > 0) { \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
} else { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0){ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while (1) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) { \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-black tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *rbh_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define RB_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define RB_INIT(root) do { \
|
||||
(root)->rbh_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_BLACK 0
|
||||
#define RB_RED 1
|
||||
#define RB_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *rbe_left; /* left element */ \
|
||||
struct type *rbe_right; /* right element */ \
|
||||
struct type *rbe_parent; /* parent element */ \
|
||||
int rbe_color; /* node color */ \
|
||||
}
|
||||
|
||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
||||
#define RB_ROOT(head) (head)->rbh_root
|
||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
||||
|
||||
#define RB_SET(elm, parent, field) do { \
|
||||
RB_PARENT(elm, field) = parent; \
|
||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
||||
RB_COLOR(elm, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
||||
RB_COLOR(black, field) = RB_BLACK; \
|
||||
RB_COLOR(red, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#ifndef RB_AUGMENT
|
||||
#define RB_AUGMENT(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_RIGHT(elm, field); \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_LEFT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_LEFT(elm, field); \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_RIGHT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
|
||||
attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_INSERT(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_FIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NFIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NEXT(struct type *); \
|
||||
attr struct type *name##_RB_PREV(struct type *); \
|
||||
attr struct type *name##_RB_MINMAX(struct name *, int); \
|
||||
\
|
||||
|
||||
/* Main rb operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define RB_GENERATE(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void \
|
||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *parent, *gparent, *tmp; \
|
||||
while ((parent = RB_PARENT(elm, field)) != NULL && \
|
||||
RB_COLOR(parent, field) == RB_RED) { \
|
||||
gparent = RB_PARENT(parent, field); \
|
||||
if (parent == RB_LEFT(gparent, field)) { \
|
||||
tmp = RB_RIGHT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_RIGHT(parent, field) == elm) { \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
||||
} else { \
|
||||
tmp = RB_LEFT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
||||
} \
|
||||
} \
|
||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr void \
|
||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
||||
elm != RB_ROOT(head)) { \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oleft; \
|
||||
if ((oleft = RB_LEFT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oleft, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_RIGHT(tmp, field)) \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oright; \
|
||||
if ((oright = RB_RIGHT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oright, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_LEFT(head, tmp, oright, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_LEFT(tmp, field)) \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (elm) \
|
||||
RB_COLOR(elm, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *child, *parent, *old = elm; \
|
||||
int color; \
|
||||
if (RB_LEFT(elm, field) == NULL) \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
else if (RB_RIGHT(elm, field) == NULL) \
|
||||
child = RB_LEFT(elm, field); \
|
||||
else { \
|
||||
struct type *left; \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while ((left = RB_LEFT(elm, field)) != NULL) \
|
||||
elm = left; \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
if (RB_PARENT(elm, field) == old) \
|
||||
parent = elm; \
|
||||
(elm)->field = (old)->field; \
|
||||
if (RB_PARENT(old, field)) { \
|
||||
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
|
||||
RB_LEFT(RB_PARENT(old, field), field) = elm;\
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
|
||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
||||
if (RB_RIGHT(old, field)) \
|
||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
||||
if (parent) { \
|
||||
left = parent; \
|
||||
do { \
|
||||
RB_AUGMENT(left); \
|
||||
} while ((left = RB_PARENT(left, field)) != NULL); \
|
||||
} \
|
||||
goto color; \
|
||||
} \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
color: \
|
||||
if (color == RB_BLACK) \
|
||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
||||
return (old); \
|
||||
} \
|
||||
\
|
||||
/* Inserts a node into the RB tree */ \
|
||||
attr struct type * \
|
||||
name##_RB_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
struct type *parent = NULL; \
|
||||
int comp = 0; \
|
||||
tmp = RB_ROOT(head); \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
comp = (cmp)(elm, parent); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
RB_SET(elm, parent, field); \
|
||||
if (parent != NULL) { \
|
||||
if (comp < 0) \
|
||||
RB_LEFT(parent, field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = elm; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
name##_RB_INSERT_COLOR(head, elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
attr struct type * \
|
||||
name##_RB_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the first node greater than or equal to the search key */ \
|
||||
attr struct type * \
|
||||
name##_RB_NFIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *res = NULL; \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) { \
|
||||
res = tmp; \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
} \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (res); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_NEXT(struct type *elm) \
|
||||
{ \
|
||||
if (RB_RIGHT(elm, field)) { \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while (RB_LEFT(elm, field)) \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_PREV(struct type *elm) \
|
||||
{ \
|
||||
if (RB_LEFT(elm, field)) { \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
while (RB_RIGHT(elm, field)) \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_MINMAX(struct name *head, int val) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *parent = NULL; \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
if (val < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
} \
|
||||
return (parent); \
|
||||
}
|
||||
|
||||
#define RB_NEGINF -1
|
||||
#define RB_INF 1
|
||||
|
||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
#define RB_FOREACH(x, name, head) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_NEXT(x))
|
||||
|
||||
#define RB_FOREACH_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_PREV(x))
|
||||
|
||||
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#endif /* _SYS_TREE_H_ */
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 _GNU_SOURCE
|
||||
# include <poll.h>
|
||||
#include "../common/private.h"
|
||||
|
||||
//XXX-FIXME TEMP
|
||||
const struct filter evfilt_proc = EVFILT_NOTIMPL;
|
||||
|
||||
/*
|
||||
* Per-thread epoll event buffer used to ferry data between
|
||||
* kevent_wait() and kevent_copyout().
|
||||
*/
|
||||
static __thread struct epoll_event epevt[MAX_KEVENT];
|
||||
|
||||
const struct kqueue_vtable kqops = {
|
||||
linux_kqueue_init,
|
||||
linux_kqueue_free,
|
||||
linux_kevent_wait,
|
||||
linux_kevent_copyout,
|
||||
NULL,
|
||||
NULL,
|
||||
linux_eventfd_init,
|
||||
linux_eventfd_close,
|
||||
linux_eventfd_raise,
|
||||
linux_eventfd_lower,
|
||||
linux_eventfd_descriptor
|
||||
};
|
||||
|
||||
int
|
||||
linux_kqueue_init(struct kqueue *kq)
|
||||
{
|
||||
kq->kq_id = epoll_create(1);
|
||||
if (kq->kq_id < 0) {
|
||||
dbg_perror("epoll_create(2)");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (filter_register_all(kq) < 0) {
|
||||
close(kq->kq_id);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
#if DEADWOOD
|
||||
//might be useful in posix
|
||||
|
||||
/* Add each filter's pollable descriptor to the epollset */
|
||||
for (i = 0; i < EVFILT_SYSCOUNT; i++) {
|
||||
filt = &kq->kq_filt[i];
|
||||
|
||||
if (filt->kf_id == 0)
|
||||
continue;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = filt;
|
||||
|
||||
if (epoll_ctl(kq->kq_id, EPOLL_CTL_ADD, filt->kf_pfd, &ev) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
close(kq->kq_id);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
linux_kqueue_free(struct kqueue *kq UNUSED)
|
||||
{
|
||||
abort();//FIXME
|
||||
}
|
||||
|
||||
static int
|
||||
linux_kevent_wait_hires(
|
||||
struct kqueue *kq,
|
||||
const struct timespec *timeout)
|
||||
{
|
||||
int n;
|
||||
#if HAVE_DECL_PPOLL
|
||||
struct pollfd fds;
|
||||
|
||||
dbg_printf("waiting for events (timeout=%ld sec %ld nsec)",
|
||||
timeout->tv_sec, timeout->tv_nsec);
|
||||
fds.fd = kqueue_epfd(kq);
|
||||
fds.events = POLLIN;
|
||||
|
||||
n = ppoll(&fds, 1, timeout, NULL);
|
||||
#else
|
||||
int epfd;
|
||||
fd_set fds;
|
||||
|
||||
dbg_printf("waiting for events (timeout=%ld sec %ld nsec)",
|
||||
timeout->tv_sec, timeout->tv_nsec);
|
||||
|
||||
epfd = kqueue_epfd(kq);
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(epfd, &fds);
|
||||
n = pselect(epfd + 1, &fds, NULL , NULL, timeout, NULL);
|
||||
#endif
|
||||
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
dbg_puts("signal caught");
|
||||
return (-1);
|
||||
}
|
||||
dbg_perror("ppoll(2) or pselect(2)");
|
||||
return (-1);
|
||||
}
|
||||
return (n);
|
||||
}
|
||||
|
||||
int
|
||||
linux_kevent_wait(
|
||||
struct kqueue *kq,
|
||||
int nevents,
|
||||
const struct timespec *ts)
|
||||
{
|
||||
int timeout, nret;
|
||||
|
||||
/* Use a high-resolution syscall if the timeout value is less than one millisecond. */
|
||||
if (ts != NULL && ts->tv_sec == 0 && ts->tv_nsec > 0 && ts->tv_nsec < 1000000) {
|
||||
nret = linux_kevent_wait_hires(kq, ts);
|
||||
if (nret <= 0)
|
||||
return (nret);
|
||||
|
||||
/* epoll_wait() should have ready events */
|
||||
timeout = 0;
|
||||
} else {
|
||||
/* Convert timeout to the format used by epoll_wait() */
|
||||
if (ts == NULL)
|
||||
timeout = -1;
|
||||
else
|
||||
timeout = (1000 * ts->tv_sec) + (ts->tv_nsec / 1000000);
|
||||
}
|
||||
|
||||
dbg_puts("waiting for events");
|
||||
nret = epoll_wait(kqueue_epfd(kq), &epevt[0], nevents, timeout);
|
||||
if (nret < 0) {
|
||||
dbg_perror("epoll_wait");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (nret);
|
||||
}
|
||||
|
||||
int
|
||||
linux_kevent_copyout(struct kqueue *kq, int nready,
|
||||
struct kevent *eventlist, int nevents UNUSED)
|
||||
{
|
||||
struct epoll_event *ev;
|
||||
struct filter *filt;
|
||||
struct knote *kn;
|
||||
int i, nret, rv;
|
||||
|
||||
nret = nready;
|
||||
for (i = 0; i < nready; i++) {
|
||||
ev = &epevt[i];
|
||||
kn = (struct knote *) ev->data.ptr;
|
||||
filt = &kq->kq_filt[~(kn->kev.filter)];
|
||||
rv = filt->kf_copyout(eventlist, kn, ev);
|
||||
if (slowpath(rv < 0)) {
|
||||
dbg_puts("knote_copyout failed");
|
||||
/* XXX-FIXME: hard to handle this without losing events */
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Certain flags cause the associated knote to be deleted
|
||||
* or disabled.
|
||||
*/
|
||||
if (eventlist->flags & EV_DISPATCH)
|
||||
knote_disable(filt, kn); //FIXME: Error checking
|
||||
if (eventlist->flags & EV_ONESHOT) {
|
||||
knote_delete(filt, kn); //FIXME: Error checking
|
||||
}
|
||||
|
||||
/* If an empty kevent structure is returned, the event is discarded. */
|
||||
/* TODO: add these semantics to windows + solaris platform.c */
|
||||
if (fastpath(eventlist->filter != 0)) {
|
||||
eventlist++;
|
||||
} else {
|
||||
dbg_puts("spurious wakeup, discarding event");
|
||||
nret--;
|
||||
}
|
||||
}
|
||||
|
||||
return (nret);
|
||||
}
|
||||
|
||||
int
|
||||
linux_eventfd_init(struct eventfd *e)
|
||||
{
|
||||
int evfd;
|
||||
|
||||
evfd = eventfd(0, 0);
|
||||
if (evfd < 0) {
|
||||
dbg_perror("eventfd");
|
||||
return (-1);
|
||||
}
|
||||
if (fcntl(evfd, F_SETFL, O_NONBLOCK) < 0) {
|
||||
dbg_perror("fcntl");
|
||||
close(evfd);
|
||||
return (-1);
|
||||
}
|
||||
e->ef_id = evfd;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
linux_eventfd_close(struct eventfd *e)
|
||||
{
|
||||
close(e->ef_id);
|
||||
e->ef_id = -1;
|
||||
}
|
||||
|
||||
int
|
||||
linux_eventfd_raise(struct eventfd *e)
|
||||
{
|
||||
uint64_t counter;
|
||||
int rv = 0;
|
||||
|
||||
dbg_puts("raising event level");
|
||||
counter = 1;
|
||||
if (write(e->ef_id, &counter, sizeof(counter)) < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
/* Not considered an error */
|
||||
break;
|
||||
|
||||
case EINTR:
|
||||
rv = -EINTR;
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_printf("write(2): %s", strerror(errno));
|
||||
rv = -1;
|
||||
}
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
linux_eventfd_lower(struct eventfd *e)
|
||||
{
|
||||
uint64_t cur;
|
||||
ssize_t n;
|
||||
int rv = 0;
|
||||
|
||||
/* Reset the counter */
|
||||
dbg_puts("lowering event level");
|
||||
n = read(e->ef_id, &cur, sizeof(cur));
|
||||
if (n < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
/* Not considered an error */
|
||||
break;
|
||||
|
||||
case EINTR:
|
||||
rv = -EINTR;
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_printf("read(2): %s", strerror(errno));
|
||||
rv = -1;
|
||||
}
|
||||
} else if (n != sizeof(cur)) {
|
||||
dbg_puts("short read");
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
linux_eventfd_descriptor(struct eventfd *e)
|
||||
{
|
||||
return (e->ef_id);
|
||||
}
|
||||
|
||||
int
|
||||
linux_get_descriptor_type(struct knote *kn)
|
||||
{
|
||||
socklen_t slen;
|
||||
struct stat sb;
|
||||
int i, lsock;
|
||||
|
||||
/*
|
||||
* Test if the descriptor is a socket.
|
||||
*/
|
||||
if (fstat(kn->kev.ident, &sb) < 0) {
|
||||
dbg_perror("fstat(2)");
|
||||
return (-1);
|
||||
}
|
||||
if (S_ISREG(sb.st_mode)) {
|
||||
kn->kn_flags |= KNFL_REGULAR_FILE;
|
||||
dbg_printf("fd %d is a regular file\n", (int)kn->kev.ident);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if the socket is active or passive.
|
||||
*/
|
||||
if (! S_ISSOCK(sb.st_mode))
|
||||
return (0);
|
||||
|
||||
slen = sizeof(lsock);
|
||||
lsock = 0;
|
||||
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_ACCEPTCONN, (char *) &lsock, &slen);
|
||||
if (i < 0) {
|
||||
switch (errno) {
|
||||
case ENOTSOCK: /* same as lsock = 0 */
|
||||
return (0);
|
||||
break;
|
||||
default:
|
||||
dbg_perror("getsockopt(3)");
|
||||
return (-1);
|
||||
}
|
||||
} else {
|
||||
if (lsock)
|
||||
kn->kn_flags |= KNFL_PASSIVE_SOCKET;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
epoll_event_dump(struct epoll_event *evt)
|
||||
{
|
||||
static __thread char buf[128];
|
||||
|
||||
if (evt == NULL)
|
||||
return "(null)";
|
||||
|
||||
#define EPEVT_DUMP(attrib) \
|
||||
if (evt->events & attrib) \
|
||||
strcat(&buf[0], #attrib" ");
|
||||
|
||||
snprintf(&buf[0], 128, " { data = %p, events = ", evt->data.ptr);
|
||||
EPEVT_DUMP(EPOLLIN);
|
||||
EPEVT_DUMP(EPOLLOUT);
|
||||
#if defined(HAVE_EPOLLRDHUP)
|
||||
EPEVT_DUMP(EPOLLRDHUP);
|
||||
#endif
|
||||
EPEVT_DUMP(EPOLLONESHOT);
|
||||
EPEVT_DUMP(EPOLLET);
|
||||
strcat(&buf[0], "}\n");
|
||||
|
||||
return (&buf[0]);
|
||||
#undef EPEVT_DUMP
|
||||
}
|
||||
|
||||
int
|
||||
epoll_update(int op, struct filter *filt, struct knote *kn, struct epoll_event *ev)
|
||||
{
|
||||
dbg_printf("op=%d fd=%d events=%s", op, (int)kn->kev.ident,
|
||||
epoll_event_dump(ev));
|
||||
if (epoll_ctl(filter_epfd(filt), op, kn->kev.ident, ev) < 0) {
|
||||
dbg_printf("epoll_ctl(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a file descriptor, return the path to the file it refers to.
|
||||
*/
|
||||
int
|
||||
linux_fd_to_path(char *buf, size_t bufsz, int fd)
|
||||
{
|
||||
char path[1024]; //TODO: Maxpathlen, etc.
|
||||
|
||||
if (snprintf(&path[0], sizeof(path), "/proc/%d/fd/%d", getpid(), fd) < 0)
|
||||
return (-1);
|
||||
|
||||
memset(buf, 0, bufsz);
|
||||
return (readlink(path, buf, bufsz));
|
||||
}
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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.
|
||||
*/
|
||||
|
||||
#ifndef _KQUEUE_LINUX_PLATFORM_H
|
||||
#define _KQUEUE_LINUX_PLATFORM_H
|
||||
|
||||
struct filter;
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/inotify.h>
|
||||
#if HAVE_SYS_EVENTFD_H
|
||||
# include <sys/eventfd.h>
|
||||
#else
|
||||
# define eventfd(a,b) syscall(SYS_eventfd, (a), (b))
|
||||
|
||||
static inline int eventfd_write(int fd, uint64_t val) {
|
||||
if (write(fd, &val, sizeof(val)) < (ssize_t) sizeof(val))
|
||||
return (-1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
#if HAVE_SYS_TIMERFD_H
|
||||
# include <sys/timerfd.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get the current thread ID
|
||||
*/
|
||||
# define _GNU_SOURCE
|
||||
# include <linux/unistd.h>
|
||||
# include <unistd.h>
|
||||
#ifndef __ANDROID__
|
||||
extern long int syscall (long int __sysno, ...);
|
||||
#endif
|
||||
|
||||
/* Convenience macros to access the epoll descriptor for the kqueue */
|
||||
#define kqueue_epfd(kq) ((kq)->kq_id)
|
||||
#define filter_epfd(filt) ((filt)->kf_kqueue->kq_id)
|
||||
|
||||
/*
|
||||
* Additional members of struct filter
|
||||
*/
|
||||
#undef FILTER_PLATFORM_SPECIFIC
|
||||
|
||||
/*
|
||||
* Additional members of struct knote
|
||||
*/
|
||||
#define KNOTE_PLATFORM_SPECIFIC \
|
||||
int kn_epollfd; /* A copy of filter->epfd */ \
|
||||
union { \
|
||||
int kn_timerfd; \
|
||||
int kn_signalfd; \
|
||||
int kn_inotifyfd; \
|
||||
int kn_eventfd; \
|
||||
} kdata
|
||||
|
||||
/*
|
||||
* Additional members of struct kqueue
|
||||
*/
|
||||
#define KQUEUE_PLATFORM_SPECIFIC \
|
||||
struct epoll_event kq_plist[MAX_KEVENT]; \
|
||||
size_t kq_nplist
|
||||
|
||||
int linux_kqueue_init(struct kqueue *);
|
||||
void linux_kqueue_free(struct kqueue *);
|
||||
|
||||
int linux_kevent_wait(struct kqueue *, int, const struct timespec *);
|
||||
int linux_kevent_copyout(struct kqueue *, int, struct kevent *, int);
|
||||
|
||||
int linux_knote_copyout(struct kevent *, struct knote *);
|
||||
|
||||
int linux_eventfd_init(struct eventfd *);
|
||||
void linux_eventfd_close(struct eventfd *);
|
||||
int linux_eventfd_raise(struct eventfd *);
|
||||
int linux_eventfd_lower(struct eventfd *);
|
||||
int linux_eventfd_descriptor(struct eventfd *);
|
||||
|
||||
/* utility functions */
|
||||
|
||||
int linux_get_descriptor_type(struct knote *);
|
||||
int linux_fd_to_path(char *, size_t, int);
|
||||
|
||||
/* epoll-related functions */
|
||||
|
||||
int epoll_update(int, struct filter *, struct knote *, struct epoll_event *);
|
||||
char * epoll_event_dump(struct epoll_event *);
|
||||
|
||||
#endif /* ! _KQUEUE_LINUX_PLATFORM_H */
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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 <linux/sockios.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
/*
|
||||
* Return the offset from the current position to end of file.
|
||||
*/
|
||||
static intptr_t
|
||||
get_eof_offset(int fd)
|
||||
{
|
||||
off_t curpos;
|
||||
struct stat sb;
|
||||
|
||||
curpos = lseek(fd, 0, SEEK_CUR);
|
||||
if (curpos == (off_t) -1) {
|
||||
dbg_perror("lseek(2)");
|
||||
curpos = 0;
|
||||
}
|
||||
if (fstat(fd, &sb) < 0) {
|
||||
dbg_perror("fstat(2)");
|
||||
sb.st_size = 1;
|
||||
}
|
||||
|
||||
dbg_printf("curpos=%zu size=%zu\n", (size_t)curpos, (size_t)sb.st_size);
|
||||
return (sb.st_size - curpos); //FIXME: can overflow
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_read_copyout(struct kevent *dst, struct knote *src, void *ptr)
|
||||
{
|
||||
struct epoll_event * const ev = (struct epoll_event *) ptr;
|
||||
|
||||
/* Special case: for regular files, return the offset from current position to end of file */
|
||||
if (src->kn_flags & KNFL_REGULAR_FILE) {
|
||||
memcpy(dst, &src->kev, sizeof(*dst));
|
||||
dst->data = get_eof_offset(src->kev.ident);
|
||||
|
||||
if (dst->data == 0) {
|
||||
dst->filter = 0; /* Will cause the kevent to be discarded */
|
||||
if (epoll_ctl(src->kn_epollfd, EPOLL_CTL_DEL, src->kdata.kn_eventfd, NULL) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#if FIXME
|
||||
/* XXX-FIXME Switch to using kn_inotifyfd to monitor for IN_ATTRIB events
|
||||
that may signify the file size has changed.
|
||||
|
||||
This code is not tested.
|
||||
*/
|
||||
int inofd;
|
||||
char path[PATH_MAX];
|
||||
|
||||
inofd = inotify_init();
|
||||
if (inofd < 0) {
|
||||
dbg_perror("inotify_init(2)");
|
||||
(void) close(inofd);
|
||||
return (-1);
|
||||
}
|
||||
src->kdata.kn_inotifyfd = inofd;
|
||||
if (linux_fd_to_path(&path[0], sizeof(path), src->kev.ident) < 0)
|
||||
return (-1);
|
||||
if (inotify_add_watch(inofd, path, IN_ATTRIB) < 0) {
|
||||
dbg_perror("inotify_add_watch");
|
||||
return (-1);
|
||||
}
|
||||
if (epoll_ctl(src->kn_epollfd, EPOLL_CTL_ADD, src->kdata.kn_inotifyfd, NULL) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
/* FIXME: race here, should we check the EOF status again ? */
|
||||
#endif
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
dbg_printf("epoll: %s", epoll_event_dump(ev));
|
||||
memcpy(dst, &src->kev, sizeof(*dst));
|
||||
#if defined(HAVE_EPOLLRDHUP)
|
||||
if (ev->events & EPOLLRDHUP || ev->events & EPOLLHUP)
|
||||
dst->flags |= EV_EOF;
|
||||
#else
|
||||
if (ev->events & EPOLLHUP)
|
||||
dst->flags |= EV_EOF;
|
||||
#endif
|
||||
if (ev->events & EPOLLERR)
|
||||
dst->fflags = 1; /* FIXME: Return the actual socket error */
|
||||
|
||||
if (src->kn_flags & KNFL_PASSIVE_SOCKET) {
|
||||
/* On return, data contains the length of the
|
||||
socket backlog. This is not available under Linux.
|
||||
*/
|
||||
dst->data = 1;
|
||||
} else {
|
||||
/* On return, data contains the number of bytes of protocol
|
||||
data available to read.
|
||||
*/
|
||||
if (ioctl(dst->ident, SIOCINQ, &dst->data) < 0) {
|
||||
/* race condition with socket close, so ignore this error */
|
||||
dbg_puts("ioctl(2) of socket failed");
|
||||
dst->data = 0;
|
||||
} else {
|
||||
if (dst->data == 0)
|
||||
dst->flags |= EV_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_read_knote_create(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
if (linux_get_descriptor_type(kn) < 0)
|
||||
return (-1);
|
||||
|
||||
/* Convert the kevent into an epoll_event */
|
||||
#if defined(HAVE_EPOLLRDHUP)
|
||||
kn->data.events = EPOLLIN | EPOLLRDHUP;
|
||||
#else
|
||||
kn->data.events = EPOLLIN;
|
||||
#endif
|
||||
if (kn->kev.flags & EV_ONESHOT || kn->kev.flags & EV_DISPATCH)
|
||||
kn->data.events |= EPOLLONESHOT;
|
||||
if (kn->kev.flags & EV_CLEAR)
|
||||
kn->data.events |= EPOLLET;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = kn->data.events;
|
||||
ev.data.ptr = kn;
|
||||
|
||||
/* Special case: for regular files, add a surrogate eventfd that is always readable */
|
||||
if (kn->kn_flags & KNFL_REGULAR_FILE) {
|
||||
int evfd;
|
||||
|
||||
kn->kn_epollfd = filter_epfd(filt);
|
||||
evfd = eventfd(0, 0);
|
||||
if (evfd < 0) {
|
||||
dbg_perror("eventfd(2)");
|
||||
return (-1);
|
||||
}
|
||||
if (eventfd_write(evfd, 1) < 0) {
|
||||
dbg_perror("eventfd_write(3)");
|
||||
(void) close(evfd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
kn->kdata.kn_eventfd = evfd;
|
||||
|
||||
if (epoll_ctl(kn->kn_epollfd, EPOLL_CTL_ADD, kn->kdata.kn_eventfd, &ev) < 0) {
|
||||
dbg_printf("epoll_ctl(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
return epoll_update(EPOLL_CTL_ADD, filt, kn, &ev);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_read_knote_modify(struct filter *filt, struct knote *kn,
|
||||
const struct kevent *kev)
|
||||
{
|
||||
(void) filt;
|
||||
(void) kn;
|
||||
(void) kev;
|
||||
return (-1); /* STUB */
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_read_knote_delete(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
if (kn->kev.flags & EV_DISABLE)
|
||||
return (0);
|
||||
|
||||
if ((kn->kn_flags & KNFL_REGULAR_FILE && kn->kdata.kn_eventfd != -1) < 0) {
|
||||
if (epoll_ctl(kn->kn_epollfd, EPOLL_CTL_DEL, kn->kdata.kn_eventfd, NULL) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
(void) close(kn->kdata.kn_eventfd);
|
||||
kn->kdata.kn_eventfd = -1;
|
||||
} else {
|
||||
return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL);
|
||||
}
|
||||
|
||||
// clang will complain about not returning a value otherwise
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_read_knote_enable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = kn->data.events;
|
||||
ev.data.ptr = kn;
|
||||
|
||||
if (kn->kn_flags & KNFL_REGULAR_FILE) {
|
||||
if (epoll_ctl(kn->kn_epollfd, EPOLL_CTL_ADD, kn->kdata.kn_eventfd, &ev) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
} else {
|
||||
return epoll_update(EPOLL_CTL_ADD, filt, kn, &ev);
|
||||
}
|
||||
|
||||
// clang will complain about not returning a value otherwise
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_read_knote_disable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
if (kn->kn_flags & KNFL_REGULAR_FILE) {
|
||||
if (epoll_ctl(kn->kn_epollfd, EPOLL_CTL_DEL, kn->kdata.kn_eventfd, NULL) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
} else {
|
||||
return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
const struct filter evfilt_read = {
|
||||
EVFILT_READ,
|
||||
NULL,
|
||||
NULL,
|
||||
evfilt_read_copyout,
|
||||
evfilt_read_knote_create,
|
||||
evfilt_read_knote_modify,
|
||||
evfilt_read_knote_delete,
|
||||
evfilt_read_knote_enable,
|
||||
evfilt_read_knote_disable,
|
||||
};
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 "private.h"
|
||||
|
||||
#if HAVE_SYS_SIGNALFD_H
|
||||
# include <sys/signalfd.h>
|
||||
#else
|
||||
#define signalfd(a,b,c) syscall(SYS_signalfd, (a), (b), (c))
|
||||
#define SFD_NONBLOCK 04000
|
||||
struct signalfd_siginfo
|
||||
{
|
||||
uint32_t ssi_signo;
|
||||
int32_t ssi_errno;
|
||||
int32_t ssi_code;
|
||||
uint32_t ssi_pid;
|
||||
uint32_t ssi_uid;
|
||||
int32_t ssi_fd;
|
||||
uint32_t ssi_tid;
|
||||
uint32_t ssi_band;
|
||||
uint32_t ssi_overrun;
|
||||
uint32_t ssi_trapno;
|
||||
int32_t ssi_status;
|
||||
int32_t ssi_int;
|
||||
uint64_t ssi_ptr;
|
||||
uint64_t ssi_utime;
|
||||
uint64_t ssi_stime;
|
||||
uint64_t ssi_addr;
|
||||
uint8_t __pad[48];
|
||||
};
|
||||
#endif
|
||||
|
||||
static void
|
||||
signalfd_reset(int sigfd)
|
||||
{
|
||||
struct signalfd_siginfo sig;
|
||||
ssize_t n;
|
||||
|
||||
/* Discard any pending signal */
|
||||
n = read(sigfd, &sig, sizeof(sig));
|
||||
if (n < 0 || n != sizeof(sig)) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
return;
|
||||
//FIXME: eintr?
|
||||
dbg_perror("read(2) from signalfd");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
signalfd_add(int epfd, int sigfd, void *ptr)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
int rv;
|
||||
|
||||
/* Add the signalfd to the kqueue's epoll descriptor set */
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = ptr;
|
||||
rv = epoll_ctl(epfd, EPOLL_CTL_ADD, sigfd, &ev);
|
||||
if (rv < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
signalfd_create(int epfd, void *ptr, int signum)
|
||||
{
|
||||
static int flags = SFD_NONBLOCK;
|
||||
sigset_t sigmask;
|
||||
int sigfd;
|
||||
|
||||
/* Create a signalfd */
|
||||
sigemptyset(&sigmask);
|
||||
sigaddset(&sigmask, signum);
|
||||
sigfd = signalfd(-1, &sigmask, flags);
|
||||
|
||||
/* WORKAROUND: Flags are broken on kernels older than Linux 2.6.27 */
|
||||
if (sigfd < 0 && errno == EINVAL && flags != 0) {
|
||||
flags = 0;
|
||||
sigfd = signalfd(-1, &sigmask, flags);
|
||||
}
|
||||
if (sigfd < 0) {
|
||||
dbg_perror("signalfd(2)");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Block the signal handler from being invoked */
|
||||
if (sigprocmask(SIG_BLOCK, &sigmask, NULL) < 0) {
|
||||
dbg_perror("sigprocmask(2)");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
signalfd_reset(sigfd);
|
||||
|
||||
if (signalfd_add(epfd, sigfd, ptr) < 0)
|
||||
goto errout;
|
||||
|
||||
dbg_printf("added sigfd %d to epfd %d (signum=%d)", sigfd, epfd, signum);
|
||||
|
||||
return (sigfd);
|
||||
|
||||
errout:
|
||||
(void) close(sigfd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_copyout(struct kevent *dst, struct knote *src, void *x UNUSED)
|
||||
{
|
||||
int sigfd;
|
||||
|
||||
sigfd = src->kdata.kn_signalfd;
|
||||
|
||||
signalfd_reset(sigfd);
|
||||
|
||||
memcpy(dst, &src->kev, sizeof(*dst));
|
||||
/* NOTE: dst->data should be the number of times the signal occurred,
|
||||
but that information is not available.
|
||||
*/
|
||||
dst->data = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_knote_create(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = signalfd_create(filter_epfd(filt), kn, kn->kev.ident);
|
||||
if (fd > 0) {
|
||||
kn->kev.flags |= EV_CLEAR;
|
||||
kn->kdata.kn_signalfd = fd;
|
||||
return (0);
|
||||
} else {
|
||||
kn->kdata.kn_signalfd = -1;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_knote_modify(struct filter *filt UNUSED,
|
||||
struct knote *kn UNUSED,
|
||||
const struct kevent *kev UNUSED)
|
||||
{
|
||||
/* Nothing to do since the signal number does not change. */
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_knote_delete(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
const int sigfd = kn->kdata.kn_signalfd;
|
||||
|
||||
/* Needed so that delete() can be called after disable() */
|
||||
if (kn->kdata.kn_signalfd == -1)
|
||||
return (0);
|
||||
|
||||
if (epoll_ctl(filter_epfd(filt), EPOLL_CTL_DEL, sigfd, NULL) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (close(sigfd) < 0) {
|
||||
dbg_perror("close(2)");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* NOTE: This does not call sigprocmask(3) to unblock the signal. */
|
||||
kn->kdata.kn_signalfd = -1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_knote_enable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
dbg_printf("enabling ident %u", (unsigned int) kn->kev.ident);
|
||||
return evfilt_signal_knote_create(filt, kn);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_knote_disable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
dbg_printf("disabling ident %u", (unsigned int) kn->kev.ident);
|
||||
return evfilt_signal_knote_delete(filt, kn);
|
||||
}
|
||||
|
||||
|
||||
const struct filter evfilt_signal = {
|
||||
EVFILT_SIGNAL,
|
||||
NULL,
|
||||
NULL,
|
||||
evfilt_signal_copyout,
|
||||
evfilt_signal_knote_create,
|
||||
evfilt_signal_knote_modify,
|
||||
evfilt_signal_knote_delete,
|
||||
evfilt_signal_knote_enable,
|
||||
evfilt_signal_knote_disable,
|
||||
};
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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 "private.h"
|
||||
|
||||
#ifndef HAVE_SYS_TIMERFD_H
|
||||
|
||||
/* Android 4.0 does not have this header, but the kernel supports timerfds */
|
||||
#ifndef SYS_timerfd_create
|
||||
#ifdef __ARM_EABI__
|
||||
#define __NR_timerfd_create (__NR_SYSCALL_BASE+350)
|
||||
#define __NR_timerfd_settime (__NR_SYSCALL_BASE+353)
|
||||
#define __NR_timerfd_gettime (__NR_SYSCALL_BASE+354)
|
||||
#else
|
||||
#error Unsupported architecture, need to get the syscall numbers
|
||||
#endif
|
||||
|
||||
#define SYS_timerfd_create __NR_timerfd_create
|
||||
#define SYS_timerfd_settime __NR_timerfd_settime
|
||||
#define SYS_timerfd_gettime __NR_timerfd_gettime
|
||||
#endif /* ! SYS_timerfd_create */
|
||||
|
||||
/* XXX-FIXME
|
||||
These are horrible hacks that are only known to be true on RHEL 5 x86.
|
||||
*/
|
||||
#ifndef SYS_timerfd_settime
|
||||
#define SYS_timerfd_settime (SYS_timerfd_create + 1)
|
||||
#endif
|
||||
#ifndef SYS_timerfd_gettime
|
||||
#define SYS_timerfd_gettime (SYS_timerfd_create + 2)
|
||||
#endif
|
||||
|
||||
int timerfd_create(int clockid, int flags)
|
||||
{
|
||||
return syscall(SYS_timerfd_create, clockid, flags);
|
||||
}
|
||||
|
||||
int timerfd_settime(int ufc, int flags, const struct itimerspec *utmr,
|
||||
struct itimerspec *otmr)
|
||||
{
|
||||
return syscall(SYS_timerfd_settime, ufc, flags, utmr, otmr);
|
||||
}
|
||||
|
||||
int timerfd_gettime(int ufc, struct itimerspec *otmr)
|
||||
{
|
||||
return syscall(SYS_timerfd_gettime, ufc, otmr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
static char *
|
||||
itimerspec_dump(struct itimerspec *ts)
|
||||
{
|
||||
static __thread char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"itimer: [ interval=%lu s %lu ns, next expire=%lu s %lu ns ]",
|
||||
ts->it_interval.tv_sec,
|
||||
ts->it_interval.tv_nsec,
|
||||
ts->it_value.tv_sec,
|
||||
ts->it_value.tv_nsec
|
||||
);
|
||||
|
||||
return (buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Convert milliseconds into seconds+nanoseconds */
|
||||
static void
|
||||
convert_msec_to_itimerspec(struct itimerspec *dst, int src, int oneshot)
|
||||
{
|
||||
time_t sec, nsec;
|
||||
|
||||
sec = src / 1000;
|
||||
nsec = (src % 1000) * 1000000;
|
||||
|
||||
/* Set the interval */
|
||||
if (oneshot) {
|
||||
dst->it_interval.tv_sec = 0;
|
||||
dst->it_interval.tv_nsec = 0;
|
||||
} else {
|
||||
dst->it_interval.tv_sec = sec;
|
||||
dst->it_interval.tv_nsec = nsec;
|
||||
}
|
||||
|
||||
/* Set the initial expiration */
|
||||
dst->it_value.tv_sec = sec;
|
||||
dst->it_value.tv_nsec = nsec;
|
||||
dbg_printf("%s", itimerspec_dump(dst));
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_copyout(struct kevent *dst, struct knote *src, void *ptr)
|
||||
{
|
||||
struct epoll_event * const ev = (struct epoll_event *) ptr;
|
||||
uint64_t expired;
|
||||
ssize_t n;
|
||||
|
||||
memcpy(dst, &src->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(src->data.pfd, &expired, sizeof(expired));
|
||||
if (n != sizeof(expired)) {
|
||||
dbg_puts("invalid read from timerfd");
|
||||
expired = 1; /* Fail gracefully */
|
||||
}
|
||||
dst->data = expired;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_knote_create(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
struct itimerspec ts;
|
||||
int tfd;
|
||||
|
||||
kn->kev.flags |= EV_CLEAR;
|
||||
|
||||
tfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||
if (tfd < 0) {
|
||||
dbg_printf("timerfd_create(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
dbg_printf("created timerfd %d", tfd);
|
||||
|
||||
convert_msec_to_itimerspec(&ts, kn->kev.data, kn->kev.flags & EV_ONESHOT);
|
||||
if (timerfd_settime(tfd, 0, &ts, NULL) < 0) {
|
||||
dbg_printf("timerfd_settime(2): %s", strerror(errno));
|
||||
close(tfd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = kn;
|
||||
if (epoll_ctl(filter_epfd(filt), EPOLL_CTL_ADD, tfd, &ev) < 0) {
|
||||
dbg_printf("epoll_ctl(2): %d", errno);
|
||||
close(tfd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
kn->data.pfd = tfd;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_knote_modify(struct filter *filt, struct knote *kn,
|
||||
const struct kevent *kev)
|
||||
{
|
||||
(void)filt;
|
||||
(void)kn;
|
||||
(void)kev;
|
||||
return (0); /* STUB */
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_knote_delete(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
if (kn->data.pfd == -1)
|
||||
return (0);
|
||||
|
||||
if (epoll_ctl(filter_epfd(filt), EPOLL_CTL_DEL, kn->data.pfd, NULL) < 0) {
|
||||
dbg_printf("epoll_ctl(2): %s", strerror(errno));
|
||||
rv = -1;
|
||||
}
|
||||
if (close(kn->data.pfd) < 0) {
|
||||
dbg_printf("close(2): %s", strerror(errno));
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
kn->data.pfd = -1;
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_knote_enable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
return evfilt_timer_knote_create(filt, kn);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_knote_disable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
return evfilt_timer_knote_delete(filt, kn);
|
||||
}
|
||||
|
||||
const struct filter evfilt_timer = {
|
||||
EVFILT_TIMER,
|
||||
NULL,
|
||||
NULL,
|
||||
evfilt_timer_copyout,
|
||||
evfilt_timer_knote_create,
|
||||
evfilt_timer_knote_modify,
|
||||
evfilt_timer_knote_delete,
|
||||
evfilt_timer_knote_enable,
|
||||
evfilt_timer_knote_disable,
|
||||
};
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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 <pthread.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 "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
/* NOTE: copy+pasted from linux_eventfd_raise() */
|
||||
static int
|
||||
eventfd_raise(int evfd)
|
||||
{
|
||||
uint64_t counter;
|
||||
int rv = 0;
|
||||
|
||||
dbg_puts("raising event level");
|
||||
counter = 1;
|
||||
if (write(evfd, &counter, sizeof(counter)) < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
/* Not considered an error */
|
||||
break;
|
||||
|
||||
case EINTR:
|
||||
rv = -EINTR;
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_printf("write(2): %s", strerror(errno));
|
||||
rv = -1;
|
||||
}
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/* NOTE: copy+pasted from linux_eventfd_lower() */
|
||||
static int
|
||||
eventfd_lower(int evfd)
|
||||
{
|
||||
uint64_t cur;
|
||||
ssize_t n;
|
||||
int rv = 0;
|
||||
|
||||
/* Reset the counter */
|
||||
dbg_puts("lowering event level");
|
||||
n = read(evfd, &cur, sizeof(cur));
|
||||
if (n < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
/* Not considered an error */
|
||||
break;
|
||||
|
||||
case EINTR:
|
||||
rv = -EINTR;
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_printf("read(2): %s", strerror(errno));
|
||||
rv = -1;
|
||||
}
|
||||
} else if (n != sizeof(cur)) {
|
||||
dbg_puts("short read");
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
linux_evfilt_user_copyout(struct kevent *dst, struct knote *src, void *ptr UNUSED)
|
||||
{
|
||||
memcpy(dst, &src->kev, sizeof(*dst));
|
||||
dst->fflags &= ~NOTE_FFCTRLMASK; //FIXME: Not sure if needed
|
||||
dst->fflags &= ~NOTE_TRIGGER;
|
||||
if (src->kev.flags & EV_ADD) {
|
||||
/* NOTE: True on FreeBSD but not consistent behavior with
|
||||
other filters. */
|
||||
dst->flags &= ~EV_ADD;
|
||||
}
|
||||
if (src->kev.flags & EV_CLEAR)
|
||||
src->kev.fflags &= ~NOTE_TRIGGER;
|
||||
if (src->kev.flags & (EV_DISPATCH | EV_CLEAR | EV_ONESHOT)) {
|
||||
if (eventfd_lower(src->kdata.kn_eventfd) < 0)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (src->kev.flags & EV_DISPATCH)
|
||||
src->kev.fflags &= ~NOTE_TRIGGER;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
linux_evfilt_user_knote_create(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
int evfd;
|
||||
|
||||
/* Create an eventfd */
|
||||
evfd = eventfd(0, 0);
|
||||
if (evfd < 0) {
|
||||
dbg_perror("eventfd");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Add the eventfd to the epoll set */
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = kn;
|
||||
if (epoll_ctl(filter_epfd(filt), EPOLL_CTL_ADD, evfd, &ev) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
kn->kdata.kn_eventfd = evfd;
|
||||
|
||||
return (0);
|
||||
|
||||
errout:
|
||||
(void) close(evfd);
|
||||
kn->kdata.kn_eventfd = -1;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
linux_evfilt_user_knote_modify(struct filter *filt UNUSED, struct knote *kn,
|
||||
const struct kevent *kev)
|
||||
{
|
||||
unsigned int ffctrl;
|
||||
unsigned int fflags;
|
||||
|
||||
/* Excerpted from sys/kern/kern_event.c in FreeBSD HEAD */
|
||||
ffctrl = kev->fflags & NOTE_FFCTRLMASK;
|
||||
fflags = kev->fflags & NOTE_FFLAGSMASK;
|
||||
switch (ffctrl) {
|
||||
case NOTE_FFNOP:
|
||||
break;
|
||||
|
||||
case NOTE_FFAND:
|
||||
kn->kev.fflags &= fflags;
|
||||
break;
|
||||
|
||||
case NOTE_FFOR:
|
||||
kn->kev.fflags |= fflags;
|
||||
break;
|
||||
|
||||
case NOTE_FFCOPY:
|
||||
kn->kev.fflags = fflags;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* XXX Return error? */
|
||||
break;
|
||||
}
|
||||
|
||||
if ((!(kn->kev.flags & EV_DISABLE)) && kev->fflags & NOTE_TRIGGER) {
|
||||
kn->kev.fflags |= NOTE_TRIGGER;
|
||||
if (eventfd_raise(kn->kdata.kn_eventfd) < 0)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
linux_evfilt_user_knote_delete(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
if (epoll_ctl(filter_epfd(filt), EPOLL_CTL_DEL,
|
||||
kn->kdata.kn_eventfd, NULL) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
if (close(kn->kdata.kn_eventfd) < 0) {
|
||||
dbg_perror("close(2)");
|
||||
return (-1);
|
||||
}
|
||||
dbg_printf("removed eventfd %d from the epollfd", kn->kdata.kn_eventfd);
|
||||
kn->kdata.kn_eventfd = -1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
linux_evfilt_user_knote_enable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
/* FIXME: what happens if NOTE_TRIGGER is in fflags?
|
||||
should the event fire? */
|
||||
return linux_evfilt_user_knote_create(filt, kn);
|
||||
}
|
||||
|
||||
int
|
||||
linux_evfilt_user_knote_disable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
return linux_evfilt_user_knote_delete(filt, kn);
|
||||
}
|
||||
|
||||
const struct filter evfilt_user = {
|
||||
EVFILT_USER,
|
||||
NULL,
|
||||
NULL,
|
||||
linux_evfilt_user_copyout,
|
||||
linux_evfilt_user_knote_create,
|
||||
linux_evfilt_user_knote_modify,
|
||||
linux_evfilt_user_knote_delete,
|
||||
linux_evfilt_user_knote_enable,
|
||||
linux_evfilt_user_knote_disable,
|
||||
};
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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 "private.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
static char *
|
||||
inotify_mask_dump(uint32_t mask)
|
||||
{
|
||||
static __thread char buf[1024];
|
||||
|
||||
#define INEVT_MASK_DUMP(attrib) \
|
||||
if (mask & attrib) \
|
||||
strcat(buf, #attrib" ");
|
||||
|
||||
snprintf(buf, sizeof(buf), "mask = %d (", mask);
|
||||
INEVT_MASK_DUMP(IN_ACCESS);
|
||||
INEVT_MASK_DUMP(IN_MODIFY);
|
||||
INEVT_MASK_DUMP(IN_ATTRIB);
|
||||
INEVT_MASK_DUMP(IN_CLOSE_WRITE);
|
||||
INEVT_MASK_DUMP(IN_CLOSE_NOWRITE);
|
||||
INEVT_MASK_DUMP(IN_OPEN);
|
||||
INEVT_MASK_DUMP(IN_MOVED_FROM);
|
||||
INEVT_MASK_DUMP(IN_MOVED_TO);
|
||||
INEVT_MASK_DUMP(IN_CREATE);
|
||||
INEVT_MASK_DUMP(IN_DELETE);
|
||||
INEVT_MASK_DUMP(IN_DELETE_SELF);
|
||||
INEVT_MASK_DUMP(IN_MOVE_SELF);
|
||||
buf[strlen(buf) - 1] = ')';
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static char *
|
||||
inotify_event_dump(struct inotify_event *evt)
|
||||
{
|
||||
static __thread char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "wd=%d mask=%s",
|
||||
evt->wd,
|
||||
inotify_mask_dump(evt->mask));
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
|
||||
/* TODO: USE this to get events with name field */
|
||||
int
|
||||
get_one_event(struct inotify_event *dst, int inofd)
|
||||
{
|
||||
ssize_t n;
|
||||
|
||||
dbg_puts("reading one inotify event");
|
||||
for (;;) {
|
||||
n = read(inofd, dst, sizeof(*dst));
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
dbg_perror("read");
|
||||
return (-1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dbg_printf("read(2) from inotify wd: %ld bytes", (long) n);
|
||||
|
||||
/* FIXME-TODO: if len > 0, read(len) */
|
||||
if (dst->len != 0)
|
||||
abort();
|
||||
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
add_watch(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
int ifd;
|
||||
char path[PATH_MAX];
|
||||
uint32_t mask;
|
||||
|
||||
/* Convert the fd to a pathname */
|
||||
if (linux_fd_to_path(&path[0], sizeof(path), kn->kev.ident) < 0)
|
||||
return (-1);
|
||||
|
||||
/* Convert the fflags to the inotify mask */
|
||||
mask = IN_CLOSE;
|
||||
if (kn->kev.fflags & NOTE_DELETE)
|
||||
mask |= IN_ATTRIB | IN_DELETE_SELF;
|
||||
if (kn->kev.fflags & NOTE_WRITE)
|
||||
mask |= IN_MODIFY | IN_ATTRIB;
|
||||
if (kn->kev.fflags & NOTE_EXTEND)
|
||||
mask |= IN_MODIFY | IN_ATTRIB;
|
||||
if ((kn->kev.fflags & NOTE_ATTRIB) ||
|
||||
(kn->kev.fflags & NOTE_LINK))
|
||||
mask |= IN_ATTRIB;
|
||||
if (kn->kev.fflags & NOTE_RENAME)
|
||||
mask |= IN_MOVE_SELF;
|
||||
if (kn->kev.flags & EV_ONESHOT)
|
||||
mask |= IN_ONESHOT;
|
||||
|
||||
/* Create an inotify descriptor */
|
||||
ifd = inotify_init();
|
||||
if (ifd < 0) {
|
||||
dbg_perror("inotify_init(2)");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Add the watch */
|
||||
dbg_printf("inotify_add_watch(2); inofd=%d, %s, path=%s",
|
||||
ifd, inotify_mask_dump(mask), path);
|
||||
kn->kev.data = inotify_add_watch(ifd, path, mask);
|
||||
if (kn->kev.data < 0) {
|
||||
dbg_perror("inotify_add_watch(2)");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Add the inotify fd to the epoll set */
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = kn;
|
||||
if (epoll_ctl(filter_epfd(filt), EPOLL_CTL_ADD, ifd, &ev) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
kn->kdata.kn_inotifyfd = ifd;
|
||||
|
||||
return (0);
|
||||
|
||||
errout:
|
||||
kn->kdata.kn_inotifyfd = -1;
|
||||
(void) close(ifd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static int
|
||||
delete_watch(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
int ifd = kn->kdata.kn_inotifyfd;
|
||||
|
||||
if (ifd < 0)
|
||||
return (0);
|
||||
if (epoll_ctl(filter_epfd(filt), EPOLL_CTL_DEL, ifd, NULL) < 0) {
|
||||
dbg_perror("epoll_ctl(2)");
|
||||
return (-1);
|
||||
}
|
||||
(void) close(ifd);
|
||||
kn->kdata.kn_inotifyfd = -1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_copyout(struct kevent *dst, struct knote *src, void *ptr UNUSED)
|
||||
{
|
||||
struct inotify_event evt;
|
||||
struct stat sb;
|
||||
|
||||
if (get_one_event(&evt, src->kdata.kn_inotifyfd) < 0)
|
||||
return (-1);
|
||||
|
||||
dbg_printf("inotify event: %s", inotify_event_dump(&evt));
|
||||
if (evt.mask & IN_IGNORED) {
|
||||
/* TODO: possibly return error when fs is unmounted */
|
||||
dst->filter = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Check if the watched file has been closed, and
|
||||
XXX-this may not exactly match the kevent() behavior if multiple file de
|
||||
scriptors reference the same file.
|
||||
*/
|
||||
if (evt.mask & IN_CLOSE_WRITE || evt.mask & IN_CLOSE_NOWRITE) {
|
||||
src->kn_flags |= EV_ONESHOT; /* KLUDGE: causes the knote to be deleted */
|
||||
dst->filter = 0; /* KLUDGE: causes the event to be discarded */
|
||||
return (0);
|
||||
}
|
||||
|
||||
memcpy(dst, &src->kev, sizeof(*dst));
|
||||
dst->data = 0;
|
||||
|
||||
/* No error checking because fstat(2) should rarely fail */
|
||||
//FIXME: EINTR
|
||||
if ((evt.mask & IN_ATTRIB || evt.mask & IN_MODIFY)
|
||||
&& fstat(src->kev.ident, &sb) == 0) {
|
||||
if (sb.st_nlink == 0 && src->kev.fflags & NOTE_DELETE)
|
||||
dst->fflags |= NOTE_DELETE;
|
||||
if (sb.st_nlink != src->data.vnode.nlink && src->kev.fflags & NOTE_LINK)
|
||||
dst->fflags |= NOTE_LINK;
|
||||
#if HAVE_NOTE_TRUNCATE
|
||||
if (sb.st_nsize == 0 && src->kev.fflags & NOTE_TRUNCATE)
|
||||
dst->fflags |= NOTE_TRUNCATE;
|
||||
#endif
|
||||
if (sb.st_size > src->data.vnode.size && src->kev.fflags & NOTE_WRITE)
|
||||
dst->fflags |= NOTE_EXTEND;
|
||||
src->data.vnode.nlink = sb.st_nlink;
|
||||
src->data.vnode.size = sb.st_size;
|
||||
}
|
||||
|
||||
if (evt.mask & IN_MODIFY && src->kev.fflags & NOTE_WRITE)
|
||||
dst->fflags |= NOTE_WRITE;
|
||||
if (evt.mask & IN_ATTRIB && src->kev.fflags & NOTE_ATTRIB)
|
||||
dst->fflags |= NOTE_ATTRIB;
|
||||
if (evt.mask & IN_MOVE_SELF && src->kev.fflags & NOTE_RENAME)
|
||||
dst->fflags |= NOTE_RENAME;
|
||||
if (evt.mask & IN_DELETE_SELF && src->kev.fflags & NOTE_DELETE)
|
||||
dst->fflags |= NOTE_DELETE;
|
||||
|
||||
if (evt.mask & IN_MODIFY && src->kev.fflags & NOTE_WRITE)
|
||||
dst->fflags |= NOTE_WRITE;
|
||||
if (evt.mask & IN_ATTRIB && src->kev.fflags & NOTE_ATTRIB)
|
||||
dst->fflags |= NOTE_ATTRIB;
|
||||
if (evt.mask & IN_MOVE_SELF && src->kev.fflags & NOTE_RENAME)
|
||||
dst->fflags |= NOTE_RENAME;
|
||||
if (evt.mask & IN_DELETE_SELF && src->kev.fflags & NOTE_DELETE)
|
||||
dst->fflags |= NOTE_DELETE;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_knote_create(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (fstat(kn->kev.ident, &sb) < 0) {
|
||||
dbg_puts("fstat failed");
|
||||
return (-1);
|
||||
}
|
||||
kn->data.vnode.nlink = sb.st_nlink;
|
||||
kn->data.vnode.size = sb.st_size;
|
||||
kn->kev.data = -1;
|
||||
|
||||
return (add_watch(filt, kn));
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_knote_modify(struct filter *filt, struct knote *kn,
|
||||
const struct kevent *kev)
|
||||
{
|
||||
(void)filt;
|
||||
(void)kn;
|
||||
(void)kev;
|
||||
return (-1); /* FIXME - STUB */
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_knote_delete(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
return delete_watch(filt, kn);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_knote_enable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
return add_watch(filt, kn);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_knote_disable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
return delete_watch(filt, kn);
|
||||
}
|
||||
|
||||
const struct filter evfilt_vnode = {
|
||||
EVFILT_VNODE,
|
||||
NULL,
|
||||
NULL,
|
||||
evfilt_vnode_copyout,
|
||||
evfilt_vnode_knote_create,
|
||||
evfilt_vnode_knote_modify,
|
||||
evfilt_vnode_knote_delete,
|
||||
evfilt_vnode_knote_enable,
|
||||
evfilt_vnode_knote_disable,
|
||||
};
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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 <linux/sockios.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
int
|
||||
evfilt_socket_copyout(struct kevent *dst, struct knote *src, void *ptr)
|
||||
{
|
||||
struct epoll_event * const ev = (struct epoll_event *) ptr;
|
||||
|
||||
epoll_event_dump(ev);
|
||||
memcpy(dst, &src->kev, sizeof(*dst));
|
||||
#if defined(HAVE_EPOLLRDHUP)
|
||||
if (ev->events & EPOLLRDHUP || ev->events & EPOLLHUP)
|
||||
dst->flags |= EV_EOF;
|
||||
#else
|
||||
if (ev->events & EPOLLHUP)
|
||||
dst->flags |= EV_EOF;
|
||||
#endif
|
||||
if (ev->events & EPOLLERR)
|
||||
dst->fflags = 1; /* FIXME: Return the actual socket error */
|
||||
|
||||
/* On return, data contains the the amount of space remaining in the write buffer */
|
||||
if (ioctl(dst->ident, SIOCOUTQ, &dst->data) < 0) {
|
||||
/* race condition with socket close, so ignore this error */
|
||||
dbg_puts("ioctl(2) of socket failed");
|
||||
dst->data = 0;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_knote_create(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
if (linux_get_descriptor_type(kn) < 0)
|
||||
return (-1);
|
||||
|
||||
/* TODO: return EBADF? */
|
||||
if (kn->kn_flags & KNFL_REGULAR_FILE)
|
||||
return (-1);
|
||||
|
||||
/* Convert the kevent into an epoll_event */
|
||||
kn->data.events = EPOLLOUT;
|
||||
if (kn->kev.flags & EV_ONESHOT || kn->kev.flags & EV_DISPATCH)
|
||||
kn->data.events |= EPOLLONESHOT;
|
||||
if (kn->kev.flags & EV_CLEAR)
|
||||
kn->data.events |= EPOLLET;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = kn->data.events;
|
||||
ev.data.ptr = kn;
|
||||
|
||||
return epoll_update(EPOLL_CTL_ADD, filt, kn, &ev);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_knote_modify(struct filter *filt, struct knote *kn,
|
||||
const struct kevent *kev)
|
||||
{
|
||||
(void) filt;
|
||||
(void) kn;
|
||||
(void) kev;
|
||||
return (-1); /* STUB */
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_knote_delete(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
if (kn->kev.flags & EV_DISABLE)
|
||||
return (0);
|
||||
else
|
||||
return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_knote_enable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = kn->data.events;
|
||||
ev.data.ptr = kn;
|
||||
|
||||
return epoll_update(EPOLL_CTL_ADD, filt, kn, &ev);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_knote_disable(struct filter *filt, struct knote *kn)
|
||||
{
|
||||
return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL);
|
||||
}
|
||||
|
||||
const struct filter evfilt_write = {
|
||||
EVFILT_WRITE,
|
||||
NULL,
|
||||
NULL,
|
||||
evfilt_socket_copyout,
|
||||
evfilt_socket_knote_create,
|
||||
evfilt_socket_knote_modify,
|
||||
evfilt_socket_knote_delete,
|
||||
evfilt_socket_knote_enable,
|
||||
evfilt_socket_knote_disable,
|
||||
};
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "../common/private.h"
|
||||
|
||||
int
|
||||
posix_kqueue_init(struct kqueue *kq UNUSED)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
posix_kqueue_free(struct kqueue *kq UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
posix_eventfd_init(struct eventfd *e)
|
||||
{
|
||||
int sd[2];
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sd) < 0) {
|
||||
return (-1);
|
||||
}
|
||||
if ((fcntl(sd[0], F_SETFL, O_NONBLOCK) < 0) ||
|
||||
(fcntl(sd[1], F_SETFL, O_NONBLOCK) < 0)) {
|
||||
close(sd[0]);
|
||||
close(sd[1]);
|
||||
return (-1);
|
||||
}
|
||||
e->ef_wfd = sd[0];
|
||||
e->ef_id = sd[1];
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
posix_eventfd_close(struct eventfd *e)
|
||||
{
|
||||
close(e->ef_id);
|
||||
close(e->ef_wfd);
|
||||
e->ef_id = -1;
|
||||
}
|
||||
|
||||
int
|
||||
posix_eventfd_raise(struct eventfd *e)
|
||||
{
|
||||
dbg_puts("raising event level");
|
||||
if (write(e->ef_wfd, ".", 1) < 0) {
|
||||
/* FIXME: handle EAGAIN and EINTR */
|
||||
dbg_printf("write(2) on fd %d: %s", e->ef_wfd, strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
posix_eventfd_lower(struct eventfd *e)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
/* Reset the counter */
|
||||
dbg_puts("lowering event level");
|
||||
if (read(e->ef_id, &buf, sizeof(buf)) < 0) {
|
||||
/* FIXME: handle EAGAIN and EINTR */
|
||||
/* FIXME: loop so as to consume all data.. may need mutex */
|
||||
dbg_printf("read(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
posix_eventfd_descriptor(struct eventfd *e)
|
||||
{
|
||||
return (e->ef_id);
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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.
|
||||
*/
|
||||
|
||||
#ifndef _KQUEUE_POSIX_PLATFORM_H
|
||||
#define _KQUEUE_POSIX_PLATFORM_H
|
||||
|
||||
/* Required by glibc for MAP_ANON */
|
||||
#define __USE_MISC 1
|
||||
|
||||
#include "../../include/sys/event.h"
|
||||
|
||||
/*
|
||||
* GCC-compatible atomic operations
|
||||
*/
|
||||
#define atomic_inc(p) __sync_add_and_fetch((p), 1)
|
||||
#define atomic_dec(p) __sync_sub_and_fetch((p), 1)
|
||||
#define atomic_cas(p, oval, nval) __sync_val_compare_and_swap(p, oval, nval)
|
||||
#define atomic_ptr_cas(p, oval, nval) __sync_val_compare_and_swap(p, oval, nval)
|
||||
|
||||
/*
|
||||
* GCC-compatible branch prediction macros
|
||||
*/
|
||||
#define fastpath(x) __builtin_expect((x), 1)
|
||||
#define slowpath(x) __builtin_expect((x), 0)
|
||||
|
||||
/*
|
||||
* GCC-compatible attributes
|
||||
*/
|
||||
#define VISIBLE __attribute__((visibility("default")))
|
||||
#define HIDDEN __attribute__((visibility("hidden")))
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <poll.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Additional members of 'struct eventfd'
|
||||
*/
|
||||
#define EVENTFD_PLATFORM_SPECIFIC \
|
||||
int ef_wfd
|
||||
|
||||
void posix_kqueue_free(struct kqueue *);
|
||||
int posix_kqueue_init(struct kqueue *);
|
||||
|
||||
int posix_kevent_wait(struct kqueue *, const struct timespec *);
|
||||
int posix_kevent_copyout(struct kqueue *, int, struct kevent *, int);
|
||||
|
||||
int posix_eventfd_init(struct eventfd *);
|
||||
void posix_eventfd_close(struct eventfd *);
|
||||
int posix_eventfd_raise(struct eventfd *);
|
||||
int posix_eventfd_lower(struct eventfd *);
|
||||
int posix_eventfd_descriptor(struct eventfd *);
|
||||
|
||||
#endif /* ! _KQUEUE_POSIX_PLATFORM_H */
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _COMMON_H
|
||||
#define _COMMON_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
|
||||
|
||||
#define die(str) do { \
|
||||
fprintf(stderr, "%s(): %s: %s\n", __func__,str, strerror(errno));\
|
||||
abort();\
|
||||
} while (0)
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/event.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
#include <poll.h>
|
||||
#include "../config.h"
|
||||
#else
|
||||
# include "../include/sys/event.h"
|
||||
# include "../src/windows/platform.h"
|
||||
#endif
|
||||
|
||||
struct test_context;
|
||||
|
||||
struct unit_test {
|
||||
const char *ut_name;
|
||||
int ut_enabled;
|
||||
void (*ut_func)(struct test_context *);
|
||||
};
|
||||
|
||||
#define MAX_TESTS 50
|
||||
struct test_context {
|
||||
struct unit_test tests[MAX_TESTS];
|
||||
char *cur_test_id;
|
||||
int iterations;
|
||||
int iteration;
|
||||
int kqfd;
|
||||
|
||||
/* EVFILT_READ and EVFILT_WRITE */
|
||||
int client_fd;
|
||||
int server_fd;
|
||||
|
||||
/* EVFILT_VNODE */
|
||||
int vnode_fd;
|
||||
char testfile[1024];
|
||||
};
|
||||
|
||||
void test_evfilt_read(struct test_context *);
|
||||
void test_evfilt_signal(struct test_context *);
|
||||
void test_evfilt_vnode(struct test_context *);
|
||||
void test_evfilt_timer(struct test_context *);
|
||||
void test_evfilt_proc(struct test_context *);
|
||||
#ifdef EVFILT_USER
|
||||
void test_evfilt_user(struct test_context *);
|
||||
#endif
|
||||
|
||||
#define test(f,ctx,...) do { \
|
||||
assert(ctx != NULL); \
|
||||
test_begin(ctx, "test_"#f"()\t"__VA_ARGS__); \
|
||||
test_##f(ctx); \
|
||||
test_end(ctx); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
extern const char * kevent_to_str(struct kevent *);
|
||||
void kevent_get(struct kevent *, int);
|
||||
void kevent_get_hires(struct kevent *, int);
|
||||
void kevent_update(int kqfd, struct kevent *kev);
|
||||
|
||||
#define kevent_cmp(a,b) _kevent_cmp(a,b, __FILE__, __LINE__)
|
||||
void _kevent_cmp(struct kevent *, struct kevent *, const char *, int);
|
||||
|
||||
void
|
||||
kevent_add(int kqfd, struct kevent *kev,
|
||||
uintptr_t ident,
|
||||
short filter,
|
||||
u_short flags,
|
||||
u_int fflags,
|
||||
intptr_t data,
|
||||
void *udata);
|
||||
|
||||
/* DEPRECATED: */
|
||||
#define KEV_CMP(kev,_ident,_filter,_flags) do { \
|
||||
if (kev.ident != (_ident) || \
|
||||
kev.filter != (_filter) || \
|
||||
kev.flags != (_flags)) \
|
||||
err(1, "kevent mismatch: got [%d,%d,%d] but expecting [%d,%d,%d]", \
|
||||
(int)_ident, (int)_filter, (int)_flags,\
|
||||
(int)kev.ident, kev.filter, kev.flags);\
|
||||
} while (0);
|
||||
|
||||
/* Checks if any events are pending, which is an error. */
|
||||
#define test_no_kevents(a) _test_no_kevents(a, __FILE__, __LINE__)
|
||||
void _test_no_kevents(int, const char *, int);
|
||||
|
||||
/* From test.c */
|
||||
void test_begin(struct test_context *, const char *);
|
||||
void test_end(struct test_context *);
|
||||
void test_atexit(void);
|
||||
void testing_begin(void);
|
||||
void testing_end(void);
|
||||
int testing_make_uid(void);
|
||||
|
||||
#endif /* _COMMON_H */
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
extern int kqfd;
|
||||
|
||||
/* Checks if any events are pending, which is an error. */
|
||||
void
|
||||
_test_no_kevents(int kqfd, const char *file, int line)
|
||||
{
|
||||
int nfds;
|
||||
struct timespec timeo;
|
||||
struct kevent kev;
|
||||
|
||||
memset(&timeo, 0, sizeof(timeo));
|
||||
nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
|
||||
if (nfds < 0)
|
||||
err(1, "kevent(2)");
|
||||
if (nfds > 0) {
|
||||
printf("\n[%s:%d]: Unexpected event:", file, line);
|
||||
err(1, kevent_to_str(&kev));
|
||||
}
|
||||
}
|
||||
|
||||
/* Retrieve a single kevent */
|
||||
void
|
||||
kevent_get(struct kevent *kev, int kqfd)
|
||||
{
|
||||
struct kevent buf;
|
||||
int nfds;
|
||||
|
||||
if (kev == NULL)
|
||||
kev = &buf;
|
||||
|
||||
nfds = kevent(kqfd, NULL, 0, kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
err(1, "kevent(2)");
|
||||
}
|
||||
|
||||
/* In Linux, a kevent() call with less than 1ms resolution
|
||||
will perform a pselect() call to obtain the higer resolution.
|
||||
This test exercises that codepath.
|
||||
*/
|
||||
void
|
||||
kevent_get_hires(struct kevent *kev, int kqfd)
|
||||
{
|
||||
int nfds;
|
||||
struct timespec timeo;
|
||||
|
||||
timeo.tv_sec = 0;
|
||||
timeo.tv_nsec = 500000;
|
||||
nfds = kevent(kqfd, NULL, 0, kev, 1, &timeo);
|
||||
if (nfds < 1)
|
||||
die("kevent(2)");
|
||||
}
|
||||
|
||||
char *
|
||||
kevent_fflags_dump(struct kevent *kev)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
#define KEVFFL_DUMP(attrib) \
|
||||
if (kev->fflags & attrib) \
|
||||
strncat(buf, #attrib" ", 64);
|
||||
|
||||
if ((buf = calloc(1, 1024)) == NULL)
|
||||
abort();
|
||||
|
||||
/* Not every filter has meaningful fflags */
|
||||
if (kev->filter != EVFILT_VNODE) {
|
||||
snprintf(buf, 1024, "fflags = %d", kev->fflags);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
snprintf(buf, 1024, "fflags = %d (", kev->fflags);
|
||||
KEVFFL_DUMP(NOTE_DELETE);
|
||||
KEVFFL_DUMP(NOTE_WRITE);
|
||||
KEVFFL_DUMP(NOTE_EXTEND);
|
||||
#if HAVE_NOTE_TRUNCATE
|
||||
KEVFFL_DUMP(NOTE_TRUNCATE);
|
||||
#endif
|
||||
KEVFFL_DUMP(NOTE_ATTRIB);
|
||||
KEVFFL_DUMP(NOTE_LINK);
|
||||
KEVFFL_DUMP(NOTE_RENAME);
|
||||
#if HAVE_NOTE_REVOKE
|
||||
KEVFFL_DUMP(NOTE_REVOKE);
|
||||
#endif
|
||||
buf[strlen(buf) - 1] = ')';
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
char *
|
||||
kevent_flags_dump(struct kevent *kev)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
#define KEVFL_DUMP(attrib) \
|
||||
if (kev->flags & attrib) \
|
||||
strncat(buf, #attrib" ", 64);
|
||||
|
||||
if ((buf = calloc(1, 1024)) == NULL)
|
||||
abort();
|
||||
|
||||
snprintf(buf, 1024, "flags = %d (", kev->flags);
|
||||
KEVFL_DUMP(EV_ADD);
|
||||
KEVFL_DUMP(EV_ENABLE);
|
||||
KEVFL_DUMP(EV_DISABLE);
|
||||
KEVFL_DUMP(EV_DELETE);
|
||||
KEVFL_DUMP(EV_ONESHOT);
|
||||
KEVFL_DUMP(EV_CLEAR);
|
||||
KEVFL_DUMP(EV_EOF);
|
||||
KEVFL_DUMP(EV_ERROR);
|
||||
#ifdef EV_DISPATCH
|
||||
KEVFL_DUMP(EV_DISPATCH);
|
||||
#endif
|
||||
#ifdef EV_RECEIPT
|
||||
KEVFL_DUMP(EV_RECEIPT);
|
||||
#endif
|
||||
buf[strlen(buf) - 1] = ')';
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
/* TODO - backport changes from src/common/kevent.c kevent_dump() */
|
||||
const char *
|
||||
kevent_to_str(struct kevent *kev)
|
||||
{
|
||||
char buf[512];
|
||||
|
||||
snprintf(&buf[0], sizeof(buf),
|
||||
"[ident=%d, filter=%d, %s, %s, data=%d, udata=%p]",
|
||||
(u_int) kev->ident,
|
||||
kev->filter,
|
||||
kevent_flags_dump(kev),
|
||||
kevent_fflags_dump(kev),
|
||||
(int) kev->data,
|
||||
kev->udata);
|
||||
|
||||
return (strdup(buf));
|
||||
}
|
||||
|
||||
void
|
||||
kevent_update(int kqfd, struct kevent *kev)
|
||||
{
|
||||
if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) {
|
||||
printf("Unable to add the following kevent:\n%s\n",
|
||||
kevent_to_str(kev));
|
||||
die("kevent");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kevent_add(int kqfd, struct kevent *kev,
|
||||
uintptr_t ident,
|
||||
short filter,
|
||||
u_short flags,
|
||||
u_int fflags,
|
||||
intptr_t data,
|
||||
void *udata)
|
||||
{
|
||||
EV_SET(kev, ident, filter, flags, fflags, data, NULL);
|
||||
if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) {
|
||||
printf("Unable to add the following kevent:\n%s\n",
|
||||
kevent_to_str(kev));
|
||||
die("kevent");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_kevent_cmp(struct kevent *k1, struct kevent *k2, const char *file, int line)
|
||||
{
|
||||
/* XXX-
|
||||
Workaround for inconsistent implementation of kevent(2)
|
||||
*/
|
||||
#if defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
|
||||
if (k1->flags & EV_ADD)
|
||||
k2->flags |= EV_ADD;
|
||||
#endif
|
||||
if (memcmp(k1, k2, sizeof(*k1)) != 0) {
|
||||
printf("[%s:%d]: kevent_cmp() failed:\n expected %s\n but got %s\n",
|
||||
file, line, kevent_to_str(k1), kevent_to_str(k2));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
Vendored
+280
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
/* Maximum number of threads that can be created */
|
||||
#define MAX_THREADS 100
|
||||
|
||||
void
|
||||
test_kqueue_descriptor_is_pollable(void)
|
||||
{
|
||||
int kq, rv;
|
||||
struct kevent kev;
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
if ((kq = kqueue()) < 0)
|
||||
die("kqueue()");
|
||||
|
||||
test_no_kevents(kq);
|
||||
kevent_add(kq, &kev, 2, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 1000, NULL);
|
||||
test_no_kevents(kq);
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(kq, &fds);
|
||||
tv.tv_sec = 5;
|
||||
tv.tv_usec = 0;
|
||||
rv = select(1, &fds, NULL, NULL, &tv);
|
||||
if (rv < 0)
|
||||
die("select() error");
|
||||
if (rv == 0)
|
||||
die("select() no events");
|
||||
if (!FD_ISSET(kq, &fds)) {
|
||||
die("descriptor is not ready for reading");
|
||||
}
|
||||
|
||||
close(kq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the method for detecting when one end of a socketpair
|
||||
* has been closed. This technique is used in kqueue_validate()
|
||||
*/
|
||||
static void
|
||||
test_peer_close_detection(void *unused)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return;
|
||||
//FIXME
|
||||
#else
|
||||
int sockfd[2];
|
||||
char buf[1];
|
||||
struct pollfd pfd;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
|
||||
die("socketpair");
|
||||
|
||||
pfd.fd = sockfd[0];
|
||||
pfd.events = POLLIN | POLLHUP;
|
||||
pfd.revents = 0;
|
||||
|
||||
if (poll(&pfd, 1, 0) > 0)
|
||||
die("unexpected data");
|
||||
|
||||
if (close(sockfd[1]) < 0)
|
||||
die("close");
|
||||
|
||||
if (poll(&pfd, 1, 0) > 0) {
|
||||
if (recv(sockfd[0], buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT) != 0)
|
||||
die("failed to detect peer shutdown");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
test_kqueue(void *unused)
|
||||
{
|
||||
int kqfd;
|
||||
|
||||
if ((kqfd = kqueue()) < 0)
|
||||
die("kqueue()");
|
||||
test_no_kevents(kqfd);
|
||||
if (close(kqfd) < 0)
|
||||
die("close()");
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent(void *unused)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
|
||||
/* Provide an invalid kqueue descriptor */
|
||||
if (kevent(-1, &kev, 1, NULL, 0, NULL) == 0)
|
||||
die("invalid kq parameter");
|
||||
}
|
||||
|
||||
void
|
||||
test_ev_receipt(void *unused)
|
||||
{
|
||||
int kq;
|
||||
struct kevent kev;
|
||||
|
||||
if ((kq = kqueue()) < 0)
|
||||
die("kqueue()");
|
||||
#if defined(EV_RECEIPT) && !defined(_WIN32)
|
||||
|
||||
EV_SET(&kev, SIGUSR2, EVFILT_SIGNAL, EV_ADD | EV_RECEIPT, 0, 0, NULL);
|
||||
if (kevent(kq, &kev, 1, &kev, 1, NULL) < 0)
|
||||
die("kevent");
|
||||
|
||||
/* TODO: check the receipt */
|
||||
|
||||
close(kq);
|
||||
#else
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
puts("Skipped -- EV_RECEIPT is not available or running on Win32");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
run_iteration(struct test_context *ctx)
|
||||
{
|
||||
struct unit_test *test;
|
||||
|
||||
for (test = &ctx->tests[0]; test->ut_name != NULL; test++) {
|
||||
if (test->ut_enabled)
|
||||
test->ut_func(ctx);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
test_harness(struct unit_test tests[MAX_TESTS], int iterations)
|
||||
{
|
||||
int i, n, kqfd;
|
||||
struct test_context *ctx;
|
||||
|
||||
printf("Running %d iterations\n", iterations);
|
||||
|
||||
testing_begin();
|
||||
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
|
||||
test(peer_close_detection, ctx);
|
||||
|
||||
test(kqueue, ctx);
|
||||
test(kevent, ctx);
|
||||
|
||||
if ((kqfd = kqueue()) < 0)
|
||||
die("kqueue()");
|
||||
|
||||
test(ev_receipt, ctx);
|
||||
/* TODO: this fails now, but would be good later
|
||||
test(kqueue_descriptor_is_pollable);
|
||||
*/
|
||||
|
||||
free(ctx);
|
||||
|
||||
n = 0;
|
||||
for (i = 0; i < iterations; i++) {
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
if (ctx == NULL)
|
||||
abort();
|
||||
ctx->iteration = n++;
|
||||
ctx->kqfd = kqfd;
|
||||
memcpy(&ctx->tests, tests, sizeof(ctx->tests));
|
||||
ctx->iterations = iterations;
|
||||
|
||||
run_iteration(ctx);
|
||||
}
|
||||
testing_end();
|
||||
|
||||
close(kqfd);
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
printf("usage:\n"
|
||||
" -h This message\n"
|
||||
" -n Number of iterations (default: 1)\n"
|
||||
"\n\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct unit_test tests[MAX_TESTS] = {
|
||||
{ "socket", 1, test_evfilt_read },
|
||||
#if !defined(_WIN32) && !defined(__ANDROID__)
|
||||
// XXX-FIXME -- BROKEN ON LINUX WHEN RUN IN A SEPARATE THREAD
|
||||
{ "signal", 1, test_evfilt_signal },
|
||||
#endif
|
||||
#if FIXME
|
||||
{ "proc", 1, test_evfilt_proc },
|
||||
#endif
|
||||
{ "timer", 1, test_evfilt_timer },
|
||||
#ifndef _WIN32
|
||||
{ "vnode", 1, test_evfilt_vnode },
|
||||
#endif
|
||||
#ifdef EVFILT_USER
|
||||
{ "user", 1, test_evfilt_user },
|
||||
#endif
|
||||
{ NULL, 0, NULL },
|
||||
};
|
||||
struct unit_test *test;
|
||||
int c, i, iterations;
|
||||
char *arg;
|
||||
int match;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Initialize the Winsock library */
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
|
||||
err(1, "WSAStartup failed");
|
||||
#endif
|
||||
|
||||
iterations = 1;
|
||||
|
||||
/* Windows does not provide a POSIX-compatible getopt */
|
||||
#ifndef _WIN32
|
||||
while ((c = getopt (argc, argv, "hn:")) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage();
|
||||
break;
|
||||
case 'n':
|
||||
iterations = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
/* If specific tests are requested, disable all tests by default */
|
||||
if (optind < argc) {
|
||||
for (test = &tests[0]; test->ut_name != NULL; test++) {
|
||||
test->ut_enabled = 0;
|
||||
}
|
||||
}
|
||||
for (i = optind; i < argc; i++) {
|
||||
match = 0;
|
||||
arg = argv[i];
|
||||
for (test = &tests[0]; test->ut_name != NULL; test++) {
|
||||
if (strcmp(arg, test->ut_name) == 0) {
|
||||
test->ut_enabled = 1;
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
printf("ERROR: invalid option: %s\n", arg);
|
||||
exit(1);
|
||||
} else {
|
||||
printf("enabled test: %s\n", arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
test_harness(tests, iterations);
|
||||
|
||||
return (0);
|
||||
}
|
||||
Vendored
+229
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
static int sigusr1_caught = 0;
|
||||
static pid_t pid;
|
||||
static int kqfd;
|
||||
|
||||
static void
|
||||
sig_handler(int signum)
|
||||
{
|
||||
sigusr1_caught = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_proc_add(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
test_no_kevents(kqfd);
|
||||
kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
|
||||
test_no_kevents(kqfd);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_proc_delete(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
test_no_kevents(kqfd);
|
||||
kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
|
||||
if (kill(pid, SIGKILL) < 0)
|
||||
die("kill");
|
||||
sleep(1);
|
||||
test_no_kevents(kqfd);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_proc_get(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, buf;
|
||||
|
||||
/* Create a child that waits to be killed and then exits */
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
pause();
|
||||
printf(" -- child caught signal, exiting\n");
|
||||
exit(2);
|
||||
}
|
||||
printf(" -- child created (pid %d)\n", (int) pid);
|
||||
|
||||
test_no_kevents(kqfd);
|
||||
kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
|
||||
|
||||
/* Cause the child to exit, then retrieve the event */
|
||||
printf(" -- killing process %d\n", (int) pid);
|
||||
if (kill(pid, SIGUSR1) < 0)
|
||||
die("kill");
|
||||
kevent_get(&buf, kqfd);
|
||||
kevent_cmp(&kev, &buf);
|
||||
test_no_kevents(kqfd);
|
||||
}
|
||||
|
||||
#ifdef TODO
|
||||
void
|
||||
test_kevent_signal_disable(struct test_context *ctx)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("%s", test_id);
|
||||
|
||||
/* Block SIGUSR1, then send it to ourselves */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
die("sigprocmask");
|
||||
if (kill(getpid(), SIGKILL) < 0)
|
||||
die("kill");
|
||||
|
||||
test_no_kevents();
|
||||
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_enable(struct test_context *ctx)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("%s", test_id);
|
||||
|
||||
/* Block SIGUSR1, then send it to ourselves */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
die("sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
kev.flags = EV_ADD | EV_CLEAR;
|
||||
#if LIBKQUEUE
|
||||
kev.data = 1; /* WORKAROUND */
|
||||
#else
|
||||
kev.data = 2; // one extra time from test_kevent_signal_disable()
|
||||
#endif
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
|
||||
/* Delete the watch */
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("%s", test_id);
|
||||
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_del(struct test_context *ctx)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
/* Delete the kevent */
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("%s", test_id);
|
||||
|
||||
/* Block SIGUSR1, then send it to ourselves */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
die("sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
test_no_kevents();
|
||||
success();
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_oneshot(struct test_context *ctx)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("%s", test_id);
|
||||
|
||||
/* Block SIGUSR1, then send it to ourselves */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
die("sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
kev.flags |= EV_CLEAR;
|
||||
kev.data = 1;
|
||||
kevent_cmp(&kev, kevent_get(kqfd));
|
||||
|
||||
/* Send another one and make sure we get no events */
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
test_no_kevents();
|
||||
|
||||
success();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
test_evfilt_proc(struct test_context *ctx)
|
||||
{
|
||||
signal(SIGUSR1, sig_handler);
|
||||
|
||||
/* Create a child that waits to be killed and then exits */
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
pause();
|
||||
exit(2);
|
||||
}
|
||||
printf(" -- child created (pid %d)\n", (int) pid);
|
||||
|
||||
test(kevent_proc_add, ctx);
|
||||
test(kevent_proc_delete, ctx);
|
||||
test(kevent_proc_get, ctx);
|
||||
|
||||
signal(SIGUSR1, SIG_DFL);
|
||||
|
||||
#if TODO
|
||||
test_kevent_signal_add();
|
||||
test_kevent_signal_del();
|
||||
test_kevent_signal_get();
|
||||
test_kevent_signal_disable();
|
||||
test_kevent_signal_enable();
|
||||
test_kevent_signal_oneshot();
|
||||
#endif
|
||||
}
|
||||
Vendored
+442
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
|
||||
/*
|
||||
* Create a connected TCP socket.
|
||||
*/
|
||||
static void
|
||||
create_socket_connection(int *client, int *server, const short port)
|
||||
{
|
||||
struct sockaddr_in sain;
|
||||
socklen_t sa_len = sizeof(sain);
|
||||
int one = 1;
|
||||
int clnt, srvr, accepted;
|
||||
|
||||
/* Create a passive socket */
|
||||
memset(&sain, 0, sizeof(sain));
|
||||
sain.sin_family = AF_INET;
|
||||
sain.sin_port = htons(port);
|
||||
if ((srvr = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err(1, "socket");
|
||||
if (setsockopt(srvr, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &one, sizeof(one)) != 0)
|
||||
err(1, "setsockopt");
|
||||
if (bind(srvr, (struct sockaddr *) &sain, sa_len) < 0) {
|
||||
printf("unable to bind to port %d\n", port);
|
||||
err(1, "bind-1");
|
||||
}
|
||||
if (listen(srvr, 100) < 0)
|
||||
err(1, "listen");
|
||||
|
||||
/* Simulate a client connecting to the server */
|
||||
sain.sin_family = AF_INET;
|
||||
sain.sin_port = htons(port);
|
||||
sain.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
if ((clnt = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err(1, "clnt: socket");
|
||||
if (connect(clnt, (struct sockaddr *) &sain, sa_len) < 0)
|
||||
err(1, "clnt: connect");
|
||||
if ((accepted = accept(srvr, NULL, 0)) < 0)
|
||||
err(1, "srvr: accept");
|
||||
|
||||
*client = clnt;
|
||||
*server = accepted;
|
||||
}
|
||||
|
||||
static void
|
||||
kevent_socket_drain(struct test_context *ctx)
|
||||
{
|
||||
char buf[1];
|
||||
|
||||
/* Drain the read buffer, then make sure there are no more events. */
|
||||
if (recv(ctx->client_fd, &buf[0], 1, 0) < 1)
|
||||
die("recv(2)");
|
||||
}
|
||||
|
||||
static void
|
||||
kevent_socket_fill(struct test_context *ctx)
|
||||
{
|
||||
if (send(ctx->server_fd, ".", 1, 0) < 1)
|
||||
die("send(2)");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
test_kevent_socket_add(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, ctx->client_fd, EVFILT_READ, EV_ADD, 0, 0, &ctx->client_fd);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_add_without_ev_add(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
/* Try to add a kevent without specifying EV_ADD */
|
||||
EV_SET(&kev, ctx->client_fd, EVFILT_READ, 0, 0, 0, &ctx->client_fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) == 0)
|
||||
die("kevent should have failed");
|
||||
|
||||
kevent_socket_fill(ctx);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
kevent_socket_drain(ctx);
|
||||
|
||||
/* Try to delete a kevent which does not exist */
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) == 0)
|
||||
die("kevent should have failed");
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_get(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
EV_SET(&kev, ctx->client_fd, EVFILT_READ, EV_ADD, 0, 0, &ctx->client_fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent:1");
|
||||
|
||||
kevent_socket_fill(ctx);
|
||||
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
kevent_socket_drain(ctx);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent:2");
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_clear(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
kevent_socket_drain(ctx);
|
||||
|
||||
EV_SET(&kev, ctx->client_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, &ctx->client_fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent1");
|
||||
|
||||
kevent_socket_fill(ctx);
|
||||
kevent_socket_fill(ctx);
|
||||
|
||||
/* Solaris does not offer a way to get the amount of data pending */
|
||||
#if defined(__sun__)
|
||||
kev.data = 1;
|
||||
#else
|
||||
kev.data = 2;
|
||||
#endif
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* We filled twice, but drain once. Edge-triggered would not generate
|
||||
additional events.
|
||||
*/
|
||||
kevent_socket_drain(ctx);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_socket_drain(ctx);
|
||||
EV_SET(&kev, ctx->client_fd, EVFILT_READ, EV_DELETE, 0, 0, &ctx->client_fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent2");
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_disable_and_enable(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
/* Add an event, then disable it. */
|
||||
EV_SET(&kev, ctx->client_fd, EVFILT_READ, EV_ADD, 0, 0, &ctx->client_fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent");
|
||||
EV_SET(&kev, ctx->client_fd, EVFILT_READ, EV_DISABLE, 0, 0, &ctx->client_fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent");
|
||||
|
||||
kevent_socket_fill(ctx);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Re-enable the knote, then see if an event is generated */
|
||||
kev.flags = EV_ENABLE;
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent");
|
||||
kev.flags = EV_ADD;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
kevent_socket_drain(ctx);
|
||||
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent");
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_del(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
EV_SET(&kev, ctx->client_fd, EVFILT_READ, EV_DELETE, 0, 0, &ctx->client_fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent");
|
||||
|
||||
kevent_socket_fill(ctx);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
kevent_socket_drain(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_oneshot(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
/* Re-add the watch and make sure no events are pending */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->client_fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &ctx->client_fd);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_socket_fill(ctx);
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Verify that the kernel watch has been deleted */
|
||||
kevent_socket_fill(ctx);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
kevent_socket_drain(ctx);
|
||||
|
||||
/* Verify that the kevent structure does not exist. */
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) == 0)
|
||||
die("kevent() should have failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if the data field returns 1 when a listen(2) socket has
|
||||
* a pending connection.
|
||||
*/
|
||||
void
|
||||
test_kevent_socket_listen_backlog(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
struct sockaddr_in sain;
|
||||
socklen_t sa_len = sizeof(sain);
|
||||
int one = 1;
|
||||
short port;
|
||||
int clnt, srvr;
|
||||
|
||||
port = 14973 + ctx->iteration;
|
||||
|
||||
/* Create a passive socket */
|
||||
memset(&sain, 0, sizeof(sain));
|
||||
sain.sin_family = AF_INET;
|
||||
sain.sin_port = htons(port);
|
||||
if ((srvr = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err(1, "socket()");
|
||||
if (setsockopt(srvr, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &one, sizeof(one)) != 0)
|
||||
err(1, "setsockopt()");
|
||||
if (bind(srvr, (struct sockaddr *) &sain, sa_len) < 0)
|
||||
err(1, "bind-2", port);
|
||||
if (listen(srvr, 100) < 0)
|
||||
err(1, "listen()");
|
||||
|
||||
/* Watch for events on the socket */
|
||||
test_no_kevents(ctx->kqfd);
|
||||
kevent_add(ctx->kqfd, &kev, srvr, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, NULL);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Simulate a client connecting to the server */
|
||||
sain.sin_family = AF_INET;
|
||||
sain.sin_port = htons(port);
|
||||
sain.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
if ((clnt = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
err(1, "socket()");
|
||||
if (connect(clnt, (struct sockaddr *) &sain, sa_len) < 0)
|
||||
err(1, "connect()");
|
||||
|
||||
/* Verify that data=1 */
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
#ifdef EV_DISPATCH
|
||||
void
|
||||
test_kevent_socket_dispatch(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
/* Re-add the watch and make sure no events are pending */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->client_fd, EVFILT_READ, EV_ADD | EV_DISPATCH, 0, 0, &ctx->client_fd);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* The event will occur only once, even though EV_CLEAR is not
|
||||
specified. */
|
||||
kevent_socket_fill(ctx);
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Re-enable the kevent */
|
||||
/* FIXME- is EV_DISPATCH needed when rearming ? */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->client_fd, EVFILT_READ, EV_ENABLE | EV_DISPATCH, 0, 0, &ctx->client_fd);
|
||||
kev.data = 1;
|
||||
kev.flags = EV_ADD | EV_DISPATCH; /* FIXME: may not be portable */
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Since the knote is disabled, the EV_DELETE operation succeeds. */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->client_fd, EVFILT_READ, EV_DELETE, 0, 0, &ctx->client_fd);
|
||||
|
||||
kevent_socket_drain(ctx);
|
||||
}
|
||||
#endif /* EV_DISPATCH */
|
||||
|
||||
#if BROKEN_ON_LINUX
|
||||
void
|
||||
test_kevent_socket_lowat(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
/* Re-add the watch and make sure no events are pending */
|
||||
puts("-- re-adding knote, setting low watermark to 2 bytes");
|
||||
EV_SET(&kev, ctx->client_fd, EVFILT_READ, EV_ADD | EV_ONESHOT, NOTE_LOWAT, 2, &ctx->client_fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("%s", test_id);
|
||||
test_no_kevents();
|
||||
|
||||
puts("-- checking that one byte does not trigger an event..");
|
||||
kevent_socket_fill(ctx);
|
||||
test_no_kevents();
|
||||
|
||||
puts("-- checking that two bytes triggers an event..");
|
||||
kevent_socket_fill(ctx);
|
||||
if (kevent(ctx->kqfd, NULL, 0, &kev, 1, NULL) != 1)
|
||||
die("%s", test_id);
|
||||
KEV_CMP(kev, ctx->client_fd, EVFILT_READ, 0);
|
||||
test_no_kevents();
|
||||
|
||||
kevent_socket_drain(ctx);
|
||||
kevent_socket_drain(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
test_kevent_socket_eof(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
/* Re-add the watch and make sure no events are pending */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->client_fd, EVFILT_READ, EV_ADD, 0, 0, &ctx->client_fd);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
//if (shutdown(ctx->server_fd, SHUT_RDWR) < 0)
|
||||
// die("close(2)");
|
||||
if (close(ctx->server_fd) < 0)
|
||||
die("close(2)");
|
||||
|
||||
kev.flags |= EV_EOF;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Delete the watch */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->client_fd, EVFILT_READ, EV_DELETE, 0, 0, &ctx->client_fd);
|
||||
}
|
||||
|
||||
/* Test if EVFILT_READ works with regular files */
|
||||
void
|
||||
test_kevent_regular_file(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
off_t curpos;
|
||||
int fd;
|
||||
|
||||
fd = open("/etc/hosts", O_RDONLY);
|
||||
if (fd < 0)
|
||||
abort();
|
||||
|
||||
EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, &fd);
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent");
|
||||
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
|
||||
/* Set file position to EOF-1 */
|
||||
ret.data--;
|
||||
if ((curpos = lseek(fd, ret.data, SEEK_SET)) != ret.data) {
|
||||
printf("seek to %u failed with rv=%lu\n",
|
||||
(unsigned int) ret.data, curpos);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Set file position to EOF */
|
||||
kevent_get(NULL, ctx->kqfd);
|
||||
ret.data = curpos + 1;
|
||||
if ((curpos = lseek(fd, ret.data, SEEK_SET)) != ret.data) {
|
||||
printf("seek to %u failed with rv=%lu\n",
|
||||
(unsigned int) ret.data, curpos);
|
||||
abort();
|
||||
}
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent");
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void
|
||||
test_evfilt_read(struct test_context *ctx)
|
||||
{
|
||||
create_socket_connection(&ctx->client_fd, &ctx->server_fd, ctx->iteration + 23456);
|
||||
|
||||
test(kevent_socket_add, ctx);
|
||||
test(kevent_socket_del, ctx);
|
||||
test(kevent_socket_add_without_ev_add, ctx);
|
||||
test(kevent_socket_get, ctx);
|
||||
test(kevent_socket_disable_and_enable, ctx);
|
||||
test(kevent_socket_oneshot, ctx);
|
||||
test(kevent_socket_clear, ctx);
|
||||
#ifdef EV_DISPATCH
|
||||
test(kevent_socket_dispatch, ctx);
|
||||
#endif
|
||||
test(kevent_socket_listen_backlog, ctx);
|
||||
test(kevent_socket_eof, ctx);
|
||||
test(kevent_regular_file, ctx);
|
||||
close(ctx->client_fd);
|
||||
close(ctx->server_fd);
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
void
|
||||
test_kevent_signal_add(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_get(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
|
||||
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
kev.flags |= EV_CLEAR;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_disable(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);
|
||||
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_enable(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);
|
||||
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
kev.flags = EV_ADD | EV_CLEAR;
|
||||
#if LIBKQUEUE
|
||||
kev.data = 1; /* WORKAROUND */
|
||||
#else
|
||||
kev.data = 2; // one extra time from test_kevent_signal_disable()
|
||||
#endif
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Delete the watch */
|
||||
kev.flags = EV_DELETE;
|
||||
if (kevent(ctx->kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
die("kevent");
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_del(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
/* Delete the kevent */
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
|
||||
|
||||
signal(SIGUSR1, SIG_IGN);
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_oneshot(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);
|
||||
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
kev.flags |= EV_CLEAR;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Send another one and make sure we get no events */
|
||||
test_no_kevents(ctx->kqfd);
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_modify(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, ((void *)-1));
|
||||
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
|
||||
kev.flags |= EV_CLEAR;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
test_kevent_signal_del(ctx);
|
||||
}
|
||||
|
||||
#ifdef EV_DISPATCH
|
||||
void
|
||||
test_kevent_signal_dispatch(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_CLEAR | EV_DISPATCH, 0, 0, NULL);
|
||||
|
||||
/* Get one event */
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Confirm that the knote is disabled */
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Enable the knote and make sure no events are pending */
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE | EV_DISPATCH, 0, 0, NULL);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Get the next event */
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
kev.flags = EV_ADD | EV_CLEAR | EV_DISPATCH;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Remove the knote and ensure the event no longer fires */
|
||||
kevent_add(ctx->kqfd, &kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
die("kill");
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
#endif /* EV_DISPATCH */
|
||||
|
||||
void
|
||||
test_evfilt_signal(struct test_context *ctx)
|
||||
{
|
||||
signal(SIGUSR1, SIG_IGN);
|
||||
|
||||
test(kevent_signal_add, ctx);
|
||||
test(kevent_signal_del, ctx);
|
||||
test(kevent_signal_get, ctx);
|
||||
test(kevent_signal_disable, ctx);
|
||||
test(kevent_signal_enable, ctx);
|
||||
test(kevent_signal_oneshot, ctx);
|
||||
test(kevent_signal_modify, ctx);
|
||||
#ifdef EV_DISPATCH
|
||||
test(kevent_signal_dispatch, ctx);
|
||||
#endif
|
||||
}
|
||||
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static int testnum = 1;
|
||||
static int error_flag = 1;
|
||||
|
||||
/* FIXME: not portable beyond linux */
|
||||
#ifndef _WIN32
|
||||
static void
|
||||
error_handler(int signum)
|
||||
{
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
void *buf[32];
|
||||
|
||||
/* FIXME: the symbols aren't printing */
|
||||
printf("***** ERROR: Program received signal %d *****\n", signum);
|
||||
backtrace_symbols_fd(buf, sizeof(buf) / sizeof(void *), 2);
|
||||
#else
|
||||
printf("***** ERROR: Program received signal %d *****\n", signum);
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
#endif /* ! _WIN32 */
|
||||
|
||||
static void
|
||||
testing_atexit(void)
|
||||
{
|
||||
if (error_flag) {
|
||||
printf(" *** TEST FAILED ***\n");
|
||||
//TODO: print detailed log
|
||||
} else {
|
||||
printf("\n---\n"
|
||||
"+OK All %d tests completed.\n", testnum - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_begin(struct test_context *ctx, const char *func)
|
||||
{
|
||||
if (ctx->cur_test_id)
|
||||
free(ctx->cur_test_id);
|
||||
ctx->cur_test_id = strdup(func);
|
||||
|
||||
printf("%d: %s\n", testnum++, ctx->cur_test_id);
|
||||
//TODO: redirect stdout/err to logfile
|
||||
}
|
||||
|
||||
void
|
||||
test_end(struct test_context *ctx)
|
||||
{
|
||||
free(ctx->cur_test_id);
|
||||
ctx->cur_test_id = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
testing_begin(void)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
struct sigaction sa;
|
||||
|
||||
/* Install a signal handler for crashes and hangs */
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = error_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
sigaction(SIGABRT, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
#endif
|
||||
|
||||
atexit(testing_atexit);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
testing_end(void)
|
||||
{
|
||||
error_flag = 0;
|
||||
}
|
||||
|
||||
/* Generate a unique ID */
|
||||
int
|
||||
testing_make_uid(void)
|
||||
{
|
||||
static int id = 0;
|
||||
|
||||
if (id == INT_MAX)
|
||||
abort();
|
||||
id++;
|
||||
|
||||
return (id);
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
void
|
||||
test_kevent_timer_add(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_timer_del(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_timer_get(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
|
||||
|
||||
kev.flags |= EV_CLEAR;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_timer_oneshot(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 2, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL);
|
||||
|
||||
/* Retrieve the event */
|
||||
kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Check if the event occurs again */
|
||||
sleep(3);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_timer_periodic(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 3, EVFILT_TIMER, EV_ADD, 0, 1000,NULL);
|
||||
|
||||
/* Retrieve the event */
|
||||
kev.flags = EV_ADD | EV_CLEAR;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Check if the event occurs again */
|
||||
sleep(1);
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Delete the event */
|
||||
kev.flags = EV_DELETE;
|
||||
kevent_update(ctx->kqfd, &kev);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_timer_disable_and_enable(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Add the watch and immediately disable it */
|
||||
kevent_add(ctx->kqfd, &kev, 4, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,NULL);
|
||||
kev.flags = EV_DISABLE;
|
||||
kevent_update(ctx->kqfd, &kev);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Re-enable and check again */
|
||||
kev.flags = EV_ENABLE;
|
||||
kevent_update(ctx->kqfd, &kev);
|
||||
|
||||
kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
}
|
||||
|
||||
#ifdef EV_DISPATCH
|
||||
void
|
||||
test_kevent_timer_dispatch(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 4, EVFILT_TIMER, EV_ADD | EV_DISPATCH, 0, 800, NULL);
|
||||
|
||||
/* Get one event */
|
||||
kev.flags = EV_ADD | EV_CLEAR | EV_DISPATCH;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Confirm that the knote is disabled */
|
||||
sleep(1);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Enable the knote and make sure no events are pending */
|
||||
kevent_add(ctx->kqfd, &kev, 4, EVFILT_TIMER, EV_ENABLE | EV_DISPATCH, 0, 800, NULL);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Get the next event */
|
||||
sleep(1);
|
||||
kev.flags = EV_ADD | EV_CLEAR | EV_DISPATCH;
|
||||
kev.data = 1;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Remove the knote and ensure the event no longer fires */
|
||||
kevent_add(ctx->kqfd, &kev, 4, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
|
||||
sleep(1);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
#endif /* EV_DISPATCH */
|
||||
|
||||
void
|
||||
test_evfilt_timer(struct test_context *ctx)
|
||||
{
|
||||
test(kevent_timer_add, ctx);
|
||||
test(kevent_timer_del, ctx);
|
||||
test(kevent_timer_get, ctx);
|
||||
test(kevent_timer_oneshot, ctx);
|
||||
test(kevent_timer_periodic, ctx);
|
||||
test(kevent_timer_disable_and_enable, ctx);
|
||||
#ifdef EV_DISPATCH
|
||||
test(kevent_timer_dispatch, ctx);
|
||||
#endif
|
||||
}
|
||||
Vendored
+170
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
static void
|
||||
test_kevent_user_add_and_delete(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_user_get(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Add the event, and then trigger it */
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL);
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
|
||||
|
||||
kev.fflags &= ~NOTE_FFCTRLMASK;
|
||||
kev.fflags &= ~NOTE_TRIGGER;
|
||||
kev.flags = EV_CLEAR;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_user_get_hires(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Add the event, and then trigger it */
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL);
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
|
||||
|
||||
kev.fflags &= ~NOTE_FFCTRLMASK;
|
||||
kev.fflags &= ~NOTE_TRIGGER;
|
||||
kev.flags = EV_CLEAR;
|
||||
kevent_get_hires(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_user_disable_and_enable(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_DISABLE, 0, 0, NULL);
|
||||
|
||||
/* Trigger the event, but since it is disabled, nothing will happen. */
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_ENABLE, 0, 0, NULL);
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
|
||||
|
||||
kev.flags = EV_CLEAR;
|
||||
kev.fflags &= ~NOTE_FFCTRLMASK;
|
||||
kev.fflags &= ~NOTE_TRIGGER;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kevent_user_oneshot(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, 2, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL);
|
||||
|
||||
puts(" -- event 1");
|
||||
kevent_add(ctx->kqfd, &kev, 2, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
|
||||
|
||||
kev.flags = EV_ONESHOT;
|
||||
kev.fflags &= ~NOTE_FFCTRLMASK;
|
||||
kev.fflags &= ~NOTE_TRIGGER;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
#ifdef EV_DISPATCH
|
||||
void
|
||||
test_kevent_user_dispatch(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Add the event, and then trigger it */
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR | EV_DISPATCH, 0, 0, NULL);
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
|
||||
|
||||
/* Retrieve one event */
|
||||
kev.fflags &= ~NOTE_FFCTRLMASK;
|
||||
kev.fflags &= ~NOTE_TRIGGER;
|
||||
kev.flags = EV_CLEAR;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
|
||||
/* Confirm that the knote is disabled automatically */
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Re-enable the kevent */
|
||||
/* FIXME- is EV_DISPATCH needed when rearming ? */
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_ENABLE | EV_CLEAR | EV_DISPATCH, 0, 0, NULL);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Trigger the event */
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
|
||||
kev.fflags &= ~NOTE_FFCTRLMASK;
|
||||
kev.fflags &= ~NOTE_TRIGGER;
|
||||
kev.flags = EV_CLEAR;
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Delete the watch */
|
||||
kevent_add(ctx->kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
#endif /* EV_DISPATCH */
|
||||
|
||||
void
|
||||
test_evfilt_user(struct test_context *ctx)
|
||||
{
|
||||
test(kevent_user_add_and_delete, ctx);
|
||||
test(kevent_user_get, ctx);
|
||||
test(kevent_user_get_hires, ctx);
|
||||
test(kevent_user_disable_and_enable, ctx);
|
||||
test(kevent_user_oneshot, ctx);
|
||||
#ifdef EV_DISPATCH
|
||||
test(kevent_user_dispatch, ctx);
|
||||
#endif
|
||||
/* TODO: try different fflags operations */
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
/* Create an empty file */
|
||||
static void
|
||||
testfile_create(const char *path)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if ((fd = open(path, O_CREAT | O_WRONLY, 0600)) < 0)
|
||||
die("open");
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
testfile_touch(const char *path)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(&buf[0], sizeof(buf), "touch %s", path);
|
||||
if (system(buf) != 0)
|
||||
die("system");
|
||||
}
|
||||
|
||||
static void
|
||||
testfile_write(const char *path)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(&buf[0], sizeof(buf), "echo hi >> %s", path);
|
||||
if (system(buf) != 0)
|
||||
die("system");
|
||||
}
|
||||
|
||||
static void
|
||||
testfile_rename(const char *path, int step)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(&buf[0], sizeof(buf), "%s.tmp", path);
|
||||
/* XXX-FIXME use of 'step' conceals a major memory corruption
|
||||
when the file is renamed twice.
|
||||
To replicate, remove "if step" conditional so
|
||||
two renames occur in this function.
|
||||
*/
|
||||
if (step == 0) {
|
||||
if (rename(path, buf) != 0)
|
||||
err(1,"rename");
|
||||
} else {
|
||||
if (rename(buf, path) != 0)
|
||||
err(1,"rename");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_add(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
testfile_create(ctx->testfile);
|
||||
|
||||
ctx->vnode_fd = open(ctx->testfile, O_RDWR);
|
||||
if (ctx->vnode_fd < 0)
|
||||
err(1, "open of %s", ctx->testfile);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_ADD,
|
||||
NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE, 0, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_note_delete(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL);
|
||||
|
||||
if (unlink(ctx->testfile) < 0)
|
||||
die("unlink");
|
||||
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_note_write(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_WRITE, 0, NULL);
|
||||
|
||||
testfile_write(ctx->testfile);
|
||||
|
||||
/* BSD kqueue adds NOTE_EXTEND even though it was not requested */
|
||||
/* BSD kqueue removes EV_ENABLE */
|
||||
kev.flags &= ~EV_ENABLE; // XXX-FIXME compatibility issue
|
||||
kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_note_attrib(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
int nfds;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
|
||||
|
||||
testfile_touch(ctx->testfile);
|
||||
|
||||
nfds = kevent(ctx->kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
die("kevent");
|
||||
if (kev.ident != ctx->vnode_fd ||
|
||||
kev.filter != EVFILT_VNODE ||
|
||||
kev.fflags != NOTE_ATTRIB)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_note_rename(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
int nfds;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_RENAME, 0, NULL);
|
||||
|
||||
testfile_rename(ctx->testfile, 0);
|
||||
|
||||
nfds = kevent(ctx->kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
die("kevent");
|
||||
if (kev.ident != ctx->vnode_fd ||
|
||||
kev.filter != EVFILT_VNODE ||
|
||||
kev.fflags != NOTE_RENAME)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
|
||||
testfile_rename(ctx->testfile, 1);
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_del(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_disable_and_enable(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev;
|
||||
int nfds;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Add the watch and immediately disable it */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
|
||||
kev.flags = EV_DISABLE;
|
||||
kevent_update(ctx->kqfd, &kev);
|
||||
|
||||
/* Confirm that the watch is disabled */
|
||||
testfile_touch(ctx->testfile);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Re-enable and check again */
|
||||
kev.flags = EV_ENABLE;
|
||||
kevent_update(ctx->kqfd, &kev);
|
||||
testfile_touch(ctx->testfile);
|
||||
nfds = kevent(ctx->kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
die("kevent");
|
||||
if (kev.ident != ctx->vnode_fd ||
|
||||
kev.filter != EVFILT_VNODE ||
|
||||
kev.fflags != NOTE_ATTRIB)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
}
|
||||
|
||||
#ifdef EV_DISPATCH
|
||||
void
|
||||
test_kevent_vnode_dispatch(struct test_context *ctx)
|
||||
{
|
||||
struct kevent kev, ret;
|
||||
int nfds;
|
||||
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_ADD | EV_DISPATCH, NOTE_ATTRIB, 0, NULL);
|
||||
|
||||
testfile_touch(ctx->testfile);
|
||||
|
||||
nfds = kevent(ctx->kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
die("kevent");
|
||||
if (kev.ident != ctx->vnode_fd ||
|
||||
kev.filter != EVFILT_VNODE ||
|
||||
kev.fflags != NOTE_ATTRIB)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
|
||||
/* Confirm that the watch is disabled automatically */
|
||||
testfile_touch(ctx->testfile);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Re-enable the kevent */
|
||||
/* FIXME- is EV_DISPATCH needed when rearming ? */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_ENABLE | EV_DISPATCH, 0, 0, NULL);
|
||||
kev.flags = EV_ADD | EV_DISPATCH; /* FIXME: may not be portable */
|
||||
kev.fflags = NOTE_ATTRIB;
|
||||
testfile_touch(ctx->testfile);
|
||||
kevent_get(&ret, ctx->kqfd);
|
||||
kevent_cmp(&kev, &ret);
|
||||
test_no_kevents(ctx->kqfd);
|
||||
|
||||
/* Delete the watch */
|
||||
kevent_add(ctx->kqfd, &kev, ctx->vnode_fd, EVFILT_VNODE, EV_DELETE, NOTE_ATTRIB, 0, NULL);
|
||||
}
|
||||
#endif /* EV_DISPATCH */
|
||||
|
||||
void
|
||||
test_evfilt_vnode(struct test_context *ctx)
|
||||
{
|
||||
#if (defined(__sun) && !defined(HAVE_PORT_SOURCE_FILE))
|
||||
puts("**NOTE** EVFILT_VNODE is not supported on this version of Solaris");
|
||||
return;
|
||||
#endif
|
||||
|
||||
char *tmpdir = getenv("TMPDIR");
|
||||
if (tmpdir == NULL)
|
||||
#ifdef __ANDROID__
|
||||
tmpdir = "/data/local/tmp";
|
||||
#else
|
||||
tmpdir = "/tmp";
|
||||
#endif
|
||||
|
||||
snprintf(ctx->testfile, sizeof(ctx->testfile), "%s/kqueue-test%d.tmp",
|
||||
tmpdir, testing_make_uid());
|
||||
|
||||
test(kevent_vnode_add, ctx);
|
||||
test(kevent_vnode_del, ctx);
|
||||
test(kevent_vnode_disable_and_enable, ctx);
|
||||
#ifdef EV_DISPATCH
|
||||
test(kevent_vnode_dispatch, ctx);
|
||||
#endif
|
||||
test(kevent_vnode_note_write, ctx);
|
||||
test(kevent_vnode_note_attrib, ctx);
|
||||
test(kevent_vnode_note_rename, ctx);
|
||||
test(kevent_vnode_note_delete, ctx);
|
||||
/* TODO: test r590 corner case where a descriptor is closed and
|
||||
the associated knote is automatically freed. */
|
||||
unlink(ctx->testfile);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
libpthread_workqueue-0.8.2
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
Version 0.8.2 r195
|
||||
released 7/16/2011
|
||||
---
|
||||
|
||||
* Use LDADD instead of LDFLAGS (fixes Debian bug #631674)
|
||||
* Make the "idle" test optional as it does not work on 32-bit Linux
|
||||
* Use time_t for PWQ_SPIN_USEC to fix a build problem on 32-bit Linux
|
||||
|
||||
Version 0.8.1 r?
|
||||
released 7/16/2011
|
||||
---
|
||||
|
||||
* Uploaded to Debian, but not generally released.
|
||||
|
||||
Version 0.8 r190
|
||||
released 7/08/2011
|
||||
---
|
||||
|
||||
* Remove the 'struct worker' datatype and related housekeeping chores.
|
||||
|
||||
* Fix incorrect usage of pthread_cond_timedwait() for overcommit threads.
|
||||
|
||||
* Various improvements and bug fixes for the Windows port.
|
||||
|
||||
* Prevent a race condition that could cause a use-after-free if a witem
|
||||
were freed before manager_workqueue_additem() returned.
|
||||
|
||||
* Prevent races involving the scoreboard variables.
|
||||
|
||||
* Fix a lost wakeup bug when calling worker_stop().
|
||||
|
||||
* Finally fixed the long standing TODO and removed the global lock for pwq enqueue/dequeue.
|
||||
|
||||
* Only signal wakeups for the pool if there are idle threads available to process data.
|
||||
|
||||
* Use atomics for updating the mask with pending workqueues as the global lock was removed.
|
||||
|
||||
* Added optional idle thread spinning by using the PWQ_SPIN_USEC and PWQ_SPIN_THREADS environment variables. The accrued changes decrease latency from 7-8 microseconds to ~1 +- 0.5 microsecond depending on spin configuration. By default, no spinning will be done.
|
||||
|
||||
* Renamed USE_RT_THREADS to PWQ_RT_THREADS for consistency.
|
||||
|
||||
* Allow specification of number of CPU:s by using the environment variable PWQ_ACTIVE_CPU - this is useful when using e.g. processor sets (fewer CPU:s are truly available to the process than is physically available in the machine). Proper auto-detection of this would be even nicer in the future, but investigation for the various platforms is required - this environment variable allows for a simple workaround in the meantime.
|
||||
|
||||
Version 0.7.1 r157
|
||||
released 7/02/2011
|
||||
---
|
||||
|
||||
* Fix a memory leak in worker_overcommit_main() when reaping idle threads.
|
||||
|
||||
Version 0.7 r150
|
||||
released 6/13/2011
|
||||
---
|
||||
|
||||
* Replace pthread emulation macros with winpthreads.h from
|
||||
http://locklessinc.com/articles/pthreads_on_windows/
|
||||
|
||||
* Fix witem_cache test to link on solaris also
|
||||
|
||||
* Avoid possible overrun of priority level
|
||||
|
||||
* Fixed possible deadlock.
|
||||
|
||||
* Cleaned up witem cache interface and usage for easier reading of code.
|
||||
|
||||
* Link with libumem on Solaris
|
||||
|
||||
* Add -Wextra to CFLAGS and fix the related warnings
|
||||
|
||||
* Implement the workqueue overcommit attribute.
|
||||
Make wqlist an array instead of an array of lists.
|
||||
Change wqlist_scan() to be more efficient.
|
||||
|
||||
Version 0.6 r134
|
||||
released 5/16/2011
|
||||
---
|
||||
|
||||
* Add a pthread_atfork() handler to reinitialize the library after fork().
|
||||
|
||||
* Defer the manager thread creation until pthread_workqueue_create_np().
|
||||
|
||||
Version 0.5.1 r125
|
||||
released 5/7/2011
|
||||
---
|
||||
|
||||
* Fix the testing/latency Makefile to work on 32-bit Linux.
|
||||
* Remove unused variables from testing/latency.c
|
||||
|
||||
Version 0.5 r120
|
||||
released 5/6/2011
|
||||
---
|
||||
|
||||
* Add CMakeLists.txt for building under CMake.
|
||||
|
||||
* Support building on Windows under MinGW and MSVC.
|
||||
|
||||
* Fixed a deadlock during startup. We could actually raise and get a lost wakeup of the pthread_cond_wait in manager_init() as the manager already managed to signal before we went to sleep. (happened around 1/1000 of startups on a large multicore).
|
||||
|
||||
* Finetune ramp-up logic when system is under heavy load - allow up to worker_idle_threshold threads regardless of system load, otherwise limit thread creation when system is under NCPU:s load rather than 2*NCPU:s (it is way too late to limit it on a larger multicore machine...).
|
||||
|
||||
* Create a witem_cache_init() function so that the TLS key can be made private to witem_cache.o
|
||||
|
||||
* Fix compilation on 32-bit Linux (Credit: Marius Zwicker)
|
||||
|
||||
* Don't reset the signal mask, it should be blocked for the manager thread (and any subsequently started threads) as well
|
||||
|
||||
* Enabled experimental support for real-time threads scheduling class on Solaris, specify PWQ_RT_THREADS to enable it. Be careful when using, may take all available resources unless used in combination with processor sets, thus effectivively hanging the machine
|
||||
|
||||
* Add option for static library build activated by defining MAKE_STATIC
|
||||
|
||||
* Enable debugging on windows by an environment variable as well
|
||||
|
||||
Version 0.4.1 r99
|
||||
released 3/13/2011
|
||||
---
|
||||
|
||||
* Add -lpthread to LDFLAGS
|
||||
|
||||
Version 0.4 r97
|
||||
released 3/12/2011
|
||||
---
|
||||
|
||||
* Improved printf debugging; to use it, define the environment variable "PWQ_DEBUG=yes"
|
||||
|
||||
* New function threads_runnable() determines how many LWPs are on the run queue and uses
|
||||
this information to improve the thread pool management heuristic.
|
||||
|
||||
* All ELF symbols are now hidden by default, and only the public API symbols are visible.
|
||||
|
||||
* Improved workqueue ramp-up and ramp-down behavior.
|
||||
|
||||
Version 0.3 r81
|
||||
released 3/6/2011
|
||||
---
|
||||
|
||||
* Fix DESTDIR support in the 'make install' target.
|
||||
|
||||
Version 0.2 r77
|
||||
released 3/6/2011
|
||||
---
|
||||
|
||||
* Add support for FreeBSD, Solaris, and Microsoft Windows
|
||||
|
||||
* Fix a race condition that would cause deadlock in rare cases when either:
|
||||
1) pthread_cond_signal() was called while no other threads were
|
||||
blocked a call to pthread_cond_wait(), or
|
||||
2) pthread_cond_signal() was called multiple times before the any thread
|
||||
blocking in a call to pthread_cond_wait() was awoken by the scheduler.
|
||||
|
||||
The fix is to ensure that the call to pthread_cond_signal() occurs while
|
||||
the calling thread holds the same mutex used by the threads that call
|
||||
pthread_cond_wait().
|
||||
|
||||
Credit to Joakim Johansson for finding the bug and providing a patch.
|
||||
|
||||
Version 0.1 r?
|
||||
released 6/13/2010
|
||||
---
|
||||
|
||||
* Initial release for Debian as a patch applied to libdispatch. There
|
||||
was no tarball released for this version.
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
#
|
||||
# Copyright (c) 2010 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.
|
||||
#
|
||||
|
||||
# Flags to pass to dpkg-buildpackage
|
||||
DPKGFLAGS=-uc -us
|
||||
|
||||
.PHONY :: install uninstall check dist dist-upload publish-www clean merge distclean fresh-build rpm edit cscope valgrind testing
|
||||
|
||||
include config.mk
|
||||
|
||||
all: $(PROGRAM).so testing
|
||||
|
||||
%.dll: $(OBJS)
|
||||
$(LD) -o $@ $(LDFLAGS) $(OBJS) $(LDADD)
|
||||
|
||||
%.o: %.c $(DEPS)
|
||||
$(CC) -c -o $@ $(CFLAGS) $<
|
||||
|
||||
$(PROGRAM).a: $(OBJS)
|
||||
$(AR) rcs $(PROGRAM).a $(OBJS)
|
||||
|
||||
$(PROGRAM).so: $(OBJS)
|
||||
$(LD) -shared $(LDFLAGS) $(OBJS) $(LDADD)
|
||||
$(LN) -sf $(PROGRAM).so.$(ABI_VERSION) $(PROGRAM).so
|
||||
$(LN) -sf $(PROGRAM).so.$(ABI_VERSION) $(PROGRAM).so.$(ABI_MAJOR)
|
||||
|
||||
install: $(PROGRAM).so
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(INCLUDEDIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INCLUDEDIR)
|
||||
$(INSTALL) -m 644 $(PROGRAM).so.$(ABI_VERSION) $(DESTDIR)$(LIBDIR)
|
||||
$(LN) -sf $(PROGRAM).so.$(ABI_VERSION) $(DESTDIR)$(LIBDIR)/$(PROGRAM).so.$(ABI_MAJOR)
|
||||
$(LN) -sf $(PROGRAM).so.$(ABI_VERSION) $(DESTDIR)$(LIBDIR)/$(PROGRAM).so
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man3
|
||||
$(INSTALL) -m 644 pthread_workqueue.3 $(DESTDIR)$(MANDIR)/man3/pthread_workqueue.3
|
||||
|
||||
uninstall:
|
||||
rm -f $(INCLUDEDIR)/pthread_workqueue.h
|
||||
rm -f $(LIBDIR)/pthread_workqueue.so
|
||||
rm -f $(LIBDIR)/pthread_workqueue.so.*
|
||||
rm -f $(LIBDIR)/pthread_workqueue.a
|
||||
rm -f $(MANDIR)/man3/pthread_workqueue.3
|
||||
|
||||
reinstall: uninstall install
|
||||
|
||||
check: $(PROGRAM).so
|
||||
cd testing && make check
|
||||
|
||||
edit:
|
||||
$(EDITOR) `find ./src -name '*.c' -o -name '*.h'` Makefile
|
||||
|
||||
$(PROGRAM)-$(VERSION).tar.gz:
|
||||
mkdir $(PROGRAM)-$(VERSION)
|
||||
cp Makefile ChangeLog configure config.inc $(MANS) $(PROGRAM)-$(VERSION)
|
||||
cp -R src testing include $(PROGRAM)-$(VERSION)
|
||||
find $(PROGRAM)-$(VERSION) -name '.svn' -exec rm -rf {} \; 2>/dev/null || true
|
||||
tar zcf $(PROGRAM)-$(VERSION).tar.gz $(PROGRAM)-$(VERSION)
|
||||
rm -rf $(PROGRAM)-$(VERSION)
|
||||
|
||||
testing:
|
||||
cd testing && make
|
||||
|
||||
dist: clean $(PROGRAM)-$(VERSION).tar.gz
|
||||
|
||||
%.asc:
|
||||
gpg --armor --detach-sign `echo '$@' | sed 's/.asc$$//'`
|
||||
|
||||
dist-upload: dist $(DISTFILE).asc
|
||||
scp $(DISTFILE) $(DISTFILE).asc heily.com:/var/www/heily.com/dist/$(PROGRAM)
|
||||
|
||||
publish-www:
|
||||
cp -R www/* ~/public_html/libkqueue/
|
||||
|
||||
clean:
|
||||
rm -f tags $(DISTFILE) $(DISTFILE).asc *.a $(OBJS) *.pc *.so *.so.* test-$(PROGRAM)
|
||||
cd testing && make clean
|
||||
rm -rf pkg
|
||||
|
||||
distclean: clean
|
||||
rm -f *.tar.gz config.mk config.h $(PROGRAM).pc $(PROGRAM).la rpm.spec
|
||||
rm -rf $(PROGRAM)-$(VERSION) 2>/dev/null || true
|
||||
|
||||
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
|
||||
|
||||
deb: clean $(DISTFILE)
|
||||
mkdir pkg
|
||||
cd pkg && tar zxf ../$(DISTFILE) && mv libpthread_workqueue-$(VERSION) libpthread-workqueue-$(VERSION)
|
||||
cp $(DISTFILE) pkg/libpthread-workqueue_$(VERSION).orig.tar.gz
|
||||
cp -R ports/debian pkg/libpthread-workqueue-$(VERSION)
|
||||
cd pkg && \
|
||||
rm -rf `find libpthread-workqueue-$(VERSION)/debian -type d -name .svn` ; \
|
||||
perl -pi -e 's/\@\@VERSION\@\@/$(VERSION)/' libpthread-workqueue-$(VERSION)/debian/changelog ; \
|
||||
cd libpthread-workqueue-$(VERSION) && dpkg-buildpackage $(DPKGFLAGS)
|
||||
lintian -i pkg/*.deb
|
||||
@printf "\nThe following packages have been created:\n"
|
||||
@find ./pkg -name '*.deb' | sed 's/^/ /'
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
program="libpthread_workqueue"
|
||||
version="0.8.2"
|
||||
abi_major="0"
|
||||
abi_minor="0"
|
||||
abi_version="$abi_major.$abi_minor"
|
||||
cflags="-Wall -Wextra -Werror -D_XOPEN_SOURCE=600 -D__EXTENSIONS__ -D_GNU_SOURCE -std=c99 -I./include -I./src"
|
||||
ldflags=""
|
||||
ldadd="-lpthread -lrt"
|
||||
sources='src/api.c src/$(API)/manager.c src/$(API)/thread_info.c src/witem_cache.c src/$(API)/thread_rt.c'
|
||||
libdepends=""
|
||||
deps="src/*.h"
|
||||
mans="pthread_workqueue.3"
|
||||
headers="include/pthread_workqueue.h"
|
||||
extra_dist="LICENSE"
|
||||
subdirs=""
|
||||
|
||||
# Package metadata
|
||||
pkg_summary="pthread_workqueue library"
|
||||
pkg_description="pthread_workqueue library"
|
||||
license="BSD"
|
||||
author="Mark Heily"
|
||||
|
||||
pre_configure_hook() {
|
||||
if [ "$debug" = "yes" ] ; then
|
||||
cflags="$cflags -g3 -O0 -DPTHREAD_WORKQUEUE_DEBUG -rdynamic"
|
||||
else
|
||||
cflags="$cflags -g -O2"
|
||||
fi
|
||||
check_header err.h
|
||||
}
|
||||
|
||||
post_configure_hook() {
|
||||
|
||||
cflags="$cflags"
|
||||
case "$target" in
|
||||
windows)
|
||||
cflags="$cflags -mthreads"
|
||||
ldflags="$ldflags -mthreads"
|
||||
;;
|
||||
solaris)
|
||||
# TODO: would like to have -fvisibility=hidden but not supported
|
||||
# by SFWgcc
|
||||
#
|
||||
cflags="$cflags -m64 -fpic"
|
||||
ldflags="$ldflags -m64 -fpic -lumem"
|
||||
;;
|
||||
*)
|
||||
if [ "`uname -m`" = "x86_64" ] ; then
|
||||
arch_flags="-m64"
|
||||
else
|
||||
arch_flags=""
|
||||
fi
|
||||
cflags="$cflags $arch_flags -fpic -fvisibility=hidden -pthread"
|
||||
ldflags="$ldflags $arch_flags -fpic -pthread"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
+351
@@ -0,0 +1,351 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2011 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.
|
||||
#
|
||||
|
||||
makeconf_version="$Revision: 10 $"
|
||||
|
||||
c_exports="program version target api cflags"
|
||||
|
||||
make_exports="program version target api distfile basedir \
|
||||
prefix bindir sbindir libdir includedir mandir \
|
||||
cflags ldflags ldadd libdepends \
|
||||
sources objs deps mans headers extra_dist subdirs \
|
||||
abi_major abi_minor abi_version \
|
||||
cc cpp ld ln ar install diff"
|
||||
|
||||
required_headers=
|
||||
optional_headers=
|
||||
|
||||
pre_configure_hook() {
|
||||
return
|
||||
}
|
||||
|
||||
post_configure_hook() {
|
||||
return
|
||||
}
|
||||
|
||||
export_to_make() {
|
||||
for id in $*
|
||||
do
|
||||
uc_id=`echo $id | $tr '[:lower:]' '[:upper:]'`;
|
||||
eval "echo \"$uc_id=\"\$$id\"\" >> config.mk"
|
||||
done
|
||||
}
|
||||
|
||||
export_to_c() {
|
||||
for id in $*
|
||||
do
|
||||
uc_id=`echo $id | $tr '[:lower:]' '[:upper:]'`;
|
||||
eval "echo \"#define $uc_id \\\"\$$id\\\"\" >> config.h"
|
||||
done
|
||||
}
|
||||
|
||||
finalize() {
|
||||
uc_id=`echo \"$1\" | $tr '[:lower:]' '[:upper:]'`;
|
||||
eval "if [ \"\$$1\" = \"\" ] ; then $1=\"$2\" ; fi"
|
||||
}
|
||||
|
||||
process_argv() {
|
||||
for arg in $*
|
||||
do
|
||||
if [ "$arg" = "--makeconf-version" ] ; then
|
||||
echo $makeconf_version | sed 's/[^0-9.]//g'
|
||||
exit 0
|
||||
fi
|
||||
id=`echo "$arg" | sed 's/=.*//; s/^--//;'`
|
||||
val=`echo "$arg" | sed 's/^.*=//'`
|
||||
if [ "$val" = "" ] ; then val=1 ; fi
|
||||
eval "$id=\"$val\""
|
||||
done
|
||||
}
|
||||
|
||||
process_env() {
|
||||
test -n "$CC" && cc="$CC"
|
||||
test -n "$CPP" && cpp="$CPP"
|
||||
test -n "$CPPFLAGS" && cppflags="$CPPFLAGS"
|
||||
test -n "$CFLAGS" && cflags="$CFLAGS"
|
||||
test -n "$LD" && ld="$LD"
|
||||
test -n "$LN" && ld="$LN"
|
||||
test -n "$LDFLAGS" && ldflags="$LDFLAGS"
|
||||
test -n "$AR" && ar="$AR"
|
||||
basedir=`pwd`
|
||||
}
|
||||
|
||||
check_header() {
|
||||
sym=`echo "have_$1" | sed 's,[./],_,g'`
|
||||
uc_sym=`echo "$sym" | $tr '[:lower:]' '[:upper:]'`;
|
||||
path=$1
|
||||
|
||||
printf "checking for $path.. "
|
||||
if [ -f "/usr/include/$path" ] ; then
|
||||
echo "yes"
|
||||
echo "#define $uc_sym 1" >> config.h
|
||||
eval "$sym=yes"
|
||||
return 0
|
||||
else
|
||||
echo "no"
|
||||
echo "#undef $uc_sym" >> config.h
|
||||
eval "$sym=no"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Determine the path to an executable binary
|
||||
check_binary() {
|
||||
id=$1
|
||||
shift
|
||||
|
||||
for path in $*
|
||||
do
|
||||
test -f $path
|
||||
if [ $? = 0 ] ; then
|
||||
eval "$id=\"$path\""
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
echo "not found"
|
||||
return
|
||||
}
|
||||
|
||||
check_headers() {
|
||||
for header in $*
|
||||
do
|
||||
check_header "$header"
|
||||
done
|
||||
}
|
||||
|
||||
check_symbol() {
|
||||
header=$1
|
||||
symbol=$2
|
||||
|
||||
uc_symbol=`echo "HAVE_$symbol" | $tr '[:lower:]' '[:upper:]' | sed 's,[./],_,g'`
|
||||
lc_symbol=`echo "have_$symbol" | $tr '[:upper:]' '[:lower:]' | sed 's,[./],_,g'`
|
||||
|
||||
if [ -f "$header" ] ; then
|
||||
path="$header"
|
||||
elif [ -f "/usr/include/$header" ] ; then
|
||||
path="/usr/include/$header"
|
||||
else
|
||||
echo "*** ERROR: Cannot find <$header>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "checking $header for $symbol.. "
|
||||
if [ "`grep $symbol $path`" != "" ] ; then
|
||||
eval "$lc_symbol=yes"
|
||||
echo "#define $uc_symbol 1" >> config.h
|
||||
echo "yes"
|
||||
return 0
|
||||
else
|
||||
eval "$lc_symbol=no"
|
||||
echo "no"
|
||||
echo "#undef $uc_symbol" >> config.h
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_install() {
|
||||
printf "checking for a BSD-compatible install.. "
|
||||
if [ "`uname -s`" = "SunOS" ] ; then
|
||||
default_install=/usr/ucb/install
|
||||
else
|
||||
default_install=/usr/bin/install
|
||||
fi
|
||||
finalize install "$default_install"
|
||||
echo "$install"
|
||||
}
|
||||
|
||||
check_target() {
|
||||
printf "checking operating system type.. "
|
||||
default_target=`uname -s | $tr '[:upper:]' '[:lower:]'`
|
||||
default_api="posix"
|
||||
case "$default_target" in
|
||||
sunos)
|
||||
default_target="solaris"
|
||||
;;
|
||||
"gnu/kfreebsd")
|
||||
default_target="freebsd"
|
||||
;;
|
||||
mingw*)
|
||||
default_target="windows"
|
||||
default_api="windows"
|
||||
;;
|
||||
esac
|
||||
finalize target "$default_target"
|
||||
finalize api "$default_api"
|
||||
echo "$api"
|
||||
}
|
||||
|
||||
check_compiler() {
|
||||
printf "checking for a C compiler.. "
|
||||
check_binary default_cc "$cc" "`which $cc 2>/dev/null`" "/usr/bin/cc" "/usr/bin/gcc" "/usr/sfw/bin/gcc" "`which gcc 2>/dev/null`"
|
||||
finalize cc "$default_cc"
|
||||
# test -x "$cc" || err "Unable to locate a C compiler"
|
||||
echo "$cc"
|
||||
}
|
||||
|
||||
check_linker() {
|
||||
printf "checking for a suitable linker.. "
|
||||
|
||||
# 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="-o $program.so.$abi_major.$abi_minor $ldflags"
|
||||
|
||||
# FIXME: port to solaris
|
||||
if [ "$target" = "linux" ] ; then
|
||||
ldflags="$ldflags -Wl,-export-dynamic -Wl,-soname,$program.so.$abi_major"
|
||||
fi
|
||||
|
||||
if [ "$target" = "solaris" ] ; then
|
||||
ldflags="$ldflags"
|
||||
fi
|
||||
|
||||
finalize ld "$default_ld"
|
||||
echo "$ld"
|
||||
}
|
||||
|
||||
check_archiver() {
|
||||
printf "checking for a suitable archiver.. "
|
||||
if [ "`uname -s`" = "SunOS" -a "`uname -v | grep Nexenta`" = "" ] ; then
|
||||
default_ar="/usr/sfw/bin/gar"
|
||||
else
|
||||
default_ar="/usr/bin/ar"
|
||||
fi
|
||||
finalize ar "$default_ar"
|
||||
echo "$ar"
|
||||
}
|
||||
|
||||
err() {
|
||||
echo "*** ERROR *** $*"
|
||||
rm -f config.mk $program.pc config.h
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_diff() {
|
||||
# TODO: Support non-GNU diff syntax
|
||||
# TODO: Search for the command
|
||||
printf "checking for a suitable diff(1) command.. "
|
||||
finalize diff "diff -ruN -dEbwBp -x .svn -x .o -x config.h -x config.mk"
|
||||
echo "found"
|
||||
}
|
||||
|
||||
subst_vars() {
|
||||
outfile=$1
|
||||
|
||||
if [ ! -f "${outfile}.in" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Creating $outfile"
|
||||
rm -f $outfile
|
||||
sed -e "
|
||||
s,@@CWD@@,`pwd`,g;
|
||||
s,@@PROGRAM@@,$program,g;
|
||||
s,@@VERSION@@,$version,g;
|
||||
s,@@PREFIX@@,$prefix,g;
|
||||
s,@@LIBDIR@@,$libdir,g;
|
||||
s,@@INCLUDEDIR@@,$includedir,g;
|
||||
s,@@MANDIR@@,$mandir,g;
|
||||
s,@@LIBDEPENDS@@,$libdepends,g;
|
||||
s,@@PKG_SUMMARY@@,$pkg_summary,g;
|
||||
s,@@RPM_DATE@@,`date +'%a %b %d %Y'`,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
|
||||
do
|
||||
rm -f $output_file
|
||||
echo "# AUTOMATICALLY GENERATED -- DO NOT EDIT" > $output_file
|
||||
done
|
||||
if [ "$sources" != "" ] ; then
|
||||
rm -f config.h
|
||||
echo "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */" > config.h
|
||||
fi
|
||||
|
||||
process_argv "$*"
|
||||
process_env
|
||||
|
||||
check_target
|
||||
check_compiler
|
||||
check_linker
|
||||
check_archiver
|
||||
check_install
|
||||
check_diff
|
||||
|
||||
finalize program "$program"
|
||||
finalize version "$version"
|
||||
finalize abi_major "$abi_major"
|
||||
finalize abi_minor "$abi_minor"
|
||||
finalize abi_version "$abi_major.$abi_minor"
|
||||
finalize prefix "/usr/local"
|
||||
finalize bindir "\\\$(PREFIX)/bin"
|
||||
finalize sbindir "\\\$(PREFIX)/sbin"
|
||||
finalize libdir "\\\$(PREFIX)/lib"
|
||||
finalize includedir "\\\$(PREFIX)/include"
|
||||
finalize mandir "\\\$(PREFIX)/share/man"
|
||||
finalize cflags "$cflags"
|
||||
finalize libdepends "$libdepends"
|
||||
finalize ldadd ""
|
||||
finalize ldflags ""
|
||||
finalize deps ""
|
||||
finalize ln "`which ln`"
|
||||
finalize distfile "$program-$version.tar.gz"
|
||||
|
||||
pre_configure_hook
|
||||
|
||||
for header in $required_headers
|
||||
do
|
||||
check_header "$header" || err "$header is required, but cannot be found."
|
||||
done
|
||||
check_headers $optional_headers
|
||||
|
||||
post_configure_hook
|
||||
|
||||
objs="`echo \"$sources\" | sed 's/\.c/\.o/g'`"
|
||||
|
||||
subst_vars "$program.pc"
|
||||
subst_vars "$program.la"
|
||||
subst_vars "rpm.spec"
|
||||
|
||||
if [ "$sources" != "" ] ; then
|
||||
echo "Creating config.h"
|
||||
export_to_c $c_exports
|
||||
fi
|
||||
|
||||
echo "Creating config.mk"
|
||||
export_to_make "$make_exports"
|
||||
@@ -0,0 +1,100 @@
|
||||
/*-
|
||||
* Copyright (c) 2010, Mark Heily <mark@heily.com>
|
||||
* Copyright (c) 2009, Stacey Son <sson@freebsd.org>
|
||||
* Copyright (c) 2000-2008, Apple Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PTHREAD_WORKQUEUE_H
|
||||
#define _PTHREAD_WORKQUEUE_H
|
||||
|
||||
#if _WIN32
|
||||
#define _PWQ_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define _PWQ_EXPORT
|
||||
#endif
|
||||
|
||||
typedef struct _pthread_workqueue * pthread_workqueue_t;
|
||||
typedef void * pthread_workitem_handle_t;
|
||||
|
||||
/* Pad size to 64 bytes. */
|
||||
typedef struct {
|
||||
unsigned int sig;
|
||||
int queueprio;
|
||||
int overcommit;
|
||||
unsigned int pad[13];
|
||||
} pthread_workqueue_attr_t;
|
||||
|
||||
/* Work queue priority attributes. */
|
||||
#define WORKQ_HIGH_PRIOQUEUE 0
|
||||
#define WORKQ_DEFAULT_PRIOQUEUE 1
|
||||
#define WORKQ_LOW_PRIOQUEUE 2
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_create_np(pthread_workqueue_t * workqp,
|
||||
const pthread_workqueue_attr_t * attr);
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_additem_np(pthread_workqueue_t workq,
|
||||
void (*workitem_func)(void *), void * workitem_arg,
|
||||
pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_attr_init_np(pthread_workqueue_attr_t * attrp);
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_attr_destroy_np(pthread_workqueue_attr_t * attr);
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_attr_setqueuepriority_np(pthread_workqueue_attr_t * attr,
|
||||
int qprio);
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_attr_getovercommit_np(
|
||||
const pthread_workqueue_attr_t * attr, int * ocommp);
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_attr_setovercommit_np(pthread_workqueue_attr_t * attr,
|
||||
int ocomm);
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_requestconcurrency_np(pthread_workqueue_t workq,
|
||||
int queue, int request_concurrency);
|
||||
|
||||
int _PWQ_EXPORT pthread_workqueue_getovercommit_np(pthread_workqueue_t workq,
|
||||
unsigned int *ocommp);
|
||||
|
||||
void _PWQ_EXPORT pthread_workqueue_main_np(void);
|
||||
|
||||
#ifdef MAKE_STATIC
|
||||
int _PWQ_EXPORT pthread_workqueue_init_np(void);
|
||||
#endif
|
||||
|
||||
/* NOTE: this is not part of the Darwin API */
|
||||
unsigned long _PWQ_EXPORT pthread_workqueue_peek_np(const char *);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef _PWQ_EXPORT
|
||||
|
||||
#endif /* _PTHREAD_WORKQUEUE_H */
|
||||
@@ -0,0 +1,231 @@
|
||||
.\" Copyright (C) 2010 mark@heily.com
|
||||
.\" Copyright (C) 2009 sson@FreeBSD.org
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice(s), this list of conditions and the following disclaimer as
|
||||
.\" the first lines of this file unmodified other than the possible
|
||||
.\" addition of one or more copyright notices.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice(s), this list of conditions and the following disclaimer in
|
||||
.\" the documentation and/or other materials provided with the
|
||||
.\" distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
|
||||
.\" EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
|
||||
.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
.\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD: $
|
||||
.Dd December 12, 2009
|
||||
.Dt PTHREAD_WORKQUEUE 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pthread_workqueue_init_np ,
|
||||
.Nm pthread_workqueue_create_np ,
|
||||
.Nm pthread_workqueue_additem_np
|
||||
.Nd thread workqueue operations
|
||||
.Pp
|
||||
.Nm pthread_workqueue_attr_init_np ,
|
||||
.Nm pthread_workqueue_attr_destroy_np ,
|
||||
.Nm pthread_workqueue_attr_getovercommit_np ,
|
||||
.Nm pthread_workqueue_attr_setovercommit_np ,
|
||||
.Nm pthread_workqueue_attr_getqueuepriority_np ,
|
||||
.Nm pthread_workqueue_attr_setqueuepriority_np
|
||||
.Nd thread workqueue attribute operations
|
||||
.Sh SYNOPSIS
|
||||
.In pthread_workqueue.h
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_init_np "void"
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_create_np "pthread_workqueue_t *workqp" "const pthread_workqueue_attr_t * attr"
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_additem_np "pthread_workqueue_t workq" "void ( *workitem_func)(void *)" "void * workitem_arg" "pthread_workitem_handle_t * itemhandlep" "unsigned int *gencountp"
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_attr_init_np "pthread_workqueue_attr_t *attr"
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_attr_destroy_np "pthread_workqueue_attr_t *attr"
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_attr_getovercommit_np "pthread_workqueue_attr_t *attr" "int *ocommp"
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_attr_setovercommit_np "pthread_workqueue_attr_t *attr" "int ocomm"
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_attr_getqueuepriority_np "pthread_workqueue_attr_t *attr" "int *qpriop"
|
||||
.Ft int
|
||||
.Fn pthread_workqueue_attr_setqueuepriority_np "pthread_workqueue_attr_t *attr" "int qprio"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn pthread_workqueue_*_np
|
||||
functions are used to create and submit work items to a thread pool.
|
||||
.Pp
|
||||
The user may create multiple work queues of different priority and
|
||||
manually overcommit the available resources.
|
||||
.Pp
|
||||
.Fn pthread_workqueue_init_np
|
||||
allocates and initializes the thread workqueue subsystem.
|
||||
.Pp
|
||||
.Fn pthread_workqueue_create_np
|
||||
creates a new thread workqueue with the attributes given by
|
||||
.Fa attr .
|
||||
If
|
||||
.Fa attr
|
||||
is NULL then the default attributes are used.
|
||||
A workqueue handle is returned in the
|
||||
.Fa workqp
|
||||
parameter.
|
||||
.Pp
|
||||
Thread workqueue attributes are used to specify parameters to
|
||||
.Fn pthread_workqueue_create_np .
|
||||
One attribute object can be used in multiple calls to
|
||||
.Fn pthread_workqueue_create_np ,
|
||||
with or without modifications between calls.
|
||||
.Pp
|
||||
.Fn pthread_workqueue_additem_np
|
||||
is used to submit work items to the thread pool specified by
|
||||
.Fa workq
|
||||
parameter.
|
||||
The work item function and function argument are given by
|
||||
.Fa workitem_func
|
||||
and
|
||||
.Fa workitem_arg .
|
||||
The work item handle is returned in
|
||||
.Fa itemhandlep .
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_attr_init_np
|
||||
function initializes
|
||||
.Fa attr
|
||||
with all the default thread workqueue attributes.
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_attr_destroy_np
|
||||
function destroys
|
||||
.Fa attr .
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_attr_set*_np
|
||||
functions set the attribute that corresponds to each function name.
|
||||
.Fn pthread_workqueue_attr_setovercommit_np
|
||||
can be used to set the overcommit flag.
|
||||
If the overcommit flag is set then more threads will be started, if
|
||||
needed, which may overcommit the physical resources of the system.
|
||||
.Fn pthread_workqueue_attr_setqueuepriority_np
|
||||
sets the queue priority attribute of the thread work queue and must be
|
||||
set to one of the following values:
|
||||
.Bl -tag -width "Va WORKQ_DEFAULT_PRIOQUEUE"
|
||||
.It Va WORKQ_HIGH_PRIOQUEUE
|
||||
Work items in the queue with this attribute will be given higher priority by
|
||||
the thread scheduler.
|
||||
.It Va WORKQ_DEFAULT_PRIOQUEUE
|
||||
Work items in the queue with this attribute are given the default
|
||||
priority.
|
||||
.It Va WORKQ_LOW_PRIOQUEUE
|
||||
Work items in the queue with this attribute will be given lower priority
|
||||
by the thread scheduler.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_attr_get*_np
|
||||
functions copy the value of the attribute that corresponds to each function name
|
||||
to the location pointed to by the second function parameter.
|
||||
.Sh RETURN VALUES
|
||||
If successful, these functions return 0.
|
||||
Otherwise, an error number is returned to indicate the error.
|
||||
.Sh ERRORS
|
||||
The
|
||||
.Fn pthread_workqueue_init_np
|
||||
function will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ENOMEM
|
||||
Out of memory.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_create_np
|
||||
function will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ENOMEM
|
||||
Out of memory.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_additem_np
|
||||
function will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
Invalid workqueue handle.
|
||||
.It Bq Er ENOMEM
|
||||
Out of memory.
|
||||
.It Bq Er ESRCH
|
||||
Can not find workqueue.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_attr_init_np
|
||||
function will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ENOMEM
|
||||
Out of memory.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_attr_destroy_np
|
||||
function will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
Invalid value for
|
||||
.Fa attr .
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_attr_setqueuepriority_np
|
||||
function will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
Invalid value for
|
||||
.Fa attr
|
||||
or for
|
||||
.Fa qprio.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_workqueue_attr_setovercommit_np ,
|
||||
.Fn pthread_workqueue_attr_getovercommit_np
|
||||
and
|
||||
.Fn pthread_workqueue_attr_getqueuepriority_np
|
||||
functions will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
Invalid value for
|
||||
.Fa attr .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr pthread 3 ,
|
||||
.Xr sysctl 3
|
||||
.Sh BUGS
|
||||
There is no way, currently, to remove or destory work queues and pending
|
||||
work items other than exiting the process.
|
||||
.Pp
|
||||
All worker threads run at the same thread priority; however, items placed on high-priority workqueues will be executed before those on lower-priority workqueues.
|
||||
.Sh HISTORY
|
||||
This thread workqueues code was created to support Grand Central Dispatch (GCD
|
||||
or libdispatch) and first appeared in
|
||||
.Fx 8.0 .
|
||||
.Sh AUTHORS
|
||||
.An "Mark Heily" Aq mark@heily.com .
|
||||
.Br
|
||||
.Pp
|
||||
Based on earlier work by
|
||||
.An "Stacey Son" Aq sson@FreeBSD.org
|
||||
and
|
||||
.An Apple, Inc.
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
/*-
|
||||
* Copyright (c) 2010, Mark Heily <mark@heily.com>
|
||||
* Copyright (c) 2009, Stacey Son <sson@freebsd.org>
|
||||
* Copyright (c) 2000-2008, Apple Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "private.h"
|
||||
|
||||
unsigned int PWQ_ACTIVE_CPU = 0;
|
||||
int DEBUG_WORKQUEUE = 0;
|
||||
char *WORKQUEUE_DEBUG_IDENT = "WQ";
|
||||
|
||||
static int
|
||||
valid_workq(pthread_workqueue_t workq)
|
||||
{
|
||||
if (workq->sig == PTHREAD_WORKQUEUE_SIG)
|
||||
return (1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
int VISIBLE CONSTRUCTOR
|
||||
pthread_workqueue_init_np(void)
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
DEBUG_WORKQUEUE = 0;
|
||||
#else
|
||||
DEBUG_WORKQUEUE = (getenv("PWQ_DEBUG") == NULL) ? 0 : 1;
|
||||
# ifndef _WIN32
|
||||
PWQ_RT_THREADS = (getenv("PWQ_RT_THREADS") == NULL) ? 0 : 1;
|
||||
PWQ_ACTIVE_CPU = (getenv("PWQ_ACTIVE_CPU") == NULL) ? 0 : atoi(getenv("PWQ_ACTIVE_CPU"));
|
||||
|
||||
if (getenv("PWQ_SPIN_USEC") != NULL)
|
||||
PWQ_SPIN_USEC = atoi(getenv("PWQ_SPIN_USEC"));
|
||||
|
||||
if (getenv("PWQ_SPIN_THREADS") != NULL)
|
||||
PWQ_SPIN_THREADS = atoi(getenv("PWQ_SPIN_THREADS"));
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
if (manager_init() < 0)
|
||||
return (-1);
|
||||
|
||||
dbg_puts("pthread_workqueue library initialized");
|
||||
return (0);
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
pthread_workqueue_create_np(pthread_workqueue_t *workqp,
|
||||
const pthread_workqueue_attr_t * attr)
|
||||
{
|
||||
pthread_workqueue_t workq;
|
||||
|
||||
if ((attr != NULL) && ((attr->sig != PTHREAD_WORKQUEUE_ATTR_SIG) ||
|
||||
(attr->queueprio < 0) || (attr->queueprio >= WORKQ_NUM_PRIOQUEUE)))
|
||||
return (EINVAL);
|
||||
if ((workq = calloc(1, sizeof(*workq))) == NULL)
|
||||
return (ENOMEM);
|
||||
workq->sig = PTHREAD_WORKQUEUE_SIG;
|
||||
workq->flags = 0;
|
||||
STAILQ_INIT(&workq->item_listhead);
|
||||
pthread_spin_init(&workq->mtx, PTHREAD_PROCESS_PRIVATE);
|
||||
if (attr == NULL) {
|
||||
workq->queueprio = WORKQ_DEFAULT_PRIOQUEUE;
|
||||
workq->overcommit = 0;
|
||||
} else {
|
||||
workq->queueprio = attr->queueprio;
|
||||
workq->overcommit = attr->overcommit;
|
||||
}
|
||||
|
||||
manager_workqueue_create(workq);
|
||||
|
||||
dbg_printf("created queue %p", (void *) workq);
|
||||
|
||||
*workqp = workq;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
pthread_workqueue_additem_np(pthread_workqueue_t workq,
|
||||
void (*workitem_func)(void *), void * workitem_arg,
|
||||
pthread_workitem_handle_t * itemhandlep,
|
||||
unsigned int *gencountp)
|
||||
{
|
||||
struct work *witem;
|
||||
|
||||
if (valid_workq(workq) == 0)
|
||||
return (EINVAL);
|
||||
|
||||
witem = witem_alloc(workitem_func, workitem_arg);
|
||||
|
||||
if (itemhandlep != NULL)
|
||||
*itemhandlep = (pthread_workitem_handle_t *) witem;
|
||||
if (gencountp != NULL)
|
||||
*gencountp = witem->gencount;
|
||||
|
||||
manager_workqueue_additem(workq, witem);
|
||||
|
||||
dbg_printf("added item %p to queue %p", (void *) witem, (void *) workq);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
pthread_workqueue_attr_init_np(pthread_workqueue_attr_t *attr)
|
||||
{
|
||||
attr->queueprio = WORKQ_DEFAULT_PRIOQUEUE;
|
||||
attr->sig = PTHREAD_WORKQUEUE_ATTR_SIG;
|
||||
attr->overcommit = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
pthread_workqueue_attr_destroy_np(pthread_workqueue_attr_t *attr)
|
||||
{
|
||||
if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG)
|
||||
return (0);
|
||||
else
|
||||
return (EINVAL); /* Not an attribute struct. */
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
pthread_workqueue_attr_getovercommit_np(
|
||||
const pthread_workqueue_attr_t *attr, int *ocommp)
|
||||
{
|
||||
if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) {
|
||||
*ocommp = attr->overcommit;
|
||||
return (0);
|
||||
} else
|
||||
return (EINVAL); /* Not an attribute struct. */
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
pthread_workqueue_attr_setovercommit_np(pthread_workqueue_attr_t *attr,
|
||||
int ocomm)
|
||||
{
|
||||
if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) {
|
||||
attr->overcommit = ocomm;
|
||||
return (0);
|
||||
} else
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
pthread_workqueue_attr_getqueuepriority_np(
|
||||
pthread_workqueue_attr_t *attr, int *qpriop)
|
||||
{
|
||||
if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) {
|
||||
*qpriop = attr->queueprio;
|
||||
return (0);
|
||||
} else
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
int VISIBLE
|
||||
pthread_workqueue_attr_setqueuepriority_np(
|
||||
pthread_workqueue_attr_t *attr, int qprio)
|
||||
{
|
||||
if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) {
|
||||
switch(qprio) {
|
||||
case WORKQ_HIGH_PRIOQUEUE:
|
||||
case WORKQ_DEFAULT_PRIOQUEUE:
|
||||
case WORKQ_LOW_PRIOQUEUE:
|
||||
attr->queueprio = qprio;
|
||||
return (0);
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
} else
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
unsigned long VISIBLE
|
||||
pthread_workqueue_peek_np(const char *key)
|
||||
{
|
||||
return manager_peek(key);
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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.
|
||||
*/
|
||||
|
||||
#ifndef _DEBUG_H
|
||||
#define _DEBUG_H
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
extern int DEBUG_WORKQUEUE;
|
||||
extern char *WORKQUEUE_DEBUG_IDENT;
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <linux/unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
# define THREAD_ID ((pid_t) syscall(__NR_gettid))
|
||||
#elif defined(__sun)
|
||||
# define THREAD_ID (pthread_self())
|
||||
#elif defined(__FreeBSD__) /* FIXME -- could use thr_self() */
|
||||
# define THREAD_ID (0)
|
||||
#elif defined(_WIN32)
|
||||
# define THREAD_ID (int)(GetCurrentThreadId())
|
||||
#else
|
||||
# error Unsupported platform
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define dbg_puts(str) do { \
|
||||
if (DEBUG_WORKQUEUE) \
|
||||
fprintf(stderr, "%s [%d]: %s(): %s\n", \
|
||||
WORKQUEUE_DEBUG_IDENT, THREAD_ID, __func__, str); \
|
||||
} while (0)
|
||||
|
||||
#define dbg_printf(fmt,...) do { \
|
||||
if (DEBUG_WORKQUEUE) \
|
||||
fprintf(stderr, "%s [%d]: %s(): "fmt"\n", \
|
||||
WORKQUEUE_DEBUG_IDENT, THREAD_ID, __func__, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define dbg_perror(str) do { \
|
||||
if (DEBUG_WORKQUEUE) \
|
||||
fprintf(stderr, "%s [%d]: %s(): %s: %s (errno=%d)\n", \
|
||||
WORKQUEUE_DEBUG_IDENT, THREAD_ID, __func__, str, \
|
||||
strerror(errno), errno); \
|
||||
} while (0)
|
||||
|
||||
# define reset_errno() do { errno = 0; } while (0)
|
||||
|
||||
# if defined(_WIN32)
|
||||
# define dbg_lasterror(str) do { \
|
||||
if (DEBUG_WORKQUEUE) \
|
||||
fprintf(stderr, "%s: [%d] %s(): %s: (LastError=%d)\n", \
|
||||
THREAD_ID, __func__, str, GetLastError()); \
|
||||
} while (0)
|
||||
# else
|
||||
# define dbg_lasterror(str) ;
|
||||
# endif
|
||||
|
||||
#else /* NDEBUG */
|
||||
# define dbg_puts(str) ;
|
||||
# define dbg_printf(fmt,...) ;
|
||||
# define dbg_perror(str) ;
|
||||
# define dbg_lasterror(str) ;
|
||||
# define reset_errno() ;
|
||||
#endif
|
||||
|
||||
#endif /* ! _DEBUG_H */
|
||||
+738
@@ -0,0 +1,738 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Joakim Johansson <jocke@tbricks.com>
|
||||
* Copyright (c) 2010, Mark Heily <mark@heily.com>
|
||||
* Copyright (c) 2009, Stacey Son <sson@freebsd.org>
|
||||
* Copyright (c) 2000-2008, Apple Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "private.h"
|
||||
#include "pthread_workqueue.h"
|
||||
#include "thread_info.h"
|
||||
#include "thread_rt.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
/* Environment setting */
|
||||
unsigned int PWQ_RT_THREADS = 0;
|
||||
time_t PWQ_SPIN_USEC = 10000; // The number of microseconds we should spin loop if desired
|
||||
unsigned int PWQ_SPIN_THREADS = 0; // The number of threads that should be kept spinning
|
||||
unsigned volatile int current_threads_spinning = 0; // The number of threads currently spinning
|
||||
|
||||
/* Tunable constants */
|
||||
|
||||
#define WORKER_IDLE_SECONDS_THRESHOLD 5
|
||||
|
||||
/* Function prototypes */
|
||||
static unsigned int get_load_average(void);
|
||||
static void * worker_main(void *arg);
|
||||
static void * overcommit_worker_main(void *arg);
|
||||
static unsigned int get_process_limit(void);
|
||||
static void manager_start(void);
|
||||
|
||||
static unsigned int cpu_count;
|
||||
static unsigned int worker_min;
|
||||
static unsigned int worker_idle_threshold; // we don't go down below this if we had to increase # workers
|
||||
|
||||
/* Overcommit */
|
||||
static struct _pthread_workqueue *ocwq[PTHREAD_WORKQUEUE_MAX];
|
||||
static int ocwq_mask;
|
||||
static pthread_mutex_t ocwq_mtx;
|
||||
static pthread_cond_t ocwq_has_work;
|
||||
static unsigned int ocwq_idle_threads;
|
||||
|
||||
/* Non-overcommit */
|
||||
static struct _pthread_workqueue *wqlist[PTHREAD_WORKQUEUE_MAX];
|
||||
static volatile unsigned int wqlist_mask; // mask of currently pending workqueues, atomics used for manipulation
|
||||
static pthread_mutex_t wqlist_mtx;
|
||||
|
||||
static pthread_cond_t wqlist_has_work;
|
||||
static int wqlist_has_manager;
|
||||
static pthread_attr_t detached_attr;
|
||||
|
||||
static struct {
|
||||
volatile unsigned int load,
|
||||
count,
|
||||
idle;
|
||||
unsigned int sb_wake_pending;
|
||||
pthread_mutex_t sb_wake_mtx;
|
||||
pthread_cond_t sb_wake_cond;
|
||||
} scoreboard;
|
||||
|
||||
static unsigned int
|
||||
worker_idle_threshold_per_cpu(void)
|
||||
{
|
||||
switch (cpu_count)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
return 2;
|
||||
case 6:
|
||||
return 3;
|
||||
case 8:
|
||||
case 12:
|
||||
return 4;
|
||||
case 16:
|
||||
case 24:
|
||||
return 6;
|
||||
case 32:
|
||||
case 64:
|
||||
return 8;
|
||||
default:
|
||||
return cpu_count / 4;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void
|
||||
manager_reinit(void)
|
||||
{
|
||||
if (manager_init() < 0)
|
||||
abort();
|
||||
}
|
||||
|
||||
int
|
||||
manager_init(void)
|
||||
{
|
||||
wqlist_has_manager = 0;
|
||||
pthread_cond_init(&wqlist_has_work, NULL);
|
||||
|
||||
pthread_mutex_init(&wqlist_mtx, NULL);
|
||||
wqlist_mask = 0;
|
||||
|
||||
pthread_cond_init(&ocwq_has_work, NULL);
|
||||
pthread_mutex_init(&ocwq_mtx, NULL);
|
||||
ocwq_mask = 0;
|
||||
ocwq_idle_threads = 0;
|
||||
|
||||
witem_cache_init();
|
||||
|
||||
cpu_count = (PWQ_ACTIVE_CPU > 0) ? (PWQ_ACTIVE_CPU) : (unsigned int) sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
pthread_attr_init(&detached_attr);
|
||||
pthread_attr_setdetachstate(&detached_attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
/* Initialize the scoreboard */
|
||||
pthread_cond_init(&scoreboard.sb_wake_cond, NULL);
|
||||
pthread_mutex_init(&scoreboard.sb_wake_mtx, NULL);
|
||||
|
||||
/* Determine the initial thread pool constraints */
|
||||
worker_min = 2; // we can start with a small amount, worker_idle_threshold will be used as new dynamic low watermark
|
||||
worker_idle_threshold = worker_idle_threshold_per_cpu();
|
||||
|
||||
if (pthread_atfork(NULL, NULL, manager_reinit) < 0) {
|
||||
dbg_perror("pthread_atfork()");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
manager_workqueue_create(struct _pthread_workqueue *workq)
|
||||
{
|
||||
pthread_mutex_lock(&wqlist_mtx);
|
||||
if (!workq->overcommit && !wqlist_has_manager)
|
||||
manager_start();
|
||||
|
||||
if (workq->overcommit) {
|
||||
if (ocwq[workq->queueprio] == NULL) {
|
||||
ocwq[workq->queueprio] = workq;
|
||||
workq->wqlist_index = workq->queueprio;
|
||||
} else {
|
||||
puts("queue already exists\n");
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
if (wqlist[workq->queueprio] == NULL) {
|
||||
wqlist[workq->queueprio] = workq; //FIXME: sort by priority
|
||||
workq->wqlist_index = workq->queueprio;
|
||||
} else {
|
||||
puts("queue already exists\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&wqlist_mtx);
|
||||
}
|
||||
|
||||
static struct work *
|
||||
wqlist_scan(int *queue_priority)
|
||||
{
|
||||
pthread_workqueue_t workq;
|
||||
struct work *witem;
|
||||
int idx;
|
||||
|
||||
idx = ffs(wqlist_mask);
|
||||
if (idx == 0)
|
||||
return (NULL);
|
||||
|
||||
workq = wqlist[idx - 1];
|
||||
|
||||
pthread_spin_lock(&workq->mtx);
|
||||
|
||||
witem = STAILQ_FIRST(&workq->item_listhead);
|
||||
if (witem != NULL) {
|
||||
STAILQ_REMOVE_HEAD(&workq->item_listhead, item_entry);
|
||||
if (STAILQ_EMPTY(&workq->item_listhead))
|
||||
{
|
||||
unsigned int wqlist_index_bit = (0x1 << workq->wqlist_index);
|
||||
unsigned int new_mask;
|
||||
// Remove this now empty wq from the mask, the only contention here is with threads performing the same
|
||||
// operation on another workqueue, so we will not be long
|
||||
// the 'bit' for this queue is protected by the spin lock, so we will only clear a bit which we have
|
||||
// ownership for (see additem() below for the corresponding part on the producer side)
|
||||
do
|
||||
{
|
||||
new_mask = atomic_and(&wqlist_mask, ~(wqlist_index_bit));
|
||||
} while (new_mask & wqlist_index_bit);
|
||||
}
|
||||
if (queue_priority != NULL)
|
||||
*queue_priority = workq->queueprio;
|
||||
|
||||
pthread_spin_unlock(&workq->mtx);
|
||||
return (witem);
|
||||
} else {
|
||||
// this could happen if multiple threads raced and found the same bit with ffs() and
|
||||
// emptied the queue completely, so we should just bail out
|
||||
pthread_spin_unlock(&workq->mtx);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void _wakeup_manager(void)
|
||||
{
|
||||
dbg_puts("asking manager to wake up");
|
||||
|
||||
pthread_mutex_lock(&scoreboard.sb_wake_mtx);
|
||||
scoreboard.sb_wake_pending = 1;
|
||||
pthread_cond_signal(&scoreboard.sb_wake_cond);
|
||||
pthread_mutex_unlock(&scoreboard.sb_wake_mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
static void *
|
||||
overcommit_worker_main(void *arg)
|
||||
{
|
||||
struct timespec ts;
|
||||
pthread_workqueue_t workq;
|
||||
void (*func)(void *);
|
||||
void *func_arg;
|
||||
struct work *witem;
|
||||
int rv, idx;
|
||||
|
||||
(void)arg;
|
||||
|
||||
pthread_mutex_lock(&ocwq_mtx);
|
||||
|
||||
for (;;) {
|
||||
/* Find the highest priority workqueue that is non-empty */
|
||||
idx = ffs(ocwq_mask);
|
||||
if (idx > 0) {
|
||||
workq = ocwq[idx - 1];
|
||||
witem = STAILQ_FIRST(&workq->item_listhead);
|
||||
if (witem != NULL) {
|
||||
/* Remove the first work item */
|
||||
STAILQ_REMOVE_HEAD(&workq->item_listhead, item_entry);
|
||||
if (STAILQ_EMPTY(&workq->item_listhead))
|
||||
ocwq_mask &= ~(0x1 << workq->wqlist_index);
|
||||
/* Execute the work item */
|
||||
pthread_mutex_unlock(&ocwq_mtx);
|
||||
func = witem->func;
|
||||
func_arg = witem->func_arg;
|
||||
witem_free(witem);
|
||||
func(func_arg);
|
||||
pthread_mutex_lock(&ocwq_mtx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for more work to be available. */
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += 15;
|
||||
ocwq_idle_threads++;
|
||||
dbg_printf("waiting for work (idle=%d)", ocwq_idle_threads);
|
||||
rv = pthread_cond_timedwait(&ocwq_has_work, &ocwq_mtx, &ts);
|
||||
if (rv != 0) {
|
||||
/* Normally, the signaler will decrement the idle counter,
|
||||
but this path is not taken in response to a signaler.
|
||||
*/
|
||||
ocwq_idle_threads--;
|
||||
pthread_mutex_unlock(&ocwq_mtx);
|
||||
|
||||
if (rv == ETIMEDOUT) {
|
||||
dbg_puts("timeout, no work available");
|
||||
break;
|
||||
} else {
|
||||
dbg_perror("pthread_cond_timedwait");
|
||||
//TODO: some kind of crash mechanism
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbg_printf("worker exiting (idle=%d)", ocwq_idle_threads);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void *
|
||||
worker_main(void *arg)
|
||||
{
|
||||
struct work *witem;
|
||||
void (*func)(void *);
|
||||
void *func_arg;
|
||||
int queue_priority = 0;
|
||||
struct timespec ts_start, ts_now;
|
||||
|
||||
(void) arg;
|
||||
dbg_puts("worker thread started");
|
||||
|
||||
if (PWQ_RT_THREADS)
|
||||
ptwq_set_current_thread_priority(WORKQ_HIGH_PRIOQUEUE); // start at highest priority possible
|
||||
|
||||
for (;;) {
|
||||
|
||||
witem = wqlist_scan(&queue_priority);
|
||||
|
||||
// Only take overhead of sleeping and/or spinning if we
|
||||
// could not get a witem cheaply using the spinlock above
|
||||
if (slowpath(!witem))
|
||||
{
|
||||
// Optional busy loop for getting the next item for a while if so configured
|
||||
// We'll only spin limited thread at a time (this is really mostly useful when running
|
||||
// in low latency configurations using dedicated processor sets)
|
||||
if ((PWQ_SPIN_THREADS > 0) && (current_threads_spinning <= PWQ_SPIN_THREADS))
|
||||
{
|
||||
atomic_inc(¤t_threads_spinning);
|
||||
|
||||
// If we are racing with another thread, let's skip
|
||||
// spinning and instead go through the slowpath below
|
||||
|
||||
if (current_threads_spinning <= PWQ_SPIN_THREADS)
|
||||
{
|
||||
clock_gettime(CLOCK_REALTIME, &ts_start);
|
||||
ts_now.tv_sec = ts_start.tv_sec;
|
||||
ts_now.tv_nsec = ts_start.tv_nsec;
|
||||
|
||||
// Spin until we get an item or PWQ_SPIN_USEC microseconds passes
|
||||
while (!witem && (((ts_now.tv_sec - ts_start.tv_sec) * 1000000) + (((ts_now.tv_nsec - ts_start.tv_nsec) / 1000)) <= PWQ_SPIN_USEC))
|
||||
{
|
||||
witem = wqlist_scan(&queue_priority);
|
||||
if (!witem)
|
||||
{
|
||||
// Perhaps a hardware pause
|
||||
// instruction could be used here to keep the pace down, probably not needed though
|
||||
clock_gettime(CLOCK_REALTIME, &ts_now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
atomic_dec(¤t_threads_spinning);
|
||||
}
|
||||
|
||||
// No witem from the busy loop, let's wait for wakeup
|
||||
if (!witem)
|
||||
{
|
||||
pthread_mutex_lock(&wqlist_mtx);
|
||||
|
||||
/*
|
||||
TODO: Consider using pthread_cond_timedwait() so that
|
||||
workers can self-terminate if they are idle too long.
|
||||
This would also be a failsafe in case there are bugs
|
||||
with the scoreboard that cause us to "leak" workers.
|
||||
*/
|
||||
while ((witem = wqlist_scan(&queue_priority)) == NULL)
|
||||
pthread_cond_wait(&wqlist_has_work, &wqlist_mtx);
|
||||
|
||||
pthread_mutex_unlock(&wqlist_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
atomic_dec(&scoreboard.idle);
|
||||
|
||||
if (slowpath(witem->func == NULL)) {
|
||||
dbg_puts("worker exiting..");
|
||||
atomic_dec(&scoreboard.count);
|
||||
witem_free(witem);
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
dbg_printf("count=%u idle=%u wake_pending=%u",
|
||||
scoreboard.count, scoreboard.idle, scoreboard.sb_wake_pending);
|
||||
|
||||
/* Force the manager thread to wakeup if all workers are busy */
|
||||
if (slowpath(scoreboard.idle == 0 && !scoreboard.sb_wake_pending))
|
||||
_wakeup_manager();
|
||||
|
||||
// If using RT threads, decrease thread prio if we aren't a high prio queue
|
||||
if (PWQ_RT_THREADS && (queue_priority != WORKQ_HIGH_PRIOQUEUE))
|
||||
ptwq_set_current_thread_priority(queue_priority);
|
||||
|
||||
/* Invoke the callback function, free witem first for possible reuse */
|
||||
func = witem->func;
|
||||
func_arg = witem->func_arg;
|
||||
witem_free(witem);
|
||||
|
||||
func(func_arg);
|
||||
|
||||
atomic_inc(&scoreboard.idle); // initial inc was one in worker_start, this is to avoid a race
|
||||
|
||||
// Only take the overhead and change RT priority back if it was not a high priority queue being serviced
|
||||
if (PWQ_RT_THREADS && (queue_priority != WORKQ_HIGH_PRIOQUEUE))
|
||||
ptwq_set_current_thread_priority(WORKQ_HIGH_PRIOQUEUE);
|
||||
}
|
||||
|
||||
/* NOTREACHED */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
worker_start(void)
|
||||
{
|
||||
pthread_t tid;
|
||||
|
||||
dbg_puts("Spawning another worker");
|
||||
|
||||
atomic_inc(&scoreboard.idle);
|
||||
atomic_inc(&scoreboard.count);
|
||||
|
||||
if (pthread_create(&tid, &detached_attr, worker_main, NULL) != 0) {
|
||||
dbg_perror("pthread_create(3)");
|
||||
atomic_dec(&scoreboard.idle);
|
||||
atomic_dec(&scoreboard.count);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
worker_stop(void)
|
||||
{
|
||||
struct work *witem;
|
||||
pthread_workqueue_t workq;
|
||||
int i;
|
||||
unsigned int wqlist_index_bit, new_mask;
|
||||
|
||||
witem = witem_alloc(NULL, NULL);
|
||||
|
||||
pthread_mutex_lock(&wqlist_mtx);
|
||||
for (i = 0; i < PTHREAD_WORKQUEUE_MAX; i++) {
|
||||
workq = wqlist[i];
|
||||
if (workq == NULL)
|
||||
continue;
|
||||
|
||||
wqlist_index_bit = (0x1 << workq->wqlist_index);
|
||||
|
||||
pthread_spin_lock(&workq->mtx);
|
||||
|
||||
do
|
||||
{
|
||||
new_mask = atomic_or(&wqlist_mask, wqlist_index_bit);
|
||||
} while (!(new_mask & wqlist_index_bit));
|
||||
|
||||
STAILQ_INSERT_TAIL(&workq->item_listhead, witem, item_entry);
|
||||
|
||||
pthread_spin_unlock(&workq->mtx);
|
||||
|
||||
pthread_cond_signal(&wqlist_has_work);
|
||||
pthread_mutex_unlock(&wqlist_mtx);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* FIXME: this means there are no workqueues.. should never happen */
|
||||
dbg_puts("Attempting to add a workitem without a workqueue");
|
||||
abort();
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void *
|
||||
manager_main(void *unused __attribute__ ((unused)))
|
||||
{
|
||||
unsigned int load_max = cpu_count;
|
||||
unsigned int worker_max, current_thread_count = 0;
|
||||
unsigned int worker_idle_seconds_accumulated = 0;
|
||||
unsigned int max_threads_to_stop = 0;
|
||||
unsigned int i;
|
||||
int cond_wait_rv = 0;
|
||||
sigset_t sigmask;
|
||||
struct timespec ts;
|
||||
struct timeval tp;
|
||||
|
||||
worker_max = get_process_limit();
|
||||
scoreboard.load = get_load_average();
|
||||
|
||||
/* Block all signals */
|
||||
sigfillset(&sigmask);
|
||||
pthread_sigmask(SIG_BLOCK, &sigmask, NULL);
|
||||
|
||||
/* Create the minimum number of workers */
|
||||
scoreboard.count = 0;
|
||||
for (i = 0; i < worker_min; i++)
|
||||
worker_start();
|
||||
|
||||
for (;;) {
|
||||
|
||||
pthread_mutex_lock(&scoreboard.sb_wake_mtx);
|
||||
|
||||
dbg_puts("manager is sleeping");
|
||||
|
||||
(void) gettimeofday(&tp, NULL); // TODO - error checking
|
||||
|
||||
/* Convert from timeval to timespec */
|
||||
ts.tv_sec = tp.tv_sec;
|
||||
ts.tv_nsec = tp.tv_usec * 1000;
|
||||
ts.tv_sec += 1; // wake once per second and check if we have too many idle threads...
|
||||
|
||||
// We should only sleep on the condition if there are no pending signal, spurious wakeup is also ok
|
||||
if (scoreboard.sb_wake_pending == 0)
|
||||
cond_wait_rv = pthread_cond_timedwait(&scoreboard.sb_wake_cond, &scoreboard.sb_wake_mtx, &ts);
|
||||
|
||||
scoreboard.sb_wake_pending = 0; // we must set this before spawning any new threads below, or we race...
|
||||
|
||||
dbg_puts("manager is awake");
|
||||
|
||||
dbg_printf("load=%u idle=%u workers=%u max_workers=%u worker_min = %u",
|
||||
scoreboard.load, scoreboard.idle, scoreboard.count, worker_max, worker_min);
|
||||
|
||||
// If no workers available, check if we should create a new one
|
||||
if (scoreboard.idle == 0 && (scoreboard.count > 0)) // last part required for an extremely unlikely race at startup
|
||||
{
|
||||
scoreboard.load = get_load_average();
|
||||
|
||||
if ((scoreboard.load < load_max) && (scoreboard.count < worker_max))
|
||||
{
|
||||
if (scoreboard.count < worker_idle_threshold) // allow cheap rampup up to worker_idle_threshold without going to /proc
|
||||
{
|
||||
worker_start();
|
||||
}
|
||||
else // check through /proc, will be a bit more expensive in terms of latency
|
||||
if (threads_runnable(¤t_thread_count) == 0)
|
||||
{
|
||||
// only start thread if we have less runnable threads than cpus
|
||||
if (current_thread_count >= cpu_count)
|
||||
{
|
||||
dbg_printf("Not spawning worker thread, thread_runnable = %d >= cpu_count = %d",
|
||||
current_thread_count, cpu_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
worker_start();
|
||||
}
|
||||
}
|
||||
else // always start thread if we can't get runnable count
|
||||
{
|
||||
worker_start();
|
||||
}
|
||||
}
|
||||
else // high load, allow rampup up to worker_idle_threshold regardless of this
|
||||
{
|
||||
if (scoreboard.count < worker_idle_threshold)
|
||||
{
|
||||
worker_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cond_wait_rv == ETIMEDOUT) // Only check for ramp down on the 'timer tick'
|
||||
{
|
||||
if ((scoreboard.idle - worker_idle_threshold) > 0) // only accumulate if there are 'too many' idle threads
|
||||
{
|
||||
worker_idle_seconds_accumulated += scoreboard.idle; // keep track of many idle 'thread seconds' we have
|
||||
|
||||
dbg_printf("worker_idle_seconds_accumulated = %d, scoreboard.idle = %d, scoreboard.count = %d\n",
|
||||
worker_idle_seconds_accumulated, scoreboard.idle, scoreboard.count);
|
||||
}
|
||||
|
||||
// Only consider ramp down if we have accumulated enough thread 'idle seconds'
|
||||
// this logic will ensure that a large number of idle threads will ramp down faster
|
||||
max_threads_to_stop = worker_idle_seconds_accumulated / WORKER_IDLE_SECONDS_THRESHOLD;
|
||||
|
||||
if (max_threads_to_stop > 0)
|
||||
{
|
||||
worker_idle_seconds_accumulated = 0;
|
||||
|
||||
if (max_threads_to_stop > (scoreboard.idle - worker_idle_threshold))
|
||||
max_threads_to_stop = (scoreboard.idle - worker_idle_threshold);
|
||||
|
||||
// Only stop threads if we actually have 'too many' idle ones in the pool
|
||||
if (scoreboard.idle > worker_idle_threshold)
|
||||
{
|
||||
for (i = 0; i < max_threads_to_stop; i++)
|
||||
{
|
||||
dbg_puts("Removing one thread from the thread pool");
|
||||
worker_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&scoreboard.sb_wake_mtx);
|
||||
}
|
||||
|
||||
/*NOTREACHED*/
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
manager_start(void)
|
||||
{
|
||||
pthread_t tid;
|
||||
int rv;
|
||||
|
||||
dbg_puts("starting the manager thread");
|
||||
|
||||
do {
|
||||
rv = pthread_create(&tid, &detached_attr, manager_main, NULL);
|
||||
if (rv == EAGAIN) {
|
||||
sleep(1);
|
||||
} else if (rv != 0) {
|
||||
/* FIXME: not nice */
|
||||
dbg_printf("thread creation failed, rv=%d", rv);
|
||||
abort();
|
||||
}
|
||||
} while (rv != 0);
|
||||
|
||||
wqlist_has_manager = 1;
|
||||
}
|
||||
|
||||
void
|
||||
manager_workqueue_additem(struct _pthread_workqueue *workq, struct work *witem)
|
||||
{
|
||||
unsigned int wqlist_index_bit = (0x1 << workq->wqlist_index);
|
||||
|
||||
if (workq->overcommit) {
|
||||
pthread_t tid;
|
||||
|
||||
pthread_mutex_lock(&ocwq_mtx);
|
||||
pthread_spin_lock(&workq->mtx);
|
||||
STAILQ_INSERT_TAIL(&workq->item_listhead, witem, item_entry);
|
||||
pthread_spin_unlock(&workq->mtx);
|
||||
ocwq_mask |= wqlist_index_bit;
|
||||
if (ocwq_idle_threads > 0) {
|
||||
dbg_puts("signaling an idle worker");
|
||||
pthread_cond_signal(&ocwq_has_work);
|
||||
ocwq_idle_threads--;
|
||||
} else {
|
||||
(void)pthread_create(&tid, &detached_attr, overcommit_worker_main, NULL);
|
||||
}
|
||||
pthread_mutex_unlock(&ocwq_mtx);
|
||||
} else {
|
||||
pthread_spin_lock(&workq->mtx);
|
||||
|
||||
// Only set the mask for the first item added to the workqueue.
|
||||
if (STAILQ_EMPTY(&workq->item_listhead))
|
||||
{
|
||||
unsigned int new_mask;
|
||||
|
||||
// The only possible contention here are with threads performing the same
|
||||
// operation on another workqueue, so we will not be blocked long...
|
||||
// Threads operating on the same workqueue will be serialized by the spinlock so it is very unlikely.
|
||||
do
|
||||
{
|
||||
new_mask = atomic_or(&wqlist_mask, wqlist_index_bit);
|
||||
} while (!(new_mask & wqlist_index_bit));
|
||||
}
|
||||
|
||||
STAILQ_INSERT_TAIL(&workq->item_listhead, witem, item_entry);
|
||||
|
||||
pthread_spin_unlock(&workq->mtx);
|
||||
|
||||
// Only signal thread wakeup if there are idle threads available
|
||||
// and no other thread have managed to race us and empty the wqlist on our behalf already
|
||||
if ((scoreboard.idle > 0)) // && ((wqlist_mask & wqlist_index_bit) != 0)) // disabling this fringe optimization for now
|
||||
{
|
||||
pthread_mutex_lock(&wqlist_mtx);
|
||||
pthread_cond_signal(&wqlist_has_work);
|
||||
pthread_mutex_unlock(&wqlist_mtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static unsigned int
|
||||
get_process_limit(void)
|
||||
{
|
||||
#if __linux__
|
||||
struct rlimit rlim;
|
||||
|
||||
if (getrlimit(RLIMIT_NPROC, &rlim) < 0) {
|
||||
dbg_perror("getrlimit(2)");
|
||||
return (50);
|
||||
} else {
|
||||
return (rlim.rlim_max);
|
||||
}
|
||||
#else
|
||||
/* Solaris doesn't define this limit anywhere I can see.. */
|
||||
return (64);
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
get_load_average(void)
|
||||
{
|
||||
double loadavg;
|
||||
|
||||
/* TODO: proper error handling */
|
||||
if (getloadavg(&loadavg, 1) != 1) {
|
||||
dbg_perror("getloadavg(3)");
|
||||
return (1);
|
||||
}
|
||||
if (loadavg > INT_MAX || loadavg < 0)
|
||||
loadavg = 1;
|
||||
|
||||
return ((int) loadavg);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
manager_peek(const char *key)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
if (strcmp(key, "combined_idle") == 0) {
|
||||
rv = scoreboard.idle;
|
||||
if (scoreboard.idle > worker_min)
|
||||
rv -= worker_min;
|
||||
rv += ocwq_idle_threads;
|
||||
} else if (strcmp(key, "idle") == 0) {
|
||||
rv = scoreboard.idle;
|
||||
if (scoreboard.idle > worker_min)
|
||||
rv -= worker_min;
|
||||
} else if (strcmp(key, "ocomm_idle") == 0) {
|
||||
rv = ocwq_idle_threads;
|
||||
} else {
|
||||
dbg_printf("invalid key: %s", key);
|
||||
abort();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef _PTWQ_POSIX_PLATFORM_H
|
||||
#define _PTWQ_POSIX_PLATFORM_H 1
|
||||
|
||||
/* Workaround to get visibility for _SC_NPROCESSORS_ONLN on FreeBSD */
|
||||
#define __BSD_VISIBLE 1
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef __sun
|
||||
# include <sys/loadavg.h>
|
||||
#endif
|
||||
|
||||
/* GCC atomic builtins.
|
||||
* See: http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
|
||||
*/
|
||||
#ifdef __sun
|
||||
# include <atomic.h>
|
||||
# define atomic_inc atomic_inc_32
|
||||
# define atomic_dec atomic_dec_32
|
||||
# define atomic_and atomic_and_uint_nv
|
||||
# define atomic_or atomic_or_uint_nv
|
||||
#else
|
||||
# define atomic_inc(p) __sync_add_and_fetch((p), 1)
|
||||
# define atomic_dec(p) __sync_sub_and_fetch((p), 1)
|
||||
# define atomic_and(p,v) __sync_and_and_fetch((p), (v))
|
||||
# define atomic_or(p,v) __sync_or_and_fetch((p), (v))
|
||||
#endif
|
||||
|
||||
#ifdef MAKE_STATIC
|
||||
# define CONSTRUCTOR
|
||||
#else
|
||||
# define CONSTRUCTOR __attribute__ ((constructor))
|
||||
#endif
|
||||
#define VISIBLE __attribute__((visibility("default")))
|
||||
|
||||
|
||||
#endif /* _PTWQ_POSIX_PLATFORM_H */
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Joakim Johansson <jocke@tbricks.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "private.h"
|
||||
|
||||
#if defined(__sun)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <procfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/*
|
||||
|
||||
/proc for Solaris
|
||||
|
||||
STRUCTURE OF /proc/pid
|
||||
A given directory /proc/pid contains the following entries.
|
||||
A process can use the invisible alias /proc/self if it
|
||||
wishes to open one of its own /proc files (invisible in the
|
||||
sense that the name ``self'' does not appear in a directory
|
||||
listing of /proc obtained from ls(1), getdents(2), or
|
||||
readdir(3C)).
|
||||
...
|
||||
lstatus
|
||||
Contains a prheader structure followed by an array of
|
||||
lwpstatus structures, one for each active lwp in the process
|
||||
(see also /proc/pid/lwp/lwpid/lwpstatus, below). The
|
||||
prheader structure describes the number and size of the
|
||||
array entries that follow.
|
||||
|
||||
typedef struct prheader {
|
||||
long pr_nent; // number of entries
|
||||
size_t pr_entsize; // size of each entry, in bytes
|
||||
} prheader_t;
|
||||
|
||||
The lwpstatus structure may grow by the addition of elements
|
||||
at the end in future releases of the system. Programs must
|
||||
use pr_entsize in the file header to index through the
|
||||
array. These comments apply to all /proc files that include
|
||||
a prheader structure (lpsinfo and lusage, below).
|
||||
|
||||
/proc/self/lstatus
|
||||
*/
|
||||
|
||||
int threads_runnable(unsigned int *threads_running)
|
||||
{
|
||||
const char *path = "/proc/self/lstatus";
|
||||
int read_fd, retval = -1, i;
|
||||
unsigned int running_count = 0;
|
||||
char *lwp_buffer;
|
||||
ssize_t actual_read;
|
||||
lwpstatus_t *lwpstatus;
|
||||
prheader_t prheader;
|
||||
|
||||
read_fd = open(path, O_RDONLY);
|
||||
if (read_fd == -1)
|
||||
{
|
||||
dbg_perror("open()");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (fcntl(read_fd, F_SETFL, O_NONBLOCK) != 0)
|
||||
{
|
||||
dbg_perror("fcntl()");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
actual_read = read(read_fd, &prheader, sizeof(prheader_t));
|
||||
|
||||
if (actual_read != sizeof(prheader_t))
|
||||
{
|
||||
dbg_printf("read returned wrong number of bytes - %ld instead of %ld", actual_read, sizeof(prheader_t));
|
||||
goto errout;
|
||||
}
|
||||
|
||||
dbg_printf("read prheader, pr_nent = %ld, pr_entsize = %ld, sizeof(lwpstatus_t) = %ld",prheader.pr_nent, prheader.pr_entsize, sizeof(lwpstatus_t));
|
||||
|
||||
lwp_buffer = malloc(prheader.pr_nent * prheader.pr_entsize);
|
||||
|
||||
if (!lwp_buffer)
|
||||
{
|
||||
dbg_perror("malloc(prheader.pr_nent * prheader.pr_entsize)");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
actual_read = read(read_fd, lwp_buffer, (prheader.pr_nent * prheader.pr_entsize));
|
||||
|
||||
if (actual_read != (prheader.pr_nent * prheader.pr_entsize))
|
||||
{
|
||||
dbg_printf("read returned wrong number of bytes - %ld instead of %ld", actual_read, prheader.pr_nent * prheader.pr_entsize);
|
||||
free(lwp_buffer);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
for (i = 0; i < prheader.pr_nent; i++)
|
||||
{
|
||||
lwpstatus = (lwpstatus_t *) (lwp_buffer + (i * prheader.pr_entsize));
|
||||
dbg_printf("lwp %d, syscall = %d", lwpstatus->pr_lwpid, lwpstatus->pr_syscall);
|
||||
|
||||
if (lwpstatus->pr_flags & PR_ASLEEP)
|
||||
{
|
||||
dbg_printf("lwp %d is sleeping",lwpstatus->pr_lwpid);
|
||||
}
|
||||
else
|
||||
{
|
||||
running_count++;
|
||||
dbg_printf("lwp %d is running",lwpstatus->pr_lwpid);
|
||||
}
|
||||
}
|
||||
|
||||
free(lwp_buffer);
|
||||
retval = 0;
|
||||
*threads_running = running_count;
|
||||
|
||||
errout:
|
||||
if (close(read_fd) != 0)
|
||||
{
|
||||
dbg_perror("close()");
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
/*
|
||||
|
||||
/proc for Linux
|
||||
|
||||
/proc/self
|
||||
This directory refers to the process accessing the /proc filesystem, and is identical to the /proc directory named by the process ID of the same process.
|
||||
|
||||
ÑÑÑÑÑÑÑ
|
||||
|
||||
/proc/[number]/stat
|
||||
Status information about the process. This is used by ps(1). It is defined in /usr/src/linux/fs/proc/array.c.
|
||||
The fields, in order, with their proper scanf(3) format specifiers, are:
|
||||
|
||||
pid %d
|
||||
The process ID.
|
||||
|
||||
comm %s
|
||||
The filename of the executable, in parentheses. This is visible whether or not the executable is swapped out.
|
||||
|
||||
state %c
|
||||
One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible wait, D is waiting in uninterruptible disk sleep, Z is zombie, T is traced or stopped (on a signal), and W is paging.
|
||||
|
||||
---------------
|
||||
|
||||
/proc/[number]/task (since kernel 2.6.0-test6)
|
||||
This is a directory that contains one subdirectory for each thread in the process. The name of each subdirectory is the numerical thread ID of the thread (see gettid(2)). Within each of these subdirectories, there is a set of files with the same names and contents as under the /proc/[number] directories. For attributes that are shared by all threads, the contents for each of the files under the task/[thread-ID] subdirectories will be the same as in the corresponding file in the parent /proc/[number] directory (e.g., in a multithreaded process, all of the task/[thread-ID]/cwd files will have the same value as the /proc/[number]/cwd file in the parent directory, since all of the threads in a process share a working directory). For attributes that are distinct for each thread, the corresponding files under task/[thread-ID] may have different values (e.g., various fields in each of the task/[thread-ID]/status files may be different for each thread).
|
||||
In a multithreaded process, the contents of the /proc/[number]/task directory are not available if the main thread has already terminated (typically by calling pthread_exit(3)).
|
||||
|
||||
---------------
|
||||
|
||||
Example:
|
||||
read data from /proc/self/task/11019/stat: [11019 (lt-dispatch_sta) D 20832 10978 20832 34819 10978 4202560 251 3489 0 0 0 2 2 5 20 0 37 0 138715543 2538807296 13818 18446744073709551615 4194304 4203988 140736876632592 139770298610200 139771956665732 0 0 0 0 0 0 0 -1 2 0 0 0 0 0
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define MAX_RESULT_SIZE 4096
|
||||
|
||||
static int _read_file(const char *path, char *result)
|
||||
{
|
||||
int read_fd, retval = -1;
|
||||
ssize_t actual_read;
|
||||
|
||||
read_fd = open(path, O_RDONLY);
|
||||
if (read_fd == -1)
|
||||
{
|
||||
dbg_perror("open()");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (fcntl(read_fd, F_SETFL, O_NONBLOCK) != 0)
|
||||
{
|
||||
dbg_perror("fcntl()");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
|
||||
actual_read = read(read_fd, result, MAX_RESULT_SIZE);
|
||||
|
||||
# ifdef __ia64__
|
||||
dbg_printf("read %ld from %s", actual_read, path);
|
||||
# else
|
||||
dbg_printf("read %zd from %s", actual_read, path);
|
||||
#endif
|
||||
|
||||
if (actual_read == 0)
|
||||
{
|
||||
goto errout;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
|
||||
errout:
|
||||
if (close(read_fd) != 0)
|
||||
{
|
||||
dbg_perror("close()");
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
int threads_runnable(unsigned int *threads_running)
|
||||
{
|
||||
DIR *dip;
|
||||
struct dirent *dit;
|
||||
const char *task_path = "/proc/self/task";
|
||||
char thread_path[1024];
|
||||
char thread_data[MAX_RESULT_SIZE+1];
|
||||
char dummy[MAX_RESULT_SIZE+1];
|
||||
char state;
|
||||
int pid;
|
||||
unsigned int running_count = 0;
|
||||
|
||||
dbg_puts("Checking threads_runnable()");
|
||||
|
||||
if ((dip = opendir(task_path)) == NULL)
|
||||
{
|
||||
dbg_perror("opendir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((dit = readdir(dip)) != NULL)
|
||||
{
|
||||
memset(thread_data, 0, sizeof(thread_data));
|
||||
|
||||
sprintf(thread_path, "%s/%s/stat",task_path, dit->d_name);
|
||||
|
||||
if (_read_file(thread_path, thread_data) == 0)
|
||||
{
|
||||
if (sscanf(thread_data, "%d %s %c", &pid, dummy, &state) == 3)
|
||||
{
|
||||
dbg_printf("The state for thread %s is %c", dit->d_name, state);
|
||||
switch (state)
|
||||
{
|
||||
case 'R':
|
||||
running_count++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_printf("Failed to scan state for thread %s (%s)", dit->d_name, thread_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closedir(dip) == -1)
|
||||
{
|
||||
perror("closedir");
|
||||
}
|
||||
|
||||
dbg_printf("Running count is %d", running_count);
|
||||
*threads_running = running_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int threads_runnable(unsigned int *threads_running)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Joakim Johansson <jocke@tbricks.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "private.h"
|
||||
|
||||
#if defined(__sun)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/procset.h>
|
||||
#include <sys/priocntl.h>
|
||||
#include <sys/rtpriocntl.h>
|
||||
#include <sys/tspriocntl.h>
|
||||
#include <sys/iapriocntl.h>
|
||||
#include <sys/fsspriocntl.h>
|
||||
#include <sys/fxpriocntl.h>
|
||||
|
||||
// Set the RT priority - it is inveresed from the queue priorities, higher is better
|
||||
// We give '0' to low prio queues and the highest possible for the 'high' prio queue (currently 2)
|
||||
|
||||
void ptwq_set_current_thread_priority(int priority)
|
||||
{
|
||||
long retval = 0;
|
||||
|
||||
dbg_printf("reconfiguring thread for priority level=%u", priority);
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
case WORKQ_LOW_PRIOQUEUE:
|
||||
retval = priocntl(P_LWPID, P_MYID, PC_SETXPARMS, "TS", 0); // run low prio queues as time sharing
|
||||
break;
|
||||
case WORKQ_DEFAULT_PRIOQUEUE:
|
||||
retval = priocntl(P_LWPID, P_MYID, PC_SETXPARMS, "RT", RT_KY_PRI, WORKQ_NUM_PRIOQUEUE - priority - 1, 0);
|
||||
break;
|
||||
case WORKQ_HIGH_PRIOQUEUE:
|
||||
retval = priocntl(P_LWPID, P_MYID, PC_SETXPARMS, "RT", RT_KY_PRI, WORKQ_NUM_PRIOQUEUE - priority - 1, 0);
|
||||
break;
|
||||
default:
|
||||
dbg_printf("Unknown priority level = %u", priority);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (retval != 0)
|
||||
dbg_perror("priocntl()");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ptwq_set_current_thread_priority(int priority __attribute__ ((unused)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Mark Heily <mark@heily.com>
|
||||
* Copyright (c) 2009, Stacey Son <sson@freebsd.org>
|
||||
* Copyright (c) 2000-2008, Apple Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PTWQ_PRIVATE_H
|
||||
#define _PTWQ_PRIVATE_H 1
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include "windows/platform.h"
|
||||
#else
|
||||
# include "posix/platform.h"
|
||||
#endif
|
||||
|
||||
#include "pthread_workqueue.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* The maximum number of workqueues that can be created.
|
||||
This is based on libdispatch only needing 6 workqueues.
|
||||
*/
|
||||
#define PTHREAD_WORKQUEUE_MAX 31
|
||||
|
||||
/* The total number of priority levels. */
|
||||
#define WORKQ_NUM_PRIOQUEUE 3
|
||||
|
||||
/* Signatures/magic numbers. */
|
||||
#define PTHREAD_WORKQUEUE_SIG 0xBEBEBEBE
|
||||
#define PTHREAD_WORKQUEUE_ATTR_SIG 0xBEBEBEBE
|
||||
|
||||
/* Whether to use real-time threads for the workers if available */
|
||||
|
||||
extern unsigned int PWQ_RT_THREADS;
|
||||
extern time_t PWQ_SPIN_USEC;
|
||||
extern unsigned int PWQ_SPIN_THREADS;
|
||||
|
||||
/* A limit of the number of cpu:s that we view as available, useful when e.g. using processor sets */
|
||||
extern unsigned int PWQ_ACTIVE_CPU;
|
||||
|
||||
#if __GNUC__
|
||||
#define fastpath(x) ((__typeof__(x))__builtin_expect((long)(x), ~0l))
|
||||
#define slowpath(x) ((__typeof__(x))__builtin_expect((long)(x), 0l))
|
||||
#else
|
||||
#define fastpath(x) (x)
|
||||
#define slowpath(x) (x)
|
||||
#endif
|
||||
|
||||
#define CACHELINE_SIZE 64
|
||||
#define ROUND_UP_TO_CACHELINE_SIZE(x) (((x) + (CACHELINE_SIZE - 1)) & ~(CACHELINE_SIZE - 1))
|
||||
|
||||
/*
|
||||
* The work item cache, has three different optional implementations:
|
||||
* 1. No cache, just normal malloc/free using the standard malloc library in use
|
||||
* 2. Libumem based object cache, requires linkage with libumem - for non-Solaris see http://labs.omniti.com/labs/portableumem
|
||||
* this is the most balanced cache supporting migration across threads of allocated/freed witems
|
||||
* 3. TSD based cache, modelled on libdispatch continuation implementation, can lead to imbalance with assymetric
|
||||
* producer/consumer threads as allocated memory is cached by the thread freeing it
|
||||
*/
|
||||
|
||||
#define WITEM_CACHE_TYPE 1 // Set to 1, 2 or 3 to specify witem cache implementation to use
|
||||
|
||||
struct work {
|
||||
STAILQ_ENTRY(work) item_entry;
|
||||
void (*func)(void *);
|
||||
void *func_arg;
|
||||
unsigned int flags;
|
||||
unsigned int gencount;
|
||||
#if (WITEM_CACHE_TYPE == 3)
|
||||
struct work *volatile wi_next;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct _pthread_workqueue {
|
||||
unsigned int sig; /* Unique signature for this structure */
|
||||
unsigned int flags;
|
||||
int queueprio;
|
||||
int overcommit;
|
||||
unsigned int wqlist_index;
|
||||
STAILQ_HEAD(,work) item_listhead;
|
||||
pthread_spinlock_t mtx;
|
||||
#ifdef WORKQUEUE_PLATFORM_SPECIFIC
|
||||
WORKQUEUE_PLATFORM_SPECIFIC;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* manager.c */
|
||||
int manager_init(void);
|
||||
unsigned long manager_peek(const char *);
|
||||
void manager_workqueue_create(struct _pthread_workqueue *);
|
||||
void manager_workqueue_additem(struct _pthread_workqueue *, struct work *);
|
||||
|
||||
struct work *witem_alloc(void (*func)(void *), void *func_arg); // returns a properly initialized witem
|
||||
void witem_free(struct work *wi);
|
||||
int witem_cache_init(void);
|
||||
void witem_cache_cleanup(void *value);
|
||||
|
||||
#endif /* _PTWQ_PRIVATE_H */
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Joakim Johansson <jocke@tbricks.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PTWQ_POSIX_THREAD_INFO_H
|
||||
#define _PTWQ_POSIX_THREAD_INFO_H 1
|
||||
|
||||
int threads_runnable(unsigned int *threads_running);
|
||||
|
||||
#endif /* _PTWQ_POSIX_THREAD_INFO_H */
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Joakim Johansson <jocke@tbricks.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PTWQ_THREAD_RT_H
|
||||
#define _PTWQ_THREAD_RT_H 1
|
||||
|
||||
void ptwq_set_current_thread_priority(int priority); // higher is better
|
||||
|
||||
#endif /* _PTWQ_THREAD_RT_H */
|
||||
@@ -0,0 +1,198 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Mark Heily <mark@heily.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "../private.h"
|
||||
#include "pthread_workqueue.h"
|
||||
|
||||
#ifdef PROVIDE_LEGACY_XP_SUPPORT
|
||||
|
||||
static LIST_HEAD(, _pthread_workqueue) wqlist[WORKQ_NUM_PRIOQUEUE];
|
||||
static pthread_rwlock_t wqlist_mtx;
|
||||
|
||||
int
|
||||
manager_init(void)
|
||||
{
|
||||
pthread_rwlock_init(&wqlist_mtx, NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
manager_workqueue_create(struct _pthread_workqueue *workq)
|
||||
{
|
||||
pthread_rwlock_wrlock(&wqlist_mtx);
|
||||
LIST_INSERT_HEAD(&wqlist[workq->queueprio], workq, wqlist_entry);
|
||||
pthread_rwlock_unlock(&wqlist_mtx);
|
||||
|
||||
pthread_spin_init(&workq->mtx, PTHREAD_PROCESS_PRIVATE);
|
||||
}
|
||||
|
||||
/* The caller must hold the wqlist_mtx. */
|
||||
static struct work *
|
||||
wqlist_scan(void)
|
||||
{
|
||||
pthread_workqueue_t workq;
|
||||
struct work *witem = NULL;
|
||||
int i;
|
||||
|
||||
pthread_rwlock_rdlock(&wqlist_mtx);
|
||||
for (i = 0; i < WORKQ_NUM_PRIOQUEUE; i++) {
|
||||
LIST_FOREACH(workq, &wqlist[i], wqlist_entry) {
|
||||
pthread_spin_lock(&workq->mtx);
|
||||
|
||||
if (STAILQ_EMPTY(&workq->item_listhead)) {
|
||||
pthread_spin_unlock(&workq->mtx);
|
||||
continue;
|
||||
}
|
||||
|
||||
witem = STAILQ_FIRST(&workq->item_listhead);
|
||||
if (witem != NULL)
|
||||
STAILQ_REMOVE_HEAD(&workq->item_listhead, item_entry);
|
||||
|
||||
pthread_spin_unlock(&workq->mtx);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
pthread_rwlock_unlock(&wqlist_mtx);
|
||||
return (witem);
|
||||
}
|
||||
|
||||
DWORD WINAPI
|
||||
worker_main(LPVOID arg)
|
||||
{
|
||||
struct work *witem;
|
||||
|
||||
witem = wqlist_scan();
|
||||
if (witem == NULL)
|
||||
return (0);
|
||||
|
||||
witem->func(witem->func_arg);
|
||||
free(witem);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
manager_workqueue_additem(struct _pthread_workqueue *workq, struct work *witem)
|
||||
{
|
||||
pthread_spin_lock(&workq->mtx);
|
||||
STAILQ_INSERT_TAIL(&workq->item_listhead, witem, item_entry);
|
||||
pthread_spin_unlock(&workq->mtx);
|
||||
if (!QueueUserWorkItem(worker_main, NULL, WT_EXECUTELONGFUNCTION))
|
||||
abort();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int
|
||||
manager_init(void)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
manager_workqueue_create(struct _pthread_workqueue *workq)
|
||||
{
|
||||
PTP_POOL pool;
|
||||
PTP_CALLBACK_ENVIRON callback;
|
||||
SYSTEM_INFO sysinfo;
|
||||
|
||||
pool = CreateThreadpool(NULL);
|
||||
if(pool == NULL){
|
||||
dbg_lasterror("CreateThreadpool()");
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeThreadpoolEnvironment(&workq->win_callback_env);
|
||||
callback = &workq->win_callback_env;
|
||||
SetThreadpoolCallbackPool(callback, pool);
|
||||
|
||||
switch(workq->queueprio){
|
||||
case WORKQ_HIGH_PRIOQUEUE:
|
||||
// weird but this seems the only valid solution !?
|
||||
SetThreadpoolCallbackPriority(callback, TP_CALLBACK_PRIORITY_LOW);
|
||||
break;
|
||||
case WORKQ_LOW_PRIOQUEUE:
|
||||
// see above
|
||||
SetThreadpoolCallbackPriority(callback, TP_CALLBACK_PRIORITY_HIGH);
|
||||
break;
|
||||
default:
|
||||
SetThreadpoolCallbackPriority(callback, TP_CALLBACK_PRIORITY_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
// we need a proper way to implement overcommitting on windows
|
||||
if(workq->overcommit){
|
||||
GetSystemInfo(&sysinfo);
|
||||
SetThreadpoolThreadMaximum(pool, sysinfo.dwNumberOfProcessors * 2);
|
||||
}
|
||||
|
||||
workq->win_thread_pool = pool;
|
||||
}
|
||||
|
||||
VOID CALLBACK
|
||||
worker_main( PTP_CALLBACK_INSTANCE instance, PVOID Parameter, PTP_WORK work )
|
||||
{
|
||||
struct work* witem = (struct work*)Parameter;
|
||||
|
||||
assert(witem);
|
||||
witem->func(witem->func_arg);
|
||||
free(witem);
|
||||
CloseThreadpoolWork(work);
|
||||
}
|
||||
|
||||
void
|
||||
manager_workqueue_additem(struct _pthread_workqueue *workq, struct work *witem)
|
||||
{
|
||||
PTP_WORK work = CreateThreadpoolWork(worker_main, witem, &workq->win_callback_env);
|
||||
if(work == NULL) {
|
||||
dbg_lasterror("CreateThreadpoolWork()");
|
||||
return;
|
||||
}
|
||||
SubmitThreadpoolWork(work);
|
||||
}
|
||||
|
||||
// TODO: We need to cleanly close the environment and threadpools!
|
||||
|
||||
#endif
|
||||
|
||||
unsigned long
|
||||
manager_peek(const char *key)
|
||||
{
|
||||
unsigned long rv;
|
||||
|
||||
if (strcmp(key, "combined_idle") == 0) {
|
||||
dbg_puts("TODO");
|
||||
abort();
|
||||
} else {
|
||||
dbg_printf("invalid key: ", key);
|
||||
abort();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Mark Heily <mark@heily.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "../private.h"
|
||||
#include "pthread_workqueue.h"
|
||||
|
||||
#ifndef MAKE_STATIC
|
||||
|
||||
// The constructor to be called
|
||||
int VISIBLE CONSTRUCTOR pthread_workqueue_init_np(void);
|
||||
|
||||
BOOL WINAPI DllMain(
|
||||
HINSTANCE hinstDLL, // handle to DLL module
|
||||
DWORD fdwReason, // reason for calling function
|
||||
LPVOID lpReserved ) // reserved
|
||||
{
|
||||
// Perform actions based on the reason for calling.
|
||||
switch( fdwReason )
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
// Initialize once for each new process.
|
||||
// Return FALSE to fail DLL load.
|
||||
if( pthread_workqueue_init_np() < 0)
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
}
|
||||
return TRUE; // Successful DLL_PROCESS_ATTACH.
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef _PTWQ_WINDOWS_PLATFORM_H
|
||||
#define _PTWQ_WINDOWS_PLATFORM_H 1
|
||||
|
||||
#define PROVIDE_LEGACY_XP_SUPPORT 1
|
||||
|
||||
#ifdef PROVIDE_LEGACY_XP_SUPPORT
|
||||
# define _WIN32_WINNT 0x0500
|
||||
#else
|
||||
# define _WIN32_WINNT 0x0610
|
||||
#endif
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#include <string.h>
|
||||
#include "winpthreads.h"
|
||||
|
||||
/* Instead of __attribute__ ((constructor)), use DllMain() */
|
||||
#define CONSTRUCTOR /* */
|
||||
|
||||
#define VISIBLE __declspec(dllexport)
|
||||
|
||||
# define __func__ __FUNCTION__
|
||||
|
||||
#undef LIST_HEAD
|
||||
#include "queue.h"
|
||||
|
||||
#define sleep(sec) Sleep(1000*sec)
|
||||
#define strdup(p) _strdup(p)
|
||||
#define random() rand()
|
||||
|
||||
#ifdef PROVIDE_LEGACY_XP_SUPPORT
|
||||
# define WORKQUEUE_PLATFORM_SPECIFIC \
|
||||
LIST_ENTRY(_pthread_workqueue) wqlist_entry
|
||||
#else
|
||||
/* Specific workqueue items */
|
||||
# define WORKQUEUE_PLATFORM_SPECIFIC \
|
||||
PTP_POOL win_thread_pool; \
|
||||
TP_CALLBACK_ENVIRON win_callback_env
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _PTWQ_WINDOWS_PLATFORM_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Mark Heily <mark@heily.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Windows condition variables
|
||||
*/
|
||||
#if WINVER >= 0x0600
|
||||
|
||||
typedef CONDITION_VARIABLE pthread_cond_t;
|
||||
typedef int pthread_condattr_t;
|
||||
|
||||
static inline int
|
||||
pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)
|
||||
{
|
||||
InitializeConditionVariable(cond);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
|
||||
{
|
||||
return (SleepConditionVariableCS(cond, mutex, INFINITE) == 0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
pthread_cond_signal(pthread_cond_t *cond)
|
||||
{
|
||||
WakeConditionVariable(cond);
|
||||
return (0);
|
||||
}
|
||||
|
||||
#else
|
||||
# error Conditional variables require Vista or newer
|
||||
#endif
|
||||
@@ -0,0 +1,624 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
* $FreeBSD: src/sys/sys/queue.h,v 1.72.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines four types of data structures: singly-linked lists,
|
||||
* singly-linked tail queues, lists and tail queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A singly-linked tail queue is headed by a pair of pointers, one to the
|
||||
* head of the list and the other to the tail of the list. The elements are
|
||||
* singly linked for minimum space and pointer manipulation overhead at the
|
||||
* expense of O(n) removal for arbitrary elements. New elements can be added
|
||||
* to the list after an existing element, at the head of the list, or at the
|
||||
* end of the list. Elements being removed from the head of the tail queue
|
||||
* should use the explicit macro for this purpose for optimum efficiency.
|
||||
* A singly-linked tail queue may only be traversed in the forward direction.
|
||||
* Singly-linked tail queues are ideal for applications with large datasets
|
||||
* and few or no removals or for implementing a FIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*
|
||||
*
|
||||
* SLIST LIST STAILQ TAILQ
|
||||
* _HEAD + + + +
|
||||
* _HEAD_INITIALIZER + + + +
|
||||
* _ENTRY + + + +
|
||||
* _INIT + + + +
|
||||
* _EMPTY + + + +
|
||||
* _FIRST + + + +
|
||||
* _NEXT + + + +
|
||||
* _PREV - - - +
|
||||
* _LAST - - + +
|
||||
* _FOREACH + + + +
|
||||
* _FOREACH_SAFE + + + +
|
||||
* _FOREACH_REVERSE - - - +
|
||||
* _FOREACH_REVERSE_SAFE - - - +
|
||||
* _INSERT_HEAD + + + +
|
||||
* _INSERT_BEFORE - + - +
|
||||
* _INSERT_AFTER + + + +
|
||||
* _INSERT_TAIL - - + +
|
||||
* _CONCAT - - + +
|
||||
* _REMOVE_AFTER + - + -
|
||||
* _REMOVE_HEAD + - + -
|
||||
* _REMOVE + + + +
|
||||
*
|
||||
*/
|
||||
#ifdef QUEUE_MACRO_DEBUG
|
||||
/* Store the last 2 places the queue element or head was altered */
|
||||
struct qm_trace {
|
||||
char * lastfile;
|
||||
int lastline;
|
||||
char * prevfile;
|
||||
int prevline;
|
||||
};
|
||||
|
||||
#define TRACEBUF struct qm_trace trace;
|
||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
||||
|
||||
#define QMD_TRACE_HEAD(head) do { \
|
||||
(head)->trace.prevline = (head)->trace.lastline; \
|
||||
(head)->trace.prevfile = (head)->trace.lastfile; \
|
||||
(head)->trace.lastline = __LINE__; \
|
||||
(head)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TRACE_ELEM(elem) do { \
|
||||
(elem)->trace.prevline = (elem)->trace.lastline; \
|
||||
(elem)->trace.prevfile = (elem)->trace.lastfile; \
|
||||
(elem)->trace.lastline = __LINE__; \
|
||||
(elem)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define QMD_TRACE_ELEM(elem)
|
||||
#define QMD_TRACE_HEAD(head)
|
||||
#define TRACEBUF
|
||||
#define TRASHIT(x)
|
||||
#endif /* QUEUE_MACRO_DEBUG */
|
||||
|
||||
#ifndef _WIN32
|
||||
/*
|
||||
* Singly-linked List declarations.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
||||
for ((varp) = &SLIST_FIRST((head)); \
|
||||
((var) = *(varp)) != NULL; \
|
||||
(varp) = &SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_INIT(head) do { \
|
||||
SLIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if (SLIST_FIRST((head)) == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = SLIST_FIRST((head)); \
|
||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
||||
curelm = SLIST_NEXT(curelm, field); \
|
||||
SLIST_REMOVE_AFTER(curelm, field); \
|
||||
} \
|
||||
TRASHIT((elm)->field.sle_next); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
SLIST_NEXT(elm, field) = \
|
||||
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
||||
} while (0)
|
||||
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first;/* first element */ \
|
||||
struct type **stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_LAST(head, type, field) \
|
||||
(STAILQ_EMPTY((head)) ? \
|
||||
NULL : \
|
||||
((struct type *)(void *) \
|
||||
((char *)((head)->stqh_last) - __offsetof(struct type, field))))
|
||||
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = STAILQ_FIRST((head)); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
TRASHIT((elm)->field.stqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_SWAP(head1, head2, type) do { \
|
||||
struct type *swap_first = STAILQ_FIRST(head1); \
|
||||
struct type **swap_last = (head1)->stqh_last; \
|
||||
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_FIRST(head2) = swap_first; \
|
||||
(head2)->stqh_last = swap_last; \
|
||||
if (STAILQ_EMPTY(head1)) \
|
||||
(head1)->stqh_last = &STAILQ_FIRST(head1); \
|
||||
if (STAILQ_EMPTY(head2)) \
|
||||
(head2)->stqh_last = &STAILQ_FIRST(head2); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* List declarations.
|
||||
* NOTE: LIST_HEAD conflicts with a Linux macro.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
|
||||
#if (defined(_KERNEL) && defined(INVARIANTS))
|
||||
#define QMD_LIST_CHECK_HEAD(head, field) do { \
|
||||
if (LIST_FIRST((head)) != NULL && \
|
||||
LIST_FIRST((head))->field.le_prev != \
|
||||
&LIST_FIRST((head))) \
|
||||
panic("Bad list head %p first->prev != head", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
|
||||
if (LIST_NEXT((elm), field) != NULL && \
|
||||
LIST_NEXT((elm), field)->field.le_prev != \
|
||||
&((elm)->field.le_next)) \
|
||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_PREV(elm, field) do { \
|
||||
if (*(elm)->field.le_prev != (elm)) \
|
||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define QMD_LIST_CHECK_HEAD(head, field)
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field)
|
||||
#define QMD_LIST_CHECK_PREV(elm, field)
|
||||
#endif /* (_KERNEL && INVARIANTS) */
|
||||
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_NEXT(listelm, field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
|
||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
||||
&LIST_NEXT((elm), field); \
|
||||
LIST_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_PREV(listelm, field); \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
LIST_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
QMD_LIST_CHECK_HEAD((head), field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
|
||||
LIST_FIRST((head)) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
QMD_LIST_CHECK_NEXT(elm, field); \
|
||||
QMD_LIST_CHECK_PREV(elm, field); \
|
||||
if (LIST_NEXT((elm), field) != NULL) \
|
||||
LIST_NEXT((elm), field)->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
||||
TRASHIT((elm)->field.le_next); \
|
||||
TRASHIT((elm)->field.le_prev); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_tmp = LIST_FIRST((head1)); \
|
||||
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
|
||||
LIST_FIRST((head2)) = swap_tmp; \
|
||||
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
|
||||
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue declarations.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#if (defined(_KERNEL) && defined(INVARIANTS))
|
||||
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
|
||||
if (!TAILQ_EMPTY(head) && \
|
||||
TAILQ_FIRST((head))->field.tqe_prev != \
|
||||
&TAILQ_FIRST((head))) \
|
||||
panic("Bad tailq head %p first->prev != head", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
|
||||
if (*(head)->tqh_last != NULL) \
|
||||
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
|
||||
if (TAILQ_NEXT((elm), field) != NULL && \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev != \
|
||||
&((elm)->field.tqe_next)) \
|
||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
|
||||
if (*(elm)->field.tqe_prev != (elm)) \
|
||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define QMD_TAILQ_CHECK_HEAD(head, field)
|
||||
#define QMD_TAILQ_CHECK_TAIL(head, headname)
|
||||
#define QMD_TAILQ_CHECK_NEXT(elm, field)
|
||||
#define QMD_TAILQ_CHECK_PREV(elm, field)
|
||||
#endif /* (_KERNEL && INVARIANTS) */
|
||||
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
QMD_TRACE_HEAD(head1); \
|
||||
QMD_TRACE_HEAD(head2); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_INIT(head) do { \
|
||||
TAILQ_FIRST((head)) = NULL; \
|
||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_NEXT(listelm, field); \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else { \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
TAILQ_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&listelm->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_PREV(listelm, field); \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
TAILQ_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&listelm->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_HEAD(head, field); \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
TAILQ_FIRST((head)) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_TAIL(head, field); \
|
||||
TAILQ_NEXT((elm), field) = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_NEXT(elm, field); \
|
||||
QMD_TAILQ_CHECK_PREV(elm, field); \
|
||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else { \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
||||
TRASHIT((elm)->field.tqe_next); \
|
||||
TRASHIT((elm)->field.tqe_prev); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_first = (head1)->tqh_first; \
|
||||
struct type **swap_last = (head1)->tqh_last; \
|
||||
(head1)->tqh_first = (head2)->tqh_first; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
(head2)->tqh_first = swap_first; \
|
||||
(head2)->tqh_last = swap_last; \
|
||||
if ((swap_first = (head1)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head1)->tqh_first; \
|
||||
else \
|
||||
(head1)->tqh_last = &(head1)->tqh_first; \
|
||||
if ((swap_first = (head2)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head2)->tqh_first; \
|
||||
else \
|
||||
(head2)->tqh_last = &(head2)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Joakim Johansson <jocke@tbricks.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
int threads_runnable(unsigned int *threads_running)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Joakim Johansson <jocke@tbricks.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
void ptwq_set_current_thread_priority(int priority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Joakim Johansson <jocke@tbricks.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "private.h"
|
||||
|
||||
|
||||
/* no witem cache */
|
||||
|
||||
#if (WITEM_CACHE_TYPE == 1)
|
||||
|
||||
int
|
||||
witem_cache_init(void)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct work *
|
||||
witem_alloc(void (*func)(void *), void *func_arg)
|
||||
{
|
||||
struct work *witem;
|
||||
|
||||
while (!(witem = fastpath(malloc(ROUND_UP_TO_CACHELINE_SIZE(sizeof(*witem)))))) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
witem->gencount = 0;
|
||||
witem->flags = 0;
|
||||
witem->item_entry.stqe_next = 0;
|
||||
witem->func = func;
|
||||
witem->func_arg = func_arg;
|
||||
|
||||
return witem;
|
||||
}
|
||||
|
||||
void
|
||||
witem_free(struct work *wi)
|
||||
{
|
||||
dbg_printf("freed work item %p", wi);
|
||||
free(wi);
|
||||
}
|
||||
|
||||
void
|
||||
witem_cache_cleanup(void *value)
|
||||
{
|
||||
(void) value;
|
||||
}
|
||||
|
||||
/* libumem based object cache */
|
||||
|
||||
#elif (WITEM_CACHE_TYPE == 2)
|
||||
|
||||
#include <umem.h>
|
||||
|
||||
static umem_cache_t *witem_cache;
|
||||
|
||||
int
|
||||
witem_cache_init(void)
|
||||
{
|
||||
witem_cache = umem_cache_create((char *) "witem_cache",
|
||||
sizeof(struct work),
|
||||
CACHELINE_SIZE,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct work *
|
||||
witem_alloc(void (*func)(void *), void *func_arg)
|
||||
{
|
||||
struct work *witem;
|
||||
|
||||
while (!(witem = fastpath(umem_cache_alloc(witem_cache, UMEM_DEFAULT)))) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
witem->gencount = 0;
|
||||
witem->flags = 0;
|
||||
witem->item_entry.stqe_next = 0;
|
||||
witem->func = func;
|
||||
witem->func_arg = func_arg;
|
||||
|
||||
return witem;
|
||||
}
|
||||
|
||||
void
|
||||
witem_free(struct work *wi)
|
||||
{
|
||||
umem_cache_free(witem_cache, wi);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
witem_cache_cleanup(void *value)
|
||||
{
|
||||
void * p;
|
||||
p = value;
|
||||
}
|
||||
|
||||
/* TSD based cacheing per thread */
|
||||
|
||||
#elif (WITEM_CACHE_TYPE == 3)
|
||||
|
||||
pthread_key_t witem_cache_key;
|
||||
|
||||
int
|
||||
witem_cache_init(void)
|
||||
{
|
||||
pthread_key_create(&witem_cache_key, witem_cache_cleanup);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct work *
|
||||
witem_alloc_from_heap(void)
|
||||
{
|
||||
struct work *witem;
|
||||
|
||||
while (!(witem = fastpath(malloc(ROUND_UP_TO_CACHELINE_SIZE(sizeof(*witem)))))) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
witem->gencount = 0;
|
||||
witem->flags = 0;
|
||||
witem->item_entry.stqe_next = 0;
|
||||
|
||||
return witem;
|
||||
}
|
||||
|
||||
struct work *
|
||||
witem_alloc(void (*func)(void *), void *func_arg)
|
||||
{
|
||||
struct work *witem = fastpath(pthread_getspecific(witem_cache_key));
|
||||
if (witem)
|
||||
{
|
||||
pthread_setspecific(witem_cache_key, witem->wi_next);
|
||||
}
|
||||
else
|
||||
{
|
||||
witem = witem_alloc_from_heap();
|
||||
}
|
||||
|
||||
witem->func = func;
|
||||
witem->func_arg = func_arg;
|
||||
|
||||
return witem;
|
||||
}
|
||||
|
||||
void
|
||||
witem_free(struct work *witem)
|
||||
{
|
||||
struct work *prev_wi = pthread_getspecific(witem_cache_key);
|
||||
|
||||
witem->wi_next = prev_wi;
|
||||
|
||||
// We need to initialize here also...
|
||||
witem->gencount = 0;
|
||||
witem->flags = 0;
|
||||
witem->item_entry.stqe_next = 0;
|
||||
witem->func = NULL;
|
||||
witem->func_arg = NULL;
|
||||
|
||||
pthread_setspecific(witem_cache_key, witem);
|
||||
}
|
||||
|
||||
void
|
||||
witem_cache_cleanup(void *value)
|
||||
{
|
||||
struct work *wi, *next_wi = value;
|
||||
|
||||
while ((wi = next_wi)) {
|
||||
next_wi = wi->wi_next;
|
||||
free(wi);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
#error Invalid witem cache type specified
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#
|
||||
# Copyright (c) 2011 Marius Zwicker <marius@mlba-team.de>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#files
|
||||
set(API api/test.c)
|
||||
set(WITEM_CACHE witem_cache/test.c)
|
||||
set(LATENCY latency/latency.c latency/latency.h)
|
||||
|
||||
|
||||
#includes
|
||||
include_directories(
|
||||
../include
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
add_definitions(
|
||||
-DNO_CONFIG_H
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(test_api_pthread_workqueue ${API})
|
||||
target_link_libraries(test_api_pthread_workqueue pthread_workqueue)
|
||||
set_target_properties(test_api_pthread_workqueue PROPERTIES DEBUG_POSTFIX "D")
|
||||
|
||||
add_executable(test_latency_pthread_workqueue ${LATENCY})
|
||||
target_link_libraries(test_latency_pthread_workqueue pthread_workqueue)
|
||||
|
||||
if(NOT WIN32)
|
||||
|
||||
#add_executable(test_witem_cache_pthread_workqueue ${WITEM_CACHE})
|
||||
#target_link_libraries(test_witem_cache_pthread_workqueue pthreads_workqueue)
|
||||
|
||||
endif()
|
||||
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Copyright (c) 2011 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.
|
||||
#
|
||||
|
||||
# TODO: add libdispatch to TESTS
|
||||
|
||||
TESTS=api latency witem_cache
|
||||
EXTRA_TESTS=idle
|
||||
|
||||
all clean check:
|
||||
for x in $(TESTS) ; do cd $$x && make $@ && cd .. ; done
|
||||
|
||||
extra-check:
|
||||
for x in $(EXTRA_TESTS) ; do cd $$x && make check && cd .. ; done
|
||||
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (c) 2011 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.
|
||||
#
|
||||
|
||||
.PHONY :: install uninstall check dist dist-upload publish-www clean merge distclean fresh-build rpm edit cscope valgrind
|
||||
|
||||
include ../../config.mk
|
||||
|
||||
all: test-$(PROGRAM)
|
||||
|
||||
test-$(PROGRAM): test.c
|
||||
$(CC) $(CFLAGS) -g -O0 -o test-$(PROGRAM) -I../.. -I../../include -L../.. test.c -lpthread_workqueue -lpthread
|
||||
|
||||
check: test-$(PROGRAM)
|
||||
LD_LIBRARY_PATH=../..:/usr/sfw/lib/amd64 ./test-$(PROGRAM)
|
||||
|
||||
debug: test-$(PROGRAM)
|
||||
LD_LIBRARY_PATH=../.. gdb ./test-$(PROGRAM)
|
||||
|
||||
valgrind: test-$(PROGRAM)
|
||||
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=20 --track-fds=yes ./test-$(PROGRAM)
|
||||
|
||||
clean:
|
||||
rm -f test-$(PROGRAM)
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef DISPATCH_WIN_POSIX_SEMAPHORE_
|
||||
#define DISPATCH_WIN_POSIX_SEMAPHORE_
|
||||
|
||||
typedef HANDLE sem_t;
|
||||
|
||||
static int sem_init(sem_t * sem, int shared, unsigned int val)
|
||||
{
|
||||
*sem = CreateSemaphore(0, val, 1, 0);
|
||||
// TODO: Proper error handling
|
||||
return *sem == 0;
|
||||
}
|
||||
|
||||
static inline int sem_destroy(sem_t* s)
|
||||
{
|
||||
return CloseHandle(s) != 1;
|
||||
}
|
||||
|
||||
static inline int sem_post(sem_t* s)
|
||||
{
|
||||
return !ReleaseSemaphore(s, 1, 0);
|
||||
}
|
||||
|
||||
static inline int sem_wait(sem_t* s)
|
||||
{
|
||||
return WaitForSingleObject(s, INFINITE) == WAIT_FAILED;
|
||||
}
|
||||
|
||||
static int sem_timedwait(sem_t * sem, const struct timespec * timeout)
|
||||
{
|
||||
DWORD duration = (DWORD)(timeout->tv_nsec / 1000000) + (DWORD)(timeout->tv_sec * 1000);
|
||||
|
||||
switch(WaitForSingleObject(sem,duration) ){
|
||||
case WAIT_TIMEOUT:
|
||||
return ETIMEDOUT;
|
||||
case WAIT_FAILED:
|
||||
return EINVAL;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DISPATCH_WIN_POSIX_SEMAPHORE_ */
|
||||
@@ -0,0 +1,292 @@
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# include <sys/wait.h>
|
||||
# if !defined(NO_CONFIG_H)
|
||||
# include "config.h"
|
||||
# endif
|
||||
# include <semaphore.h>
|
||||
#else
|
||||
# define inline _inline
|
||||
# include "../../src/windows/platform.h"
|
||||
# include "posix_semaphore.h"
|
||||
#endif
|
||||
#include "../../src/private.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 <pthread_workqueue.h>
|
||||
|
||||
static int work_cnt;
|
||||
|
||||
/* If non-zero, extra debugging statements will be printed */
|
||||
static int dbg = 0;
|
||||
|
||||
static sem_t test_complete;
|
||||
static int test_rounds;
|
||||
#undef dbg_puts
|
||||
#define dbg_puts(s) if (dbg) puts(s)
|
||||
#undef dbg_printf
|
||||
#define dbg_printf(fmt,...) if (dbg) fprintf(stderr, fmt, __VA_ARGS__)
|
||||
|
||||
void additem(pthread_workqueue_t wq, void (*func)(void *),
|
||||
void * arg)
|
||||
{
|
||||
|
||||
int rv;
|
||||
|
||||
rv = pthread_workqueue_additem_np(wq, *func, arg, NULL, NULL);
|
||||
if (rv != 0)
|
||||
errx(1, "unable to add item: %s", strerror(rv));
|
||||
dbg_puts("added item\n");
|
||||
}
|
||||
|
||||
void
|
||||
mark_progress(void)
|
||||
{
|
||||
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
pthread_mutex_lock(&mtx);
|
||||
test_rounds--;
|
||||
pthread_mutex_unlock(&mtx);
|
||||
|
||||
dbg_printf("rounds = %d\n", test_rounds);
|
||||
if (test_rounds == 0) {
|
||||
sem_post(&test_complete);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sem_up(void *arg)
|
||||
{
|
||||
dbg_puts("semaphore UP\n");
|
||||
sem_post((sem_t *) arg);
|
||||
mark_progress();
|
||||
}
|
||||
|
||||
void
|
||||
sem_down(void *arg)
|
||||
{
|
||||
dbg_puts("semaphore DOWN\n");
|
||||
sem_wait((sem_t *) arg);
|
||||
dbg_puts("semaphore UP\n");
|
||||
sem_post((sem_t *) arg);
|
||||
mark_progress();
|
||||
}
|
||||
|
||||
void
|
||||
compute(void *arg)
|
||||
{
|
||||
int *count = (int *) arg;
|
||||
#define nval 5000
|
||||
int val[nval];
|
||||
int i,j;
|
||||
|
||||
/* Do some useless computation */
|
||||
for (i = 0; i < nval; i++) {
|
||||
val[i] = INT_MAX;
|
||||
}
|
||||
for (j = 0; j < nval; j++) {
|
||||
for (i = 0; i < nval; i++) {
|
||||
val[i] /= 3;
|
||||
val[i] *= 2;
|
||||
val[i] /= 4;
|
||||
val[i] *= 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (count != NULL)
|
||||
(*count)--;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sleepy(void *msg)
|
||||
{
|
||||
printf("%s\n", (char *) msg);
|
||||
if (strcmp(msg, "done") == 0)
|
||||
exit(0);
|
||||
sleep(random() % 6);
|
||||
}
|
||||
|
||||
void
|
||||
lazy(void *arg)
|
||||
{
|
||||
sleep(3);
|
||||
dbg_printf("item %lu complete\n", (unsigned long) arg);
|
||||
work_cnt--;
|
||||
}
|
||||
|
||||
void
|
||||
run_blocking_test(pthread_workqueue_t wq, int rounds)
|
||||
{
|
||||
long i = 0;
|
||||
work_cnt = rounds;
|
||||
for (i = 0; i < rounds; i++) {
|
||||
additem(wq, lazy, (void *) i);
|
||||
}
|
||||
while (work_cnt > 0)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
void
|
||||
run_cond_wait_test(pthread_workqueue_t wq)
|
||||
{
|
||||
const int rounds = 10;
|
||||
long i = 0;
|
||||
|
||||
sleep(3); /* Allow time for the workers to enter pthread_cond_wait() */
|
||||
work_cnt = rounds;
|
||||
for (i = 0; i < rounds; i++) {
|
||||
additem(wq, lazy, (void *) i);
|
||||
sleep(1);
|
||||
}
|
||||
while (work_cnt > 0)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
void
|
||||
run_load_test(pthread_workqueue_t wq)
|
||||
{
|
||||
char buf[16];
|
||||
int i = 0;
|
||||
for (i = 0; i < 1024; i++) {
|
||||
sprintf(buf, "%d", i);
|
||||
additem(wq, sleepy, strdup(buf));
|
||||
additem(wq, compute, NULL);
|
||||
}
|
||||
additem(wq, sleepy, "done");
|
||||
}
|
||||
|
||||
/* Try to overwhelm the CPU with computation requests */
|
||||
void
|
||||
run_stress_test(pthread_workqueue_t wq, int rounds)
|
||||
{
|
||||
int i = 0;
|
||||
work_cnt = rounds;
|
||||
for (i = 0; i < rounds; i++) {
|
||||
additem(wq, compute, &work_cnt);
|
||||
}
|
||||
while (work_cnt > 0)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that the library is reinitialized after fork(2) is called.
|
||||
*/
|
||||
void
|
||||
run_fork_test(pthread_workqueue_t wq)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
pid_t pid;
|
||||
int rv, status, timeout;
|
||||
|
||||
puts("fork test... ");
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
err(1, "fork");
|
||||
|
||||
if (pid == 0) {
|
||||
/* Child */
|
||||
wq = NULL;
|
||||
rv = pthread_workqueue_create_np(&wq, NULL);
|
||||
if (rv < 0)
|
||||
errx(1, "pthread_workqueue_create_np");
|
||||
work_cnt = 1;
|
||||
timeout = 5;
|
||||
additem(wq, compute, &work_cnt);
|
||||
while (work_cnt > 0) {
|
||||
sleep(1);
|
||||
if (--timeout == 0)
|
||||
errx(1, "work was not completed");
|
||||
}
|
||||
exit(0);
|
||||
} else {
|
||||
/* Parent */
|
||||
if (wait(&status) != pid)
|
||||
err(1, "waitpid");
|
||||
if (WEXITSTATUS(status) != 0)
|
||||
errx(1, "fork test failed");
|
||||
puts("ok\n");
|
||||
}
|
||||
#else
|
||||
puts("fork test... N/A\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
run_overcommit_test(pthread_workqueue_t wq)
|
||||
{
|
||||
sem_t sem;
|
||||
pthread_workqueue_t ocwq;
|
||||
pthread_workqueue_attr_t attr;
|
||||
int i, rv;
|
||||
|
||||
(void)wq;
|
||||
sem_init(&sem, 0, 0);
|
||||
|
||||
printf("pthread_workqueue_create_np() - overcommit enabled ");
|
||||
pthread_workqueue_attr_init_np(&attr);
|
||||
pthread_workqueue_attr_setovercommit_np(&attr, 1);
|
||||
rv = pthread_workqueue_create_np(&ocwq, &attr);
|
||||
if (rv != 0)
|
||||
err(1, "failed");
|
||||
|
||||
puts("ok\n");
|
||||
|
||||
printf("stress test - overcommit enabled ");
|
||||
run_stress_test(ocwq, 25);
|
||||
puts("ok\n");
|
||||
|
||||
/* FIXME: should use a multiple of the number of CPUs instead of magic number */
|
||||
printf("deadlock test - overcommit enabled ");
|
||||
test_rounds = 41;
|
||||
for (i = 0; i < 40; i++) {
|
||||
additem(ocwq, sem_down, &sem);
|
||||
}
|
||||
additem(ocwq, sem_up, &sem);
|
||||
sem_wait(&test_complete);
|
||||
puts("ok\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_workqueue_t wq;
|
||||
int rv;
|
||||
|
||||
#ifdef MAKE_STATIC
|
||||
pthread_workqueue_init_np();
|
||||
#endif
|
||||
|
||||
sem_init(&test_complete, 0, 0);
|
||||
|
||||
run_overcommit_test(NULL);
|
||||
|
||||
printf("pthread_workqueue_create_np().. ");
|
||||
rv = pthread_workqueue_create_np(&wq, NULL);
|
||||
if (rv != 0)
|
||||
err(1, "failed");
|
||||
printf("ok\n");
|
||||
|
||||
printf("stress test.. ");
|
||||
run_stress_test(wq, 25);
|
||||
printf("ok\n");
|
||||
|
||||
run_fork_test(wq);
|
||||
|
||||
//run_deadlock_test();
|
||||
// run_cond_wait_test();
|
||||
// run_blocking_test();
|
||||
//run_load_test();
|
||||
|
||||
|
||||
puts("All tests completed.\n");
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="test.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\libpthread_workqueue.vcxproj">
|
||||
<Project>{c8953032-a7fd-b3b1-6606-8dd078518b9a}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{6D7A0EA0-AE20-4584-AEE6-25B49823073A}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>test_api</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>..\..\include;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Copyright (c) 2011 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 ../../config.mk
|
||||
|
||||
all: idle
|
||||
|
||||
idle: main.c
|
||||
$(CC) $(CFLAGS) -g -O0 -o $@ -I../.. -I../../include -L../.. -Wl,-rpath,$(BASEDIR) main.c -lpthread_workqueue -lpthread
|
||||
|
||||
check: idle
|
||||
LD_LIBRARY_PATH=../..:/usr/sfw/lib/amd64 PWQ_DEBUG=yes ./idle
|
||||
|
||||
clean:
|
||||
rm -f idle
|
||||
@@ -0,0 +1,108 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Mark Heily <mark@heily.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <pthread_workqueue.h>
|
||||
|
||||
void f(void *arg)
|
||||
{
|
||||
long x = (long) arg;
|
||||
|
||||
printf("worker %ld running\n", x);
|
||||
sleep(1);
|
||||
printf("worker %ld finished\n", x);
|
||||
}
|
||||
|
||||
void run_idle_test(pthread_workqueue_t wq)
|
||||
{
|
||||
long i;
|
||||
int rv;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
rv = pthread_workqueue_additem_np(wq, f, (void *) i, NULL, NULL);
|
||||
if (rv != 0) abort();
|
||||
}
|
||||
|
||||
sleep(2);
|
||||
|
||||
int rounds = 0;
|
||||
for (;;) {
|
||||
unsigned long idle = pthread_workqueue_peek_np("combined_idle");
|
||||
unsigned long norml_idle = pthread_workqueue_peek_np("idle");
|
||||
unsigned long ocomm_idle = pthread_workqueue_peek_np("ocomm_idle");
|
||||
printf("idle = %lu (overcommit = %lu non-overcommit = %lu)\n",
|
||||
idle, ocomm_idle, norml_idle);
|
||||
if (idle == 0 || (norml_idle == 1 && ocomm_idle == 0))
|
||||
break;
|
||||
|
||||
sleep(1);
|
||||
if (rounds++ > 240) {
|
||||
printf("\n*** ERROR: idle threads were not reaped properly\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue a large number of short-lived workitems, to allow observation
|
||||
* of how idle threads are terminated.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pthread_workqueue_t wq;
|
||||
pthread_workqueue_t ocwq;
|
||||
pthread_workqueue_attr_t attr;
|
||||
pthread_workqueue_attr_t ocattr;
|
||||
int i, rounds;
|
||||
int rv;
|
||||
|
||||
if (argc == 2)
|
||||
rounds = atoi(argv[1]);
|
||||
else
|
||||
rounds = 1;
|
||||
|
||||
pthread_workqueue_attr_init_np(&attr);
|
||||
pthread_workqueue_attr_setovercommit_np(&attr, 0);
|
||||
rv = pthread_workqueue_create_np(&wq, &attr);
|
||||
if (rv != 0) abort();
|
||||
|
||||
pthread_workqueue_attr_init_np(&ocattr);
|
||||
pthread_workqueue_attr_setovercommit_np(&ocattr, 1);
|
||||
rv = pthread_workqueue_create_np(&ocwq, &ocattr);
|
||||
if (rv != 0) abort();
|
||||
|
||||
for (i = 0; i < rounds; i++) {
|
||||
run_idle_test(wq);
|
||||
run_idle_test(ocwq);
|
||||
}
|
||||
printf("\n---\nOK: all excess idle threads have been terminated after %d rounds.\n", rounds);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
CC=gcc
|
||||
|
||||
include ../../config.mk
|
||||
|
||||
all: latency
|
||||
|
||||
latency:
|
||||
$(CC) $(CFLAGS) -I../../include -L../.. -Wl,-rpath,$(BASEDIR) -o latency latency.c -lpthread_workqueue -lpthread -lrt
|
||||
|
||||
solaris:
|
||||
$(CC) $(CFLAGS) -I../../include -L../.. -R$(BASEDIR) -o latency latency.c -lpthread_workqueue -lpthread -lrt
|
||||
|
||||
macosx:
|
||||
$(CC) $(CFLAGS) -I../../include -L../.. -R$(BASEDIR) -o latencym latency.c -lpthread
|
||||
|
||||
check: latency
|
||||
LD_LIBRARY_PATH=../..:/usr/sfw/lib/amd64 ./latency
|
||||
|
||||
clean:
|
||||
rm -f latency
|
||||
rm -f latencym
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Joakim Johansson <jocke@tbricks.com>.
|
||||
*
|
||||
* @APPLE_APACHE_LICENSE_HEADER_START@
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_APACHE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
# include <pthread.h>
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "latency.h"
|
||||
|
||||
pthread_workqueue_t workqueues[WORKQUEUE_COUNT];
|
||||
struct wq_statistics workqueue_statistics[WORKQUEUE_COUNT];
|
||||
struct wq_event_generator workqueue_generator[GENERATOR_WORKQUEUE_COUNT];
|
||||
|
||||
struct wq_statistics global_statistics;
|
||||
unsigned int global_stats_used = 0;
|
||||
|
||||
pthread_mutex_t generator_mutex;
|
||||
pthread_cond_t generator_condition;
|
||||
static unsigned int events_processed;
|
||||
|
||||
#define PERCENTILE_COUNT 8
|
||||
double percentiles[PERCENTILE_COUNT] = {50.0, 80.0, 98.0, 99.0, 99.5, 99.8, 99.9, 99.99};
|
||||
mytime_t real_start, real_end;
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include <assert.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
static mach_timebase_info_data_t sTimebaseInfo;
|
||||
|
||||
// From http://developer.apple.com/library/mac/#qa/qa2004/qa1398.html
|
||||
unsigned long gettime(void)
|
||||
{
|
||||
return (mach_absolute_time() * sTimebaseInfo.numer / sTimebaseInfo.denom);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static mytime_t gettime(void)
|
||||
{
|
||||
#ifdef __linux__
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
|
||||
fprintf(stderr, "Failed to get high resolution clock! errno = %d\n", errno);
|
||||
return ((ts.tv_sec * NANOSECONDS_PER_SECOND) + ts.tv_nsec);
|
||||
#elif defined(_WIN32)
|
||||
LARGE_INTEGER now;
|
||||
LARGE_INTEGER freq;
|
||||
if (!QueryPerformanceCounter(&now) )
|
||||
fprintf(stderr, "Failed to get performance counter!\n");
|
||||
if (!QueryPerformanceFrequency(&freq) )
|
||||
fprintf(stderr, "Failed to get performance frequency!\n");
|
||||
|
||||
return (mytime_t)(now.QuadPart * NANOSECONDS_PER_SECOND / freq.QuadPart);
|
||||
#else
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_HIGHRES, &ts) != 0)
|
||||
fprintf(stderr, "Failed to get high resolution clock! errno = %d\n", errno);
|
||||
return ((ts.tv_sec * NANOSECONDS_PER_SECOND) + ts.tv_nsec);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static void my_sleep(unsigned long nanoseconds) {
|
||||
LARGE_INTEGER start, end;
|
||||
LARGE_INTEGER freq;
|
||||
|
||||
QueryPerformanceCounter(&start);
|
||||
QueryPerformanceFrequency(&freq);
|
||||
|
||||
// sleep with ms resolution ...
|
||||
Sleep(nanoseconds / 1000000);
|
||||
|
||||
// ... and busy-wait afterwards, until the requested delay was reached
|
||||
QueryPerformanceCounter(&end);
|
||||
while( (end.QuadPart - start.QuadPart) * NANOSECONDS_PER_SECOND / freq.QuadPart < nanoseconds ){
|
||||
YieldProcessor();
|
||||
QueryPerformanceCounter(&end);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// real resolution on solaris is at best system clock tick, i.e. 100Hz unless having the
|
||||
// high res system clock (1000Hz in that case)
|
||||
|
||||
static void my_sleep(unsigned long nanoseconds)
|
||||
{
|
||||
struct timespec timeout0;
|
||||
struct timespec timeout1;
|
||||
struct timespec* tmp;
|
||||
struct timespec* t0 = &timeout0;
|
||||
struct timespec* t1 = &timeout1;
|
||||
|
||||
t0->tv_sec = nanoseconds / NANOSECONDS_PER_SECOND;
|
||||
t0->tv_nsec = nanoseconds % NANOSECONDS_PER_SECOND;
|
||||
|
||||
while ((nanosleep(t0, t1) == (-1)) && (errno == EINTR))
|
||||
{
|
||||
tmp = t0;
|
||||
t0 = t1;
|
||||
t1 = tmp;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void _process_data(void* context)
|
||||
{
|
||||
struct wq_event *event = (struct wq_event *) context;
|
||||
mytime_t elapsed_time;
|
||||
|
||||
elapsed_time = gettime() - event->start_time;
|
||||
|
||||
workqueue_statistics[event->queue_index].avg = ((workqueue_statistics[event->queue_index].count * workqueue_statistics[event->queue_index].avg) + elapsed_time) / (workqueue_statistics[event->queue_index].count + 1);
|
||||
workqueue_statistics[event->queue_index].total += elapsed_time;
|
||||
workqueue_statistics[event->queue_index].count += 1;
|
||||
|
||||
if (elapsed_time < workqueue_statistics[event->queue_index].min ||
|
||||
workqueue_statistics[event->queue_index].min == 0)
|
||||
workqueue_statistics[event->queue_index].min = elapsed_time;
|
||||
|
||||
if (elapsed_time > workqueue_statistics[event->queue_index].max)
|
||||
workqueue_statistics[event->queue_index].max = elapsed_time;
|
||||
|
||||
if ((elapsed_time / 1000) < DISTRIBUTION_BUCKETS)
|
||||
workqueue_statistics[event->queue_index].distribution[(int)(elapsed_time / 1000)] += 1;
|
||||
else
|
||||
workqueue_statistics[event->queue_index].distribution[DISTRIBUTION_BUCKETS-1] += 1;
|
||||
|
||||
// allow generator thread to continue when all events have been processed
|
||||
if (atomic_dec_nv(&events_processed) == 0)
|
||||
{
|
||||
pthread_mutex_lock(&generator_mutex);
|
||||
pthread_cond_signal(&generator_condition);
|
||||
pthread_mutex_unlock(&generator_mutex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform a small microburst for this tick
|
||||
static void _event_tick(void* context)
|
||||
{
|
||||
struct wq_event *current_event;
|
||||
long i, generator_workqueue = (long) context;
|
||||
|
||||
for (i = 0; i < EVENTS_GENERATED_PER_TICK; i++)
|
||||
{
|
||||
current_event = &workqueue_generator[generator_workqueue].wq_events[i];
|
||||
current_event->start_time = gettime();
|
||||
current_event->queue_index = (current_event->start_time % WORKQUEUE_COUNT);
|
||||
|
||||
(void) pthread_workqueue_additem_np(workqueues[current_event->queue_index], _process_data, current_event, NULL, NULL);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void _generate_simulated_events()
|
||||
{
|
||||
unsigned long i = 0, tick;
|
||||
mytime_t overhead;
|
||||
mytime_t start, current, overhead_start = 0, overhead_end = 0;
|
||||
|
||||
start = current = gettime();
|
||||
|
||||
for (tick = 0; tick < TOTAL_TICKS_TO_RUN; tick++)
|
||||
{
|
||||
start = current = overhead_end;
|
||||
overhead = overhead_end - overhead_start;
|
||||
|
||||
// wait until we have waited proper amount of time for current rate
|
||||
// we should remove overhead of previous lap to not lag behind in data rate
|
||||
// one call to gethrtime() alone is around 211ns on Nehalem 2.93
|
||||
// use busy waiting in case the frequency is higher than the supported resolution of nanosleep()
|
||||
|
||||
if (overhead > EVENT_TIME_SLICE)
|
||||
{
|
||||
printf("Warning: Event processing overhead > event time slice, readjust test parameters.\n");
|
||||
}
|
||||
else
|
||||
if ((EVENT_GENERATION_FREQUENCY > SYSTEM_CLOCK_RESOLUTION) || FORCE_BUSY_LOOP)
|
||||
{
|
||||
while ((current - start) < (EVENT_TIME_SLICE - overhead))
|
||||
current = gettime();
|
||||
}
|
||||
else
|
||||
{
|
||||
my_sleep(EVENT_TIME_SLICE - overhead);
|
||||
}
|
||||
|
||||
overhead_start = gettime();
|
||||
|
||||
events_processed = GENERATOR_WORKQUEUE_COUNT * EVENTS_GENERATED_PER_TICK; // number of items that will be processed
|
||||
|
||||
#if (LATENCY_RUN_GENERATOR_IN_MAIN_THREAD == 0)
|
||||
for (i = 0; i < GENERATOR_WORKQUEUE_COUNT; i++)
|
||||
(void) pthread_workqueue_additem_np(workqueue_generator[i].wq, _event_tick, (void *) i, NULL, NULL);
|
||||
#else
|
||||
_event_tick((void *)i);
|
||||
#endif
|
||||
|
||||
// wait for all events to be processed
|
||||
pthread_mutex_lock(&generator_mutex);
|
||||
while (events_processed > 0)
|
||||
pthread_cond_wait(&generator_condition, &generator_mutex);
|
||||
pthread_mutex_unlock(&generator_mutex);
|
||||
|
||||
overhead_end = gettime();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void _gather_statistics(unsigned long queue_index)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
if (workqueue_statistics[queue_index].count > 0)
|
||||
{
|
||||
global_stats_used ++;
|
||||
|
||||
global_statistics.avg = ((global_statistics.count * global_statistics.avg) + (workqueue_statistics[queue_index].avg * workqueue_statistics[queue_index].count)) / (global_statistics.count + workqueue_statistics[queue_index].count);
|
||||
global_statistics.total += workqueue_statistics[queue_index].total;
|
||||
global_statistics.count += workqueue_statistics[queue_index].count;
|
||||
|
||||
if (workqueue_statistics[queue_index].min < global_statistics.min || global_statistics.min == 0)
|
||||
global_statistics.min = workqueue_statistics[queue_index].min;
|
||||
|
||||
if (workqueue_statistics[queue_index].max > global_statistics.max)
|
||||
global_statistics.max = workqueue_statistics[queue_index].max;
|
||||
|
||||
for (i = 0; i < DISTRIBUTION_BUCKETS; i++)
|
||||
global_statistics.distribution[i] += workqueue_statistics[queue_index].distribution[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void _print_statistics()
|
||||
{
|
||||
unsigned long i, j, total_events = 0, last_percentile = 0, accumulated_percentile = 0;
|
||||
|
||||
printf("Collecting statistics...\n");
|
||||
|
||||
for (i = 0; i < WORKQUEUE_COUNT; i++)
|
||||
_gather_statistics(i);
|
||||
|
||||
printf("Test is done, run time was %.3f seconds, %.1fM events generated and processed.\n", (double)((double)(real_end - real_start) / (double) NANOSECONDS_PER_SECOND), total_events/1000000.0);
|
||||
|
||||
//FIXME - casting from mytime_t (u_long) to int will truncate the result
|
||||
printf("Global dispatch queue aggregate statistics for %d queues: %dM events, min = %d ns, avg = %.1f ns, max = %d ns\n",
|
||||
global_stats_used, global_statistics.count/1000000, (int) global_statistics.min, global_statistics.avg, (int) global_statistics.max);
|
||||
|
||||
printf("\nDistribution:\n");
|
||||
for (i = 0; i < DISTRIBUTION_BUCKETS; i++)
|
||||
{
|
||||
printf("%3ld us: %d ", i, global_statistics.distribution[i]);
|
||||
for (j=0; j<(((double) global_statistics.distribution[i] / (double) global_statistics.count) * 400.0); j++)
|
||||
printf("*");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("\nPercentiles:\n");
|
||||
|
||||
for (i = 0; i < DISTRIBUTION_BUCKETS; i++)
|
||||
{
|
||||
while ((last_percentile < PERCENTILE_COUNT) && ((100.0 * ((double) accumulated_percentile / (double) global_statistics.count)) > percentiles[last_percentile]))
|
||||
{
|
||||
printf("%.2f < %ld us\n", percentiles[last_percentile], i-1);
|
||||
last_percentile++;
|
||||
}
|
||||
accumulated_percentile += global_statistics.distribution[i];
|
||||
}
|
||||
|
||||
while ((last_percentile < PERCENTILE_COUNT) && ((100.0 * ((double) accumulated_percentile / (double) global_statistics.count)) > percentiles[last_percentile]))
|
||||
{
|
||||
printf("%.2f > %d us\n", percentiles[last_percentile], DISTRIBUTION_BUCKETS-1);
|
||||
last_percentile++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
pthread_workqueue_attr_t attr;
|
||||
|
||||
#ifdef __APPLE__
|
||||
(void) mach_timebase_info(&sTimebaseInfo);
|
||||
#endif
|
||||
|
||||
#ifdef MAKE_STATIC
|
||||
pthread_workqueue_init_np();
|
||||
#endif
|
||||
|
||||
memset(&workqueues, 0, sizeof(workqueues));
|
||||
memset(&workqueue_statistics, 0, sizeof(workqueue_statistics));
|
||||
memset(&global_statistics, 0, sizeof(global_statistics));
|
||||
memset(&workqueue_generator, 0, sizeof(workqueue_generator));
|
||||
|
||||
pthread_mutex_init(&generator_mutex, NULL);
|
||||
pthread_cond_init(&generator_condition, NULL);
|
||||
|
||||
if (pthread_workqueue_attr_init_np(&attr) != 0)
|
||||
fprintf(stderr, "Failed to set workqueue attributes\n");
|
||||
|
||||
for (i = 0; i < GENERATOR_WORKQUEUE_COUNT; i++)
|
||||
{
|
||||
if (pthread_workqueue_attr_setqueuepriority_np(&attr, i) != 0)
|
||||
fprintf(stderr, "Failed to set workqueue priority\n");
|
||||
|
||||
if (pthread_workqueue_attr_setovercommit_np(&attr, 1) != 0)
|
||||
fprintf(stderr, "Failed to set workqueue overcommit\n");
|
||||
|
||||
workqueue_generator[i].wq_events = malloc(sizeof(struct wq_event) * EVENTS_GENERATED_PER_TICK);
|
||||
memset(workqueue_generator[i].wq_events, 0, (sizeof(struct wq_event) * EVENTS_GENERATED_PER_TICK));
|
||||
|
||||
if (pthread_workqueue_create_np(&workqueue_generator[i].wq, &attr) != 0)
|
||||
fprintf(stderr, "Failed to create workqueue\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < WORKQUEUE_COUNT; i++)
|
||||
{
|
||||
if (pthread_workqueue_attr_init_np(&attr) != 0)
|
||||
fprintf(stderr, "Failed to set workqueue attributes\n");
|
||||
|
||||
if (pthread_workqueue_attr_setqueuepriority_np(&attr, i) != 0)
|
||||
fprintf(stderr, "Failed to set workqueue priority\n");
|
||||
|
||||
if (pthread_workqueue_create_np(&workqueues[i], &attr) != 0)
|
||||
fprintf(stderr, "Failed to create workqueue\n");
|
||||
}
|
||||
|
||||
if (SLEEP_BEFORE_START > 0)
|
||||
{
|
||||
printf("Sleeping for %d seconds to allow for processor set configuration...\n",SLEEP_BEFORE_START);
|
||||
sleep(SLEEP_BEFORE_START);
|
||||
}
|
||||
|
||||
printf("%d workqueues, running for %d seconds at %d Hz, %d events per tick.\n",WORKQUEUE_COUNT, SECONDS_TO_RUN, EVENT_GENERATION_FREQUENCY, EVENTS_GENERATED_PER_TICK);
|
||||
|
||||
printf("Running %d generator threads at %dK events/s, the aggregated data rate is %dK events/s. %.2f MB is used for %.2fK events.\n",
|
||||
GENERATOR_WORKQUEUE_COUNT,AGGREGATE_DATA_RATE_PER_SECOND/1000, TOTAL_DATA_PER_SECOND/1000,
|
||||
(double) GENERATOR_WORKQUEUE_COUNT * ((sizeof(struct wq_event) * EVENTS_GENERATED_PER_TICK + sizeof(workqueues))/(1024.0*1024.0)),
|
||||
GENERATOR_WORKQUEUE_COUNT * EVENTS_GENERATED_PER_TICK/1000.0);
|
||||
|
||||
real_start = gettime();
|
||||
|
||||
_generate_simulated_events();
|
||||
|
||||
real_end = gettime();
|
||||
|
||||
_print_statistics();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Joakim Johansson <jocke@tbricks.com>.
|
||||
*
|
||||
* @APPLE_APACHE_LICENSE_HEADER_START@
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_APACHE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#include "pthread_workqueue.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include "../../src/windows/platform.h"
|
||||
#endif
|
||||
|
||||
// Run settings
|
||||
#define SECONDS_TO_RUN 10
|
||||
#define WORKQUEUE_COUNT 3
|
||||
#define GENERATOR_WORKQUEUE_COUNT 1
|
||||
#define SLEEP_BEFORE_START 0
|
||||
#define FORCE_BUSY_LOOP 0
|
||||
#define LATENCY_RUN_GENERATOR_IN_MAIN_THREAD 0
|
||||
|
||||
// Data rates
|
||||
#define EVENTS_GENERATED_PER_TICK 100 // simulate some small bursting
|
||||
#define EVENT_GENERATION_FREQUENCY 100 // events/s base rate, need to use busy loop = 1 if > 100Hz due to nanosleep resolution
|
||||
|
||||
#define AGGREGATE_DATA_RATE_PER_SECOND (EVENT_GENERATION_FREQUENCY * EVENTS_GENERATED_PER_TICK)
|
||||
#define EVENTS_TO_GENERATE (SECONDS_TO_RUN * AGGREGATE_DATA_RATE_PER_SECOND)
|
||||
#define TOTAL_DATA_PER_SECOND (AGGREGATE_DATA_RATE_PER_SECOND*GENERATOR_WORKQUEUE_COUNT)
|
||||
#define TOTAL_TICKS_TO_RUN (SECONDS_TO_RUN * EVENT_GENERATION_FREQUENCY)
|
||||
|
||||
#define NANOSECONDS_PER_SECOND 1000000000
|
||||
#define DISTRIBUTION_BUCKETS 20 // 1us per bucket
|
||||
#define EVENT_TIME_SLICE (NANOSECONDS_PER_SECOND / EVENT_GENERATION_FREQUENCY)
|
||||
#define SYSTEM_CLOCK_RESOLUTION 100
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef unsigned long long mytime_t;
|
||||
#else
|
||||
typedef unsigned long mytime_t;
|
||||
#endif
|
||||
|
||||
struct wq_event
|
||||
{
|
||||
unsigned int queue_index;
|
||||
mytime_t start_time;
|
||||
};
|
||||
|
||||
struct wq_statistics
|
||||
{
|
||||
mytime_t min;
|
||||
mytime_t max;
|
||||
double avg;
|
||||
mytime_t total;
|
||||
unsigned int count;
|
||||
unsigned int count_over_threshold;
|
||||
unsigned int distribution[DISTRIBUTION_BUCKETS];
|
||||
};
|
||||
|
||||
// We create our own separate workqueues for event generation
|
||||
struct wq_event_generator
|
||||
{
|
||||
pthread_workqueue_t wq;
|
||||
struct wq_event *wq_events;
|
||||
};
|
||||
|
||||
#ifdef __sun
|
||||
# include <atomic.h>
|
||||
# define atomic_inc atomic_inc_32
|
||||
# define atomic_dec atomic_dec_32
|
||||
# define atomic_inc_nv atomic_inc_32_nv
|
||||
# define atomic_dec_nv atomic_dec_32_nv
|
||||
#elif defined(_WIN32)
|
||||
# define atomic_inc(p) (void) InterlockedIncrement((p))
|
||||
# define atomic_dec(p) (void) InterlockedDecrement((p))
|
||||
# define atomic_inc_nv(p) InterlockedIncrement((p))
|
||||
# define atomic_dec_nv(p) InterlockedDecrement((p))
|
||||
#else
|
||||
# define atomic_inc(p) (void) __sync_add_and_fetch((p), 1)
|
||||
# define atomic_dec(p) (void) __sync_sub_and_fetch((p), 1)
|
||||
# define atomic_inc_nv(p) __sync_add_and_fetch((p), 1)
|
||||
# define atomic_dec_nv(p) __sync_sub_and_fetch((p), 1)
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
CC=clang
|
||||
|
||||
dispatch_api:
|
||||
$(CC) -o dispatch_api dispatch_api.c -lpthread -lrt -ldispatch -lBlocksRuntime -lkqueue -lpthread_workqueue
|
||||
|
||||
check: dispatch_api
|
||||
./dispatch_api
|
||||
|
||||
clean:
|
||||
rm -f dispatch_api
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Mark Heily.
|
||||
* Copyright (c) 2008-2009 Apple Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_APACHE_LICENSE_HEADER_START@
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_APACHE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
void
|
||||
work(void *context __attribute__((unused)))
|
||||
{
|
||||
puts("work complete");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
dispatch_queue_t q = dispatch_get_main_queue();
|
||||
dispatch_sync_f(dispatch_get_main_queue(), NULL, work);
|
||||
dispatch_main();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# Copyright (c) 2011 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.
|
||||
#
|
||||
|
||||
.PHONY :: install uninstall check dist dist-upload publish-www clean merge distclean fresh-build rpm edit cscope valgrind
|
||||
|
||||
include ../../config.mk
|
||||
|
||||
all: test-$(PROGRAM)
|
||||
|
||||
test-$(PROGRAM): test.c
|
||||
$(CC) $(CFLAGS) -g -O0 -o test-$(PROGRAM) -I../.. -I../../include -L../.. test.c -lpthread_workqueue -lpthread -lrt
|
||||
|
||||
check: test-$(PROGRAM)
|
||||
LD_LIBRARY_PATH=../..:/usr/sfw/lib/amd64 ./test-$(PROGRAM)
|
||||
|
||||
debug: test-$(PROGRAM)
|
||||
LD_LIBRARY_PATH=../.. gdb ./test-$(PROGRAM)
|
||||
|
||||
valgrind: test-$(PROGRAM)
|
||||
LD_LIBRARY_PATH=../..:/usr/sfw/lib/amd64 \
|
||||
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=20 --track-fds=yes ./test-$(PROGRAM)
|
||||
|
||||
clean:
|
||||
rm -f test-$(PROGRAM)
|
||||
@@ -0,0 +1,85 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Mark Heily <mark@heily.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "src/private.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 "pthread_workqueue.h"
|
||||
|
||||
pthread_workqueue_t wq;
|
||||
|
||||
void additem(void (*func)(void *),
|
||||
void * arg)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = pthread_workqueue_additem_np(wq, *func, arg, NULL, NULL);
|
||||
if (rv != 0)
|
||||
errx(1, "unable to add item: %s", strerror(rv));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
feedback(void *arg)
|
||||
{
|
||||
int *i = (int *) arg;
|
||||
struct timespec tv;
|
||||
|
||||
(*i)--;
|
||||
if ((*i) <= 0) {
|
||||
puts("All tests completed.\n");
|
||||
exit(0);
|
||||
} else {
|
||||
additem(feedback, arg);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_nsec = 750;
|
||||
nanosleep(&tv, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int i = 10000;
|
||||
|
||||
pthread_workqueue_create_np(&wq, NULL);
|
||||
additem(feedback, &i);
|
||||
pause();
|
||||
}
|
||||
Reference in New Issue
Block a user