]> git.saurik.com Git - apple/libdispatch.git/commitdiff
libdispatch-84.5.3.tar.gz mac-os-x-1062 v84.5.3
authorApple <opensource@apple.com>
Mon, 5 Oct 2009 20:50:20 +0000 (20:50 +0000)
committerApple <opensource@apple.com>
Mon, 5 Oct 2009 20:50:20 +0000 (20:50 +0000)
59 files changed:
EXPORT.APPLE [new file with mode: 0755]
install [new file with mode: 0755]
install_Libsystem_pieces [new file with mode: 0755]
src/legacy.h
src/queue.c
src/queue_internal.h
testing/Makefile [new file with mode: 0644]
testing/apply_strtoull.c [new file with mode: 0644]
testing/bench.mm [new file with mode: 0644]
testing/conc.c [new file with mode: 0644]
testing/dispatch_after.c [new file with mode: 0644]
testing/dispatch_api.c [new file with mode: 0644]
testing/dispatch_apply.c [new file with mode: 0644]
testing/dispatch_c99.c [new file with mode: 0644]
testing/dispatch_cascade.c [new file with mode: 0644]
testing/dispatch_cffd.c [new file with mode: 0644]
testing/dispatch_deadname.c [new file with mode: 0644]
testing/dispatch_debug.c [new file with mode: 0644]
testing/dispatch_drift.c [new file with mode: 0644]
testing/dispatch_group.c [new file with mode: 0644]
testing/dispatch_overcommit.c [new file with mode: 0644]
testing/dispatch_pingpong.c [new file with mode: 0644]
testing/dispatch_plusplus.cpp [new file with mode: 0644]
testing/dispatch_priority.c [new file with mode: 0644]
testing/dispatch_proc.c [new file with mode: 0644]
testing/dispatch_rdlock_bmark.c [new file with mode: 0644]
testing/dispatch_read.c [new file with mode: 0644]
testing/dispatch_read2.c [new file with mode: 0644]
testing/dispatch_readsync.c [new file with mode: 0644]
testing/dispatch_sema.c [new file with mode: 0644]
testing/dispatch_starfish.c [new file with mode: 0644]
testing/dispatch_suspend_timer.c [new file with mode: 0644]
testing/dispatch_test.c [new file with mode: 0644]
testing/dispatch_test.h [new file with mode: 0644]
testing/dispatch_test_sync_on_main.c [new file with mode: 0644]
testing/dispatch_timer.c [new file with mode: 0644]
testing/dispatch_timer_bit31.c [new file with mode: 0644]
testing/dispatch_timer_bit63.c [new file with mode: 0644]
testing/dispatch_timer_oneshot.c [new file with mode: 0644]
testing/dispatch_timer_set_time.c [new file with mode: 0644]
testing/fast_apply_bench.c [new file with mode: 0644]
testing/fd_stress.c [new file with mode: 0644]
testing/float_parsing.c [new file with mode: 0644]
testing/fork-join.c [new file with mode: 0644]
testing/func.c [new file with mode: 0644]
testing/harness.c [new file with mode: 0644]
testing/leaks-wrapper [new file with mode: 0755]
testing/mach_server.c [new file with mode: 0644]
testing/mig/Makefile [new file with mode: 0644]
testing/mig/client.c [new file with mode: 0644]
testing/mig/hello_logger.defs [new file with mode: 0644]
testing/mig/hello_logger_types.h [new file with mode: 0644]
testing/mig/server.c [new file with mode: 0644]
testing/nsoperation.m [new file with mode: 0644]
testing/queue_finalizer.c [new file with mode: 0644]
testing/slice_benchmarks.c [new file with mode: 0644]
testing/summarize.c [new file with mode: 0644]
testing/test.c [new file with mode: 0644]
testing/yet-another-apply-test.c [new file with mode: 0644]

diff --git a/EXPORT.APPLE b/EXPORT.APPLE
new file mode 100755 (executable)
index 0000000..2af942a
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh -x
+
+# copy cwd to SRCROOT like installsrc
+
+if [ -z "$SRCROOT" ]; then
+       PWD=`pwd`
+       SRCROOT=/tmp/`basename "$PWD"`
+fi
+
+echo Exporting to $SRCROOT
+mkdir -p "$SRCROOT"
+
+gnutar cf - \
+       --exclude=.svn \
+       --exclude=CVS \
+       --exclude=build \
+       --exclude=testing \
+       --exclude=\*.APPLE \
+       --exclude=install\* \
+       . | (cd "$SRCROOT"; gnutar xf - )
+
diff --git a/install b/install
new file mode 100755 (executable)
index 0000000..221a93e
--- /dev/null
+++ b/install
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+if [ $EUID -ne 0 ]; then
+       echo "install script must be run as root" 2>&1
+       exit 1
+fi
+
+## Copy Libsystem pieces from ~rc
+copylibs=0
+## Install results in /
+noinstall=0
+build=$(sw_vers -buildVersion)
+train=$(~rc/bin/getTrainForBuild --quiet "$build")
+
+while [ $# -gt 0 ]; do
+       if [ "${1/=*/}" = "--build" ]; then
+               build="${1/*=/}"
+       elif [ "$1" = "--noinstall" ]; then
+               noinstall=1
+       elif [ "$1" = "--copylibs" ]; then
+               copylibs=1
+       else
+               echo "install: [--build=10A400] [--noinstall] [--copylibs]" 2>&1
+               exit 1
+       fi
+       shift
+done
+
+ROOTS=/var/tmp/GCDRoots."$build"
+
+# Building for another version implies copylibs and noinstall
+if [ "$build" != "$(sw_vers -buildVersion)" ]; then
+       copylibs=1
+       noinstall=1
+fi
+
+set -ex
+mkdir -p "$ROOTS"
+
+function BUILDIT() {
+       ~rc/bin/buildit -arch i386 -arch ppc -arch x86_64 -arch armv6 \
+               -release "$train" -rootsDirectory "$ROOTS" "$@" .
+}
+
+BUILDIT -project libdispatch -merge / -noverify
+
+if [ $copylibs = 1 ]; then
+       ALTUSRLOCALLIBSYSTEM="$ROOTS"/system
+       mkdir -p "$ALTUSRLOCALLIBSYSTEM"
+       export ALTUSRLOCALLIBSYSTEM
+       ./install_Libsystem_pieces "$build"
+       cp /usr/local/lib/system/libdispatch* "$ALTUSRLOCALLIBSYSTEM"
+fi
+
+LIBSYSTEM=$(~rc/bin/getvers "$train$build" Libsystem)
+if [ -z "$LIBSYSTEM" ]; then
+       exit 1
+fi
+SRCROOT="/var/tmp/$LIBSYSTEM"
+if [ ! -e "$SRCROOT" ]; then
+       cd $(dirname "$SRCROOT")
+       svn co http://src.apple.com/svn/BSD/Libsystem/tags/"$LIBSYSTEM"
+fi
+cd "$SRCROOT"
+BUILDIT
+
+if [ $noinstall -eq 1 ]; then
+       exit 0
+fi
+
+if [ ! -e /usr/lib/libSystem.B.dylib.orig ]; then
+       cp /usr/lib/libSystem.B.dylib /usr/lib/libSystem.B.dylib.orig
+fi
+if [ ! -e /usr/lib/libSystem.B_debug.dylib.orig ] ; then
+       cp /usr/lib/libSystem.B_debug.dylib /usr/lib/libSystem.B_debug.dylib.orig
+fi
+if [ ! -e /usr/lib/libSystem.B_profile.dylib.orig ] ; then
+       cp /usr/lib/libSystem.B_profile.dylib /usr/lib/libSystem.B_profile.dylib.orig
+fi
+cp -R "$ROOTS"/"$LIBSYSTEM".roots/"$LIBSYSTEM"~sym/libSystem* /usr/lib/
+update_dyld_shared_cache
diff --git a/install_Libsystem_pieces b/install_Libsystem_pieces
new file mode 100755 (executable)
index 0000000..39015d9
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+dependencies="Libc \
+       Libc_debug \
+       Libc_profile \
+       CommonCrypto \
+       copyfile \
+       mDNSResponderSystemLibraries \
+       libdyld \
+       Libinfo \
+       keymgr \
+       launchd_libs \
+       Libm \
+       cctools_ofiles \
+       configd_libSystem \
+       Libnotify \
+       quarantine \
+       removefile \
+       Sandbox_libs \
+       Seatbelt_libs \
+       Libsyscall \
+       libclosure \
+       libcache \
+       libdispatch \
+       libunwind \
+       Libcompiler_rt \
+       UserNotification"
+
+function rcpath() {
+        dir="$1"
+        build="$2"
+        project="$3"
+        train=$(~rc/bin/getTrainForBuild --quiet "$build")
+        update="$train$build"
+        echo ~rc/Software/$train/Updates/$update/$dir/$project
+}
+
+if [ $# -eq 1 ]; then build="$1" ; else build=$(sw_vers -buildVersion) ; fi
+
+DSTROOT=/usr/local/lib/system
+if [ -n "$ALTUSRLOCALLIBSYSTEM" ]; then
+DSTROOT="$ALTUSRLOCALLIBSYSTEM"
+fi
+
+echo Copying in Libsystem dependencies for $build ...
+for project in $dependencies ; do
+       echo ... $project
+       ditto $(rcpath Roots "$build" "$project")/usr/local/lib/system "$DSTROOT"
+done
index e6bffbc592e71457f6f3572f0e9c03ebb621af5b..fb154f88d9f4359e05425df43cf9cc94dbf2fa2a 100644 (file)
@@ -28,7 +28,7 @@
  * LEGACY: This header file describles LEGACY interfaces to libdispatch from an
  * earlier revision of the API. These interfaces WILL be removed in the future.
  */
+
 #ifndef __DISPATCH_LEGACY__
 #define __DISPATCH_LEGACY__
 
index a3e89360fbdec8894466165187472b8907606288..9111a0fde436362365ac5b0c9d7426461429fe69 100644 (file)
@@ -594,6 +594,22 @@ _dispatch_queue_dispose(dispatch_queue_t dq)
        _dispatch_dispose(dq);
 }
 
+DISPATCH_NOINLINE 
+void
+_dispatch_queue_push_list_slow(dispatch_queue_t dq, struct dispatch_object_s *obj)
+{
+       // The queue must be retained before dq_items_head is written in order
+       // to ensure that the reference is still valid when _dispatch_wakeup is
+       // called. Otherwise, if preempted between the assignment to
+       // dq_items_head and _dispatch_wakeup, the blocks submitted to the
+       // queue may release the last reference to the queue when invoked by
+       // _dispatch_queue_drain. <rdar://problem/6932776>
+       _dispatch_retain(dq);
+       dq->dq_items_head = obj;
+       _dispatch_wakeup(dq);
+       _dispatch_release(dq);
+}
+
 DISPATCH_NOINLINE
 static void
 _dispatch_barrier_async_f_slow(dispatch_queue_t dq, void *context, dispatch_function_t func)
@@ -674,8 +690,9 @@ dispatch_async_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
 }
 
 struct dispatch_barrier_sync_slow2_s {
+       dispatch_queue_t dbss2_dq;
        dispatch_function_t dbss2_func;
-       dispatch_function_t dbss2_ctxt;
+       dispatch_function_t dbss2_ctxt; 
        dispatch_semaphore_t dbss2_sema;
 };
 
@@ -684,7 +701,13 @@ _dispatch_barrier_sync_f_slow_invoke(void *ctxt)
 {
        struct dispatch_barrier_sync_slow2_s *dbss2 = ctxt;
 
-       dbss2->dbss2_func(dbss2->dbss2_ctxt);
+       dispatch_assert(dbss2->dbss2_dq == dispatch_get_current_queue());
+       // ALL blocks on the main queue, must be run on the main thread
+       if (dbss2->dbss2_dq == dispatch_get_main_queue()) {
+               dbss2->dbss2_func(dbss2->dbss2_ctxt);
+       } else {
+               dispatch_suspend(dbss2->dbss2_dq);
+       }
        dispatch_semaphore_signal(dbss2->dbss2_sema);
 }
 
@@ -692,9 +715,15 @@ DISPATCH_NOINLINE
 static void
 _dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
 {
+       
+       // It's preferred to execute synchronous blocks on the current thread
+       // due to thread-local side effects, garbage collection, etc. However,
+       // blocks submitted to the main thread MUST be run on the main thread
+       
        struct dispatch_barrier_sync_slow2_s dbss2 = {
+               .dbss2_dq = dq,
                .dbss2_func = func,
-               .dbss2_ctxt = ctxt,
+               .dbss2_ctxt = ctxt,             
                .dbss2_sema = _dispatch_get_thread_semaphore(),
        };
        struct dispatch_barrier_sync_slow_s {
@@ -704,17 +733,17 @@ _dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function
                .dc_func = _dispatch_barrier_sync_f_slow_invoke,
                .dc_ctxt = &dbss2,
        };
-
+       
+       dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key);
        _dispatch_queue_push(dq, (void *)&dbss);
+       dispatch_semaphore_wait(dbss2.dbss2_sema, DISPATCH_TIME_FOREVER);
 
-       while (dispatch_semaphore_wait(dbss2.dbss2_sema, dispatch_time(0, 3ull * NSEC_PER_SEC))) {
-               if (DISPATCH_OBJECT_SUSPENDED(dq)) {
-                       continue;
-               }
-               if (_dispatch_queue_trylock(dq)) {
-                       _dispatch_queue_drain(dq);
-                       _dispatch_queue_unlock(dq);
-               }
+       if (dq != dispatch_get_main_queue()) {
+               _dispatch_thread_setspecific(dispatch_queue_key, dq);
+               func(ctxt);
+               _dispatch_workitem_inc();
+               _dispatch_thread_setspecific(dispatch_queue_key, old_dq);
+               dispatch_resume(dq);
        }
        _dispatch_put_thread_semaphore(dbss2.dbss2_sema);
 }
@@ -723,6 +752,13 @@ _dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function
 void
 dispatch_barrier_sync(dispatch_queue_t dq, void (^work)(void))
 {
+       // Blocks submitted to the main queue MUST be run on the main thread,
+       // therefore we must Block_copy in order to notify the thread-local
+       // garbage collector that the objects are transferring to the main thread
+       if (dq == dispatch_get_main_queue()) {
+               dispatch_block_t block = Block_copy(work);
+               return dispatch_barrier_sync_f(dq, block, _dispatch_call_block_and_release);
+       }       
        struct Block_basic *bb = (void *)work;
 
        dispatch_barrier_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke);
index b70c2d0a0c55e676a69e4acf789deddc2e7b2dd9..51b4fca5b1607c937e482fa1e75f35ab3284165b 100644 (file)
@@ -82,6 +82,7 @@ extern struct dispatch_queue_s _dispatch_mgr_q;
 void _dispatch_queue_init(dispatch_queue_t dq);
 void _dispatch_queue_drain(dispatch_queue_t dq);
 void _dispatch_queue_dispose(dispatch_queue_t dq);
+void _dispatch_queue_push_list_slow(dispatch_queue_t dq, struct dispatch_object_s *obj);
 
 __attribute__((always_inline))
 static inline void
@@ -90,17 +91,14 @@ _dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, dispatch
        struct dispatch_object_s *prev, *head = _head._do, *tail = _tail._do;
 
        tail->do_next = NULL;
-       _dispatch_retain(dq);
        prev = fastpath(dispatch_atomic_xchg(&dq->dq_items_tail, tail));
        if (prev) {
                // if we crash here with a value less than 0x1000, then we are at a known bug in client code
                // for example, see _dispatch_queue_dispose or _dispatch_atfork_child
                prev->do_next = head;
        } else {
-               dq->dq_items_head = head;
-               _dispatch_wakeup(dq);
+               _dispatch_queue_push_list_slow(dq, head);
        }
-       _dispatch_release(dq);
 }
 
 #define _dispatch_queue_push(x, y) _dispatch_queue_push_list((x), (y), (y))
