libpthread_workqueue-0.8.2/0000755000175000017500000000000011610433055015304 5ustar mheilymheilylibpthread_workqueue-0.8.2/Makefile0000644000175000017500000001005211610433055016742 0ustar mheilymheily# # Copyright (c) 2010 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # 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/^/ /' libpthread_workqueue-0.8.2/ChangeLog0000644000175000017500000001320111610433055017053 0ustar mheilymheilyVersion 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. libpthread_workqueue-0.8.2/configure0000755000175000017500000002077611610433055017227 0ustar mheilymheily#!/bin/sh # # Copyright (c) 2009-2011 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # 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 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" libpthread_workqueue-0.8.2/config.inc0000644000175000017500000000266111610433055017251 0ustar mheilymheilyprogram="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 } libpthread_workqueue-0.8.2/pthread_workqueue.30000644000175000017500000001601211610433055021126 0ustar mheilymheily.\" 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. libpthread_workqueue-0.8.2/src/0000755000175000017500000000000011610433055016073 5ustar mheilymheilylibpthread_workqueue-0.8.2/src/posix/0000755000175000017500000000000011610433055017235 5ustar mheilymheilylibpthread_workqueue-0.8.2/src/posix/thread_info.c0000755000175000017500000002263111610433055021672 0ustar mheilymheily/* * Copyright (c) 2011, Joakim Johansson * * 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 #include #include #include #include #include /* /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 #include #include #include #include #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 libpthread_workqueue-0.8.2/src/posix/platform.h0000644000175000017500000000211311610433055021227 0ustar mheilymheily#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 #include #include #include #include #include #include #ifdef __sun # include #endif /* GCC atomic builtins. * See: http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html */ #ifdef __sun # include # define atomic_inc atomic_inc_32 # define atomic_dec atomic_dec_32 # 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 */ libpthread_workqueue-0.8.2/src/posix/manager.c0000755000175000017500000005724711610433055021035 0ustar mheilymheily/*- * Copyright (c) 2011, Joakim Johansson * Copyright (c) 2010, Mark Heily * Copyright (c) 2009, Stacey Son * 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 /* Environment setting */ unsigned int PWQ_RT_THREADS = 0; time_t PWQ_SPIN_USEC = 10000; // The number of microseconds we should spin loop if desired unsigned int PWQ_SPIN_THREADS = 0; // The number of threads that should be kept spinning unsigned volatile int current_threads_spinning = 0; // The number of threads currently spinning /* Tunable constants */ #define WORKER_IDLE_SECONDS_THRESHOLD 5 /* Function prototypes */ static unsigned int get_load_average(void); static void * worker_main(void *arg); static void * overcommit_worker_main(void *arg); static unsigned int get_process_limit(void); static void manager_start(void); static unsigned int cpu_count; static unsigned int worker_min; static unsigned int worker_idle_threshold; // we don't go down below this if we had to increase # workers /* Overcommit */ static struct _pthread_workqueue *ocwq[PTHREAD_WORKQUEUE_MAX]; static int ocwq_mask; static pthread_mutex_t ocwq_mtx; static pthread_cond_t ocwq_has_work; static unsigned int ocwq_idle_threads; /* Non-overcommit */ static struct _pthread_workqueue *wqlist[PTHREAD_WORKQUEUE_MAX]; static volatile unsigned int wqlist_mask; // mask of currently pending workqueues, atomics used for manipulation static pthread_mutex_t wqlist_mtx; static pthread_cond_t wqlist_has_work; static int wqlist_has_manager; static pthread_attr_t detached_attr; static struct { volatile unsigned int load, count, idle; unsigned int sb_wake_pending; pthread_mutex_t sb_wake_mtx; pthread_cond_t sb_wake_cond; } scoreboard; static unsigned int worker_idle_threshold_per_cpu(void) { switch (cpu_count) { case 0: case 1: case 2: case 4: return 2; case 6: return 3; case 8: case 12: return 4; case 16: case 24: return 6; case 32: case 64: return 8; default: return cpu_count / 4; } return 2; } static void manager_reinit(void) { if (manager_init() < 0) abort(); } int manager_init(void) { wqlist_has_manager = 0; pthread_cond_init(&wqlist_has_work, NULL); pthread_mutex_init(&wqlist_mtx, NULL); wqlist_mask = 0; pthread_cond_init(&ocwq_has_work, NULL); pthread_mutex_init(&ocwq_mtx, NULL); ocwq_mask = 0; ocwq_idle_threads = 0; witem_cache_init(); cpu_count = (PWQ_ACTIVE_CPU > 0) ? (PWQ_ACTIVE_CPU) : (unsigned int) sysconf(_SC_NPROCESSORS_ONLN); pthread_attr_init(&detached_attr); pthread_attr_setdetachstate(&detached_attr, PTHREAD_CREATE_DETACHED); /* Initialize the scoreboard */ pthread_cond_init(&scoreboard.sb_wake_cond, NULL); pthread_mutex_init(&scoreboard.sb_wake_mtx, NULL); /* Determine the initial thread pool constraints */ worker_min = 2; // we can start with a small amount, worker_idle_threshold will be used as new dynamic low watermark worker_idle_threshold = worker_idle_threshold_per_cpu(); if (pthread_atfork(NULL, NULL, manager_reinit) < 0) { dbg_perror("pthread_atfork()"); return (-1); } return (0); } void manager_workqueue_create(struct _pthread_workqueue *workq) { pthread_mutex_lock(&wqlist_mtx); if (!workq->overcommit && !wqlist_has_manager) manager_start(); if (workq->overcommit) { if (ocwq[workq->queueprio] == NULL) { ocwq[workq->queueprio] = workq; workq->wqlist_index = workq->queueprio; } else { puts("queue already exists\n"); abort(); } } else { if (wqlist[workq->queueprio] == NULL) { wqlist[workq->queueprio] = workq; //FIXME: sort by priority workq->wqlist_index = workq->queueprio; } else { puts("queue already exists\n"); abort(); } } pthread_mutex_unlock(&wqlist_mtx); } static struct work * wqlist_scan(int *queue_priority) { pthread_workqueue_t workq; struct work *witem; int idx; idx = ffs(wqlist_mask); if (idx == 0) return (NULL); workq = wqlist[idx - 1]; pthread_spin_lock(&workq->mtx); witem = STAILQ_FIRST(&workq->item_listhead); if (witem != NULL) { STAILQ_REMOVE_HEAD(&workq->item_listhead, item_entry); if (STAILQ_EMPTY(&workq->item_listhead)) { unsigned int wqlist_index_bit = (0x1 << workq->wqlist_index); unsigned int new_mask; // Remove this now empty wq from the mask, the only contention here is with threads performing the same // operation on another workqueue, so we will not be long // the 'bit' for this queue is protected by the spin lock, so we will only clear a bit which we have // ownership for (see additem() below for the corresponding part on the producer side) do { new_mask = atomic_and(&wqlist_mask, ~(wqlist_index_bit)); } while (new_mask & wqlist_index_bit); } if (queue_priority != NULL) *queue_priority = workq->queueprio; pthread_spin_unlock(&workq->mtx); return (witem); } else { // this could happen if multiple threads raced and found the same bit with ffs() and // emptied the queue completely, so we should just bail out pthread_spin_unlock(&workq->mtx); return (NULL); } } static void _wakeup_manager(void) { dbg_puts("asking manager to wake up"); pthread_mutex_lock(&scoreboard.sb_wake_mtx); scoreboard.sb_wake_pending = 1; pthread_cond_signal(&scoreboard.sb_wake_cond); pthread_mutex_unlock(&scoreboard.sb_wake_mtx); return; } static void * overcommit_worker_main(void *arg) { struct timespec ts; pthread_workqueue_t workq; void (*func)(void *); void *func_arg; struct work *witem; int rv, idx; (void)arg; pthread_mutex_lock(&ocwq_mtx); for (;;) { /* Find the highest priority workqueue that is non-empty */ idx = ffs(ocwq_mask); if (idx > 0) { workq = ocwq[idx - 1]; witem = STAILQ_FIRST(&workq->item_listhead); if (witem != NULL) { /* Remove the first work item */ STAILQ_REMOVE_HEAD(&workq->item_listhead, item_entry); if (STAILQ_EMPTY(&workq->item_listhead)) ocwq_mask &= ~(0x1 << workq->wqlist_index); /* Execute the work item */ pthread_mutex_unlock(&ocwq_mtx); func = witem->func; func_arg = witem->func_arg; witem_free(witem); func(func_arg); pthread_mutex_lock(&ocwq_mtx); continue; } } /* Wait for more work to be available. */ clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 15; ocwq_idle_threads++; dbg_printf("waiting for work (idle=%d)", ocwq_idle_threads); rv = pthread_cond_timedwait(&ocwq_has_work, &ocwq_mtx, &ts); if (rv != 0) { /* Normally, the signaler will decrement the idle counter, but this path is not taken in response to a signaler. */ ocwq_idle_threads--; pthread_mutex_unlock(&ocwq_mtx); if (rv == ETIMEDOUT) { dbg_puts("timeout, no work available"); break; } else { dbg_perror("pthread_cond_timedwait"); //TODO: some kind of crash mechanism break; } } } dbg_printf("worker exiting (idle=%d)", ocwq_idle_threads); pthread_exit(NULL); } static void * worker_main(void *arg) { struct work *witem; void (*func)(void *); void *func_arg; int queue_priority = 0; struct timespec ts_start, ts_now; (void) arg; dbg_puts("worker thread started"); if (PWQ_RT_THREADS) ptwq_set_current_thread_priority(WORKQ_HIGH_PRIOQUEUE); // start at highest priority possible for (;;) { witem = wqlist_scan(&queue_priority); // Only take overhead of sleeping and/or spinning if we // could not get a witem cheaply using the spinlock above if (slowpath(!witem)) { // Optional busy loop for getting the next item for a while if so configured // We'll only spin limited thread at a time (this is really mostly useful when running // in low latency configurations using dedicated processor sets) if ((PWQ_SPIN_THREADS > 0) && (current_threads_spinning <= PWQ_SPIN_THREADS)) { atomic_inc(¤t_threads_spinning); // If we are racing with another thread, let's skip // spinning and instead go through the slowpath below if (current_threads_spinning <= PWQ_SPIN_THREADS) { clock_gettime(CLOCK_REALTIME, &ts_start); ts_now.tv_sec = ts_start.tv_sec; ts_now.tv_nsec = ts_start.tv_nsec; // Spin until we get an item or PWQ_SPIN_USEC microseconds passes while (!witem && (((ts_now.tv_sec - ts_start.tv_sec) * 1000000) + (((ts_now.tv_nsec - ts_start.tv_nsec) / 1000)) <= PWQ_SPIN_USEC)) { witem = wqlist_scan(&queue_priority); if (!witem) { // Perhaps a hardware pause // instruction could be used here to keep the pace down, probably not needed though clock_gettime(CLOCK_REALTIME, &ts_now); } } } atomic_dec(¤t_threads_spinning); } // No witem from the busy loop, let's wait for wakeup if (!witem) { pthread_mutex_lock(&wqlist_mtx); /* TODO: Consider using pthread_cond_timedwait() so that workers can self-terminate if they are idle too long. This would also be a failsafe in case there are bugs with the scoreboard that cause us to "leak" workers. */ while ((witem = wqlist_scan(&queue_priority)) == NULL) pthread_cond_wait(&wqlist_has_work, &wqlist_mtx); pthread_mutex_unlock(&wqlist_mtx); } } atomic_dec(&scoreboard.idle); if (slowpath(witem->func == NULL)) { dbg_puts("worker exiting.."); atomic_dec(&scoreboard.count); witem_free(witem); pthread_exit(0); } dbg_printf("count=%u idle=%u wake_pending=%u", scoreboard.count, scoreboard.idle, scoreboard.sb_wake_pending); /* Force the manager thread to wakeup if all workers are busy */ if (slowpath(scoreboard.idle == 0 && !scoreboard.sb_wake_pending)) _wakeup_manager(); // If using RT threads, decrease thread prio if we aren't a high prio queue if (PWQ_RT_THREADS && (queue_priority != WORKQ_HIGH_PRIOQUEUE)) ptwq_set_current_thread_priority(queue_priority); /* Invoke the callback function, free witem first for possible reuse */ func = witem->func; func_arg = witem->func_arg; witem_free(witem); func(func_arg); atomic_inc(&scoreboard.idle); // initial inc was one in worker_start, this is to avoid a race // Only take the overhead and change RT priority back if it was not a high priority queue being serviced if (PWQ_RT_THREADS && (queue_priority != WORKQ_HIGH_PRIOQUEUE)) ptwq_set_current_thread_priority(WORKQ_HIGH_PRIOQUEUE); } /* NOTREACHED */ return (NULL); } static int worker_start(void) { pthread_t tid; dbg_puts("Spawning another worker"); atomic_inc(&scoreboard.idle); atomic_inc(&scoreboard.count); if (pthread_create(&tid, &detached_attr, worker_main, NULL) != 0) { dbg_perror("pthread_create(3)"); atomic_dec(&scoreboard.idle); atomic_dec(&scoreboard.count); return (-1); } return (0); } static int worker_stop(void) { struct work *witem; pthread_workqueue_t workq; int i; unsigned int wqlist_index_bit, new_mask; witem = witem_alloc(NULL, NULL); pthread_mutex_lock(&wqlist_mtx); for (i = 0; i < PTHREAD_WORKQUEUE_MAX; i++) { workq = wqlist[i]; if (workq == NULL) continue; wqlist_index_bit = (0x1 << workq->wqlist_index); pthread_spin_lock(&workq->mtx); do { new_mask = atomic_or(&wqlist_mask, wqlist_index_bit); } while (!(new_mask & wqlist_index_bit)); STAILQ_INSERT_TAIL(&workq->item_listhead, witem, item_entry); pthread_spin_unlock(&workq->mtx); pthread_cond_signal(&wqlist_has_work); pthread_mutex_unlock(&wqlist_mtx); return (0); } /* FIXME: this means there are no workqueues.. should never happen */ dbg_puts("Attempting to add a workitem without a workqueue"); abort(); return (-1); } static void * manager_main(void *unused __attribute__ ((unused))) { unsigned int load_max = cpu_count; unsigned int worker_max, current_thread_count = 0; unsigned int worker_idle_seconds_accumulated = 0; unsigned int max_threads_to_stop = 0; unsigned int i; int cond_wait_rv = 0; sigset_t sigmask; struct timespec ts; struct timeval tp; worker_max = get_process_limit(); scoreboard.load = get_load_average(); /* Block all signals */ sigfillset(&sigmask); pthread_sigmask(SIG_BLOCK, &sigmask, NULL); /* Create the minimum number of workers */ scoreboard.count = 0; for (i = 0; i < worker_min; i++) worker_start(); for (;;) { pthread_mutex_lock(&scoreboard.sb_wake_mtx); dbg_puts("manager is sleeping"); (void) gettimeofday(&tp, NULL); // TODO - error checking /* Convert from timeval to timespec */ ts.tv_sec = tp.tv_sec; ts.tv_nsec = tp.tv_usec * 1000; ts.tv_sec += 1; // wake once per second and check if we have too many idle threads... // We should only sleep on the condition if there are no pending signal, spurious wakeup is also ok if (scoreboard.sb_wake_pending == 0) cond_wait_rv = pthread_cond_timedwait(&scoreboard.sb_wake_cond, &scoreboard.sb_wake_mtx, &ts); scoreboard.sb_wake_pending = 0; // we must set this before spawning any new threads below, or we race... dbg_puts("manager is awake"); dbg_printf("load=%u idle=%u workers=%u max_workers=%u worker_min = %u", scoreboard.load, scoreboard.idle, scoreboard.count, worker_max, worker_min); // If no workers available, check if we should create a new one if (scoreboard.idle == 0 && (scoreboard.count > 0)) // last part required for an extremely unlikely race at startup { scoreboard.load = get_load_average(); if ((scoreboard.load < load_max) && (scoreboard.count < worker_max)) { if (scoreboard.count < worker_idle_threshold) // allow cheap rampup up to worker_idle_threshold without going to /proc { worker_start(); } else // check through /proc, will be a bit more expensive in terms of latency if (threads_runnable(¤t_thread_count) == 0) { // only start thread if we have less runnable threads than cpus if (current_thread_count >= cpu_count) { dbg_printf("Not spawning worker thread, thread_runnable = %d >= cpu_count = %d", current_thread_count, cpu_count); } else { worker_start(); } } else // always start thread if we can't get runnable count { worker_start(); } } else // high load, allow rampup up to worker_idle_threshold regardless of this { if (scoreboard.count < worker_idle_threshold) { worker_start(); } } } else { if (cond_wait_rv == ETIMEDOUT) // Only check for ramp down on the 'timer tick' { if ((scoreboard.idle - worker_idle_threshold) > 0) // only accumulate if there are 'too many' idle threads { worker_idle_seconds_accumulated += scoreboard.idle; // keep track of many idle 'thread seconds' we have dbg_printf("worker_idle_seconds_accumulated = %d, scoreboard.idle = %d, scoreboard.count = %d\n", worker_idle_seconds_accumulated, scoreboard.idle, scoreboard.count); } // Only consider ramp down if we have accumulated enough thread 'idle seconds' // this logic will ensure that a large number of idle threads will ramp down faster max_threads_to_stop = worker_idle_seconds_accumulated / WORKER_IDLE_SECONDS_THRESHOLD; if (max_threads_to_stop > 0) { worker_idle_seconds_accumulated = 0; if (max_threads_to_stop > (scoreboard.idle - worker_idle_threshold)) max_threads_to_stop = (scoreboard.idle - worker_idle_threshold); // Only stop threads if we actually have 'too many' idle ones in the pool if (scoreboard.idle > worker_idle_threshold) { for (i = 0; i < max_threads_to_stop; i++) { dbg_puts("Removing one thread from the thread pool"); worker_stop(); } } } } } pthread_mutex_unlock(&scoreboard.sb_wake_mtx); } /*NOTREACHED*/ return (NULL); } static void manager_start(void) { pthread_t tid; int rv; dbg_puts("starting the manager thread"); do { rv = pthread_create(&tid, &detached_attr, manager_main, NULL); if (rv == EAGAIN) { sleep(1); } else if (rv != 0) { /* FIXME: not nice */ dbg_printf("thread creation failed, rv=%d", rv); abort(); } } while (rv != 0); wqlist_has_manager = 1; } void manager_workqueue_additem(struct _pthread_workqueue *workq, struct work *witem) { unsigned int wqlist_index_bit = (0x1 << workq->wqlist_index); if (workq->overcommit) { pthread_t tid; pthread_mutex_lock(&ocwq_mtx); pthread_spin_lock(&workq->mtx); STAILQ_INSERT_TAIL(&workq->item_listhead, witem, item_entry); pthread_spin_unlock(&workq->mtx); ocwq_mask |= wqlist_index_bit; if (ocwq_idle_threads > 0) { dbg_puts("signaling an idle worker"); pthread_cond_signal(&ocwq_has_work); ocwq_idle_threads--; } else { (void)pthread_create(&tid, &detached_attr, overcommit_worker_main, NULL); } pthread_mutex_unlock(&ocwq_mtx); } else { pthread_spin_lock(&workq->mtx); // Only set the mask for the first item added to the workqueue. if (STAILQ_EMPTY(&workq->item_listhead)) { unsigned int new_mask; // The only possible contention here are with threads performing the same // operation on another workqueue, so we will not be blocked long... // Threads operating on the same workqueue will be serialized by the spinlock so it is very unlikely. do { new_mask = atomic_or(&wqlist_mask, wqlist_index_bit); } while (!(new_mask & wqlist_index_bit)); } STAILQ_INSERT_TAIL(&workq->item_listhead, witem, item_entry); pthread_spin_unlock(&workq->mtx); // Only signal thread wakeup if there are idle threads available // and no other thread have managed to race us and empty the wqlist on our behalf already if ((scoreboard.idle > 0)) // && ((wqlist_mask & wqlist_index_bit) != 0)) // disabling this fringe optimization for now { pthread_mutex_lock(&wqlist_mtx); pthread_cond_signal(&wqlist_has_work); pthread_mutex_unlock(&wqlist_mtx); } } } static unsigned int get_process_limit(void) { #if __linux__ struct rlimit rlim; if (getrlimit(RLIMIT_NPROC, &rlim) < 0) { dbg_perror("getrlimit(2)"); return (50); } else { return (rlim.rlim_max); } #else /* Solaris doesn't define this limit anywhere I can see.. */ return (64); #endif } static unsigned int get_load_average(void) { double loadavg; /* TODO: proper error handling */ if (getloadavg(&loadavg, 1) != 1) { dbg_perror("getloadavg(3)"); return (1); } if (loadavg > INT_MAX || loadavg < 0) loadavg = 1; return ((int) loadavg); } unsigned long manager_peek(const char *key) { uint64_t rv; if (strcmp(key, "combined_idle") == 0) { rv = scoreboard.idle; if (scoreboard.idle > worker_min) rv -= worker_min; rv += ocwq_idle_threads; } else if (strcmp(key, "idle") == 0) { rv = scoreboard.idle; if (scoreboard.idle > worker_min) rv -= worker_min; } else if (strcmp(key, "ocomm_idle") == 0) { rv = ocwq_idle_threads; } else { dbg_printf("invalid key: %s", key); abort(); } return rv; } libpthread_workqueue-0.8.2/src/posix/thread_rt.c0000755000175000017500000000547711610433055021375 0ustar mheilymheily/* * Copyright (c) 2011, Joakim Johansson * * 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 #include #include #include #include #include #include #include #include #include #include // 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 libpthread_workqueue-0.8.2/src/private.h0000755000175000017500000001073111610433055017723 0ustar mheilymheily/*- * Copyright (c) 2011, Mark Heily * Copyright (c) 2009, Stacey Son * 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 #include #include #include #include #include #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 */ libpthread_workqueue-0.8.2/src/windows/0000755000175000017500000000000011610433055017565 5ustar mheilymheilylibpthread_workqueue-0.8.2/src/windows/pthread_cond.h0000644000175000017500000000366611610433055022403 0ustar mheilymheily/*- * Copyright (c) 2011, Mark Heily * 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 libpthread_workqueue-0.8.2/src/windows/thread_info.c0000755000175000017500000000263511610433055022224 0ustar mheilymheily/* * Copyright (c) 2011, Joakim Johansson * * 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; } libpthread_workqueue-0.8.2/src/windows/platform.c0000644000175000017500000000401611610433055021556 0ustar mheilymheily/*- * Copyright (c) 2011, Mark Heily * 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. } #endiflibpthread_workqueue-0.8.2/src/windows/platform.h0000644000175000017500000000160711610433055021566 0ustar mheilymheily#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 #include #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 */ libpthread_workqueue-0.8.2/src/windows/queue.h0000644000175000017500000005117111610433055021067 0ustar mheilymheily/*- * 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_ */ libpthread_workqueue-0.8.2/src/windows/manager.c0000644000175000017500000001177311610433055021354 0ustar mheilymheily/*- * Copyright (c) 2011, Mark Heily * 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; } libpthread_workqueue-0.8.2/src/windows/thread_rt.c0000755000175000017500000000263611610433055021717 0ustar mheilymheily/* * Copyright (c) 2011, Joakim Johansson * * 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; } libpthread_workqueue-0.8.2/src/windows/winpthreads.h0000644000175000017500000006435411610433055022302 0ustar mheilymheily/* * Posix Threads library for Microsoft Windows * * Use at own risk, there is no implied warranty to this code. * It uses undocumented features of Microsoft Windows that can change * at any time in the future. * * (C) 2010 Lockless 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: * * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 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. * * Neither the name of Lockless Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN * 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 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. */ #ifndef WIN_PTHREADS #define WIN_PTHREADS #include #include #include #include #include #include #include #include # #define PTHREAD_CANCEL_DISABLE 0 #define PTHREAD_CANCEL_ENABLE 0x01 #define PTHREAD_CANCEL_DEFERRED 0 #define PTHREAD_CANCEL_ASYNCHRONOUS 0x02 #define PTHREAD_CREATE_JOINABLE 0 #define PTHREAD_CREATE_DETACHED 0x04 #define PTHREAD_EXPLICT_SCHED 0 #define PTHREAD_INHERIT_SCHED 0x08 #define PTHREAD_SCOPE_PROCESS 0 #define PTHREAD_SCOPE_SYSTEM 0x10 #define PTHREAD_DEFAULT_ATTR (PTHREAD_CANCEL_ENABLE) #define PTHREAD_CANCELED ((void *) 0xDEADBEEF) #define PTHREAD_ONCE_INIT 0 #define PTHREAD_MUTEX_INITIALIZER {(void*)-1,-1,0,0,0,0} #define PTHREAD_RWLOCK_INITIALIZER {0} #define PTHREAD_COND_INITIALIZER {0} #define PTHREAD_BARRIER_INITIALIZER \ {0,0,PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER} #define PTHREAD_SPINLOCK_INITIALIZER 0 #define PTHREAD_DESTRUCTOR_ITERATIONS 256 #define PTHREAD_KEYS_MAX (1<<20) #define PTHREAD_MUTEX_NORMAL 0 #define PTHREAD_MUTEX_ERRORCHECK 1 #define PTHREAD_MUTEX_RECURSIVE 2 #define PTHREAD_MUTEX_DEFAULT 3 #define PTHREAD_MUTEX_SHARED 4 #define PTHREAD_MUTEX_PRIVATE 0 #define PTHREAD_PRIO_NONE 0 #define PTHREAD_PRIO_INHERIT 8 #define PTHREAD_PRIO_PROTECT 16 #define PTHREAD_PRIO_MULT 32 #define PTHREAD_PROCESS_SHARED 0 #define PTHREAD_PROCESS_PRIVATE 1 #define PTHREAD_BARRIER_SERIAL_THREAD 1 /* Windows doesn't have this, so declare it ourselves. */ struct timespec { /* long long in windows is the same as long in unix for 64bit */ long long tv_sec; long long tv_nsec; }; typedef struct _pthread_cleanup _pthread_cleanup; struct _pthread_cleanup { void (*func)(void *); void *arg; _pthread_cleanup *next; }; struct _pthread_v { void *ret_arg; void *(* func)(void *); _pthread_cleanup *clean; HANDLE h; int cancelled; unsigned p_state; unsigned int keymax; void **keyval; jmp_buf jb; }; typedef struct _pthread_v *pthread_t; typedef struct pthread_barrier_t pthread_barrier_t; struct pthread_barrier_t { int count; int total; CRITICAL_SECTION m; CONDITION_VARIABLE cv; }; typedef struct pthread_attr_t pthread_attr_t; struct pthread_attr_t { unsigned p_state; void *stack; size_t s_size; }; typedef long pthread_once_t; typedef unsigned pthread_mutexattr_t; typedef SRWLOCK pthread_rwlock_t; typedef CRITICAL_SECTION pthread_mutex_t; typedef unsigned pthread_key_t; typedef void *pthread_barrierattr_t; typedef long pthread_spinlock_t; typedef int pthread_condattr_t; typedef CONDITION_VARIABLE pthread_cond_t; typedef int pthread_rwlockattr_t; volatile long _pthread_cancelling; int _pthread_concur; /* Will default to zero as needed */ pthread_once_t _pthread_tls_once; DWORD _pthread_tls; /* Note initializer is zero, so this works */ pthread_rwlock_t _pthread_key_lock; unsigned int _pthread_key_max; unsigned int _pthread_key_sch; void (**_pthread_key_dest)(void *); /* Prototypes */ static int pthread_rwlock_unlock(pthread_rwlock_t *l); #define pthread_cleanup_push(F, A)\ {\ const _pthread_cleanup _pthread_cup = {(F), (A), pthread_self()->clean};\ _ReadWriteBarrier();\ pthread_self()->clean = (_pthread_cleanup *) &_pthread_cup;\ _ReadWriteBarrier() /* Note that if async cancelling is used, then there is a race here */ #define pthread_cleanup_pop(E)\ (pthread_self()->clean = _pthread_cup.next, (E?_pthread_cup.func(_pthread_cup.arg):0));} static void _pthread_once_cleanup(pthread_once_t *o) { *o = 0; } static pthread_t pthread_self(void); static int pthread_once(pthread_once_t *o, void (*func)(void)) { long state = *o; _ReadWriteBarrier(); while (state != 1) { if (!state) { if (!_InterlockedCompareExchange(o, 2, 0)) { /* Success */ pthread_cleanup_push(_pthread_once_cleanup, o); func(); pthread_cleanup_pop(0); /* Mark as done */ *o = 1; return 0; } } YieldProcessor(); _ReadWriteBarrier(); state = *o; } /* Done */ return 0; } static int _pthread_once_raw(pthread_once_t *o, void (*func)(void)) { long state = *o; _ReadWriteBarrier(); while (state != 1) { if (!state) { if (!_InterlockedCompareExchange(o, 2, 0)) { /* Success */ func(); /* Mark as done */ *o = 1; return 0; } } YieldProcessor(); _ReadWriteBarrier(); state = *o; } /* Done */ return 0; } static int pthread_mutex_lock(pthread_mutex_t *m) { EnterCriticalSection(m); return 0; } static int pthread_mutex_unlock(pthread_mutex_t *m) { LeaveCriticalSection(m); return 0; } static int pthread_mutex_trylock(pthread_mutex_t *m) { return TryEnterCriticalSection(m) ? 0 : EBUSY; } static int pthread_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t *a) { (void) a; InitializeCriticalSection(m); return 0; } static int pthread_mutex_destroy(pthread_mutex_t *m) { DeleteCriticalSection(m); return 0; } #define pthread_mutex_getprioceiling(M, P) ENOTSUP #define pthread_mutex_setprioceiling(M, P) ENOTSUP static int pthread_equal(pthread_t t1, pthread_t t2) { return t1 == t2; } static void pthread_testcancel(void); static int pthread_rwlock_init(pthread_rwlock_t *l, pthread_rwlockattr_t *a) { (void) a; InitializeSRWLock(l); return 0; } static int pthread_rwlock_destroy(pthread_rwlock_t *l) { (void) *l; return 0; } static int pthread_rwlock_rdlock(pthread_rwlock_t *l) { pthread_testcancel(); AcquireSRWLockShared(l); return 0; } static int pthread_rwlock_wrlock(pthread_rwlock_t *l) { pthread_testcancel(); AcquireSRWLockExclusive(l); return 0; } static void pthread_tls_init(void) { _pthread_tls = TlsAlloc(); /* Cannot continue if out of indexes */ if (_pthread_tls == TLS_OUT_OF_INDEXES) abort(); } static void _pthread_cleanup_dest(pthread_t t) { unsigned int i, j; for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { int flag = 0; for (i = 0; i < t->keymax; i++) { void *val = t->keyval[i]; if (val) { pthread_rwlock_rdlock(&_pthread_key_lock); if ((uintptr_t) _pthread_key_dest[i] > 1) { /* Call destructor */ t->keyval[i] = NULL; _pthread_key_dest[i](val); flag = 1; } pthread_rwlock_unlock(&_pthread_key_lock); } } /* Nothing to do? */ if (!flag) return; } } static pthread_t pthread_self(void) { pthread_t t; _pthread_once_raw(&_pthread_tls_once, pthread_tls_init); t = TlsGetValue(_pthread_tls); /* Main thread? */ if (!t) { t = malloc(sizeof(struct _pthread_v)); /* If cannot initialize main thread, then the only thing we can do is abort */ if (!t) abort(); t->ret_arg = NULL; t->func = NULL; t->clean = NULL; t->cancelled = 0; t->p_state = PTHREAD_DEFAULT_ATTR; t->keymax = 0; t->keyval = NULL; t->h = GetCurrentThread(); /* Save for later */ TlsSetValue(_pthread_tls, t); if (setjmp(t->jb)) { /* Make sure we free ourselves if we are detached */ if (!t->h) free(t); /* Time to die */ _endthreadex(0); } } return t; } static int pthread_rwlock_unlock(pthread_rwlock_t *l) { void *state = *(void **)l; if (state == (void *) 1) { /* Known to be an exclusive lock */ ReleaseSRWLockExclusive(l); } else { /* A shared unlock will work */ ReleaseSRWLockShared(l); } return 0; } static int pthread_rwlock_tryrdlock(pthread_rwlock_t *l) { /* Get the current state of the lock */ void *state = *(void **) l; if (!state) { /* Unlocked to locked */ if (!InterlockedCompareExchangePointer((void *) l, (void *)0x11, NULL)) return 0; return EBUSY; } /* A single writer exists */ if (state == (void *) 1) return EBUSY; /* Multiple writers exist? */ if ((uintptr_t) state & 14) return EBUSY; if (InterlockedCompareExchangePointer((void *) l, (void *) ((uintptr_t)state + 16), state) == state) return 0; return EBUSY; } static int pthread_rwlock_trywrlock(pthread_rwlock_t *l) { /* Try to grab lock if it has no users */ if (!InterlockedCompareExchangePointer((void *) l, (void *)1, NULL)) return 0; return EBUSY; } static unsigned long long _pthread_time_in_ms(void) { struct __timeb64 tb; _ftime64_s(&tb); return tb.time * 1000 + tb.millitm; } static unsigned long long _pthread_time_in_ms_from_timespec(const struct timespec *ts) { unsigned long long t = ts->tv_sec * 1000; t += ts->tv_nsec / 1000000; return t; } static unsigned long long _pthread_rel_time_in_ms(const struct timespec *ts) { unsigned long long t1 = _pthread_time_in_ms_from_timespec(ts); unsigned long long t2 = _pthread_time_in_ms(); /* Prevent underflow */ if (t1 < t2) return 1; return t1 - t2; } static int pthread_rwlock_timedrdlock(pthread_rwlock_t *l, const struct timespec *ts) { unsigned long long ct = _pthread_time_in_ms(); unsigned long long t = _pthread_time_in_ms_from_timespec(ts); pthread_testcancel(); /* Use a busy-loop */ while (1) { /* Try to grab lock */ if (!pthread_rwlock_tryrdlock(l)) return 0; /* Get current time */ ct = _pthread_time_in_ms(); /* Have we waited long enough? */ if (ct > t) return ETIMEDOUT; } } static int pthread_rwlock_timedwrlock(pthread_rwlock_t *l, const struct timespec *ts) { unsigned long long ct = _pthread_time_in_ms(); unsigned long long t = _pthread_time_in_ms_from_timespec(ts); pthread_testcancel(); /* Use a busy-loop */ while (1) { /* Try to grab lock */ if (!pthread_rwlock_trywrlock(l)) return 0; /* Get current time */ ct = _pthread_time_in_ms(); /* Have we waited long enough? */ if (ct > t) return ETIMEDOUT; } } static int pthread_get_concurrency(int *val) { *val = _pthread_concur; return 0; } static int pthread_set_concurrency(int val) { _pthread_concur = val; return 0; } #define pthread_getschedparam(T, P, S) ENOTSUP #define pthread_setschedparam(T, P, S) ENOTSUP #define pthread_getcpuclockid(T, C) ENOTSUP static int pthread_exit(void *res) { pthread_t t = pthread_self(); t->ret_arg = res; _pthread_cleanup_dest(t); longjmp(t->jb, 1); } static void _pthread_invoke_cancel(void) { _pthread_cleanup *pcup; _InterlockedDecrement(&_pthread_cancelling); /* Call cancel queue */ for (pcup = pthread_self()->clean; pcup; pcup = pcup->next) { pcup->func(pcup->arg); } pthread_exit(PTHREAD_CANCELED); } static void pthread_testcancel(void) { if (_pthread_cancelling) { pthread_t t = pthread_self(); if (t->cancelled && (t->p_state & PTHREAD_CANCEL_ENABLE)) { _pthread_invoke_cancel(); } } } static int pthread_cancel(pthread_t t) { if (t->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) { /* Dangerous asynchronous cancelling */ CONTEXT ctxt; /* Already done? */ if (t->cancelled) return ESRCH; ctxt.ContextFlags = CONTEXT_CONTROL; SuspendThread(t->h); GetThreadContext(t->h, &ctxt); #ifdef _M_X64 ctxt.Rip = (uintptr_t) _pthread_invoke_cancel; #else ctxt.Eip = (uintptr_t) _pthread_invoke_cancel; #endif SetThreadContext(t->h, &ctxt); /* Also try deferred Cancelling */ t->cancelled = 1; /* Notify everyone to look */ _InterlockedIncrement(&_pthread_cancelling); ResumeThread(t->h); } else { /* Safe deferred Cancelling */ t->cancelled = 1; /* Notify everyone to look */ _InterlockedIncrement(&_pthread_cancelling); } return 0; } static unsigned _pthread_get_state(pthread_attr_t *attr, unsigned flag) { return attr->p_state & flag; } static int _pthread_set_state(pthread_attr_t *attr, unsigned flag, unsigned val) { if (~flag & val) return EINVAL; attr->p_state &= ~flag; attr->p_state |= val; return 0; } static int pthread_attr_init(pthread_attr_t *attr) { attr->p_state = PTHREAD_DEFAULT_ATTR; attr->stack = NULL; attr->s_size = 0; return 0; } static int pthread_attr_destroy(pthread_attr_t *attr) { /* No need to do anything */ return 0; } static int pthread_attr_setdetachstate(pthread_attr_t *a, int flag) { return _pthread_set_state(a, PTHREAD_CREATE_DETACHED, flag); } static int pthread_attr_getdetachstate(pthread_attr_t *a, int *flag) { *flag = _pthread_get_state(a, PTHREAD_CREATE_DETACHED); return 0; } static int pthread_attr_setinheritsched(pthread_attr_t *a, int flag) { return _pthread_set_state(a, PTHREAD_INHERIT_SCHED, flag); } static int pthread_attr_getinheritsched(pthread_attr_t *a, int *flag) { *flag = _pthread_get_state(a, PTHREAD_INHERIT_SCHED); return 0; } static int pthread_attr_setscope(pthread_attr_t *a, int flag) { return _pthread_set_state(a, PTHREAD_SCOPE_SYSTEM, flag); } static int pthread_attr_getscope(pthread_attr_t *a, int *flag) { *flag = _pthread_get_state(a, PTHREAD_SCOPE_SYSTEM); return 0; } static int pthread_attr_getstackaddr(pthread_attr_t *attr, void **stack) { *stack = attr->stack; return 0; } static int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stack) { attr->stack = stack; return 0; } static int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *size) { *size = attr->s_size; return 0; } static int pthread_attr_setstacksize(pthread_attr_t *attr, size_t size) { attr->s_size = size; return 0; } #define pthread_attr_getguardsize(A, S) ENOTSUP #define pthread_attr_setgaurdsize(A, S) ENOTSUP #define pthread_attr_getschedparam(A, S) ENOTSUP #define pthread_attr_setschedparam(A, S) ENOTSUP #define pthread_attr_getschedpolicy(A, S) ENOTSUP #define pthread_attr_setschedpolicy(A, S) ENOTSUP static int pthread_setcancelstate(int state, int *oldstate) { pthread_t t = pthread_self(); if ((state & PTHREAD_CANCEL_ENABLE) != state) return EINVAL; if (oldstate) *oldstate = t->p_state & PTHREAD_CANCEL_ENABLE; t->p_state &= ~PTHREAD_CANCEL_ENABLE; t->p_state |= state; return 0; } static int pthread_setcanceltype(int type, int *oldtype) { pthread_t t = pthread_self(); if ((type & PTHREAD_CANCEL_ASYNCHRONOUS) != type) return EINVAL; if (oldtype) *oldtype = t->p_state & PTHREAD_CANCEL_ASYNCHRONOUS; t->p_state &= ~PTHREAD_CANCEL_ASYNCHRONOUS; t->p_state |= type; return 0; } static int __stdcall pthread_create_wrapper(void *args) { struct _pthread_v *tv = args; _pthread_once_raw(&_pthread_tls_once, pthread_tls_init); TlsSetValue(_pthread_tls, tv); if (!setjmp(tv->jb)) { /* Call function and save return value */ tv->ret_arg = tv->func(tv->ret_arg); /* Clean up destructors */ _pthread_cleanup_dest(tv); } /* If we exit too early, then we can race with create */ while (tv->h == (HANDLE) -1) { YieldProcessor(); _ReadWriteBarrier(); } /* Make sure we free ourselves if we are detached */ if (!tv->h) free(tv); return 0; } static int pthread_create(pthread_t *th, pthread_attr_t *attr, void *(* func)(void *), void *arg) { struct _pthread_v *tv = malloc(sizeof(struct _pthread_v)); unsigned ssize = 0; if (!tv) return 1; *th = tv; /* Save data in pthread_t */ tv->ret_arg = arg; tv->func = func; tv->clean = NULL; tv->cancelled = 0; tv->p_state = PTHREAD_DEFAULT_ATTR; tv->keymax = 0; tv->keyval = NULL; tv->h = (HANDLE) -1; if (attr) { tv->p_state = attr->p_state; ssize = (unsigned int)attr->s_size; } /* Make sure tv->h has value of -1 */ _ReadWriteBarrier(); tv->h = (HANDLE) _beginthreadex(NULL, ssize, pthread_create_wrapper, tv, 0, NULL); /* Failed */ if (!tv->h) return 1; if (tv->p_state & PTHREAD_CREATE_DETACHED) { CloseHandle(tv->h); _ReadWriteBarrier(); tv->h = 0; } return 0; } static int pthread_join(pthread_t t, void **res) { struct _pthread_v *tv = t; pthread_testcancel(); WaitForSingleObject(tv->h, INFINITE); CloseHandle(tv->h); /* Obtain return value */ if (res) *res = tv->ret_arg; free(tv); return 0; } static int pthread_detach(pthread_t t) { struct _pthread_v *tv = t; /* * This can't race with thread exit because * our call would be undefined if called on a dead thread. */ CloseHandle(tv->h); _ReadWriteBarrier(); tv->h = 0; return 0; } static int pthread_mutexattr_init(pthread_mutexattr_t *a) { *a = 0; return 0; } static int pthread_mutexattr_destroy(pthread_mutexattr_t *a) { (void) a; return 0; } static int pthread_mutexattr_gettype(pthread_mutexattr_t *a, int *type) { *type = *a & 3; return 0; } static int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type) { if ((unsigned) type > 3) return EINVAL; *a &= ~3; *a |= type; return 0; } static int pthread_mutexattr_getpshared(pthread_mutexattr_t *a, int *type) { *type = *a & 4; return 0; } static int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type) { if ((type & 4) != type) return EINVAL; *a &= ~4; *a |= type; return 0; } static int pthread_mutexattr_getprotocol(pthread_mutexattr_t *a, int *type) { *type = *a & (8 + 16); return 0; } static int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type) { if ((type & (8 + 16)) != 8 + 16) return EINVAL; *a &= ~(8 + 16); *a |= type; return 0; } static int pthread_mutexattr_getprioceiling(pthread_mutexattr_t *a, int * prio) { *prio = *a / PTHREAD_PRIO_MULT; return 0; } static int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio) { *a &= (PTHREAD_PRIO_MULT - 1); *a += prio * PTHREAD_PRIO_MULT; return 0; } static int pthread_mutex_timedlock(pthread_mutex_t *m, struct timespec *ts) { unsigned long long t, ct; struct _pthread_crit_t { void *debug; LONG count; LONG r_count; HANDLE owner; HANDLE sem; ULONG_PTR spin; }; /* Try to lock it without waiting */ if (!pthread_mutex_trylock(m)) return 0; ct = _pthread_time_in_ms(); t = _pthread_time_in_ms_from_timespec(ts); while (1) { /* Have we waited long enough? */ if (ct > t) return ETIMEDOUT; /* Wait on semaphore within critical section */ WaitForSingleObject(((struct _pthread_crit_t *)m)->sem, (DWORD)(t - ct)); /* Try to grab lock */ if (!pthread_mutex_trylock(m)) return 0; /* Get current time */ ct = _pthread_time_in_ms(); } } static int pthread_barrier_destroy(pthread_barrier_t *b) { DeleteCriticalSection(&b->m); return 0; } static int pthread_barrier_init(pthread_barrier_t *b, void *attr, int count) { /* Ignore attr */ (void) attr; InitializeCriticalSection(&b->m); InitializeConditionVariable(&b->cv); b->count = count; b->total = 0; return 0; } #define _PTHREAD_BARRIER_FLAG (1<<30) static int pthread_barrier_wait(pthread_barrier_t *b) { EnterCriticalSection(&b->m); while (b->total > _PTHREAD_BARRIER_FLAG) { /* Wait until everyone exits the barrier */ SleepConditionVariableCS(&b->cv, &b->m, INFINITE); } /* Are we the first to enter? */ if (b->total == _PTHREAD_BARRIER_FLAG) b->total = 0; b->total++; if (b->total == b->count) { b->total += _PTHREAD_BARRIER_FLAG - 1; WakeAllConditionVariable(&b->cv); LeaveCriticalSection(&b->m); return 1; } else { while (b->total < _PTHREAD_BARRIER_FLAG) { /* Wait until enough threads enter the barrier */ SleepConditionVariableCS(&b->cv, &b->m, INFINITE); } b->total--; /* Get entering threads to wake up */ if (b->total == _PTHREAD_BARRIER_FLAG) WakeAllConditionVariable(&b->cv); LeaveCriticalSection(&b->m); return 0; } } static int pthread_barrierattr_init(void **attr) { *attr = NULL; return 0; } static int pthread_barrierattr_destroy(void **attr) { /* Ignore attr */ (void) attr; return 0; } static int pthread_barrierattr_setpshared(void **attr, int s) { *attr = (void *) s; return 0; } static int pthread_barrierattr_getpshared(void **attr, int *s) { *s = (int) (size_t) *attr; return 0; } static int pthread_key_create(pthread_key_t *key, void (* dest)(void *)) { unsigned int i; long nmax; void (**d)(void *); if (!key) return EINVAL; pthread_rwlock_wrlock(&_pthread_key_lock); for (i = _pthread_key_sch; i < _pthread_key_max; i++) { if (!_pthread_key_dest[i]) { *key = i; if (dest) { _pthread_key_dest[i] = dest; } else { _pthread_key_dest[i] = (void(*)(void *))1; } pthread_rwlock_unlock(&_pthread_key_lock); return 0; } } for (i = 0; i < _pthread_key_sch; i++) { if (!_pthread_key_dest[i]) { *key = i; if (dest) { _pthread_key_dest[i] = dest; } else { _pthread_key_dest[i] = (void(*)(void *))1; } pthread_rwlock_unlock(&_pthread_key_lock); return 0; } } if (!_pthread_key_max) _pthread_key_max = 1; if (_pthread_key_max == PTHREAD_KEYS_MAX) { pthread_rwlock_unlock(&_pthread_key_lock); return ENOMEM; } nmax = _pthread_key_max * 2; if (nmax > PTHREAD_KEYS_MAX) nmax = PTHREAD_KEYS_MAX; /* No spare room anywhere */ d = realloc(_pthread_key_dest, nmax * sizeof(*d)); if (!d) { pthread_rwlock_unlock(&_pthread_key_lock); return ENOMEM; } /* Clear new region */ memset((void *) &d[_pthread_key_max], 0, (nmax-_pthread_key_max)*sizeof(void *)); /* Use new region */ _pthread_key_dest = d; _pthread_key_sch = _pthread_key_max + 1; *key = _pthread_key_max; _pthread_key_max = nmax; if (dest) { _pthread_key_dest[*key] = dest; } else { _pthread_key_dest[*key] = (void(*)(void *))1; } pthread_rwlock_unlock(&_pthread_key_lock); return 0; } static int pthread_key_delete(pthread_key_t key) { if (key > _pthread_key_max) return EINVAL; if (!_pthread_key_dest) return EINVAL; pthread_rwlock_wrlock(&_pthread_key_lock); _pthread_key_dest[key] = NULL; /* Start next search from our location */ if (_pthread_key_sch > key) _pthread_key_sch = key; pthread_rwlock_unlock(&_pthread_key_lock); return 0; } static void *pthread_getspecific(pthread_key_t key) { pthread_t t = pthread_self(); if (key >= t->keymax) return NULL; return t->keyval[key]; } static int pthread_setspecific(pthread_key_t key, const void *value) { pthread_t t = pthread_self(); if (key > t->keymax) { int keymax = (key + 1) * 2; void **kv = realloc(t->keyval, keymax * sizeof(void *)); if (!kv) return ENOMEM; /* Clear new region */ memset(&kv[t->keymax], 0, (keymax - t->keymax)*sizeof(void*)); t->keyval = kv; t->keymax = keymax; } t->keyval[key] = (void *) value; return 0; } static int pthread_spin_init(pthread_spinlock_t *l, int pshared) { (void) pshared; *l = 0; return 0; } static int pthread_spin_destroy(pthread_spinlock_t *l) { (void) l; return 0; } /* No-fair spinlock due to lack of knowledge of thread number */ static int pthread_spin_lock(pthread_spinlock_t *l) { while (_InterlockedExchange(l, EBUSY)) { /* Don't lock the bus whilst waiting */ while (*l) { YieldProcessor(); /* Compiler barrier. Prevent caching of *l */ _ReadWriteBarrier(); } } return 0; } static int pthread_spin_trylock(pthread_spinlock_t *l) { return _InterlockedExchange(l, EBUSY); } static int pthread_spin_unlock(pthread_spinlock_t *l) { /* Compiler barrier. The store below acts with release symmantics */ _ReadWriteBarrier(); *l = 0; return 0; } static int pthread_cond_init(pthread_cond_t *c, pthread_condattr_t *a) { (void) a; InitializeConditionVariable(c); return 0; } static int pthread_cond_signal(pthread_cond_t *c) { WakeConditionVariable(c); return 0; } static int pthread_cond_broadcast(pthread_cond_t *c) { WakeAllConditionVariable(c); return 0; } static int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) { pthread_testcancel(); SleepConditionVariableCS(c, m, INFINITE); return 0; } static int pthread_cond_destroy(pthread_cond_t *c) { (void) c; return 0; } static int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, struct timespec *t) { unsigned long long tm = _pthread_rel_time_in_ms(t); pthread_testcancel(); if (!SleepConditionVariableCS(c, m, (DWORD) tm)) return ETIMEDOUT; /* We can have a spurious wakeup after the timeout */ if (!_pthread_rel_time_in_ms(t)) return ETIMEDOUT; return 0; } static int pthread_condattr_destroy(pthread_condattr_t *a) { (void) a; return 0; } #define pthread_condattr_getclock(A, C) ENOTSUP #define pthread_condattr_setclock(A, C) ENOTSUP static int pthread_condattr_init(pthread_condattr_t *a) { *a = 0; return 0; } static int pthread_condattr_getpshared(pthread_condattr_t *a, int *s) { *s = *a; return 0; } static int pthread_condattr_setpshared(pthread_condattr_t *a, int s) { *a = s; return 0; } static int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a) { (void) a; return 0; } static int pthread_rwlockattr_init(pthread_rwlockattr_t *a) { *a = 0; } static int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s) { *s = *a; return 0; } static int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s) { *a = s; return 0; } /* No fork() in windows - so ignore this */ #define pthread_atfork(F1,F2,F3) 0 /* Windows has rudimentary signals support */ #define pthread_kill(T, S) 0 #define pthread_sigmask(H, S1, S2) 0 /* Wrap cancellation points */ //FIXME: removed wrappers, some of them caused compilation errors #endif /* WIN_PTHREADS */ libpthread_workqueue-0.8.2/src/thread_rt.h0000644000175000017500000000276711610433055020234 0ustar mheilymheily/* * Copyright (c) 2011, Joakim Johansson * * 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 */ libpthread_workqueue-0.8.2/src/thread_info.h0000644000175000017500000000277311610433055020537 0ustar mheilymheily/* * Copyright (c) 2011, Joakim Johansson * * 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 */ libpthread_workqueue-0.8.2/src/witem_cache.c0000755000175000017500000001065111610433055020515 0ustar mheilymheily/* * Copyright (c) 2011, Joakim Johansson * * 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 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 libpthread_workqueue-0.8.2/src/api.c0000755000175000017500000001333111610433055017014 0ustar mheilymheily/*- * Copyright (c) 2010, Mark Heily * Copyright (c) 2009, Stacey Son * 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); } libpthread_workqueue-0.8.2/src/debug.h0000644000175000017500000000567311610433055017345 0ustar mheilymheily/* * Copyright (c) 2011 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _DEBUG_H #define _DEBUG_H #include extern int DEBUG_WORKQUEUE; extern char *WORKQUEUE_DEBUG_IDENT; #if defined(__linux__) #include #include #include # 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 */ libpthread_workqueue-0.8.2/testing/0000755000175000017500000000000011610433055016761 5ustar mheilymheilylibpthread_workqueue-0.8.2/testing/latency/0000755000175000017500000000000011610433055020420 5ustar mheilymheilylibpthread_workqueue-0.8.2/testing/latency/latency.c0000644000175000017500000003231511610433055022227 0ustar mheilymheily/* * Copyright (c) 2011 Joakim Johansson . * * @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 #include #include #include #include #include #ifndef _WIN32 # include # include # include #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 #include #include #include 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; } libpthread_workqueue-0.8.2/testing/latency/latency.h0000644000175000017500000000565311610433055022241 0ustar mheilymheily/* * Copyright (c) 2011 Joakim Johansson . * * @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 # 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 libpthread_workqueue-0.8.2/testing/latency/Makefile0000644000175000017500000000076111610433055022064 0ustar mheilymheilyCC=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 libpthread_workqueue-0.8.2/testing/libdispatch/0000755000175000017500000000000011610433055021247 5ustar mheilymheilylibpthread_workqueue-0.8.2/testing/libdispatch/dispatch_api.c0000644000175000017500000000207211610433055024044 0ustar mheilymheily/* * 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 #include #include #include 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; } libpthread_workqueue-0.8.2/testing/libdispatch/Makefile0000644000175000017500000000030611610433055022706 0ustar mheilymheilyCC=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 libpthread_workqueue-0.8.2/testing/witem_cache/0000755000175000017500000000000011610433055021231 5ustar mheilymheilylibpthread_workqueue-0.8.2/testing/witem_cache/test.c0000644000175000017500000000453311610433055022361 0ustar mheilymheily/*- * Copyright (c) 2011, Mark Heily * 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 #include #include #include #include #include #include "config.h" #include "src/private.h" #if HAVE_ERR_H # include #else # define err(rc,msg,...) do { perror(msg); exit(rc); } while (0) # define errx(rc,msg,...) do { puts(msg); exit(rc); } while (0) #endif #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(); } libpthread_workqueue-0.8.2/testing/witem_cache/Makefile0000644000175000017500000000265611610433055022702 0ustar mheilymheily# # Copyright (c) 2011 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # .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) libpthread_workqueue-0.8.2/testing/api/0000755000175000017500000000000011610433055017532 5ustar mheilymheilylibpthread_workqueue-0.8.2/testing/api/test.c0000644000175000017500000001341111610433055020655 0ustar mheilymheily#include #include #include #include #if !defined(_WIN32) # include # if !defined(NO_CONFIG_H) # include "config.h" # endif # include #else # define inline _inline # include "../../src/windows/platform.h" # include "posix_semaphore.h" #endif #include "../../src/private.h" #if HAVE_ERR_H # include #else # define err(rc,msg,...) do { perror(msg); exit(rc); } while (0) # define errx(rc,msg,...) do { puts(msg); exit(rc); } while (0) #endif #include 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); } libpthread_workqueue-0.8.2/testing/api/test_api.vcxproj0000644000175000017500000001010211610433055022751 0ustar mheilymheilyο»Ώ Debug Win32 Release Win32 {c8953032-a7fd-b3b1-6606-8dd078518b9a} {6D7A0EA0-AE20-4584-AEE6-25B49823073A} Win32Proj test_api Application true Unicode Application false true Unicode true ..\..\include;$(IncludePath) false NotUsing Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Use MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true libpthread_workqueue-0.8.2/testing/api/Makefile0000644000175000017500000000257511610433055021203 0ustar mheilymheily# # Copyright (c) 2011 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # .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) libpthread_workqueue-0.8.2/testing/api/posix_semaphore.h0000644000175000017500000000155111610433055023112 0ustar mheilymheily#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_ */ libpthread_workqueue-0.8.2/testing/idle/0000755000175000017500000000000011610433055017676 5ustar mheilymheilylibpthread_workqueue-0.8.2/testing/idle/main.c0000644000175000017500000000653511610433055020777 0ustar mheilymheily/*- * Copyright (c) 2011, Mark Heily * 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 #include #include #include #include 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); } libpthread_workqueue-0.8.2/testing/idle/Makefile0000644000175000017500000000201611610433055021335 0ustar mheilymheily# # Copyright (c) 2011 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # 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 libpthread_workqueue-0.8.2/testing/CMakeLists.txt0000644000175000017500000000276111610433055021527 0ustar mheilymheily# # Copyright (c) 2011 Marius Zwicker # # 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() libpthread_workqueue-0.8.2/testing/Makefile0000644000175000017500000000176211610433055020427 0ustar mheilymheily# # Copyright (c) 2011 Mark Heily # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # 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 libpthread_workqueue-0.8.2/include/0000755000175000017500000000000011610433055016727 5ustar mheilymheilylibpthread_workqueue-0.8.2/include/pthread_workqueue.h0000644000175000017500000000674111610433055022646 0ustar mheilymheily/*- * Copyright (c) 2010, Mark Heily * Copyright (c) 2009, Stacey Son * 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 */