Adding libdispatch, libkqueue and libpwq code

This commit is contained in:
Lubos Dolezel
2014-12-11 15:11:27 +01:00
parent 3585c73d00
commit f53189279c
81 changed files with 14711 additions and 0 deletions
Vendored Submodule
+1
Submodule external/libdispatch added at 617a4e1a31
Vendored Symlink
+1
View File
@@ -0,0 +1 @@
libkqueue-2.0.1
+279
View File
@@ -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
+42
View File
@@ -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.
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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_ */
+515
View File
@@ -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
View File
@@ -0,0 +1 @@
libkqueue
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
}
+280
View File
@@ -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);
}
+229
View File
@@ -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
}
+442
View File
@@ -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
View File
@@ -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
}
+112
View File
@@ -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
View File
@@ -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
}
+170
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1 @@
libpthread_workqueue-0.8.2
+160
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+231
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(&current_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(&current_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(&current_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
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+34
View File
@@ -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 */
+34
View File
@@ -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
+624
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+26
View File
@@ -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_ */
+292
View File
@@ -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
+108
View File
@@ -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();
}