--- /dev/null
+#!/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 - )
+
--- /dev/null
+#!/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
--- /dev/null
+#!/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
* 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__
_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)
}
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;
};
{
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);
}
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 {
.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);
}
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);
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
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))
--- /dev/null
+# 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)
--- /dev/null
+#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);
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+extern "C" {
+void
+func(void)
+{
+}
+#ifdef __BLOCKS__
+void (^block)(void) = ^{ };
+#endif
+};
--- /dev/null
+#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;
+}
--- /dev/null
+#!/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
--- /dev/null
+#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;
+}
+
--- /dev/null
+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
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
--- /dev/null
+#ifndef _HELLO_WORLD_TYPES_H_
+#define _HELLO_WORLD_TYPES_H_
+
+#define HELLO_LOGGER_BOOTSTRAP_NAME "com.example.hello_logger"
+
+typedef char *string_t;
+
+#endif
--- /dev/null
+#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();
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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);
+}