diff --git a/testing/Makefile b/testing/Makefile
new file mode 100644 (file)
index 0000000..1f2ec73
--- /dev/null
@@ -0,0 +1,127 @@
+# No workie: dispatch_sema
+TESTS= dispatch_apply \
+       dispatch_api \
+       dispatch_c99 \
+       dispatch_cffd \
+       dispatch_deadname \
+       dispatch_debug \
+       queue_finalizer \
+       dispatch_group \
+       dispatch_overcommit \
+       dispatch_pingpong \
+       dispatch_plusplus \
+       dispatch_priority \
+       dispatch_priority2 \
+       dispatch_proc \
+       dispatch_read \
+       dispatch_read2 \
+       dispatch_after \
+       dispatch_timer \
+       dispatch_sema \
+       dispatch_suspend_timer \
+       dispatch_timer_bit31 \
+       dispatch_timer_bit63 \
+       dispatch_timer_oneshot \
+       dispatch_timer_set_time \
+       dispatch_starfish \
+       dispatch_cascade \
+       dispatch_drift \
+       dispatch_readsync \
+       nsoperation
+
+all: harness summarize bench $(TESTS)
+       @lipo -remove x86_64 -output dispatch_timer_bit31 dispatch_timer_bit31 2>/dev/null || true
+
+logs: $(addsuffix .testlog, $(TESTS))
+debuglogs: $(addsuffix .debuglog, $(TESTS))
+
+testbots:
+       $(MAKE) test
+
+test: clean-logs
+       $(MAKE) _test
+
+_test: all logs debuglogs
+       @cat *.testlog *.debuglog
+       @cat *.testlog *.debuglog | ./summarize
+
+# Override ARCHS and SDKROOT to cross-build test suite
+
+SRCS = dispatch_test.c
+OBJS = $(SRCS:%.c=%.o)
+ARCHS=i386 x86_64 ppc
+CFLAGS = -Werror -Wall -Wextra -Wshadow -mdynamic-no-pic -Os -g $(patsubst %, -arch %,$(ARCHS))
+CPPFLAGS = $(CFLAGS)
+LDFLAGS = $(patsubst %, -arch %,$(ARCHS))
+LDLIBS = -lstdc++
+
+ifneq ($(SDKROOT),)
+CFLAGS += -isysroot $(SDKROOT)
+LDFLAGS += -isysroot $(SDKROOT)
+CC = xcrun -sdk $(SDKROOT) gcc
+endif
+
+harness: harness.o $(OBJS)
+summarize: summarize.o
+bench: bench.o func.o
+       $(CC) $(LDFLAGS) -framework Foundation $(LDLIBS) -o $@ $^
+
+bench.o: bench.mm
+       $(CC) -x objective-c++ $(CFLAGS) -c $^ -o $@
+func.o: func.c
+       $(CC) -x c++ $(CFLAGS) -c $^ -o $@
+
+dispatch_apply: dispatch_apply.o $(OBJS)
+dispatch_api: dispatch_api.o $(OBJS)
+dispatch_c99: dispatch_c99.o $(OBJS)
+dispatch_cffd: dispatch_cffd.o $(OBJS)
+       $(CC) $(LDFLAGS) -framework CoreFoundation -o $@ $^
+dispatch_deadname: dispatch_deadname.o $(OBJS)
+dispatch_debug: dispatch_debug.o $(OBJS)
+dispatch_group: dispatch_group.o $(OBJS)
+dispatch_overcommit: dispatch_overcommit.o $(OBJS)
+dispatch_pingpong: dispatch_pingpong.o $(OBJS)
+dispatch_plusplus: dispatch_plusplus.o $(OBJS)
+dispatch_priority: dispatch_priority.o $(OBJS)
+dispatch_priority2: dispatch_priority2.o $(OBJS)
+dispatch_proc: dispatch_proc.o $(OBJS)
+queue_finalizer: queue_finalizer.o $(OBJS)
+dispatch_read:  dispatch_read.o $(OBJS)
+dispatch_read2: dispatch_read2.o $(OBJS)
+dispatch_after: dispatch_after.o $(OBJS)
+dispatch_timer: dispatch_timer.o $(OBJS)
+dispatch_suspend_timer: dispatch_suspend_timer.o $(OBJS)
+dispatch_sema: dispatch_sema.o $(OBJS)
+dispatch_timer_bit31: dispatch_timer_bit31.o $(OBJS)
+dispatch_timer_bit63: dispatch_timer_bit63.o $(OBJS)
+dispatch_timer_oneshot: dispatch_timer_oneshot.o $(OBJS)
+dispatch_timer_set_time: dispatch_timer_set_time.o $(OBJS)
+dispatch_drift: dispatch_drift.o $(OBJS)
+dispatch_starfish: dispatch_starfish.o $(OBJS)
+dispatch_cascade: dispatch_cascade.o $(OBJS)
+dispatch_timer_bit31: dispatch_timer_bit31.o $(OBJS)
+dispatch_readsync: dispatch_readsync.o $(OBJS)
+ENVIRON_nsoperation = NOLEAKS=1
+nsoperation: nsoperation.o $(OBJS)
+       $(CC) $(LDFLAGS) -framework Foundation -o $@ $^
+
+dispatch_api.o: dispatch_api.c
+       $(CC) -c $(CFLAGS) -include $(SDKROOT)/usr/include/dispatch/dispatch.h -pendantic -o $@ $^
+
+dispatch_c99.o: dispatch_c99.c
+       $(CC) -c $(CFLAGS) -std=c99 -pedantic -o $@ $^
+
+dispatch_priority2.o: dispatch_priority.c
+       $(CC) -c $(CFLAGS) -DUSE_SET_TARGET_QUEUE=1 -o $@ $^
+
+$(addsuffix .testlog, $(TESTS)): harness $(TESTS)
+       $(ENVIRON_$(basename $@)) ./harness ./$(basename $@) > $@
+
+$(addsuffix .debuglog, $(TESTS)): harness $(TESTS)
+       $(ENVIRON_$(basename $@)) DYLD_IMAGE_SUFFIX=_debug ./harness ./$(basename $@) > $@
+
+clean-logs:
+       rm -f *.testlog *.debuglog *.leakslog
+
+clean: clean-logs
+       rm -f *.o *.dSYM bench harness summarize $(TESTS)
diff --git a/testing/apply_strtoull.c b/testing/apply_strtoull.c
new file mode 100644 (file)
index 0000000..82671a0
--- /dev/null
@@ -0,0 +1,64 @@
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <dispatch.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+int
+main(int argc, char *argv[])
+{
+       struct stat sb;
+       char **numbers;
+       char *map;
+       size_t i, j;
+       int r, fd;
+
+       if (argc != 2) {
+               fprintf(stderr, "usage: %s <file>\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       fd = open(argv[1], O_RDONLY);
+       assert(fd != -1);
+
+       r = fstat(fd, &sb);
+       assert(r != -1);
+
+       if (sb.st_len == 0) {
+               fprintf(stderr, "The file is zero length.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       map = mmap(NULL, sb.st_len, PROT_READ, MAP_FILE, fd, 0);
+       assert(map != MAP_FAILED);
+
+       numbers = malloc(sb.st_len * sizeof(void *)); /* more than enough */
+       assert(numbers);
+
+       /* XXX finish me */
+       numbers[0] = map;
+       j = 1;
+       for (i = 0; i < sb.st_len; i++) {
+               if (map[i] == '\n') {
+                       numbers[j] = map + i;
+                       j++;
+                       continue;
+               }
+               i++;
+       }
+       i
+                       ; i < sb.st_len; i++) {
+               if (map[i]
+       }
+
+       dispatch_apply(b, cnt);
+
+       exit(EXIT_SUCCESS);
+}
diff --git a/testing/bench.mm b/testing/bench.mm
new file mode 100644 (file)
index 0000000..51809b2
--- /dev/null
@@ -0,0 +1,488 @@
+#include <Foundation/Foundation.h>
+#include <libkern/OSAtomic.h>
+#include <sys/sysctl.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <math.h>
+#ifdef __BLOCKS__
+#include <Block.h>
+#endif
+#include <dispatch/dispatch.h>
+#include <dispatch/private.h>
+
+extern "C" {
+__private_extern__ void func(void);
+#ifdef __BLOCKS__
+__private_extern__ void (^block)(void);
+#endif
+static void backflip(void *ctxt);
+static void backflip_done(void);
+}
+
+@interface BasicObject : NSObject
+{
+}
+- (void) method;
+@end
+
+@implementation BasicObject
+- (void) method
+{
+}
+@end
+
+class BasicClass {
+public:
+       virtual void virtfunc(void) {
+       };
+};
+
+static void *
+force_a_thread(void *arg)
+{
+       pause();
+       abort();
+       return arg;
+}
+
+static volatile int32_t global;
+
+static const size_t cnt = 10000000;
+static const size_t cnt2 = 100000;
+
+static uint64_t bfs;
+static long double loop_cost;
+static long double cycles_per_nanosecond;
+static mach_timebase_info_data_t tbi;
+
+//static void func2(void *, dispatch_item_t di);
+
+static void __attribute__((noinline))
+print_result(uint64_t s, const char *str)
+{
+       uint64_t d, e = mach_absolute_time();
+       long double dd;
+
+       d = e - s;
+
+       if (tbi.numer != tbi.denom) {
+               d *= tbi.numer;
+               d /= tbi.denom;
+       }
+
+       dd = (typeof(dd))d / (typeof(dd))cnt;
+
+       dd -= loop_cost;
+
+       if (loop_cost == 0.0) {
+               loop_cost = dd;
+       }
+
+       dd *= cycles_per_nanosecond;
+
+       printf("%-45s%15.3Lf cycles\n", str, dd);
+}
+
+static void __attribute__((noinline))
+print_result2(uint64_t s, const char *str)
+{
+       uint64_t d, e = mach_absolute_time();
+       long double dd;
+
+       d = e - s;
+
+       if (tbi.numer != tbi.denom) {
+               d *= tbi.numer;
+               d /= tbi.denom;
+       }
+
+       dd = (typeof(dd))d / (typeof(dd))cnt2;
+
+       dd -= loop_cost;
+       dd *= cycles_per_nanosecond;
+
+       printf("%-45s%15.3Lf cycles\n", str, dd);
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+static inline uint64_t
+rdtsc(void)
+{
+       uint32_t lo, hi;
+
+       asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
+
+       return (uint64_t)hi << 32 | lo;
+}
+#endif
+
+static struct fml {
+       struct fml *fml_next;
+} *fixed_malloc_lifo_head;
+
+struct fml *fixed_malloc_lifo(void);// __attribute__((noinline));
+void fixed_free_lifo(struct fml *fml);// __attribute__((noinline));
+
+struct fml *
+fixed_malloc_lifo(void)
+{
+       struct fml *fml_r = fixed_malloc_lifo_head;
+
+       if (fml_r) {
+               fixed_malloc_lifo_head = fml_r->fml_next;
+               return fml_r;
+       } else {
+               return (struct fml *)malloc(32);
+       }
+}
+
+void
+fixed_free_lifo(struct fml *fml)
+{
+       fml->fml_next = fixed_malloc_lifo_head;
+       fixed_malloc_lifo_head = fml;
+}
+
+int
+main(void)
+{
+       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+       pthread_mutex_t plock = PTHREAD_MUTEX_INITIALIZER;
+       OSSpinLock slock = OS_SPINLOCK_INIT;
+       BasicObject *bo;
+       BasicClass *bc;
+       pthread_t pthr_pause;
+       dispatch_queue_t q, mq;
+       kern_return_t kr;
+       semaphore_t sem;
+       uint64_t freq;
+       uint64_t s;
+       size_t freq_len = sizeof(freq);
+       size_t bf_cnt = cnt;
+       unsigned i;
+       int r;
+
+       r = sysctlbyname("hw.cpufrequency", &freq, &freq_len, NULL, 0);
+       assert(r != -1);
+       assert(freq_len == sizeof(freq));
+
+       cycles_per_nanosecond = (long double)freq / (long double)NSEC_PER_SEC;
+
+       assert(pool);
+
+       /* Malloc has different logic for threaded apps. */
+       r = pthread_create(&pthr_pause, NULL, force_a_thread, NULL);
+       assert(r == 0);
+
+       kr = mach_timebase_info(&tbi);
+       assert(kr == 0);
+#if defined(__i386__) || defined(__x86_64__)
+       assert(tbi.numer == tbi.denom); /* This will fail on PowerPC. */
+#endif
+
+       bo = [[BasicObject alloc] init];
+       assert(bo);
+
+       bc = new BasicClass();
+       assert(bc);
+
+       q = dispatch_queue_create("com.apple.bench-dispatch", NULL);
+       assert(q);
+
+       mq = dispatch_get_main_queue();
+       assert(mq);
+
+       printf("%-45s%15Lf\n\n", "Cycles per nanosecond:", cycles_per_nanosecond);
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               asm volatile("");
+       }
+       print_result(s, "Empty loop:");
+
+       printf("\nLoop cost subtracted from the following:\n\n");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               mach_absolute_time();
+       }
+       print_result(s, "mach_absolute_time():");
+
+#if defined(__i386__) || defined(__x86_64__)
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               rdtsc();
+       }
+       print_result(s, "rdtsc():");
+#endif
+
+       s = mach_absolute_time();
+       for (i = cnt2; i; i--) {
+               pthread_t pthr;
+               void *pr;
+
+               r = pthread_create(&pthr, NULL, (void *(*)(void *))func, NULL);
+               assert(r == 0);
+               r = pthread_join(pthr, &pr);
+               assert(r == 0);
+       }
+       print_result2(s, "pthread create+join:");
+
+       s = mach_absolute_time();
+       for (i = cnt2; i; i--) {
+               kr = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0);
+               assert(kr == 0);
+               kr = semaphore_destroy(mach_task_self(), sem);
+               assert(kr == 0);
+       }
+       print_result2(s, "Mach semaphore create/destroy:");
+
+       kr = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0);
+       assert(kr == 0);
+       s = mach_absolute_time();
+       for (i = cnt2; i; i--) {
+               kr = semaphore_signal(sem);
+               assert(kr == 0);
+       }
+       print_result2(s, "Mach semaphore signal:");
+       kr = semaphore_destroy(mach_task_self(), sem);
+       assert(kr == 0);
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               pthread_yield_np();
+       }
+       print_result(s, "pthread_yield_np():");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               free(malloc(32));
+       }
+       print_result(s, "free(malloc(32)):");
+
+       s = mach_absolute_time();
+       for (i = cnt / 2; i; i--) {
+               void *m1 = malloc(32);
+               void *m2 = malloc(32);
+               free(m1);
+               free(m2);
+       }
+       print_result(s, "Avoiding the MRU cache of free(malloc(32)):");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               fixed_free_lifo(fixed_malloc_lifo());
+       }
+       print_result(s, "per-thread/fixed free(malloc(32)):");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               assert(strtoull("18446744073709551615", NULL, 0) == ~0ull);
+       }
+       print_result(s, "strtoull(\"18446744073709551615\") == ~0ull:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               func();
+       }
+       print_result(s, "Empty function call:");
+
+#ifdef __BLOCKS__
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               block();
+       }
+       print_result(s, "Empty block call:");
+#endif
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               [bo method];
+       }
+       print_result(s, "Empty ObjC call:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               bc->virtfunc();
+       }
+       print_result(s, "Empty C++ virtual call:");
+
+       s = mach_absolute_time();
+       for (i = cnt2; i; i--) {
+               [bo description];
+       }
+       print_result2(s, "\"description\" ObjC call:");
+
+       [pool release];
+
+       pool = NULL;
+
+#if defined(__i386__) || defined(__x86_64__)
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               asm("nop");
+       }
+       print_result(s, "raw 'nop':");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               asm("pause");
+       }
+       print_result(s, "raw 'pause':");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               asm("mfence");
+       }
+       print_result(s, "Atomic mfence:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               asm("lfence");
+       }
+       print_result(s, "Atomic lfence:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               asm("sfence");
+       }
+       print_result(s, "Atomic sfence:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               uint64_t sidt_rval;
+               asm("sidt %0" : "=m" (sidt_rval));
+       }
+       print_result(s, "'sidt' instruction:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               int prev;
+               asm volatile("cmpxchg %1,%2" : "=a" (prev) : "r" (0l), "m" (global), "0" (1l));
+       }
+       print_result(s, "'cmpxchg' without the 'lock' prefix:");
+#endif
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               __sync_lock_test_and_set(&global, 0);
+       }
+       print_result(s, "Atomic xchg:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               __sync_val_compare_and_swap(&global, 1, 0);
+       }
+       print_result(s, "Atomic cmpxchg:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               __sync_fetch_and_add(&global, 1);
+       }
+       print_result(s, "Atomic increment:");
+
+       global = 0;
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               OSAtomicIncrement32Barrier(&global);
+       }
+       print_result(s, "OSAtomic increment:");
+
+       global = 0;
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               while (!__sync_bool_compare_and_swap(&global, 0, 1)) {
+                       do {
+#if defined(__i386__) || defined(__x86_64__)
+                               asm("pause");
+#endif
+                       } while (global);
+               }
+               global = 0;
+       }
+       print_result(s, "Inlined spin lock/unlock:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               OSSpinLockLock(&slock);
+               OSSpinLockUnlock(&slock);
+       }
+       print_result(s, "OS spin lock/unlock:");
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               r = pthread_mutex_lock(&plock);
+               assert(r == 0);
+               r = pthread_mutex_unlock(&plock);
+               assert(r == 0);
+       }
+       print_result(s, "pthread lock/unlock:");
+
+#ifdef __BLOCKS__
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               dispatch_sync(q, ^{ });
+       }
+       print_result(s, "dispatch_sync:");
+#endif
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               dispatch_sync_f(q, NULL, (void (*)(void *))func);
+       }
+       print_result(s, "dispatch_sync_f:");
+
+#ifdef __BLOCKS__
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               dispatch_barrier_sync(q, ^{ });
+       }
+       print_result(s, "dispatch_barrier_sync:");
+#endif
+
+       s = mach_absolute_time();
+       for (i = cnt; i; i--) {
+               dispatch_barrier_sync_f(q, NULL, (void (*)(void *))func);
+       }
+       print_result(s, "dispatch_barrier_sync_f:");
+
+       s = mach_absolute_time();
+       dispatch_apply_f(cnt, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL, (void (*)(void *, size_t))func);
+       s += loop_cost; /* cancel out the implicit subtraction done by the next line */
+       print_result(s, "dispatch_apply_f():");
+
+       // we do a "double backflip" to hit the fast-path of the enqueue/dequeue logic
+       bfs = mach_absolute_time();
+       dispatch_async_f(dispatch_get_main_queue(), &bf_cnt, backflip);
+       dispatch_async_f(dispatch_get_main_queue(), &bf_cnt, backflip);
+
+       dispatch_main();
+}
+
+__attribute__((noinline))
+void
+backflip_done(void)
+{
+       print_result(bfs, "dispatch_async_f():");
+       exit(EXIT_SUCCESS);
+}
+
+void
+backflip(void *ctxt)
+{
+       size_t *bf_cnt = (size_t *)ctxt;
+       if (--(*bf_cnt)) {
+               return dispatch_async_f(dispatch_get_main_queue(), ctxt, backflip);
+       }
+       backflip_done();
+}
diff --git a/testing/conc.c b/testing/conc.c
new file mode 100644 (file)
index 0000000..40766e2
--- /dev/null
@@ -0,0 +1,33 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <dispatch.h>
+#include <dispatch_private.h>
+#include <pthread.h>
+
+int
+main(void)
+{
+       dispatch_block_t wb = ^(dispatch_item_t di) { printf("\t\t%p\tstart\n", pthread_self()); sleep(3); };
+       dispatch_block_t cb = ^(dispatch_item_t di) { printf("\t\t%p\tdone\n", pthread_self()); };
+       dispatch_queue_t dq;
+       bool r;
+       int i;
+
+       dq = dispatch_queue_new("conc", DISPATCH_QUEUE_CONCURRENT, NULL, NULL, NULL);
+       assert(dq);
+
+       for (i = 0; i < 10; i++) {
+               r = dispatch_call(dq, wb, cb, NULL, NULL);
+               assert(r);
+       }
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_after.c b/testing/dispatch_after.c
new file mode 100644 (file)
index 0000000..7bb174a
--- /dev/null
@@ -0,0 +1,59 @@
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+#include <Block.h>
+
+void done(void *arg __unused) {
+    sleep(1);
+    test_stop();
+}
+
+int
+main(void)
+{
+    __block dispatch_time_t time_a_min, time_a, time_a_max;
+    __block dispatch_time_t time_b_min, time_b, time_b_max;
+    __block dispatch_time_t time_c_min, time_c, time_c_max;
+
+
+       test_start("Dispatch After");
+
+    dispatch_async(dispatch_get_main_queue(), ^{
+            time_a_min = dispatch_time(0,  5.5*NSEC_PER_SEC);
+            time_a     = dispatch_time(0,   6*NSEC_PER_SEC);
+            time_a_max = dispatch_time(0,  6.5*NSEC_PER_SEC);
+            dispatch_after(time_a, dispatch_get_current_queue(), ^{
+                    dispatch_time_t now_a = dispatch_time(0, 0);
+                    test_long_less_than("can't finish faster than 5.5s", 0, now_a - time_a_min);
+                    test_long_less_than("must finish faster than  6.5s", 0, time_a_max - now_a);
+                    
+                    time_b_min = dispatch_time(0,  1.5*NSEC_PER_SEC);
+                    time_b     = dispatch_time(0,    2*NSEC_PER_SEC);
+                    time_b_max = dispatch_time(0,  2.5*NSEC_PER_SEC);
+                    dispatch_after(time_b, dispatch_get_current_queue(), ^{
+                            dispatch_time_t now_b = dispatch_time(0, 0);
+                            test_long_less_than("can't finish faster than 1.5s", 0, now_b - time_b_min);
+                            test_long_less_than("must finish faster than  2.5s", 0, time_b_max - now_b);
+                            
+                            time_c_min = dispatch_time(0,  0*NSEC_PER_SEC);
+                            time_c     = dispatch_time(0,  0*NSEC_PER_SEC);
+                            time_c_max = dispatch_time(0,  .5*NSEC_PER_SEC);
+                            dispatch_after(time_c, dispatch_get_current_queue(), ^{
+                                    dispatch_time_t now_c = dispatch_time(0, 0);
+                                    test_long_less_than("can't finish faster than 0s", 0, now_c - time_c_min);
+                                    test_long_less_than("must finish faster than .5s", 0, time_c_max - now_c);
+
+                                    dispatch_async_f(dispatch_get_current_queue(), NULL, done);
+                                });
+                        });
+                });
+        });
+    
+    dispatch_main();
+    return 0;
+}
diff --git a/testing/dispatch_api.c b/testing/dispatch_api.c
new file mode 100644 (file)
index 0000000..e1fb916
--- /dev/null
@@ -0,0 +1,20 @@
+#include <stdlib.h>
+
+#include "dispatch_test.h"
+
+void
+work(void *context __attribute__((unused)))
+{
+       test_stop();
+       exit(0);
+}
+
+int main(void) {
+       test_start("Dispatch (Public) API");
+       dispatch_queue_t q = dispatch_get_main_queue();
+       test_ptr_notnull("dispatch_get_main_queue", q);
+
+       dispatch_async_f(dispatch_get_main_queue(), NULL, work);
+       dispatch_main();
+       return 0;
+}
diff --git a/testing/dispatch_apply.c b/testing/dispatch_apply.c
new file mode 100644 (file)
index 0000000..60e075f
--- /dev/null
@@ -0,0 +1,29 @@
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+
+int
+main(void)
+{
+       test_start("Dispatch Apply");
+
+       volatile __block int32_t count = 0;
+       const int32_t final = 32;
+
+       dispatch_queue_t queue = dispatch_get_concurrent_queue(0);
+       test_ptr_notnull("dispatch_get_concurrent_queue", queue);
+
+       dispatch_apply(final, queue, ^(size_t i __attribute__((unused))) {
+               OSAtomicIncrement32(&count);
+       });
+
+       test_long("count", count, final);
+       test_stop();
+
+       return 0;
+}
diff --git a/testing/dispatch_c99.c b/testing/dispatch_c99.c
new file mode 100644 (file)
index 0000000..2205c9e
--- /dev/null
@@ -0,0 +1,21 @@
+#include <dispatch/dispatch.h>
+#include <stdlib.h>
+
+#include "dispatch_test.h"
+
+void
+work(void *context __attribute__((unused)))
+{
+       test_stop();
+       exit(0);
+}
+
+int main(void) {
+       test_start("Dispatch C99");
+       dispatch_queue_t q = dispatch_get_main_queue();
+       test_ptr_notnull("dispatch_get_main_queue", q);
+
+       dispatch_async_f(dispatch_get_main_queue(), NULL, work);
+       dispatch_main();
+       return 0;
+}
diff --git a/testing/dispatch_cascade.c b/testing/dispatch_cascade.c
new file mode 100644 (file)
index 0000000..7e09e41
--- /dev/null
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <dispatch/dispatch.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "dispatch_test.h"
+
+int done = 0;
+
+#define QUEUES 80
+dispatch_queue_t queues[QUEUES];
+
+
+#define BLOCKS 10000
+union {
+       size_t index;
+       char padding[64];
+} indices[BLOCKS];
+
+size_t iterations = QUEUES * BLOCKS * 0.25;
+
+void
+histogram(void) {
+       size_t counts[QUEUES] = {};
+       size_t maxcount = 0;
+       
+       size_t q;
+       for (q = 0; q < QUEUES; ++q) {
+               size_t i;
+               for (i = 0; i < BLOCKS; ++i) {
+                       if (indices[i].index == q) {
+                               ++counts[q];
+                       }
+               }
+       }
+       
+       for (q = 0; q < QUEUES; ++q) {
+               if (counts[q] > maxcount) {
+                       maxcount = counts[q];
+               }
+       }
+       
+       printf("maxcount = %ld\n", maxcount);
+       
+       size_t x,y;
+       for (y = 20; y > 0; --y) {
+               for (x = 0; x < QUEUES; ++x) {
+                       double fraction = (double)counts[x] / (double)maxcount;
+                       double value = fraction * (double)20;
+                       printf("%s", (value > y) ? "*" : " ");
+               }
+               printf("\n");
+       }
+}
+
+void
+cascade(void* context) {
+       size_t idx, *idxptr = context;
+
+       if (done) return;
+       
+       idx = *idxptr + 1;
+
+       if (idx < QUEUES) {
+               *idxptr = idx;
+               dispatch_async_f(queues[idx], context, cascade);
+       }
+
+       if (__sync_sub_and_fetch(&iterations, 1) == 0) {
+               done = 1;
+               histogram();
+               test_stop();
+               exit(0);
+       }
+}
+
+int
+main(int argc __attribute__((unused)), char* argv[] __attribute__((unused))) {
+       int i;
+
+       test_start("Dispatch Cascade");
+       
+       for (i = 0; i < QUEUES; ++i) {
+               queues[i] = dispatch_queue_create(NULL, NULL);
+       }
+
+       for (i = 0; i < BLOCKS; ++i) {
+               cascade(&indices[i].index);
+       }
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_cffd.c b/testing/dispatch_cffd.c
new file mode 100644 (file)
index 0000000..13e4ac3
--- /dev/null
@@ -0,0 +1,139 @@
+#include <dispatch/dispatch.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include <CoreServices/CoreServices.h>
+
+#include "dispatch_test.h"
+
+int debug = 0;
+
+#define DEBUG(...) do { \
+               if (debug) fprintf(stderr, __VA_ARGS__); \
+       } while(0);
+
+#define assert_errno(str, expr) do { \
+       if (!(expr)) { \
+               fprintf(stderr, "%s: %s\n", (str), strerror(errno)); \
+               exit(1); \
+       } } while(0);
+
+int
+init_kqueue(void)
+{
+       int kq;
+       int res;
+       struct kevent ke;
+       static struct timespec t0;
+
+       kq = kqueue();
+       assert_errno("kqueue", kq >= 0);
+       
+       EV_SET(&ke, 1, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 1, 0);
+       
+       res = kevent(kq, &ke, 1, NULL, 0, &t0);
+       assert_errno("kevent", res == 0);
+
+       return kq;
+}
+
+int
+read_kevent(int kq)
+{
+       int res;
+       struct kevent ke;
+       //static struct timespec t0;
+
+       res = kevent(kq, NULL, 0, &ke, 1, NULL);
+       assert_errno("kevent", res >= 0);
+
+       fprintf(stdout, "kevent.data = %ld\n", ke.data);
+
+       return (res < 0);
+}
+
+
+static void
+cffd_callback(CFFileDescriptorRef cffd,
+       CFOptionFlags callBackTypes __attribute__((unused)),
+       void *info __attribute__((unused)))
+{
+       int kq;
+
+       kq = CFFileDescriptorGetNativeDescriptor(cffd);
+       if (read_kevent(kq) == 0) {
+               // ...
+       }
+       CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
+}
+
+void
+timer()
+{
+       dispatch_source_t ds;
+       ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+       assert(ds);
+       dispatch_source_set_timer(ds, dispatch_time(0, 1*NSEC_PER_SEC), NSEC_PER_SEC, 0);
+       dispatch_source_set_event_handler(ds, ^{
+               printf("ping\n");
+       });
+       dispatch_resume(ds);
+}
+
+void
+hangup()
+{
+       dispatch_source_t ds;
+       ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, dispatch_get_main_queue());
+       assert(ds);
+       dispatch_source_set_event_handler(ds, ^{
+               printf("hangup\n");
+       });
+       dispatch_resume(ds);
+}
+
+int
+main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+       int kq;
+       CFFileDescriptorRef cffd;
+       CFRunLoopSourceRef  rls;
+       CFFileDescriptorContext ctx;
+
+       test_start("CFFileDescriptor");
+
+       signal(SIGHUP, SIG_IGN);
+
+       kq = init_kqueue();
+
+       memset(&ctx, 0, sizeof(CFFileDescriptorContext));
+       cffd = CFFileDescriptorCreate(NULL, kq, 1, cffd_callback, &ctx);
+       assert(cffd);
+    
+       rls = CFFileDescriptorCreateRunLoopSource(NULL, cffd, 0);
+       assert(rls);
+       CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+       CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
+
+//     timer();
+//     hangup();
+       
+       CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10.0, false);
+
+       test_stop();
+
+       return 0;
+}
+
diff --git a/testing/dispatch_deadname.c b/testing/dispatch_deadname.c
new file mode 100644 (file)
index 0000000..1dfc1c7
--- /dev/null
@@ -0,0 +1,44 @@
+#include <dispatch/dispatch.h>
+#include <mach/mach.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "dispatch_test.h"
+
+int
+main(void)
+{
+       test_start("Dispatch dead-name notification");
+
+       dispatch_async(dispatch_get_concurrent_queue(0), ^{
+               mach_port_t mp = pthread_mach_thread_np(pthread_self());
+               dispatch_source_t ds0;
+               kern_return_t kr;
+
+               assert(mp);
+
+               kr = mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1);
+
+               assert(kr == 0);
+
+               ds0 = dispatch_source_machport_create(mp, DISPATCH_MACHPORT_DEAD, NULL, dispatch_get_main_queue(),
+                                       ^(dispatch_event_t de) {
+                       dispatch_release(dispatch_event_get_source(de));
+                       test_stop();
+                       exit(EXIT_SUCCESS);
+               });
+
+               test_ptr_notnull("dispatch_source_machport_create", ds0);
+
+               // give the mgr queue time to start, otherwise the mgr queue will run
+               // on this thread, thus defeating the test which assumes that this
+               // thread will die.
+               sleep(1);
+       });
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_debug.c b/testing/dispatch_debug.c
new file mode 100644 (file)
index 0000000..8f55106
--- /dev/null
@@ -0,0 +1,30 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+int main(void)
+{
+       test_start("Dispatch Debug");
+
+       dispatch_queue_t main_q = dispatch_get_main_queue();
+       dispatch_debug(main_q, "dispatch_queue_t");
+       
+       dispatch_queue_t default_q = dispatch_get_concurrent_queue(0);
+       dispatch_debug(default_q, "dispatch_queue_t");
+       
+       dispatch_source_attr_t attr = dispatch_source_attr_create();
+       dispatch_debug(attr, "dispatch_source_attr_t");
+
+       dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+               1000000000ull, 0, attr, main_q, ^(dispatch_event_t ev __attribute__((unused))) {});
+       dispatch_debug(s, "dispatch_source_t");
+
+       dispatch_group_t g = dispatch_group_create();
+       dispatch_debug(g, "dispatch_group_t");
+
+       return 0;
+}
diff --git a/testing/dispatch_drift.c b/testing/dispatch_drift.c
new file mode 100644 (file)
index 0000000..902d278
--- /dev/null
@@ -0,0 +1,61 @@
+#include <dispatch/dispatch.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "dispatch_test.h"
+
+int
+main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
+{
+       __block uint32_t count = 0;
+       __block uint64_t first_time_m = 0ULL;
+       __block double first_time_d;
+       __block double last_jitter = 0;
+       // 10 times a second
+       uint64_t interval = 1000000000 / 10;
+       double interval_d = interval / 1000000000.0;
+       // for 25 seconds
+       unsigned int target = 25 / interval_d;
+
+       test_start("Timer drift test");
+
+       dispatch_source_t t = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, interval, 0, NULL, dispatch_get_main_queue(),
+               ^(dispatch_event_t event __attribute__((unused))) {
+                   struct timeval now_tv;
+                   static double first = 0;
+                   gettimeofday(&now_tv, NULL);
+                   double now = now_tv.tv_sec + now_tv.tv_usec / 1000000.0;
+
+                   if (count == 0) {
+                           // Because this is taken at 1st timer fire,
+                           // later jitter values may be negitave.
+                           // This doesn't effect the drift calculation.
+                           first = now;
+                   }
+                   double goal = first + interval_d * count;
+                   double jitter = goal - now;
+                   double drift = jitter - last_jitter;
+
+                   printf("%4d: jitter %f, drift %f\n", count, jitter, drift);
+#if 0
+                   test_double_less_than("drift", now_d - expected_fire_time_d, .001);
+#endif
+
+                   if (target <= ++count) {
+                           if (drift < 0) {
+                                   drift = -drift;
+                           }
+                           test_double_less_than("drift", drift, 0.0001);
+                           test_stop();
+                   }
+                   last_jitter = jitter;
+               });
+       
+       test_ptr_notnull("timer source", t);
+
+       dispatch_main();
+       return 0;
+}
+
diff --git a/testing/dispatch_group.c b/testing/dispatch_group.c
new file mode 100644 (file)
index 0000000..bda3a03
--- /dev/null
@@ -0,0 +1,89 @@
+#include <dispatch/dispatch.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+
+#ifndef NSEC_PER_SEC
+#define NSEC_PER_SEC 1000000000
+#endif
+
+dispatch_group_t
+create_group(size_t count, int delay)
+{
+       size_t i;
+
+       dispatch_group_t group = dispatch_group_create();
+
+       for (i = 0; i < count; ++i) {
+               dispatch_queue_t queue = dispatch_queue_create(NULL, NULL);
+               assert(queue);
+
+               dispatch_group_async(group, queue, ^{
+                       if (delay) {
+                               fprintf(stderr, "sleeping...\n");
+                               sleep(delay);
+                               fprintf(stderr, "done.\n");
+                       }
+               });
+
+               dispatch_release(queue);
+        }
+       return group;
+}
+
+int
+main(void)
+{
+       long res;
+
+       test_start("Dispatch Group");
+
+       dispatch_group_t group;
+
+       group = create_group(100, 0);
+       test_ptr_notnull("dispatch_group_async", group);
+
+       dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+       
+       // should be OK to re-use a group
+       dispatch_group_async(group, dispatch_get_concurrent_queue(0), ^{});
+       dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+
+       dispatch_release(group);
+       group = NULL;
+       
+       group = create_group(3, 7);
+       test_ptr_notnull("dispatch_group_async", group);
+
+       res = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC));
+       test_long("dispatch_group_wait", !res, 0);
+
+       // retry after timeout (this time succeed)
+       res = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC));
+       test_long("dispatch_group_wait", res, 0);
+
+       dispatch_release(group);
+       group = NULL;
+
+       group = create_group(100, 0);
+       test_ptr_notnull("dispatch_group_async", group);
+
+       dispatch_group_notify(group, dispatch_get_main_queue(), ^{
+               dispatch_queue_t m = dispatch_get_main_queue();
+               dispatch_queue_t c = dispatch_get_current_queue();
+               test_ptr("Notification Received", m, c);
+               test_stop();
+       });
+       
+       dispatch_release(group);
+       group = NULL;
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_overcommit.c b/testing/dispatch_overcommit.c
new file mode 100644 (file)
index 0000000..af5e59a
--- /dev/null
@@ -0,0 +1,45 @@
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+
+int32_t count = 0;
+const int32_t final = 32;
+
+int
+main(void)
+{
+       test_start("Dispatch Overcommit");
+
+       dispatch_queue_attr_t attr = dispatch_queue_attr_create();
+       test_ptr_notnull("dispatch_queue_attr_create", attr);
+       dispatch_queue_attr_set_flags(attr, DISPATCH_QUEUE_OVERCOMMIT);
+       
+       int i;
+       for (i = 0; i < final; ++i) {
+               char* name;
+               asprintf(&name, "test.overcommit.%d", i);
+               
+               dispatch_queue_t queue = dispatch_queue_create(name, attr);
+               test_ptr_notnull("dispatch_queue_create", queue);
+               free(name);
+               
+               dispatch_async(queue, ^{
+                       OSAtomicIncrement32(&count);
+                       if (count == final) {
+                               test_long("count", count, final);
+                               test_stop();
+                       } else {
+                               while (1); // spin
+                       }
+               });
+       }
+       
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_pingpong.c b/testing/dispatch_pingpong.c
new file mode 100644 (file)
index 0000000..f40825e
--- /dev/null
@@ -0,0 +1,37 @@
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+
+#include "dispatch_test.h"
+
+uint32_t count = 0;
+const uint32_t final = 1000000; // 10M
+
+void pingpongloop(dispatch_group_t group, dispatch_queue_t ping, dispatch_queue_t pong, size_t counter) {
+       //printf("[%p] %s: %lu\n", (void*)(uintptr_t)pthread_self(), dispatch_queue_get_label(dispatch_get_current_queue()), counter);
+       if (counter < final) {
+               dispatch_group_async(group, pong, ^{ pingpongloop(group, pong, ping, counter+1); });
+       } else {
+               count = counter;
+       }
+}
+
+int main(void) {
+       
+       test_start("Dispatch Ping Pong");
+
+       dispatch_queue_t ping = dispatch_queue_create("ping", NULL);
+       test_ptr_notnull("dispatch_queue_create(ping)", ping);
+       dispatch_queue_t pong = dispatch_queue_create("pong", NULL);
+       test_ptr_notnull("dispatch_queue_create(pong)", pong);
+       
+       dispatch_group_t group = dispatch_group_create();
+       test_ptr_notnull("dispatch_group_create", group);
+       
+       pingpongloop(group, ping, pong, 0);
+       dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+
+       test_long("count", count, final);
+       test_stop();
+
+       return 0;
+}
diff --git a/testing/dispatch_plusplus.cpp b/testing/dispatch_plusplus.cpp
new file mode 100644 (file)
index 0000000..6baccab
--- /dev/null
@@ -0,0 +1,17 @@
+#include <dispatch/dispatch.h>
+#include <stdlib.h>
+
+#include "dispatch_test.h"
+
+int main(void) {
+       test_start("Dispatch C++");
+       dispatch_queue_t q = dispatch_get_main_queue();
+       test_ptr_notnull("dispatch_get_main_queue", q);
+
+       dispatch_async(dispatch_get_main_queue(), ^{
+               test_stop();
+               exit(0);
+       });
+       dispatch_main();
+       return 0;
+}
diff --git a/testing/dispatch_priority.c b/testing/dispatch_priority.c
new file mode 100644 (file)
index 0000000..a964765
--- /dev/null
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <dispatch/dispatch.h>
+#include <dispatch/queue_private.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <TargetConditionals.h>
+
+#include "dispatch_test.h"
+
+int done = 0;
+
+#define BLOCKS 128
+#define PRIORITIES 3
+
+#if TARGET_OS_EMBEDDED
+#define LOOP_COUNT 2000000
+#else
+#define LOOP_COUNT 100000000
+#endif
+
+char *labels[PRIORITIES] = { "LOW", "DEFAULT", "HIGH" };
+int priorities[PRIORITIES] = { DISPATCH_QUEUE_PRIORITY_LOW, DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_PRIORITY_HIGH };
+
+union {
+       size_t count;
+       char padding[64];
+} counts[PRIORITIES];
+
+#define ITERATIONS (size_t)(PRIORITIES * BLOCKS * 0.50)
+size_t iterations = ITERATIONS;
+
+void
+histogram(void) {
+       size_t maxcount = BLOCKS;
+       size_t sc[PRIORITIES];
+       
+       size_t total = 0;
+       
+       size_t x,y;
+       for (y = 0; y < PRIORITIES; ++y) {
+               sc[y] = counts[y].count;
+       }
+
+       for (y = 0; y < PRIORITIES; ++y) {
+               printf("%s: %ld\n", labels[y], sc[y]);
+               total += sc[y];
+               
+               double fraction = (double)sc[y] / (double)maxcount;
+               double value = fraction * (double)80;
+               for (x = 0; x < 80; ++x) {
+                       printf("%s", (value > x) ? "*" : " ");
+               }
+               printf("\n");
+       }
+       
+       test_long("blocks completed", total, ITERATIONS);
+       test_long_less_than("high priority precedence", (long)sc[0], (long)sc[2]);
+}
+
+void
+cpubusy(void* context)
+{
+       size_t *count = context;
+       size_t iterdone;
+
+       size_t idx;
+       for (idx = 0; idx < LOOP_COUNT; ++idx) {
+               if (done) break;
+       }
+       
+       if ((iterdone = __sync_sub_and_fetch(&iterations, 1)) == 0) {
+               __sync_add_and_fetch(&done, 1);
+               __sync_add_and_fetch(count, 1);
+               histogram();
+               test_stop();
+               exit(0);
+       } else if (iterdone > 0) {
+               __sync_add_and_fetch(count, 1);
+       }
+}
+
+void
+submit_work(dispatch_queue_t queue, void* context)
+{
+       int i;
+
+       for (i = 0; i < BLOCKS; ++i) {
+               dispatch_async_f(queue, context, cpubusy);
+       }
+
+#if USE_SET_TARGET_QUEUE
+       dispatch_release(queue);
+#endif
+}
+
+int
+main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
+{
+       dispatch_queue_t q[PRIORITIES];
+       int i;
+
+#if USE_SET_TARGET_QUEUE
+       test_start("Dispatch Priority (Set Target Queue)");
+       for(i = 0; i < PRIORITIES; i++) {
+               q[i] = dispatch_queue_create(labels[i], NULL);
+               test_ptr_notnull("q[i]", q[i]);
+               assert(q[i]);
+               dispatch_set_target_queue(q[i], dispatch_get_global_queue(priorities[i], 0));
+               dispatch_queue_set_width(q[i], DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS);
+       }
+#else
+       test_start("Dispatch Priority");
+       for(i = 0; i < PRIORITIES; i++) {
+               q[i] = dispatch_get_global_queue(priorities[i], 0);
+       }
+#endif
+       
+       for(i = 0; i < PRIORITIES; i++) {
+               submit_work(q[i], &counts[i].count);
+       }
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_proc.c b/testing/dispatch_proc.c
new file mode 100644 (file)
index 0000000..6f5ef02
--- /dev/null
@@ -0,0 +1,89 @@
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <spawn.h>
+#include <signal.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+
+#define PID_CNT 5
+
+static long event_cnt;
+static long cancel_cnt;
+
+int
+main(void)
+{
+       dispatch_source_t proc;
+       int res;
+       pid_t pid;
+
+       test_start("Dispatch Proc");
+       
+       // Creates a process and register multiple observers.  Send a signal,
+       // exit the process, etc., and verify all observers were notified.
+       
+       posix_spawnattr_t attr;
+       res = posix_spawnattr_init(&attr);
+       assert(res == 0);
+       res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
+       assert(res == 0);
+
+       char* args[] = {
+               "/bin/sleep", "2", NULL
+       };
+       
+       res = posix_spawnp(&pid, args[0], NULL, &attr, args, NULL);
+       if (res < 0) {
+               perror(args[0]);
+               exit(127);
+       }
+
+       res = posix_spawnattr_destroy(&attr);
+       assert(res == 0);
+
+       dispatch_queue_t semaphore = dispatch_queue_create("semaphore", NULL);
+       
+       assert(pid > 0);
+
+       int i;
+       for (i = 0; i < PID_CNT; ++i) {
+               dispatch_suspend(semaphore);
+               proc = dispatch_source_proc_create(pid, DISPATCH_PROC_EXIT, NULL, dispatch_get_main_queue(),
+                       ^(dispatch_event_t ev) {
+                               long err_dom, err_val;
+                               if ((err_dom = dispatch_event_get_error(ev, &err_val))) {
+                                       test_long("PROC error domain", err_dom, DISPATCH_ERROR_DOMAIN_POSIX);
+                                       test_long("PROC error value", err_val, ECANCELED);
+                                       cancel_cnt++;
+                                       dispatch_resume(semaphore);
+                               } else {
+                                       long flags = dispatch_event_get_flags(ev);
+                                       test_long("DISPATCH_PROC_EXIT", flags, DISPATCH_PROC_EXIT);
+                                       event_cnt++;
+                                       dispatch_release(dispatch_event_get_source(ev));
+                               }
+               });
+               test_ptr_notnull("dispatch_source_proc_create", proc);
+       }
+
+       dispatch_async(semaphore, ^{
+               int status;
+               int res2 = waitpid(pid, &status, 0);
+               assert(res2 != -1);
+               //int passed = (WIFEXITED(status) && WEXITSTATUS(status) == 0);
+               test_long("Sub-process exited", WEXITSTATUS(status) | WTERMSIG(status), 0);
+               test_long("Event count", event_cnt, PID_CNT);
+               test_long("Cancel count", cancel_cnt, PID_CNT);
+               test_stop();
+       });
+
+       kill(pid, SIGCONT);
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_rdlock_bmark.c b/testing/dispatch_rdlock_bmark.c
new file mode 100644 (file)
index 0000000..ec87100
--- /dev/null
@@ -0,0 +1,140 @@
+#include <dispatch/dispatch.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#define LAPS (1024 * 1024)
+#define THREADS 1
+
+static pthread_rwlock_t pthr_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static semaphore_t wake_port;
+static void (*test_func)(void *);
+static unsigned int thr_count;
+
+static void
+reader(void *ctxt __attribute__((unused)))
+{
+}
+
+static void
+pthr_worker(void *ctxt __attribute__((unused)))
+{
+       size_t i;
+       int r;
+
+       for (i = 0; i < (LAPS / THREADS); i++) {
+               r = pthread_rwlock_rdlock(&pthr_rwlock);
+               assert(r == 0);
+               r = pthread_rwlock_unlock(&pthr_rwlock);
+               assert(r == 0);
+       }
+}
+
+static void
+gcd_worker(void *ctxt)
+{
+       dispatch_queue_t dq = ctxt;
+       size_t i;
+
+       for (i = 0; i < (LAPS / THREADS); i++) {
+               dispatch_read_sync_f(dq, NULL, reader);
+       }
+}
+
+static void *
+worker(void *ctxt)
+{
+       kern_return_t kr;
+
+       __sync_add_and_fetch(&thr_count, 1);
+
+       kr = semaphore_wait(wake_port);
+       assert(kr == 0);
+
+       test_func(ctxt);
+
+       if (__sync_sub_and_fetch(&thr_count, 1) == 0) {
+               kr = semaphore_signal(wake_port);
+               assert(kr == 0);
+       }
+
+       return NULL;
+}
+
+static uint64_t
+benchmark(void *ctxt)
+{
+       pthread_t pthr[THREADS];
+       uint64_t cycles;
+       void *rval;
+       size_t i;
+       int r;
+
+       for (i = 0; i < THREADS; i++) {
+               r = pthread_create(&pthr[i], NULL, worker, ctxt);
+               assert(r == 0);
+       }
+
+       while (thr_count != THREADS) {
+               sleep(1);
+       }
+
+       sleep(1);
+
+       cycles = dispatch_benchmark(1, ^{
+               kern_return_t kr;
+
+               kr = semaphore_signal_all(wake_port);
+               assert(kr == 0);
+               kr = semaphore_wait(wake_port);
+               assert(kr == 0);
+       });
+
+       for (i = 0; i < THREADS; i++) {
+               r = pthread_join(pthr[i], &rval);
+               assert(r == 0);
+       }
+
+       return cycles;
+}
+
+int
+main(void)
+{
+       uint64_t pthr_cycles, gcd_cycles;
+       long double ratio;
+       dispatch_queue_t dq;
+       kern_return_t kr;
+       int r;
+
+       dq = dispatch_queue_create("test", NULL);
+       assert(dq);
+
+       // pthreads lazily inits the object
+       // do not benchmark that fact
+       r = pthread_rwlock_rdlock(&pthr_rwlock);
+       assert(r == 0);
+       r = pthread_rwlock_unlock(&pthr_rwlock);
+       assert(r == 0);
+
+       kr = semaphore_create(mach_task_self(), &wake_port, SYNC_POLICY_FIFO, 0);
+       assert(kr == 0);
+
+       test_func = pthr_worker;
+       pthr_cycles = benchmark(NULL);
+
+       test_func = gcd_worker;
+       gcd_cycles = benchmark(dq);
+
+       dispatch_release(dq);
+
+       ratio = pthr_cycles;
+       ratio /= gcd_cycles;
+
+       printf("Cycles:\n\tPOSIX\t%llu\n", pthr_cycles);
+       printf("\tGCD\t%llu\n", gcd_cycles);
+       printf("Ratio:\t%Lf\n", ratio);
+
+       return 0;
+}
diff --git a/testing/dispatch_read.c b/testing/dispatch_read.c
new file mode 100644 (file)
index 0000000..02ede77
--- /dev/null
@@ -0,0 +1,84 @@
+#include <sys/stat.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+static size_t bytes_total;
+static size_t bytes_read;
+
+int main(void)
+{
+       const char *path = "/usr/share/dict/words";
+       struct stat sb;
+
+       test_start("Dispatch Source Read");
+       
+       int infd = open(path, O_RDONLY);
+       if (infd == -1) {
+               perror(path);
+               exit(EXIT_FAILURE);
+       }
+       if (fstat(infd, &sb) == -1) {
+               perror(path);
+               exit(EXIT_FAILURE);
+       }
+       bytes_total = sb.st_size;
+
+       if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) {
+               perror(path);
+               exit(EXIT_FAILURE);
+       }
+
+       dispatch_queue_t main_q = dispatch_get_main_queue();
+       test_ptr_notnull("dispatch_get_main_queue", main_q);
+       
+       dispatch_source_attr_t attr = dispatch_source_attr_create();
+       test_ptr_notnull("dispatch_source_attr_create", attr);
+       
+       dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) {
+               test_ptr_notnull("finalizer ran", ds);
+               int res = close(infd);
+               test_errno("close", res == -1 ? errno : 0, 0);
+               test_stop();
+       });
+       
+       dispatch_source_t reader = dispatch_source_read_create(infd, attr,
+               main_q, ^(dispatch_event_t ev) {
+                       long err_val;
+                       long err_dom = dispatch_event_get_error(ev, &err_val);
+                       if (!err_dom) {
+                               size_t estimated = dispatch_event_get_bytes_available(ev);
+                               printf("bytes available: %zu\n", estimated);
+                               const ssize_t bufsiz = 1024*500; // 500 KB buffer
+                               static char buffer[1024*500];   // 500 KB buffer
+                               ssize_t actual = read(infd, buffer, sizeof(buffer));
+                               bytes_read += actual;
+                               printf("bytes read: %zd\n", actual);
+                               if (actual < bufsiz) {
+                                       actual = read(infd, buffer, sizeof(buffer));
+                                       bytes_read += actual;
+                                       // confirm EOF condition
+                                       test_long("EOF", actual, 0);
+                                       dispatch_release(dispatch_event_get_source(ev));
+                               }
+                       } else {
+                               test_long("Error domain", err_dom, DISPATCH_ERROR_DOMAIN_POSIX);
+                               test_long("Error value", err_val, ECANCELED);
+                               test_long("Bytes read", bytes_read, bytes_total);
+                       }
+               });
+
+       printf("reader = %p\n", reader);
+       assert(reader);
+
+       dispatch_release(attr);
+
+       dispatch_main();
+}
diff --git a/testing/dispatch_read2.c b/testing/dispatch_read2.c
new file mode 100644 (file)
index 0000000..1be8150
--- /dev/null
@@ -0,0 +1,69 @@
+#include <sys/stat.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+static size_t bytes_total;
+static size_t bytes_read;
+
+int main(void)
+{
+       const char *path = "/usr/share/dict/words";
+       struct stat sb;
+
+       test_start("Dispatch Source Read");
+       
+       int infd = open(path, O_RDONLY);
+       if (infd == -1) {
+               perror(path);
+               exit(EXIT_FAILURE);
+       }
+       if (fstat(infd, &sb) == -1) {
+               perror(path);
+               exit(EXIT_FAILURE);
+       }
+       bytes_total = sb.st_size;
+
+       if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) {
+               perror(path);
+               exit(EXIT_FAILURE);
+       }
+
+       dispatch_queue_t main_q = dispatch_get_main_queue();
+       test_ptr_notnull("dispatch_get_main_queue", main_q);
+               
+       dispatch_source_t reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, main_q);
+       test_ptr_notnull("dispatch_source_create", reader);
+       assert(reader);
+       
+       dispatch_source_set_event_handler(reader, ^{
+                       size_t estimated = dispatch_source_get_data(reader);
+                       printf("bytes available: %zu\n", estimated);
+                       const ssize_t bufsiz = 1024*500; // 500 KB buffer
+                       static char buffer[1024*500];   // 500 KB buffer
+                       ssize_t actual = read(infd, buffer, sizeof(buffer));
+                       bytes_read += actual;
+                       printf("bytes read: %zd\n", actual);
+                       if (actual < bufsiz) {
+                               actual = read(infd, buffer, sizeof(buffer));
+                               bytes_read += actual;
+                               // confirm EOF condition
+                               test_long("EOF", actual, 0);
+                               dispatch_source_cancel(reader);
+                       }
+       });
+       dispatch_source_set_cancel_handler(reader, ^{
+               test_long("Bytes read", bytes_read, bytes_total);
+               test_stop();
+       });
+       dispatch_resume(reader);
+
+       dispatch_main();
+}
diff --git a/testing/dispatch_readsync.c b/testing/dispatch_readsync.c
new file mode 100644 (file)
index 0000000..64da538
--- /dev/null
@@ -0,0 +1,59 @@
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <assert.h>
+
+#include "dispatch_test.h"
+
+#define LAPS 10000
+#define INTERVAL 100
+
+static size_t r_count = LAPS;
+static size_t w_count = LAPS / INTERVAL;
+
+static void
+writer(void *ctxt __attribute__((unused)))
+{
+       if (--w_count == 0) {
+               if (r_count == 0) {
+                       test_stop();
+               }
+       }
+}
+
+static void
+reader(void *ctxt __attribute__((unused)))
+{
+       if (__sync_sub_and_fetch(&r_count, 1) == 0) {
+               if (r_count == 0) {
+                       test_stop();
+               }
+       }
+}
+
+int
+main(void)
+{
+       dispatch_queue_t dq;
+
+       test_start("Dispatch Reader/Writer Queues");
+
+       dq = dispatch_queue_create("com.apple.libdispatch.test_readsync", NULL);
+       assert(dq);
+
+       dispatch_queue_set_width(dq, LONG_MAX);
+
+       dispatch_apply(LAPS, dispatch_get_concurrent_queue(0), ^(size_t idx) {
+               dispatch_sync_f(dq, NULL, reader);
+
+               if (idx % INTERVAL) {
+                       dispatch_barrier_async_f(dq, NULL, writer);
+               }
+       });
+
+       dispatch_release(dq);
+
+       dispatch_main();
+}
diff --git a/testing/dispatch_sema.c b/testing/dispatch_sema.c
new file mode 100644 (file)
index 0000000..f30a2c8
--- /dev/null
@@ -0,0 +1,33 @@
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "dispatch_test.h"
+
+#define LAPS 10000
+
+int
+main(void)
+{
+       static size_t total;
+       dispatch_semaphore_t dsema;
+
+       test_start("Dispatch Semaphore");
+
+       dsema = dispatch_semaphore_create(1);
+       assert(dsema);
+
+       dispatch_apply(LAPS, dispatch_get_concurrent_queue(0), ^(size_t idx __attribute__((unused))) {
+               dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER);
+               total++;
+               dispatch_semaphore_signal(dsema);
+       });
+
+       dispatch_release(dsema);
+
+       test_long("count", total, LAPS);
+       test_stop();
+
+       return 0;
+}
diff --git a/testing/dispatch_starfish.c b/testing/dispatch_starfish.c
new file mode 100644 (file)
index 0000000..fb1f709
--- /dev/null
@@ -0,0 +1,141 @@
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <TargetConditionals.h>
+
+#include "dispatch_test.h"
+
+#if TARGET_OS_EMBEDDED
+#define COUNT  300ul
+#define LAPS   10ul
+#else
+#define COUNT  1000ul
+#define LAPS   10ul
+#endif
+
+static dispatch_queue_t queues[COUNT];
+static size_t lap_count_down = LAPS;
+static size_t count_down;
+static uint64_t start;
+static mach_timebase_info_data_t tbi;
+
+static void do_test(void);
+
+static void
+collect(void *context __attribute__((unused)))
+{
+       uint64_t delta;
+       long double math;
+       size_t i;
+
+       if (--count_down) {
+               return;
+       }
+
+       delta = mach_absolute_time() - start;
+       delta *= tbi.numer;
+       delta /= tbi.denom;
+       math = delta;
+       math /= COUNT * COUNT * 2ul + COUNT * 2ul;
+
+       printf("lap: %ld\n", lap_count_down);
+       printf("count: %lu\n", COUNT);
+       printf("delta: %llu ns\n", delta);
+       printf("math: %Lf ns / lap\n", math);
+
+       for (i = 0; i < COUNT; i++) {
+               dispatch_release(queues[i]);
+       }
+
+       // our malloc could be a lot better,
+       // this result is really a malloc torture test
+       test_long_less_than("Latency" , (unsigned long)math, 1000);
+
+       if (--lap_count_down) {
+               return do_test();
+       }
+
+       // give the threads some time to settle before test_stop() runs "leaks"
+       // ...also note, this is a total cheat.   dispatch_after lets this
+       // thread go idle, so dispatch cleans up the continuations cache.
+       // Doign the "old style" sleep left that stuff around and leaks
+       // took a LONG TIME to complete.   Long enough that the test harness
+       // decided to kill us.
+       dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), NULL, test_stop_after_delay);
+}
+
+static void
+pong(void *context)
+{
+       dispatch_queue_t this_q = context;
+       size_t replies = (size_t)dispatch_get_context(this_q);
+
+       dispatch_set_context(this_q, (void *)--replies);
+       if (!replies) {
+               //printf("collect from: %s\n", dispatch_queue_get_label(dispatch_get_current_queue()));
+               dispatch_async_f(dispatch_get_main_queue(), NULL, collect);
+       }
+}
+
+static void
+ping(void *context)
+{
+       dispatch_queue_t reply_q = context;
+
+       dispatch_async_f(reply_q, reply_q, pong);
+}
+
+static void
+start_node(void *context)
+{
+       dispatch_queue_t this_q = context;
+       size_t i;
+
+       dispatch_set_context(this_q, (void *)COUNT);
+
+       for (i = 0; i < COUNT; i++) {
+               dispatch_async_f(queues[i], this_q, ping);
+       }
+}
+
+void
+do_test(void)
+{
+       size_t i;
+       kern_return_t kr;
+
+       count_down = COUNT;
+
+       kr = mach_timebase_info(&tbi);
+       assert(kr == 0);
+
+       start = mach_absolute_time();
+
+       for (i = 0; i < COUNT; i++) {
+               char buf[1000];
+               snprintf(buf, sizeof(buf), "com.example.starfish-node#%ld", i);
+               queues[i] = dispatch_queue_create(buf, NULL);
+               dispatch_suspend(queues[i]);
+       }
+
+       for (i = 0; i < COUNT; i++) {
+               dispatch_async_f(queues[i], queues[i], start_node);
+       }
+
+       for (i = 0; i < COUNT; i++) {
+               dispatch_resume(queues[i]);
+       }
+}
+
+int
+main(void)
+{
+    test_start("Dispatch Starfish");
+
+       do_test();
+
+       dispatch_main();
+}
diff --git a/testing/dispatch_suspend_timer.c b/testing/dispatch_suspend_timer.c
new file mode 100644 (file)
index 0000000..f271f39
--- /dev/null
@@ -0,0 +1,76 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+dispatch_source_t tweedledee;
+dispatch_source_t tweedledum;
+
+int main(void)
+{
+       test_start("Dispatch Suspend Timer");
+
+       dispatch_queue_t main_q = dispatch_get_main_queue();
+       test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
+
+       __block int i = 0;
+       __block int j = 0;
+
+       dispatch_source_attr_t attr = dispatch_source_attr_create();
+       test_ptr_notnull("dispatch_source_attr_create", attr);
+       
+       dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) {
+               test_ptr_notnull("finalizer ran", ds);
+               if (ds == tweedledum) test_stop();
+       });
+
+       tweedledee = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+               1000000000ull, 0, attr, main_q, ^(dispatch_event_t ev) {
+                       long err;
+                       if (dispatch_event_get_error(ev, &err)) {
+                               test_errno("dispatch_event_get_error", err, ECANCELED);
+                               dispatch_release(dispatch_event_get_source(ev));
+                       } else {
+                               fprintf(stderr, "%d\n", ++i);
+                               if (i == 10) {
+                                       dispatch_cancel(dispatch_event_get_source(ev));
+                               }
+                       }
+               });
+       test_ptr_notnull("dispatch_source_timer_create", tweedledee);
+       dispatch_retain(tweedledee);
+
+       tweedledum = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+               3000000000ull, 0, attr, main_q, ^(dispatch_event_t ev) {
+                       long err;
+                       if (dispatch_event_get_error(ev, &err)) {
+                               test_errno("dispatch_event_get_error", err, ECANCELED);
+                               dispatch_release(dispatch_event_get_source(ev));
+                       } else {
+                               switch(++j) {
+                                       case 1:
+                                               fprintf(stderr, "suspending timer for 3 seconds\n");
+                                               dispatch_suspend(tweedledee);
+                                               break;
+                                       case 2:
+                                               fprintf(stderr, "resuming timer\n");
+                                               dispatch_resume(tweedledee);
+                                               dispatch_release(tweedledee);
+                                               break;
+                                       default:
+                                               dispatch_cancel(dispatch_event_get_source(ev));
+                                               break;
+                               }
+                       }
+               });
+       test_ptr_notnull("dispatch_source_timer_create", tweedledum);
+       
+       dispatch_release(attr);
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_test.c b/testing/dispatch_test.c
new file mode 100644 (file)
index 0000000..82b404f
--- /dev/null
@@ -0,0 +1,148 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/errno.h>
+#include <string.h>
+
+#include "dispatch_test.h"
+
+#define _test_print(_file, _line, _desc, \
+       _expr, _fmt1, _val1, _fmt2, _val2) do { \
+       const char* _exprstr = _expr ? "PASS" : "FAIL"; \
+       char _linestr[BUFSIZ]; \
+       if (!_expr) { \
+               snprintf(_linestr, sizeof(_linestr), \
+                       " (%s:%ld)", _file, _line); \
+       } else { \
+               _linestr[0] = 0; \
+       } \
+       if (_fmt2 == 0) { \
+               printf("\tValue: " _fmt1 "\n"           \
+                       "[%s] %s%s\n",                  \
+                       _val1,                          \
+                       _exprstr,                       \
+                       _desc,                          \
+                       _linestr);                      \
+       } else { \
+               printf("\tActual: " _fmt1 "\n"     \
+                       "\tExpected: " _fmt2 "\n"  \
+                       "[%s] %s%s\n",             \
+                       _val1,                     \
+                       _val2,                     \
+                       _exprstr,                  \
+                       _desc,                     \
+                       _linestr);                 \
+       } \
+       if (!_expr) { \
+               printf("\t%s:%ld\n", _file, _line); \
+       } \
+       fflush(stdout); \
+} while (0);
+
+void
+test_start(const char* desc) {
+       printf("\n==================================================\n");
+       printf("[TEST] %s\n", desc);
+       printf("[PID] %d\n", getpid());
+       printf("==================================================\n\n");
+       usleep(100000); // give 'gdb --waitfor=' a chance to find this proc
+}
+
+#define test_ptr_null(a,b) _test_ptr_null(__FILE__, __LINE__, a, b)
+void
+_test_ptr_null(const char* file, long line, const char* desc, const void* ptr) {
+       _test_print(file, line, desc,
+               (ptr == NULL), "%p", ptr, "%p", (void*)0);
+}
+
+#define test_ptr_notnull(a,b) _test_ptr_notnull(__FILE__, __LINE__, a, b)
+void
+_test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr) {
+       _test_print(file, line, desc,
+               (ptr != NULL), "%p", ptr, "%p", ptr ?: (void*)~0);
+}
+
+#define test_ptr(a,b,c) _test_ptr(__FILE__, __LINE__, a, b, c)
+void
+_test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected) {
+       _test_print(file, line, desc,
+               (actual == expected), "%p", actual, "%p", expected);
+}
+
+#define test_long(a,b,c) _test_long(__FILE__, __LINE__, a, b, c)
+void
+_test_long(const char* file, long line, const char* desc, long actual, long expected) {
+       _test_print(file, line, desc,
+               (actual == expected), "%ld", actual, "%ld", expected);
+}
+
+#define test_long_less_than(a, b, c) _test_long_less_than(__FILE__, __LINE__, a, b, c)
+void
+_test_long_less_than(const char* file, long line, const char* desc, long actual, long expected_max) {
+       _test_print(file, line, desc, (actual < expected_max), "%ld", actual, "<%ld", expected_max);
+}
+
+#define test_double_less_than(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+void
+_test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected) {
+       _test_print(file, line, desc, (val < max_expected), "%f", val, "<%f", max_expected);
+}
+
+#define test_double_less_than_or_equal(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+void
+_test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected) {
+       _test_print(file, line, desc, (val <= max_expected), "%f", val, "<%f", max_expected);
+}
+
+#define test_errno(a,b,c) _test_errno(__FILE__, __LINE__, a, b, c)
+void
+_test_errno(const char* file, long line, const char* desc, long actual, long expected) {
+       char* actual_str;
+       char* expected_str;
+       asprintf(&actual_str, "%ld\t%s", actual, actual ? strerror(actual) : "");
+       asprintf(&expected_str, "%ld\t%s", expected, expected ? strerror(expected) : "");
+       _test_print(file, line, desc,
+               (actual == expected), "%s", actual_str, "%s", expected_str);
+       free(actual_str);
+       free(expected_str);
+}
+
+#include <spawn.h>
+
+extern char **environ;
+
+void
+test_stop(void) {
+       test_stop_after_delay((void *)(intptr_t)0);
+}
+
+void
+test_stop_after_delay(void *delay) {
+       int res;
+       pid_t pid;
+       char pidstr[10];
+
+       if (delay != NULL) {
+               sleep((int)(intptr_t)delay);
+       }
+
+       if (getenv("NOLEAKS")) _exit(EXIT_SUCCESS);
+
+       /* leaks doesn't work against debug variant malloc */
+       if (getenv("DYLD_IMAGE_SUFFIX")) _exit(EXIT_SUCCESS);
+       
+       snprintf(pidstr, sizeof(pidstr), "%d", getpid());
+       char* args[] = { "./leaks-wrapper", pidstr, NULL };
+       res = posix_spawnp(&pid, args[0], NULL, NULL, args, environ);
+       if (res == 0 && pid > 0) {
+               int status;
+               waitpid(pid, &status, 0);
+               test_long("Leaks", status, 0);
+       } else {
+               perror(args[0]);
+       }
+       
+       _exit(EXIT_SUCCESS);
+}
diff --git a/testing/dispatch_test.h b/testing/dispatch_test.h
new file mode 100644 (file)
index 0000000..59547f4
--- /dev/null
@@ -0,0 +1,33 @@
+#include <errno.h>
+
+__BEGIN_DECLS
+
+void test_start(const char* desc);
+void test_stop(void);
+void test_stop_after_delay(void *delay);
+
+void _test_ptr_null(const char* file, long line, const char* desc, const void* ptr);
+#define test_ptr_null(a,b) _test_ptr_null(__FILE__, __LINE__, a, b)
+
+void _test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr);
+#define test_ptr_notnull(a,b) _test_ptr_notnull(__FILE__, __LINE__, a, b)
+
+void _test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected);
+#define test_ptr(a,b,c) _test_ptr(__FILE__, __LINE__, a, b, c)
+
+void _test_long(const char* file, long line, const char* desc, long actual, long expected);
+#define test_long(a,b,c) _test_long(__FILE__, __LINE__, a, b, c)
+
+void _test_long_less_than(const char* file, long line, const char* desc, long actual, long max_expected);
+#define test_long_less_than(a,b,c) _test_long_less_than(__FILE__, __LINE__, a, b, c)
+
+void _test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected);
+#define test_double_less_than_or_equal(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+
+void _test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected);
+#define test_double_less_than(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+
+void _test_errno(const char* file, long line, const char* desc, long actual, long expected);
+#define test_errno(a,b,c) _test_errno(__FILE__, __LINE__, a, b, c)
+
+__END_DECLS
diff --git a/testing/dispatch_test_sync_on_main.c b/testing/dispatch_test_sync_on_main.c
new file mode 100644 (file)
index 0000000..6adba96
--- /dev/null
@@ -0,0 +1,46 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+#include <assert.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+int global_count;
+
+void
+main_work(void* ctxt)
+{
+       if (global_count == 20) {
+               exit(0);
+       }
+       uint64_t time = random() % NSEC_PER_SEC;
+       printf("Firing timer on main %d\n", ++global_count);
+       dispatch_after_f(dispatch_time(0, time), dispatch_get_main_queue(), NULL, main_work);
+}
+
+
+int main(void) {
+       global_count = 0;
+
+       dispatch_queue_t dq = dispatch_queue_create("foo.bar", NULL);
+               dispatch_async(dq, ^{
+                       
+                       dispatch_async_f(dispatch_get_main_queue(), NULL, main_work);
+                       
+                       int i;
+                       for (i=0; i<5; ++i) {
+                               dispatch_sync(dispatch_get_main_queue(), ^{
+                                       printf("Calling sync %d\n", i);
+                                       assert(pthread_main_np() == 1);
+                                       if (i==4) {
+                                               global_count = 20;
+                                       }
+                               });
+                       }
+               });
+
+       //dispatch_main();
+       CFRunLoopRun();
+       return 0;
+}
diff --git a/testing/dispatch_timer.c b/testing/dispatch_timer.c
new file mode 100644 (file)
index 0000000..ddda1a9
--- /dev/null
@@ -0,0 +1,65 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+int main(void)
+{
+       test_start("Dispatch Source Timer");
+
+       dispatch_queue_t main_q = dispatch_get_main_queue();
+       test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
+
+       uint64_t j;
+
+       // create several timers and release them.
+       for (j = 1; j <= 5; ++j)
+       {
+               dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+                       (uint64_t)j * (uint64_t)1000000000ull, 0, NULL, dispatch_get_concurrent_queue(0),
+                       ^(dispatch_event_t ev) {
+                               if (!dispatch_event_get_error(ev, NULL)) {
+                                       fprintf(stderr, "timer[%lld]\n", j);
+                                       dispatch_release(dispatch_event_get_source(ev));
+                               }
+               });
+               test_ptr_notnull("dispatch_source_timer_create", s);
+       }
+
+       dispatch_source_attr_t attr = dispatch_source_attr_create();
+       dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) {
+               test_ptr_notnull("finalizer ran", ds);
+               test_stop();
+       });
+       
+       __block int i = 0;
+       
+       dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+               1000000000ull,
+               0,
+               attr,
+               main_q,
+               ^(dispatch_event_t ev) {
+                       long err;
+                       if (dispatch_event_get_error(ev, &err)) {
+                               test_errno("dispatch_event_get_error", err, ECANCELED);
+                               dispatch_release(dispatch_event_get_source(ev));
+                       } else {
+                               fprintf(stderr, "%d\n", ++i);
+                               if (i >= 3) {
+                                       dispatch_cancel(dispatch_event_get_source(ev));
+                               }
+                       }
+               });
+       test_ptr_notnull("dispatch_source_timer_create", s);
+
+       dispatch_release(attr);
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_timer_bit31.c b/testing/dispatch_timer_bit31.c
new file mode 100644 (file)
index 0000000..131c8c0
--- /dev/null
@@ -0,0 +1,56 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+int main(void)
+{
+       test_start("Dispatch Source Timer, bit 31");
+
+       dispatch_queue_t main_q = dispatch_get_main_queue();
+       test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
+
+       __block int i = 0;
+       struct timeval start_time;
+
+       gettimeofday(&start_time, NULL);
+       dispatch_source_attr_t attr = dispatch_source_attr_create();
+       dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) {
+               struct timeval end_time;
+               gettimeofday(&end_time, NULL);
+               test_ptr_notnull("finalizer ran", ds);
+               // XXX: check, s/b 2.0799... seconds, which is <4 seconds
+               // when it could end on a bad boundry.
+               test_long_less_than("needs to finish faster than 4 seconds", end_time.tv_sec - start_time.tv_sec, 4);
+               // And it has to take at least two seconds...
+               test_long_less_than("can't finish faster than 2 seconds", 1, end_time.tv_sec - start_time.tv_sec);
+               test_stop();
+       });
+
+       dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+               0x80000000ull,
+               0,
+               attr,
+               main_q,
+               ^(dispatch_event_t ev) {
+                       long err;
+                       if (dispatch_event_get_error(ev, &err)) {
+                               test_errno("dispatch_event_get_error", err, ECANCELED);
+                               dispatch_release(dispatch_event_get_source(ev));
+                       } else {
+                               fprintf(stderr, "%d\n", ++i);
+                               dispatch_cancel(dispatch_event_get_source(ev));
+                       }
+               });
+       test_ptr_notnull("dispatch_source_timer_create", s);
+
+       dispatch_release(attr);
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_timer_bit63.c b/testing/dispatch_timer_bit63.c
new file mode 100644 (file)
index 0000000..6630794
--- /dev/null
@@ -0,0 +1,42 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+int main(void)
+{
+       test_start("Dispatch Source Timer, bit 63");
+
+       //uint64_t interval = 0xffffffffffffffffull;
+       uint64_t interval = 0x8000000000000001ull;
+
+       dispatch_queue_t mainq = dispatch_get_main_queue();
+
+       __block int i = 0;
+       struct timeval start_time;
+
+       gettimeofday(&start_time, NULL);
+
+       dispatch_source_t ds;
+       ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, mainq);
+       assert(ds);
+       dispatch_source_set_event_handler(ds, ^{
+               assert(i < 1);
+               printf("%d\n", i++);
+       });
+       dispatch_source_set_timer(ds, DISPATCH_TIME_NOW, interval, 0);
+       dispatch_resume(ds);
+
+       dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC),
+               dispatch_get_main_queue(), ^{
+               test_stop();
+       });
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_timer_oneshot.c b/testing/dispatch_timer_oneshot.c
new file mode 100644 (file)
index 0000000..9860601
--- /dev/null
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+static void
+oneshot(void* context __attribute__((unused)), dispatch_event_t de)
+{
+       dispatch_source_t ds = dispatch_event_get_source(de);
+       test_ptr_notnull("dispatch_event_get_source", ds);
+
+       if (!dispatch_event_get_error(de, NULL)) {
+               long canceled = dispatch_testcancel(ds);
+               test_long("dispatch_testcancel", canceled, 0);
+
+               dispatch_release(ds);
+               test_stop();
+       }
+}
+
+int
+main(void)
+{
+       test_start("Dispatch Timer One-Shot");
+
+       dispatch_source_t s;
+
+       s  = dispatch_source_timer_create_f(DISPATCH_TIMER_ONESHOT,
+               (uint64_t)1000000000ull, // 1s
+               0,
+               NULL,
+               dispatch_get_concurrent_queue(0),
+               NULL,
+               &oneshot);
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/dispatch_timer_set_time.c b/testing/dispatch_timer_set_time.c
new file mode 100644 (file)
index 0000000..189db33
--- /dev/null
@@ -0,0 +1,60 @@
+#include <sys/time.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+int main(void)
+{
+       test_start("Dispatch Update Timer");
+
+       dispatch_queue_t main_q = dispatch_get_main_queue();
+       test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
+
+       __block int i = 0;
+       struct timeval start_time;
+
+       gettimeofday(&start_time, NULL);
+       dispatch_source_attr_t attr = dispatch_source_attr_create();
+       dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) {
+               struct timeval end_time;
+               gettimeofday(&end_time, NULL);
+               // Make sure we actually managed to adjust the interval
+               // duration.  Seven one second ticks would blow past
+               // this.
+               test_long_less_than("total duration", end_time.tv_sec - start_time.tv_sec, 3);
+               test_ptr_notnull("finalizer ran", ds);
+               test_stop();
+       });
+
+       dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+               1000000000ull,
+               0,
+               attr,
+               main_q,
+               ^(dispatch_event_t ev) {
+                       long err;
+                       if (dispatch_event_get_error(ev, &err)) {
+                               test_errno("dispatch_event_get_error", err, ECANCELED);
+                               dispatch_release(dispatch_event_get_source(ev));
+                       } else {
+                               fprintf(stderr, "%d\n", ++i);
+                               if (i >= 7) {
+                                       dispatch_cancel(dispatch_event_get_source(ev));
+                               } else if (i == 1) {
+                                       dispatch_source_timer_set_time(dispatch_event_get_source(ev), 100, 0);
+                               }
+                       }
+               });
+       test_ptr_notnull("dispatch_source_timer_create", s);
+
+       dispatch_release(attr);
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/fast_apply_bench.c b/testing/fast_apply_bench.c
new file mode 100644 (file)
index 0000000..599845e
--- /dev/null
@@ -0,0 +1,98 @@
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+static inline uint64_t
+rdtsc(void)
+{
+       uint32_t lo, hi;
+
+       asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
+
+       return (uint64_t)hi << 32 | lo;
+}
+
+__attribute__((noinline)) void
+apply_p(void (^b)(size_t), size_t offset, size_t count)
+{
+       /* This would feed through to the existing dispatch_apply() */
+       abort();
+}
+
+/* a dynamically variable to eventually be added to the kernel/user 'commpage' */
+size_t total_active_cpus = 8;
+
+__attribute__((noinline)) void
+apply(void (^b)(size_t), size_t offset, size_t count)
+{
+       const size_t too_long = 100000; /* 100 us */
+       const size_t laps = 16;
+       uint64_t delta, tmp, now;
+       size_t i;
+
+       if (total_active_cpus == 1) {
+               for (i = 0; i < count; i++) {
+                       b(offset + i);
+               }
+               return;
+       }
+
+       now = mach_absolute_time();
+
+       for (i = 0; i < count; i++) {
+               b(offset + i);
+
+               if (i % laps) {
+                       continue;
+               }
+
+               tmp = mach_absolute_time();
+               delta = tmp - now;
+               now = tmp;
+
+               if (delta > (too_long * laps) || (i == 0 && delta > too_long)) {
+                       apply_p(b, offset + i + 1, count - (i + 1));
+                       return;
+               }
+       }
+}
+
+int
+main(void)
+{
+       void (^b)(size_t) = ^(size_t index) {
+               asm volatile(""); /* defeat compiler optimizations */
+       };
+       const size_t laps = 10000000;
+       mach_timebase_info_data_t tbi;
+       kern_return_t kr;
+       long double dd;
+       uint64_t s, e;
+       size_t i;
+
+        kr = mach_timebase_info(&tbi);
+       assert(kr == 0);
+       assert(tbi.numer == tbi.denom); /* This will fail on PowerPC. */
+
+       s = mach_absolute_time();
+       for (i = 0; i < laps; i++) {
+               b(i);
+       }
+       e = mach_absolute_time();
+       dd = e - s;
+       dd /= laps;
+       printf("direct:\t%Lf ns\n", dd);
+
+       s = mach_absolute_time();
+       apply(b, 0, laps);
+       e = mach_absolute_time();
+       dd = e - s;
+       dd /= laps;
+       printf("apply:\t%Lf ns\n", dd);
+
+       return 0;
+}
diff --git a/testing/fd_stress.c b/testing/fd_stress.c
new file mode 100644 (file)
index 0000000..fb2cd5a
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * fd_stress.c
+ *
+ * Stress test for dispatch read and write sources.
+ */
+
+#include <dispatch/dispatch.h>
+
+#include <assert.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+static inline size_t max(size_t a, size_t b) {
+       return (a > b) ? a : b;
+}
+
+static inline size_t min(size_t a, size_t b) {
+       return (a < b) ? a : b;
+}
+
+int debug = 0;
+
+#define DEBUG(...) do { \
+               if (debug) fprintf(stderr, __VA_ARGS__); \
+       } while(0);
+
+#define assert_errno(str, expr) do { \
+       if (!(expr)) { \
+               fprintf(stderr, "%s: %s\n", (str), strerror(errno)); \
+               exit(1); \
+       } } while(0);
+
+#define assert_gai_errno(str, expr) do { \
+       if (!(expr)) { \
+               fprintf(stderr, "%s: %s\n", (str), gai_strerror(errno)); \
+               exit(1); \
+       } } while(0);
+
+
+/* sock_context
+ *
+ * Context structure used by the reader and writer queues.
+ *
+ * Writers begin by generating a random length and writing it to the descriptor.
+ * The write buffer is filled with a random byte value and written until empty
+ * or until the total length is reached. The write buffer is refilled with more
+ * random data when empty. Each write updates an MD5 digest which is written to
+ * the descriptor once the total length is reached.
+ *
+ * Readers begin by reading the total length of data. The read buffer is filled
+ * and an MD5 digest is computed on the bytes as they are received. Once the
+ * total length of data has be read, an MD5 digest is read from the descriptor
+ * and compared with the computed value.
+ */ 
+struct sock_context {
+       enum {
+               LENGTH,
+               DATA,
+               CKSUM,
+               DONE,
+       } state;
+       char label[64];
+       uint32_t len;
+       off_t offset;
+       char buf[8192];
+       size_t buflen;
+       CC_MD5_CTX md5ctx;
+       char md5[CC_MD5_DIGEST_LENGTH];
+};
+
+dispatch_source_t
+create_writer(int wfd, dispatch_block_t completion)
+{
+       dispatch_source_t ds;
+       struct sock_context *ctx = calloc(1, sizeof(struct sock_context));
+       assert(ctx);
+
+       snprintf(ctx->label, sizeof(ctx->label), "writer.fd.%d", wfd);
+       dispatch_queue_t queue = dispatch_queue_create(ctx->label, 0);
+       
+       ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, wfd, 0, queue);
+       assert(ds);
+       dispatch_release(queue);
+       
+       uint32_t len;
+       do {
+               len = (arc4random() & 0x7FFF);
+       } while (len == 0);
+       ctx->state = LENGTH;
+       CC_MD5_Init(&ctx->md5ctx);
+       ctx->len = len;
+       ctx->buflen = sizeof(len);
+       len = htonl(len);
+       memcpy(ctx->buf, &len, ctx->buflen);
+       DEBUG("%s: LENGTH %d\n", ctx->label, ctx->len);
+       
+       dispatch_source_set_event_handler(ds, ^{
+               DEBUG("%s: available %ld\n", ctx->label, dispatch_source_get_data(ds));
+               ssize_t res;
+               size_t wrsz = min(ctx->len, ctx->buflen);
+               res = write(wfd, &ctx->buf[ctx->offset], wrsz);
+               DEBUG("%s: write(%d, %p, %ld): %ld\n", ctx->label, wfd, &ctx->buf[ctx->offset], wrsz, res);
+               if (res > 0) {
+                       if (ctx->state == DATA) {
+                               CC_MD5_Update(&ctx->md5ctx, &ctx->buf[ctx->offset], res);
+                               ctx->len -= res;
+                       }
+                       ctx->offset += res;
+                       ctx->buflen -= res;
+                       assert(ctx->offset >= 0);
+                       assert(ctx->len >= 0);
+                       assert(ctx->buflen >= 0);
+                       if (ctx->buflen == 0 || ctx->len == 0) {
+                               if (ctx->state == LENGTH) {
+                                       // finished writing length, move on to data.
+                                       ctx->state = DATA;
+                                       ctx->buflen = sizeof(ctx->buf);
+                                       char pattern = arc4random() & 0xFF;
+                                       memset(ctx->buf, pattern, ctx->buflen);
+                               } else if (ctx->state == DATA && ctx->len == 0) {
+                                       // finished writing data, move on to cksum.
+                                       ctx->state = CKSUM;
+                                       ctx->len = sizeof(ctx->md5);
+                                       ctx->buflen = sizeof(ctx->md5);
+                                       CC_MD5_Final(ctx->md5, &ctx->md5ctx);
+                                       memcpy(ctx->buf, ctx->md5, ctx->buflen);
+                               } else if (ctx->state == DATA) {
+                                       ctx->buflen = sizeof(ctx->buf);
+                                       char pattern = arc4random() & 0xFF;
+                                       memset(ctx->buf, pattern, ctx->buflen);
+                               } else if (ctx->state == CKSUM) {
+                                       ctx->state = DONE;
+                                       dispatch_source_cancel(ds);
+                               } else {
+                                       assert(0);
+                               }
+                               ctx->offset = 0;
+                       }
+               } else if (res == 0) {
+                       assert(ctx->state == DONE);
+                       assert(0);
+               } else if (res == -1 && errno == EAGAIN) {
+                       DEBUG("%s: EAGAIN\n", ctx->label);
+               } else {
+                       assert_errno("write", res >= 0);
+               }
+       });
+       dispatch_source_set_cancel_handler(ds, ^{
+               DEBUG("%s: close(%d)\n", ctx->label, wfd);
+               int res = close(wfd);
+               assert_errno("close", res == 0);
+               completion();
+               dispatch_release(ds);
+               free(ctx);
+       });
+       dispatch_resume(ds);
+       return ds;
+}
+
+dispatch_source_t
+create_reader(int rfd, dispatch_block_t completion)
+{
+       dispatch_source_t ds;
+       struct sock_context *ctx = calloc(1, sizeof(struct sock_context));
+       assert(ctx);
+       
+       snprintf(ctx->label, sizeof(ctx->label), "reader.fd.%d", rfd);
+       dispatch_queue_t queue = dispatch_queue_create(ctx->label, 0);
+       
+       ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, rfd, 0, queue);
+       assert(ds);
+       dispatch_release(queue);
+       
+       ctx->state = LENGTH;
+       ctx->len = sizeof(ctx->len);
+       ctx->buflen = sizeof(ctx->len);
+       CC_MD5_Init(&ctx->md5ctx);
+       
+       dispatch_source_set_event_handler(ds, ^{
+               DEBUG("%s: available %ld\n", ctx->label, dispatch_source_get_data(ds));
+               ssize_t res;
+               size_t rdsz = min(ctx->len, ctx->buflen);
+               res = read(rfd, &ctx->buf[ctx->offset], rdsz);
+               DEBUG("%s: read(%d,%p,%ld): %ld\n", ctx->label, rfd, &ctx->buf[ctx->offset], rdsz, res);
+
+               // log unexpected data lengths...
+               long expected = dispatch_source_get_data(ds);
+               long actual = res;
+               if (actual >= 0 && (actual != expected && actual != rdsz)) {
+                       fprintf(stderr, "%s: expected %ld, actual %ld (rdsz = %ld)\n", ctx->label, expected, actual, rdsz);
+               }
+
+               if (res > 0) {
+                       if (ctx->state == DATA) {
+                               CC_MD5_Update(&ctx->md5ctx, &ctx->buf[ctx->offset], res);
+                               ctx->len -= res;
+                       }
+                       ctx->offset += res;
+                       ctx->buflen -= res;
+                       if (ctx->buflen == 0 || ctx->len == 0) {
+                               if (ctx->state == LENGTH) {
+                                       // buffer is full, interpret as uint32_t
+                                       memcpy(&ctx->len, ctx->buf, sizeof(ctx->len));
+                                       ctx->len = ntohl(ctx->len);
+                                       ctx->buflen = sizeof(ctx->buf);
+                                       ctx->state = DATA;
+                               } else if (ctx->state == DATA && ctx->len == 0) {
+                                       CC_MD5_Final(ctx->md5, &ctx->md5ctx);
+                                       ctx->state = CKSUM;
+                                       ctx->len = CC_MD5_DIGEST_LENGTH;
+                                       ctx->buflen = ctx->len;
+                               } else if (ctx->state == DATA) {
+                                       ctx->buflen = sizeof(ctx->buf);
+                               } else if (ctx->state == CKSUM) {
+                                       ctx->state = DONE;
+                                       res = memcmp(ctx->buf, ctx->md5, sizeof(ctx->md5));
+                                       if (res != 0) {
+                                               DEBUG("%s: MD5 FAILURE\n", ctx->label);
+                                       }
+                                       assert(res == 0);
+                               }
+                               ctx->offset = 0;
+                       }
+               } else if (res == 0) {
+                       assert(ctx->state == DONE);
+                       DEBUG("%s: EOF\n", ctx->label);
+                       dispatch_source_cancel(ds);
+               } else {
+                       assert_errno("read", res >= 0);
+               }
+       });
+       dispatch_source_set_cancel_handler(ds, ^{
+               DEBUG("%s: close(%d)\n", ctx->label, rfd);
+               int res = close(rfd);
+               assert_errno("close", res == 0);
+               completion();
+               dispatch_release(ds);
+               free(ctx);
+       });
+       dispatch_resume(ds);
+       return ds;
+}
+
+void
+set_nonblock(int fd)
+{
+       int res, flags;
+       flags = fcntl(fd, F_GETFL);
+
+       flags |= O_NONBLOCK;
+       res = fcntl(fd, F_SETFL, flags);
+       assert_errno("fcntl(F_SETFL,O_NONBLOCK)", res == 0);
+}
+
+void
+create_fifo(int *rfd, int *wfd)
+{
+       int res;
+       char *name;
+       
+       char path[MAXPATHLEN];
+       strlcpy(path, "/tmp/fd_stress.fifo.XXXXXX", sizeof(path));
+       name = mktemp(path);
+       
+       res = unlink(name);
+
+       res = mkfifo(name, 0700);
+       assert_errno(name, res == 0);
+       
+       *rfd = open(name, O_RDONLY | O_NONBLOCK);
+       assert_errno(name, *rfd >= 0);
+       
+       *wfd = open(name, O_WRONLY | O_NONBLOCK);
+       assert_errno(name, *wfd >= 0);
+}
+
+void
+create_pipe(int *rfd, int *wfd)
+{
+       int res;
+       int fildes[2];
+       
+       res = pipe(fildes);
+       assert_errno("pipe", res == 0);
+       
+       *rfd = fildes[0];
+       *wfd = fildes[1];
+
+       set_nonblock(*rfd);
+       set_nonblock(*wfd);
+}
+
+void
+create_server_socket(int *rfd, struct sockaddr_in *sa)
+{
+       int res;
+       int value;
+       socklen_t salen = sizeof(*sa);
+
+       memset(sa, 0, salen);
+       sa->sin_len = salen;
+       sa->sin_family = AF_INET;
+       sa->sin_port = htons(12345);
+       sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+       *rfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       assert_errno("socket", *rfd >= 0);
+       
+       value = 1;
+       res = setsockopt(*rfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
+       assert_errno("setsockopt(SO_REUSEADDR)", res == 0);
+
+       value = 1;
+       res = setsockopt(*rfd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
+       assert_errno("setsockopt(SO_REUSEPORT)", res == 0);
+       
+       res = bind(*rfd, (const struct sockaddr *)sa, salen);
+       assert_errno("bind", res == 0);
+
+       res = listen(*rfd, 128);
+       assert_errno("listen", res == 0);
+}
+
+void
+create_client_socket(int *wfd, const struct sockaddr_in *sa)
+{
+       int res;
+
+       *wfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+       assert_errno("socket", *wfd >= 0);
+       
+       set_nonblock(*wfd);
+
+       res = connect(*wfd, (const struct sockaddr *)sa, sa->sin_len);
+       assert_errno("connect", res == 0 || errno == EINPROGRESS);
+}
+
+extern int optind;
+
+void
+usage(void)
+{
+       fprintf(stderr, "usage: fd_stress [-d] iterations width\n");
+       exit(1);
+}
+
+int
+main(int argc, char* argv[])
+{
+       int serverfd;
+       struct sockaddr_in sa;
+       create_server_socket(&serverfd, &sa);
+
+       int ch;
+       
+       while ((ch = getopt(argc, argv, "d")) != -1) {
+               switch (ch) {
+                       case 'd':
+                               debug = 1;
+                               break;
+                       case '?':
+                       default:
+                               usage();
+                               break;
+               }
+       }
+       argc -= optind;
+       argv += optind;
+       
+       if (argc != 2) {
+               usage();
+       }
+       
+       size_t iterations = strtol(argv[0], NULL, 10);
+       size_t width = strtol(argv[1], NULL, 10);
+       
+       if (iterations == 0 || width == 0) {
+               usage();
+       }
+
+       fprintf(stdout, "pid %d\n", getpid());
+
+       dispatch_group_t group;
+       group = dispatch_group_create();
+       assert(group);
+
+#if 0
+       dispatch_queue_t queue = dispatch_queue_create("server", NULL);
+
+       dispatch_source_t ds;
+       ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, serverfd, 0, queue);
+       assert(ds);
+       dispatch_source_set_event_handler(ds, ^{
+               int res;
+               int fd;
+               struct sockaddr peer;
+               socklen_t peerlen;
+
+               fd = accept(serverfd, &peer, &peerlen);
+               assert_errno("accept", fd >= 0);
+
+               set_nonblock(fd);
+               
+               char host[NI_MAXHOST], serv[NI_MAXSERV];
+               host[0] = 0;
+               serv[0] = 0;
+               res = getnameinfo(&peer, peerlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST|NI_NUMERICSERV);
+               DEBUG("ACCEPTED %d (%s:%s)\n", fd, host, serv);
+               
+               create_reader(fd, ^{ dispatch_group_leave(group); });
+       });
+       dispatch_resume(ds);
+#endif
+
+       size_t i;
+       for (i = 1; i < iterations; ++i) {
+               fprintf(stderr, "iteration %ld\n", i);
+
+               size_t j;
+               for (j = 0; j < width; ++j) {
+                       int rfd, wfd;
+                       dispatch_group_enter(group);
+                       create_pipe(&rfd, &wfd);
+                       DEBUG("PIPE %d %d\n", rfd, wfd);
+                       dispatch_source_t reader;
+                       reader = create_reader(rfd, ^{ dispatch_group_leave(group); });
+                       create_writer(wfd, ^{});
+               }
+               
+#if 0
+               int clientfd;
+               dispatch_group_enter(group);
+               create_client_socket(&clientfd, &sa);
+               DEBUG("CLIENT %d\n", clientfd);
+               create_writer(clientfd, ^{});
+
+               dispatch_group_enter(group);
+               create_fifo(&rfd, &wfd);
+               DEBUG("FIFO %d %d\n", rfd, wfd);
+               create_writer(wfd, ^{});
+               create_reader(rfd, ^{ dispatch_group_leave(group); });
+#endif
+
+               dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+       }
+       fprintf(stdout, "pid %d\n", getpid());
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/float_parsing.c b/testing/float_parsing.c
new file mode 100644 (file)
index 0000000..716bf50
--- /dev/null
@@ -0,0 +1,136 @@
+#include <dispatch/dispatch.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+#define MAXLAPS (512 * 1024)
+
+static void
+test(size_t LAPS, char *nums)
+{
+       uint64_t concurrent_cycles;
+       uint64_t serial_cycles;
+       char **result_strings;
+       char *nums_off;
+       double *results;
+       size_t i;
+
+       result_strings = calloc(1, sizeof(char *) * LAPS);
+       assert(result_strings);
+
+       results = calloc(1, sizeof(double) * LAPS);
+       assert(results);
+
+       printf("%zu random floats\n", LAPS);
+
+       i = 0;
+       nums_off = nums;
+       do {
+               result_strings[i] = nums_off;
+               do {
+                       nums_off++;
+                       assert(*nums_off);
+               } while (*nums_off != '\n');
+               i++;
+               nums_off++;
+       } while (i < LAPS);
+
+       for (i = 0; i < LAPS; i++) {
+               assert(result_strings[i]);
+       }
+
+       concurrent_cycles = dispatch_benchmark(10, ^{
+               dispatch_apply(LAPS, dispatch_get_concurrent_queue(0), ^(size_t idx) {
+                       results[idx] = strtod(result_strings[idx], NULL);
+               });
+       });
+
+       for (i = 0; i < LAPS; i++) {
+               assert(results[i]);
+       }
+
+       serial_cycles = dispatch_benchmark(10, ^{
+               size_t k = 0;
+               do {
+                       results[k] = strtod(result_strings[k], NULL);
+                       k++;
+               } while (k < LAPS);
+       });
+
+       for (i = 0; i < LAPS; i++) {
+               assert(results[i]);
+       }
+
+       printf( "\tserial cycles:\t%llu\n"
+                       "\tapply() cycles:\t%llu\n"
+                       "\tserial / concurrent: %.2Lf\n",
+               serial_cycles, concurrent_cycles,
+               (long double)serial_cycles / (long double)concurrent_cycles);
+
+       free(result_strings);
+       free(results);
+}
+
+int
+main(void)
+{
+       char path[PATH_MAX] = "/tmp/random_floats_XXXXXX";
+       struct stat sb;
+       double words[1000];
+       char buf[1024];
+       char *nums;
+       int fd, rfd;
+       size_t i, j;
+       ssize_t r;
+
+       rfd = open("/dev/random", O_RDONLY);
+       assert(rfd != -1);
+
+       fd = mkstemp(path);
+       assert(fd != -1);
+
+       r = unlink(path);
+       assert(r != -1);
+
+       i = 0;
+       do {
+               r = read(rfd, words, sizeof(words));
+               assert(r == sizeof(words));
+               for (j = 0; j < 1000; j++) {
+                       if (isnormal(words[j])) {
+                               r = write(fd, buf, snprintf(buf, sizeof(buf), "%.20e\n", words[j]));
+                               assert(r != -1);
+                               i++;
+                       }
+               }
+       } while (i < MAXLAPS);
+
+       r = close(rfd);
+       assert(r != -1);
+
+       r = fstat(fd, &sb);
+       assert(r != -1);
+
+       nums = mmap(NULL, sb.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0);
+       assert(nums != MAP_FAILED);
+
+       for (i = MAXLAPS; i > 0; i /= 2) {
+               test(i, nums);
+       }
+
+       r = munmap(nums, sb.st_size);
+       assert(r != -1);
+
+       r = close(fd);
+       assert(r != -1);
+
+       return 0;
+}
diff --git a/testing/fork-join.c b/testing/fork-join.c
new file mode 100644 (file)
index 0000000..d1b0e57
--- /dev/null
@@ -0,0 +1,40 @@
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <dispatch/dispatch.h>
+#include <dispatch/private.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+int
+main(void)
+{
+       long double nano_per_lap;
+       size_t i, cnt = 1000000;
+       dispatch_future_t *df;
+       uint64_t s, e;
+
+       df = malloc(cnt * sizeof(df));
+       assert(df);
+
+       s = mach_absolute_time();
+
+       for (i = 0; i < cnt; i++) {
+               df[i] = dispatch_fork(dispatch_get_concurrent_queue(0), ^{
+               });
+               assert(df[i]);
+       }
+
+       for (i = 0; i < cnt; i++) {
+               dispatch_join(df[i]);
+       }
+
+       e = mach_absolute_time();
+
+       nano_per_lap = (e - s);
+       nano_per_lap /= cnt;
+
+       printf("%Lf nanoseconds per lap\n", nano_per_lap);
+
+       return 0;
+}
diff --git a/testing/func.c b/testing/func.c
new file mode 100644 (file)
index 0000000..223fd84
--- /dev/null
@@ -0,0 +1,9 @@
+extern "C" {
+void
+func(void)
+{
+}
+#ifdef __BLOCKS__
+void (^block)(void) = ^{ };
+#endif
+};
diff --git a/testing/harness.c b/testing/harness.c
new file mode 100644 (file)
index 0000000..d0430c2
--- /dev/null
@@ -0,0 +1,83 @@
+#include <dispatch/dispatch.h>
+#include <assert.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <mach/clock_types.h>
+
+#include "dispatch_test.h"
+
+extern char **environ;
+
+int
+main(int argc, char *argv[])
+{
+       dispatch_source_t tmp_ds;
+       int res;
+       pid_t pid;
+
+       if (argc < 2) {
+               fprintf(stderr, "usage: harness [...]\n");
+               exit(1);
+       }
+
+       posix_spawnattr_t attr;
+       res = posix_spawnattr_init(&attr);
+       assert(res == 0);
+       res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
+       assert(res == 0);
+
+       int i;
+       char** newargv = calloc(argc, sizeof(void*));
+       for (i = 1; i < argc; ++i) {
+               newargv[i-1] = argv[i];
+       }
+       newargv[i-1] = NULL;
+
+       res = posix_spawnp(&pid, newargv[0], NULL, &attr, newargv, environ);
+       if (res) {
+               errno = res;
+               perror(newargv[0]);
+               exit(EXIT_FAILURE);
+       }
+       //fprintf(stderr, "pid = %d\n", pid);
+       assert(pid > 0);
+
+       dispatch_queue_t main_q = dispatch_get_main_queue();
+
+       tmp_ds = dispatch_source_proc_create(pid, DISPATCH_PROC_EXIT, NULL, main_q,
+               ^(dispatch_event_t ev __attribute__((unused))) {
+                       int status;
+                       int res2 = waitpid(pid, &status, 0);
+                       assert(res2 != -1);
+                       //int passed = (WIFEXITED(status) && WEXITSTATUS(status) == 0);
+                       test_long("Process exited", WEXITSTATUS(status) | WTERMSIG(status), 0);
+                       exit(0);
+       });
+       assert(tmp_ds);
+       
+       uint64_t timeout = 30LL * NSEC_PER_SEC;
+
+       tmp_ds = dispatch_source_timer_create(DISPATCH_TIMER_ONESHOT, timeout, 0, NULL, main_q,
+               ^(dispatch_event_t ev __attribute__((unused))) {
+                       kill(pid, SIGKILL);
+                       fprintf(stderr, "Terminating unresponsive process (%0.1lfs)\n", (double)timeout/NSEC_PER_SEC);
+       });
+       assert(tmp_ds);
+
+       signal(SIGINT, SIG_IGN);
+       tmp_ds = dispatch_source_signal_create(SIGINT, NULL, main_q,
+               ^(dispatch_event_t ev __attribute__((unused))) {
+                       fprintf(stderr, "Terminating process due to signal\n");
+                       kill(pid, SIGKILL);
+       });
+       assert(tmp_ds);
+
+       kill(pid, SIGCONT);
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/leaks-wrapper b/testing/leaks-wrapper
new file mode 100755 (executable)
index 0000000..6e9f9ed
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+/usr/bin/leaks "$@" 2>&1 | tee "$@.leakslog" | grep -q " 0 leaks for 0 total leaked bytes"
+
+if [ $? -eq 0 ]; then
+    rm "$@.leakslog"
+    exit 0
+else
+    exit $?
+fi
diff --git a/testing/mach_server.c b/testing/mach_server.c
new file mode 100644 (file)
index 0000000..24535ae
--- /dev/null
@@ -0,0 +1,131 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <launch.h>
+#include <dispatch/dispatch.h>
+#include <mach/mach.h>
+#include <mach/mach_port.h>
+#include <asl.h>
+#include <mach-o/dyld.h>
+#include <limits.h>
+
+void
+load(void)
+{
+       launch_data_t msg, config, dict, array, val;
+       
+       config = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
+       
+       val = launch_data_new_string("com.apple.test.mach-server");
+       launch_data_dict_insert(config, val, LAUNCH_JOBKEY_LABEL);
+       
+       val = launch_data_new_bool(1);
+       dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
+       launch_data_dict_insert(dict, val, "com.apple.test.mach-server");
+       launch_data_dict_insert(config, dict, LAUNCH_JOBKEY_MACHSERVICES);
+
+       char path[PATH_MAX];
+       uint32_t size = sizeof(path);
+       _NSGetExecutablePath(path, &size);
+       
+       array = launch_data_alloc(LAUNCH_DATA_ARRAY);
+       val = launch_data_new_string(path);
+       launch_data_array_set_index(array, val, 0);
+       val = launch_data_new_string("-launchd");
+       launch_data_array_set_index(array, val, 1);
+       launch_data_dict_insert(config, array, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
+       
+       
+       msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
+       launch_data_dict_insert(msg, config, LAUNCH_KEY_SUBMITJOB);
+       fprintf(stderr, "loading launchd job com.apple.test.mach-server\n");
+       msg = launch_msg(msg);
+       if (msg && launch_data_get_type(config) == LAUNCH_DATA_ERRNO) {
+               fprintf(stderr, "launch load failed: %s\n", strerror(launch_data_get_errno(msg)));
+       } else {
+               fprintf(stderr, "successful\n");
+       }
+}
+
+mach_port_t
+checkin(void)
+{
+       launch_data_t config = NULL, checkin = NULL;
+       checkin = launch_data_new_string(LAUNCH_KEY_CHECKIN);
+       config = launch_msg(checkin);
+       if (!config || launch_data_get_type(config) == LAUNCH_DATA_ERRNO)
+               return MACH_PORT_NULL;
+       
+       launch_data_t svc;
+       svc = launch_data_dict_lookup(config, LAUNCH_JOBKEY_MACHSERVICES);
+       if (!svc) return MACH_PORT_NULL;
+
+       svc = launch_data_dict_lookup(svc, "com.apple.test.mach-server");
+       if (!svc) return MACH_PORT_NULL;
+
+       mach_port_t mp = launch_data_get_machport(svc);
+       return mp;
+}
+
+
+#include <CoreFoundation/CoreFoundation.h>
+
+void my_cf_callback(CFMachPortRef mp, void *msg, CFIndex size, void *info)
+{
+       char *payload = (char *)((uintptr_t)msg + sizeof(mach_msg_header_t));
+       asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "message received: %s %s", payload, (char *)info);
+}
+
+DISPATCH_CFMACHPORT_CALLBACK_DECL(my_mig_callback, my_cf_callback);
+
+struct strmsg {
+       mach_msg_header_t header;
+       char payload[32];
+       mach_msg_security_trailer_t trailer;
+};
+
+int
+client(int argc, char* argv[])
+{
+       mach_port_t mp;
+       kern_return_t kr;
+       struct strmsg msg;
+
+       char *str = (argc > 1) ? argv[1] : "<unspecified>";
+
+       kr = bootstrap_look_up(bootstrap_port, "com.apple.test.mach-server",
+               &mp);
+       printf("lookup %s\n", !kr ? "successful" : mach_error_string(kr));
+
+       strlcpy(msg.payload, str, sizeof(msg.payload));
+       msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE);
+       msg.header.msgh_size = round_msg(sizeof(msg.header) + strlen(msg.payload) + 1);
+       msg.header.msgh_remote_port = mp;
+       msg.header.msgh_local_port = MACH_PORT_NULL;
+       msg.header.msgh_id = 0x12345678;
+       kr = mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+       printf("client send %s\n", !kr ? "successful" : mach_error_string(kr));
+}
+
+int main(int argc, char* argv[])
+{
+       if (argc > 1 && strcmp(argv[1], "-launchd") == 0) {
+               mach_port_t mp = checkin();
+               printf("checkin %s\n", mp ? "successful" : "failed");
+               if (mp) {
+                       kern_return_t kr;
+                       kr = mach_port_set_context(mach_task_self(), mp, (mach_vm_address_t)"phear");
+                       
+                       dispatch_source_t mig = dispatch_source_mig_create(mp, 0 /* max msg size */,
+                                                                                                                          NULL, dispatch_get_main_queue(), my_mig_callback);
+                       dispatch_main();
+               }
+       } else if (argc > 1) {
+               client(argc, argv);
+       } else {
+               // load the job
+               load();
+       }
+
+       return 0;
+}
+
diff --git a/testing/mig/Makefile b/testing/mig/Makefile
new file mode 100644 (file)
index 0000000..bd44a2f
--- /dev/null
@@ -0,0 +1,17 @@
+CFLAGS = -g -Os -Wall -Wextra -Wshadow -Wmissing-prototypes -Wmissing-declarations -Werror
+
+all: client server
+
+hello_logger.h hello_loggerServer.c hello_loggerUser.c hello_loggerServer.h: hello_logger.defs
+       mig -sheader hello_loggerServer.h hello_logger.defs
+
+client.o: hello_logger.h
+
+server.o: hello_loggerServer.h
+
+client: hello_loggerUser.o client.o
+
+server: hello_loggerServer.o server.o
+
+clean:
+       -rm -f *.o client server hello_logger.h hello_loggerServer.c hello_loggerUser.c hello_loggerServer.h
diff --git a/testing/mig/client.c b/testing/mig/client.c
new file mode 100644 (file)
index 0000000..15fc515
--- /dev/null
@@ -0,0 +1,34 @@
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <servers/bootstrap.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "hello_logger.h"
+
+int main(int argc, char *argv[])
+{
+       kern_return_t kr;
+       mach_port_t mp;
+
+       if (argc != 2) {
+               fprintf(stderr, "I need a string to send!\n");
+               exit(EXIT_FAILURE);
+       }
+
+       kr = bootstrap_look_up(bootstrap_port, HELLO_LOGGER_BOOTSTRAP_NAME, &mp);
+       if (kr != KERN_SUCCESS) {
+               fprintf(stderr, "bootstrap_look_up(): %s\n", bootstrap_strerror(kr));
+               exit(EXIT_FAILURE);
+       }
+
+       kr = example(mp, argv[1]);
+       if (kr != KERN_SUCCESS) {
+               fprintf(stderr, "test_hello_logger(): %s\n", mach_error_string(kr));
+               exit(EXIT_FAILURE);
+       }
+
+       exit(EXIT_SUCCESS);
+}
diff --git a/testing/mig/hello_logger.defs b/testing/mig/hello_logger.defs
new file mode 100644 (file)
index 0000000..0eb8c38
--- /dev/null
@@ -0,0 +1,12 @@
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+import "hello_logger_types.h";
+
+type string_t = c_string[*:1024];
+
+subsystem hello_logger 12345;
+serverprefix do_;
+
+routine example(
+                               test_port       : mach_port_t;
+                               some_string     : string_t);
diff --git a/testing/mig/hello_logger_types.h b/testing/mig/hello_logger_types.h
new file mode 100644 (file)
index 0000000..a3bafb2
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _HELLO_WORLD_TYPES_H_
+#define _HELLO_WORLD_TYPES_H_
+
+#define HELLO_LOGGER_BOOTSTRAP_NAME "com.example.hello_logger"
+
+typedef char *string_t;
+
+#endif
diff --git a/testing/mig/server.c b/testing/mig/server.c
new file mode 100644 (file)
index 0000000..fb1e19f
--- /dev/null
@@ -0,0 +1,48 @@
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <servers/bootstrap.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <dispatch/dispatch.h>
+
+#include "hello_logger.h"
+#include "hello_loggerServer.h"
+
+static mach_port_t checkin_or_register(char *bname);
+
+int main(void)
+{
+       mach_port_t mp = checkin_or_register(HELLO_LOGGER_BOOTSTRAP_NAME);
+       dispatch_source_t ds = dispatch_source_mig_new(mp, do_hello_logger_subsystem.maxsize, hello_logger_server, NULL, NULL, NULL);
+
+       assert(ds);
+
+       dispatch_main();
+
+       exit(EXIT_SUCCESS);
+}
+
+kern_return_t
+do_example(mach_port_t test_port __attribute__((unused)), string_t somestring)
+{
+       fprintf(stdout, "%s\n", somestring);
+
+       return KERN_SUCCESS;
+}
+
+mach_port_t
+checkin_or_register(char *bname)
+{
+       kern_return_t kr;
+       mach_port_t mp;
+
+       /* If we're started by launchd or the old mach_init */
+       kr = bootstrap_check_in(bootstrap_port, bname, &mp);
+       if (kr == KERN_SUCCESS)
+               return mp;
+
+       abort();
+}
diff --git a/testing/nsoperation.m b/testing/nsoperation.m
new file mode 100644 (file)
index 0000000..bb5b065
--- /dev/null
@@ -0,0 +1,52 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <Foundation/Foundation.h>
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+@interface MYOperation : NSOperation
+{
+}
+@end
+
+@implementation MYOperation
+
+- (id) init
+{
+       self = [super init];
+       return self;
+}
+
+- (void)main
+{
+       test_stop();
+}
+
+@end
+
+int
+main(void)
+{
+       test_start("NSOperation");
+
+       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+       
+       NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
+       test_ptr_notnull("NSOperationQueue", queue);
+       
+       MYOperation *operation = [[MYOperation alloc] init];
+       test_ptr_notnull("NSOperation", operation);
+       
+       [queue addOperation:operation];
+       [operation release];
+       
+       [[NSRunLoop mainRunLoop] run];
+       
+       [pool release];
+       
+       return 0;
+}
diff --git a/testing/queue_finalizer.c b/testing/queue_finalizer.c
new file mode 100644 (file)
index 0000000..6411b9a
--- /dev/null
@@ -0,0 +1,46 @@
+#include <dispatch/dispatch.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "dispatch_test.h"
+
+int main(void) {
+       long res;
+
+       test_start("Dispatch Queue Finalizer");
+
+#ifdef __LP64__
+       void* ctxt_magic = (void*)((uintptr_t)arc4random() << 32 | arc4random());
+#else
+       void* ctxt_magic = (void*)arc4random();
+#endif
+
+       dispatch_queue_attr_t attr = dispatch_queue_attr_create();
+       test_ptr_notnull("dispatch_queue_attr_create", attr);
+
+       __block long finalizer_ran = 0;
+
+       res = dispatch_queue_attr_set_finalizer(attr, ^(dispatch_queue_t dq) {
+               void* ctxt = dispatch_queue_get_context(dq);
+               test_ptr("dispatch_queue_get_context", ctxt, ctxt_magic);
+               test_ptr_notnull("finalizer ran", dq);
+               test_stop();
+       });
+       test_long("dispatch_queue_attr_set_finalizer", res, 0);
+
+       dispatch_queue_t q = dispatch_queue_create(NULL, attr);
+       test_ptr_notnull("dispatch_queue_new", q);
+
+       dispatch_queue_set_context(q, ctxt_magic);
+
+       dispatch_release(attr);
+
+       dispatch_release(q);
+
+       dispatch_main();
+       
+       return 0;
+}
diff --git a/testing/slice_benchmarks.c b/testing/slice_benchmarks.c
new file mode 100644 (file)
index 0000000..0f9c054
--- /dev/null
@@ -0,0 +1,445 @@
+#include <libkern/OSAtomic.h>
+#include <sys/sysctl.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <math.h>
+#include <libproc.h>
+#include <sys/proc_info.h>
+#include <dispatch/dispatch.h>
+// #include "../src/private.h"
+#include <Block.h>
+
+// "normal" loop size
+#define LOOP 100000
+#define SMALL_LOOP 1000
+
+void report(const char *func, char *full_name, double x, unsigned long loops, char *unit) {
+    // XXX: make cols pretty & stuff
+    const char *prefix = "bench_";
+    const int plen = strlen(prefix);
+    assert(!strncmp(func, prefix, plen));
+    func += plen;
+    char *name;
+    asprintf(&name, "[%s] %s", func, full_name);
+    assert(name);
+
+    x /= loops;
+
+    if (!strcmp("mach", unit)) {
+       static mach_timebase_info_data_t mtb;
+       if (!mtb.denom) {
+           (void)mach_timebase_info(&mtb);
+       }
+       x = (x * mtb.numer) / mtb.denom;
+       unit = "ns";
+    }
+
+    printf("%-64s %13f%-2s\n", name, x, unit);
+    free(name);
+}
+
+void bench_queue_mem_use(void) {
+    struct proc_taskinfo pti;
+    uint64_t target_size;
+
+    // The 1st call eats a little memory that isn't accounted for
+    // until the 2nd call.   Also the _first_ printf eats >1M, so
+    // if you insert some for debugging make sure it isn't the first!
+    proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &pti, sizeof(pti));
+    proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &pti, sizeof(pti));
+    target_size = pti.pti_virtual_size + 1024*1024;
+    int n;
+
+    for(n = 0; target_size >= pti.pti_virtual_size; n++) {
+       dispatch_queue_t leak = dispatch_queue_create("to be deleted", NULL);
+       assert(leak);
+       proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &pti, sizeof(pti));
+       //printf("pti_virtual_size %qd; togo %qd, n %d\n", pti.pti_virtual_size, target_size - pti.pti_virtual_size, n);
+    }
+
+    report(__FUNCTION__, "#queues to grow VSIZE 1Mbyte", n-1, 1, "x");
+}
+
+void bench_message_round_trip(void) {
+    dispatch_queue_t q1 = dispatch_queue_create("q1", NULL);
+    dispatch_queue_t q2 = dispatch_queue_create("q2", NULL);
+    uint64_t start = mach_absolute_time();
+
+    int i;
+    for(i = 0; i < LOOP; i++) {
+       // make sure we don't build up too much of a backlog
+       if (i && !(i & 0x3ff)) {
+           dispatch_sync(q2, ^{});
+       }
+       dispatch_queue_retain(q2);
+       dispatch_async(q1, ^{
+           dispatch_async(q2, ^{
+               dispatch_queue_release(q2);
+           });
+       });
+    }
+
+    // Make sure eveything has drained before we take the end timestamp
+    dispatch_sync(q1, ^{});
+    dispatch_sync(q2, ^{});
+
+    uint64_t end = mach_absolute_time();
+    report(__FUNCTION__, "round trip (async async - implicit copy)", (end - start), LOOP, "mach");
+    dispatch_queue_release(q1);
+    dispatch_queue_release(q2);
+}
+
+void bench_precopy_message_round_trip(void) {
+    dispatch_queue_t q1 = dispatch_queue_create("q1", NULL);
+    dispatch_queue_t q2 = dispatch_queue_create("q2", NULL);
+    assert(q1 && q2);
+
+    unsigned long rc;
+
+    dispatch_block_t b2 = Block_copy(^{
+    });
+    dispatch_block_t b1 = Block_copy(^{
+       unsigned long rc = dispatch_async(q2, b2);
+       assert(!rc);
+       dispatch_queue_release(q2);
+    });
+    dispatch_block_t be = Block_copy(^{});
+    assert(b1 && b2);
+    uint64_t start = mach_absolute_time();
+
+    int i;
+    for(i = 0; i < LOOP; i++) {
+       // make sure we don't build up too much of a backlog
+       if (i && !(i & 0x3ff)) {
+           dispatch_sync(q2, be);
+       }
+       dispatch_queue_retain(q2);
+       rc = dispatch_async(q1, b1);
+       assert(!rc);
+    }
+
+    // Make sure eveything has drained before we take the end timestamp
+    dispatch_sync(q1, be);
+    dispatch_sync(q2, be);
+
+    uint64_t end = mach_absolute_time();
+    report(__FUNCTION__, "round trip (a/a - precopy)", (end - start), LOOP, "mach");
+    dispatch_queue_release(q1);
+    dispatch_queue_release(q2);
+}
+
+void bench_message_round_type_syncasync(void) {
+    dispatch_queue_t q1 = dispatch_queue_create("q1", NULL);
+    dispatch_queue_t q2 = dispatch_queue_create("q2", NULL);
+    uint64_t start = mach_absolute_time();
+
+    int i;
+    for(i = 0; i < LOOP; i++) {
+       dispatch_queue_retain(q2);
+       dispatch_sync(q1, ^{
+           dispatch_async(q2, ^{
+               dispatch_queue_release(q2);
+           });
+       });
+    }
+
+    // Make sure eveything has drained before we take the end timestamp
+    dispatch_sync(q1, ^{});
+    dispatch_sync(q2, ^{});
+
+    uint64_t end = mach_absolute_time();
+    report(__FUNCTION__, "round trip (s/a - implicit copy)", (end - start), LOOP, "mach");
+    dispatch_queue_release(q1);
+    dispatch_queue_release(q2);
+}
+
+void nothing_f(void *ignored) {
+}
+
+void brt_f_q1(void *vq2) {
+    unsigned long rc = dispatch_async_f((dispatch_queue_t)vq2, NULL, nothing_f);
+    assert(!rc);
+}
+
+void bench_message_round_trip_f(void) {
+    dispatch_queue_t q1 = dispatch_queue_create("q1", NULL);
+    dispatch_queue_t q2 = dispatch_queue_create("q2", NULL);
+    uint64_t start = mach_absolute_time();
+    unsigned long rc;
+
+    int i;
+    for(i = 0; i < LOOP; i++) {
+       // make sure we don't build up too much of a backlog
+       if (i && !(i & 0x3ff)) {
+           dispatch_sync_f(q2, NULL, nothing_f);
+       }
+       rc = dispatch_async_f(q1, q2, brt_f_q1);
+       assert(!rc);
+    }
+
+    // Make sure eveything has drained before we take the end timestamp
+    dispatch_sync_f(q1, NULL, nothing_f);
+    dispatch_sync_f(q2, NULL, nothing_f);
+
+    uint64_t end = mach_absolute_time();
+    report(__FUNCTION__, "round trip (a/a - no blocks)", (end - start), LOOP, "mach");
+    dispatch_queue_release(q1);
+    dispatch_queue_release(q2);
+}
+
+void bench_message_round_type_syncasync_f(void) {
+}
+
+struct baton {
+    // should extend to keep data on times for latency calc
+    int passes_left;
+    int at_q;
+    int baton_number;
+
+    // Avoid false ache line shares.   Big speed difference on a Mac Pro
+    char pad[128 - sizeof(int)*3];
+};
+
+pthread_mutex_t kludge;
+static int n_baton_kludge;
+
+void pass(dispatch_queue_t *q, struct baton *bat, const int n_queues, dispatch_queue_t complete_q) {
+    //fprintf(stderr, "bat#%d q#%d, passes left: %d\n", bat->baton_number, bat->at_q, bat->baton_number);
+    if (0 == --(bat->passes_left)) {
+       dispatch_queue_resume(complete_q);
+       // XXX: atomic
+       if (!__sync_sub_and_fetch(&n_baton_kludge, 1)) {
+               pthread_mutex_unlock(&kludge);
+       }
+       return;
+    }
+    bat->at_q = (bat->at_q + 1) % n_queues;
+    unsigned long rc = dispatch_async(q[bat->at_q], ^{ pass(q, bat, n_queues, complete_q); });
+    assert(rc == 0);
+}
+
+void bench_baton() {
+    const int n_queues = 128;
+    const int q_div_b = 4;
+    const int n_batons = n_queues / q_div_b;
+    assert(q_div_b * n_batons == n_queues);
+    n_baton_kludge = n_batons;
+    dispatch_queue_t *q;
+    dispatch_queue_t complete_q = dispatch_queue_create("completion q", NULL);;
+    char *q_labels[n_queues];
+    int i;
+    unsigned long rc;
+
+    // creting a queue ("C"), suspending it, blocking in a dispatch_sync, and 
+    // having another queue resume C does not appear to ever unblock the 
+    // dispatch_sync.   XXX: make test case and file radar.   (if it still
+    // works that way on recent builds, with dispatch inside libsystem, and
+    // such)
+
+
+    pthread_mutex_init(&kludge, NULL);
+    rc = pthread_mutex_trylock(&kludge);
+    assert(!rc);
+    q = alloca(n_queues * sizeof(dispatch_queue_t));
+
+    for(i = 0; i < n_queues; i++) {
+       asprintf(q_labels + i, "relay#%d (%s)", i, __FUNCTION__);
+       assert(q_labels[i]);
+       q[i] = dispatch_queue_create(q_labels[i], NULL);
+       assert(q[i]);
+    }
+
+    uint64_t start_time = mach_absolute_time();
+
+    for(i = 0; i < n_queues; i += q_div_b) {
+       struct baton *bat = valloc(sizeof(struct baton));
+       assert(bat);
+       bat->passes_left = SMALL_LOOP;
+       bat->at_q = i;
+       bat->baton_number = i / q_div_b;
+       dispatch_queue_suspend(complete_q);
+       rc = dispatch_async(q[i], ^{
+           pass(q, bat, n_queues, complete_q);
+       });
+       assert(rc == 0);
+    }
+
+    // XXX: dispatch_sync(complete_q, ^{});
+    rc = pthread_mutex_lock(&kludge);
+    assert(!rc);
+    uint64_t end_time = mach_absolute_time();
+    report(__FUNCTION__, "baton pass", (end_time - start_time), SMALL_LOOP*n_batons, "mach");
+    // dispatch_queue_release(q);
+}
+
+void bench_overload2() {
+       const int n_queues = 128;
+       const int q_div_b = 1;
+       const int n_batons = n_queues / q_div_b;
+       n_baton_kludge = n_batons;
+       assert(q_div_b * n_batons == n_queues);
+       dispatch_queue_t *q = alloca(n_queues * sizeof(dispatch_queue_t));
+       dispatch_source_t *ds = alloca(n_queues * sizeof(dispatch_source_t));
+       dispatch_queue_t complete_q = dispatch_queue_create("completion q", NULL);
+       __block uint64_t start_time = 0;
+       uint64_t time_to_start;
+       uint64_t end_time;
+       char *q_labels[n_queues];
+       int i;
+       unsigned int rc;
+
+       rc = pthread_mutex_unlock(&kludge);
+       assert(!rc);
+       rc = pthread_mutex_trylock(&kludge);
+       assert(!rc);
+
+       // Start all batons one to two seconds from now.
+       time_to_start = (2 + time(NULL)) * 1000000000;
+
+       for(i = 0; i < n_queues; i++) {
+               asprintf(q_labels + i, "queue#%d (%s)", i, __FUNCTION__);
+               assert(q_labels[i]);
+               q[i] = dispatch_queue_create(q_labels[i], NULL);
+               assert(q[i]);
+               struct baton *bat = valloc(sizeof(struct baton));
+               assert(bat);
+               bat->passes_left = SMALL_LOOP;
+               bat->at_q = i;
+               bat->baton_number = i / q_div_b;
+               dispatch_queue_suspend(complete_q);
+               ds[i] = dispatch_source_timer_create(DISPATCH_TIMER_ABSOLUTE, time_to_start, 0, NULL, q[i], ^(dispatch_event_t event){
+                       assert(!dispatch_event_get_error(event, NULL));
+                       // We want to measure the time from the first
+                       // baton pass, and NOT include hte wait time
+                       // for eveyone to start to fire
+                       if (!start_time) {
+                               uint64_t s = mach_absolute_time();
+                               __sync_bool_compare_and_swap(&start_time, 0, s);
+                       }
+                       pass(q, bat, n_queues, complete_q);
+               });
+               assert(ds[i]);
+       }
+
+       // XXX: dispatch_sync(complete_q, ^{});
+       rc = pthread_mutex_lock(&kludge);
+       assert(!rc);
+
+       end_time = mach_absolute_time();
+       report(__FUNCTION__, "overload#2", (end_time - start_time), SMALL_LOOP*n_batons, "mach");
+       // Many releases and free()s
+
+}
+
+void bench_overload1() {
+       const int n_queues = 128;
+       const int q_div_b = 1;
+       const int n_batons = n_queues / q_div_b;
+       n_baton_kludge = n_batons;
+       assert(q_div_b * n_batons == n_queues);
+       dispatch_queue_t *q = alloca(n_queues * sizeof(dispatch_queue_t));
+       dispatch_queue_t complete_q = dispatch_queue_create("completion q", NULL);
+       __block uint64_t start_time = 0;
+       struct timeval time_to_start;
+       uint64_t end_time;
+       char *q_labels[n_queues];
+       int i;
+       unsigned int rc;
+
+       rc = pthread_mutex_unlock(&kludge);
+       assert(!rc);
+       rc = pthread_mutex_trylock(&kludge);
+       assert(!rc);
+
+       // Start all batons one to two seconds from now.
+       gettimeofday(&time_to_start, NULL);
+       time_to_start.tv_sec += 2;
+
+       for(i = 0; i < n_queues; i++) {
+               asprintf(q_labels + i, "queue#%d (%s)", i, __FUNCTION__);
+               assert(q_labels[i]);
+               q[i] = dispatch_queue_create(q_labels[i], NULL);
+               assert(q[i]);
+               struct baton *bat = valloc(sizeof(struct baton));
+               assert(bat);
+               bat->passes_left = SMALL_LOOP;
+               bat->at_q = i;
+               bat->baton_number = i / q_div_b;
+               dispatch_queue_suspend(complete_q);
+               dispatch_async(q[i], ^(void) {
+                       struct timeval now;
+                       gettimeofday(&now, NULL);
+                       int sec = time_to_start.tv_sec - now.tv_sec;
+                       if (sec >= 0) {
+                               int usec = time_to_start.tv_usec + now.tv_usec;
+                               if (usec > 0 || sec > 0) {
+                                       usleep(1000000 * sec + usec);
+                               } else {
+                                       // XXX: log here
+                               }
+                       } 
+
+                       // We want to measure the time from the first
+                       // baton pass, and NOT include hte wait time
+                       // for eveyone to start to fire
+                       if (!start_time) {
+                               uint64_t s = mach_absolute_time();
+                               __sync_bool_compare_and_swap(&start_time, 0, s);
+                       }
+
+                       pass(q, bat, n_queues, complete_q);
+               });
+       }
+
+       // XXX: dispatch_sync(complete_q, ^{});
+       rc = pthread_mutex_lock(&kludge);
+       assert(!rc);
+
+       end_time = mach_absolute_time();
+       report(__FUNCTION__, "overload#1", (end_time - start_time), SMALL_LOOP*n_batons, "mach");
+       // Many releases and free()s
+
+}
+
+int main(int argc, char *argv[]) {
+    // Someday we will be able to take a list of tests to run, or exclude, or something.
+
+    // There are somewhat diffrent perfomance chararistics when using the
+    // main queue, so we use a "normal" queue for all our tests.
+    dispatch_queue_t bench_q = dispatch_queue_create("benhmark Q", NULL);
+    
+    dispatch_async(bench_q, ^{
+       // These two aren't as intresting in duel core, they queue all
+       // the calls before making them which isn't really what we
+       // want to test, is it?     It also limites the number of loops
+       // we can spin around.
+#if 1
+       bench_message_round_trip();
+       bench_precopy_message_round_trip();
+
+       bench_message_round_type_syncasync();
+       bench_message_round_trip_f();
+       bench_message_round_type_syncasync_f();
+#endif
+       bench_baton();
+       bench_overload1();
+       bench_overload2();
+
+       // This leaks, so we run it last.  Also it gives
+       // wrong results if stdio hasn't been started already,
+       // so we definitly don't want to run it first even if
+       // the leaks are fixed (or ignored)
+       bench_queue_mem_use();
+
+       exit(0);
+    });
+
+    dispatch_main();
+}
diff --git a/testing/summarize.c b/testing/summarize.c
new file mode 100644 (file)
index 0000000..6eeb0d0
--- /dev/null
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+has_prefix(const char* str, const char* prefix) {
+       return (strncmp(str, prefix, strlen(prefix)) == 0);
+}
+
+int
+print_summary(FILE* f, long total, long pass, long fail) {
+       fprintf(f, "Total:  %ld\n", total);
+       fprintf(f, "Passed: %ld (%0.0lf%%)\n", pass, ((double)pass / (double)total) * (double)100.0);
+       fprintf(f, "Failed: %ld (%0.0lf%%)\n", fail, ((double)fail / (double)total) * (double)100.0);
+       fprintf(f, "\n");
+       return 0;
+}
+
+int main(int argc, char* argv[]) {
+       if (argc > 1) {
+               fprintf(stderr, "%s: usage: summarize\n", argv[0]);
+               exit(1);
+       }
+       
+       /*
+       FILE* f = fopen(argv[1], "w");
+       if (f == NULL) {
+               perror(argv[1]);
+               exit(1);
+       }
+       */
+       FILE* f = stdout;
+
+       fprintf(f, "\n==================================================\n");
+       fprintf(f, "[SUMMARY] Test Summary\n");
+       fprintf(f, "==================================================\n\n");
+       
+       size_t len;
+       char* ln;
+       long total = 0;
+       long pass = 0;
+       long fail = 0;
+       long total_total = 0;
+       long total_pass = 0;
+       long total_fail = 0;
+       for(;;) {
+               ln = fgetln(stdin, &len);
+               //if (ln) fprintf(stdout, "%.*s", (int)len, ln);
+               if (ln == NULL || has_prefix(ln, "[TEST]")) {
+                       if (total) {
+                               print_summary(f, total, pass, fail);
+                       }
+                       total_total += total;
+                       total_pass += pass;
+                       total_fail += fail;
+                       total = 0;
+                       pass = 0;
+                       fail = 0;
+                       if (ln) {
+                               fprintf(f, "%.*s", (int)len, ln);
+                       } else {
+                               fprintf(f, "[TOTAL]\n");
+                               print_summary(f, total_total, total_pass, total_fail);
+                               break;
+                       }
+               } else if (has_prefix(ln, "[PASS]")) {
+                       ++total;
+                       ++pass;
+               } else if (has_prefix(ln, "[FAIL]")) {
+                       ++total;
+                       ++fail;
+               }
+       }
+       
+       return (total_fail ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/testing/test.c b/testing/test.c
new file mode 100644 (file)
index 0000000..631cb3b
--- /dev/null
@@ -0,0 +1,45 @@
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pthread.h>
+#include <dispatch.h>
+
+int
+main(void)
+{
+       void (^wb)(dispatch_item_t) = ^(dispatch_item_t di) {
+               printf("%p\t%p\t%s:\t%llu\n", pthread_self(), di, __func__, mach_absolute_time());
+       };
+       void (^cb)(dispatch_item_t) = ^(dispatch_item_t di) {
+               printf("%p\t%p\t%s:\t%llu\n", pthread_self(), di, __func__, mach_absolute_time());
+       };
+       dispatch_queue_t q;
+       dispatch_item_t di_r;
+       size_t i;
+       bool r;
+
+       q = dispatch_queue_new("test", 0, NULL, NULL, NULL);
+       assert(q != NULL);
+
+       for (i = 0; i < 1000; i++) {
+               r = dispatch_call_wait(q, wb, NULL);
+               assert(r);
+       }
+
+       printf("done with dispatch_call_wait()\n");
+
+       r = dispatch_apply_wait(wb, 10, NULL);
+       assert(r);
+
+       r = dispatch_call(q, wb, cb, NULL, &di_r);
+       assert(r);
+       assert(di_r);
+
+       printf("waiting for dispatch_call() callback\n");
+
+       dispatch_main();
+
+       return 0;
+}
diff --git a/testing/yet-another-apply-test.c b/testing/yet-another-apply-test.c
new file mode 100644 (file)
index 0000000..7329cdf
--- /dev/null
@@ -0,0 +1,101 @@
+#include <dispatch/dispatch.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <assert.h>
+
+static uint64_t total;
+
+#define LAPS (256 * 1024 * 1024)
+#define SIZE (LAPS * sizeof(int))
+
+int
+main(int argc, char *argv[])
+{
+       dispatch_queue_t cq = dispatch_get_concurrent_queue(0);
+       struct stat sb;
+       long double cycles;
+       uint64_t s, e, d;
+       uint64_t tmp_total;
+       int r, fd;
+       const int *nums;
+       size_t i, stride;
+
+       if (argc != 2) {
+               fprintf(stderr, "usage: %s <file>\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       // make sure to have 2GB + of RAM installed and run this before hand:
+       // dd if=/dev/random bs=1024k count=1024 of=/tmp/testfile
+       fd = open(argv[1], O_RDONLY);
+       assert(fd != -1);
+
+       r = fstat(fd, &sb);
+       assert(r != -1);
+       assert(sb.st_size >= (off_t)SIZE);
+
+       nums = mmap(NULL, SIZE, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
+       assert(nums != MAP_FAILED);
+
+       // force pages to be faulted in
+       for (i = 0; i < LAPS; i++) {
+               total += nums[i];
+       }
+
+       for (stride = 1; stride < (LAPS + 1); stride <<= 1) {
+               total = 0;
+               s = mach_absolute_time();
+               dispatch_apply(LAPS / stride, cq, ^(size_t idx) {
+                       const int *nums2 = nums + (idx * stride);
+                       uint64_t ptotal = 0;
+                       size_t idx2 = 0;
+
+                       // assert(stride > 0);
+                       do {
+                               ptotal += nums2[idx2++];
+                       } while (idx2 < stride);
+
+                       __sync_fetch_and_add(&total, ptotal);
+               });
+               e = mach_absolute_time();
+               d = e - s;
+               cycles = d;
+               cycles /= LAPS;
+               printf("da%lu:\t%Lf ns\n", stride, cycles);
+       }
+
+       tmp_total = 0;
+       total = 0;
+       s = mach_absolute_time();
+       for (i = 0; i < LAPS; i++) {
+               tmp_total += nums[i];
+       }
+       total = tmp_total;
+       e = mach_absolute_time();
+       d = e - s;
+       cycles = d;
+       cycles /= LAPS;
+       printf("naïve:\t%Lf ns\n", cycles);
+
+
+       tmp_total = 0;
+       total = 0;
+       s = mach_absolute_time();
+#pragma omp parallel for reduction(+:tmp_total)
+       for (i = 0; i < LAPS; i++) {
+               tmp_total += nums[i];
+       }
+       total = tmp_total;
+       e = mach_absolute_time();
+       d = e - s;
+       cycles = d;
+       cycles /= LAPS;
+       printf("OpenMP:\t%Lf ns\n", cycles);
+
+       exit(EXIT_SUCCESS);
+}