[Libdispatch-changes] [13] trunk
source_changes at macosforge.org
source_changes at macosforge.org
Thu Sep 10 16:26:20 PDT 2009
Revision: 13
http://trac.macosforge.org/projects/libdispatch/changeset/13
Author: kvv at apple.com
Date: 2009-09-08 17:25:04 -0700 (Tue, 08 Sep 2009)
Log Message:
-----------
added basic test suite
Added Paths:
-----------
trunk/testing/
trunk/testing/Makefile
trunk/testing/dispatch_after.c
trunk/testing/dispatch_api.c
trunk/testing/dispatch_apply.c
trunk/testing/dispatch_c99.c
trunk/testing/dispatch_cascade.c
trunk/testing/dispatch_cffd.c
trunk/testing/dispatch_debug.c
trunk/testing/dispatch_drift.c
trunk/testing/dispatch_group.c
trunk/testing/dispatch_pingpong.c
trunk/testing/dispatch_plusplus.cpp
trunk/testing/dispatch_priority.c
trunk/testing/dispatch_proc.c
trunk/testing/dispatch_read.c
trunk/testing/dispatch_readsync.c
trunk/testing/dispatch_sema.c
trunk/testing/dispatch_starfish.c
trunk/testing/dispatch_test.c
trunk/testing/dispatch_test.h
trunk/testing/dispatch_timer_bit31.c
trunk/testing/dispatch_timer_bit63.c
trunk/testing/dispatch_timer_set_time.c
trunk/testing/fd_stress.c
trunk/testing/harness.c
trunk/testing/leaks-wrapper
trunk/testing/nsoperation.m
trunk/testing/queue_finalizer.c
trunk/testing/summarize.c
Added: trunk/testing/Makefile
===================================================================
--- trunk/testing/Makefile (rev 0)
+++ trunk/testing/Makefile 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,108 @@
+# No workie: dispatch_sema
+TESTS= dispatch_apply \
+ dispatch_api \
+ dispatch_c99 \
+ dispatch_cffd \
+ dispatch_debug \
+ queue_finalizer \
+ dispatch_group \
+ dispatch_pingpong \
+ dispatch_plusplus \
+ dispatch_priority \
+ dispatch_priority2 \
+ dispatch_proc \
+ dispatch_read \
+ dispatch_after \
+ dispatch_sema \
+ dispatch_timer_bit31 \
+ dispatch_timer_bit63 \
+ 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)) -DDISPATCH_NO_LEGACY
+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
+
+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_debug: dispatch_debug.o $(OBJS)
+dispatch_group: dispatch_group.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_drift: dispatch_drift.o $(OBJS)
+dispatch_starfish: dispatch_starfish.o $(OBJS)
+dispatch_cascade: dispatch_cascade.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)
Added: trunk/testing/dispatch_after.c
===================================================================
--- trunk/testing/dispatch_after.c (rev 0)
+++ trunk/testing/dispatch_after.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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;
+}
Added: trunk/testing/dispatch_api.c
===================================================================
--- trunk/testing/dispatch_api.c (rev 0)
+++ trunk/testing/dispatch_api.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+
+#include <dispatch/dispatch.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;
+}
Added: trunk/testing/dispatch_apply.c
===================================================================
--- trunk/testing/dispatch_apply.c (rev 0)
+++ trunk/testing/dispatch_apply.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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_global_queue(0, 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;
+}
Added: trunk/testing/dispatch_c99.c
===================================================================
--- trunk/testing/dispatch_c99.c (rev 0)
+++ trunk/testing/dispatch_c99.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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;
+}
Added: trunk/testing/dispatch_cascade.c
===================================================================
--- trunk/testing/dispatch_cascade.c (rev 0)
+++ trunk/testing/dispatch_cascade.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <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;
+}
Added: trunk/testing/dispatch_cffd.c
===================================================================
--- trunk/testing/dispatch_cffd.c (rev 0)
+++ trunk/testing/dispatch_cffd.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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;
+}
+
Added: trunk/testing/dispatch_debug.c
===================================================================
--- trunk/testing/dispatch_debug.c (rev 0)
+++ trunk/testing/dispatch_debug.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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_global_queue(0, 0);
+ dispatch_debug(default_q, "dispatch_queue_t");
+
+ dispatch_source_t s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
+ dispatch_debug(s, "dispatch_source_t");
+
+ dispatch_group_t g = dispatch_group_create();
+ dispatch_debug(g, "dispatch_group_t");
+
+ return 0;
+}
Added: trunk/testing/dispatch_drift.c
===================================================================
--- trunk/testing/dispatch_drift.c (rev 0)
+++ trunk/testing/dispatch_drift.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#include <math.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "dispatch_test.h"
+
+//
+// Verify that dispatch timers do not drift, that is to say, increasingly
+// stray from their intended interval.
+//
+// The jitter of the event handler is defined to be the amount of time
+// difference between the actual and expected timing of the event handler
+// invocation. The drift is defined to be the amount that the jitter changes
+// over time.
+//
+// Important: be sure to use the same clock when comparing actual and expected
+// values. Skew between different clocks is to be expected.
+//
+
+int
+main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
+{
+ __block uint32_t count = 0;
+ __block double last_jitter = 0;
+
+ // interval is 1/10th of a second
+ uint64_t interval = NSEC_PER_SEC / 10;
+ double interval_d = (double)interval / (double)NSEC_PER_SEC;
+
+ // for 25 seconds
+ unsigned int target = 25 / interval_d;
+
+ __block double first = 0;
+ __block double jittersum = 0;
+ __block double driftsum = 0;
+
+ test_start("Timer drift test");
+
+ dispatch_source_t timer;
+ timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+ test_ptr_notnull("DISPATCH_SOURCE_TYPE_TIMER", timer);
+
+ dispatch_source_set_event_handler(timer, ^{
+ struct timeval now_tv;
+ gettimeofday(&now_tv, NULL);
+ double now = now_tv.tv_sec + ((double)now_tv.tv_usec / (double)USEC_PER_SEC);
+
+ if (first == 0) {
+ first = now;
+ }
+
+ double goal = first + interval_d * count;
+ double jitter = goal - now;
+ double drift = jitter - last_jitter;
+
+ count += dispatch_source_get_data(timer);
+ jittersum += jitter;
+ driftsum += drift;
+
+ printf("%4d: jitter %f, drift %f\n", count, jitter, drift);
+
+ if (count >= target) {
+ test_double_less_than("average jitter", fabs(jittersum) / (double)count, 0.0001);
+ test_double_less_than("average drift", fabs(driftsum) / (double)count, 0.0001);
+ test_stop();
+ }
+ last_jitter = jitter;
+ });
+
+ struct timeval now_tv;
+ struct timespec now_ts;
+
+ gettimeofday(&now_tv, NULL);
+ now_ts.tv_sec = now_tv.tv_sec;
+ now_ts.tv_nsec = now_tv.tv_usec * NSEC_PER_USEC;
+
+ dispatch_source_set_timer(timer, dispatch_walltime(&now_ts, interval), interval, 0);
+
+ dispatch_resume(timer);
+
+ dispatch_main();
+ return 0;
+}
+
Added: trunk/testing/dispatch_group.c
===================================================================
--- trunk/testing/dispatch_group.c (rev 0)
+++ trunk/testing/dispatch_group.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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_global_queue(0, 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;
+}
Added: trunk/testing/dispatch_pingpong.c
===================================================================
--- trunk/testing/dispatch_pingpong.c (rev 0)
+++ trunk/testing/dispatch_pingpong.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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;
+}
Added: trunk/testing/dispatch_plusplus.cpp
===================================================================
--- trunk/testing/dispatch_plusplus.cpp (rev 0)
+++ trunk/testing/dispatch_plusplus.cpp 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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;
+}
Added: trunk/testing/dispatch_priority.c
===================================================================
--- trunk/testing/dispatch_priority.c (rev 0)
+++ trunk/testing/dispatch_priority.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <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;
+}
Added: trunk/testing/dispatch_proc.c
===================================================================
--- trunk/testing/dispatch_proc.c (rev 0)
+++ trunk/testing/dispatch_proc.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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;
+
+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.
+
+ //
+ // Simple child process that sleeps 2 seconds.
+ //
+
+ 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 completion = dispatch_queue_create("completion", NULL);
+
+ assert(pid > 0);
+
+ //
+ // Suspend the "completion" queue when each observer is created.
+ // Each observer resumes the queue when the child process exits.
+ // If the queue is resumed too few times (indicating that not all
+ // observers received the exit event) then the test case will not exit
+ // within the alloted time and result in failure.
+ //
+
+ int i;
+ for (i = 0; i < PID_CNT; ++i) {
+ dispatch_suspend(completion);
+ proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
+ test_ptr_notnull("DISPATCH_SOURCE_TYPE_PROC", proc);
+
+ dispatch_source_set_event_handler(proc, ^{
+ long flags = dispatch_source_get_data(proc);
+ test_long("DISPATCH_PROC_EXIT", flags, DISPATCH_PROC_EXIT);
+ event_cnt++;
+ dispatch_release(proc);
+ dispatch_resume(completion);
+ });
+
+ dispatch_resume(proc);
+ }
+
+
+ //
+ // The completion block will be pending on the completion queue until it
+ // has been fully resumed, at which point the test will exit successfully.
+ //
+
+ dispatch_async(completion, ^{
+ int status;
+ int res2 = waitpid(pid, &status, 0);
+ assert(res2 != -1);
+ test_long("Sub-process exited", WEXITSTATUS(status) | WTERMSIG(status), 0);
+ test_long("Event count", event_cnt, PID_CNT);
+ test_stop();
+ });
+
+ kill(pid, SIGCONT);
+
+ dispatch_main();
+
+ return 0;
+}
Added: trunk/testing/dispatch_read.c
===================================================================
--- trunk/testing/dispatch_read.c (rev 0)
+++ trunk/testing/dispatch_read.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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;
+
+ reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, main_q);
+ test_ptr_notnull("DISPATCH_SOURCE_TYPE_READ", 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_release(reader);
+ }
+ });
+
+ dispatch_source_set_cancel_handler(reader, ^{
+ test_long("Bytes read", bytes_read, bytes_total);
+ int res = close(infd);
+ test_errno("close", res == -1 ? errno : 0, 0);
+ test_stop();
+ });
+
+ dispatch_resume(reader);
+
+ dispatch_main();
+}
Added: trunk/testing/dispatch_readsync.c
===================================================================
--- trunk/testing/dispatch_readsync.c (rev 0)
+++ trunk/testing/dispatch_readsync.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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_global_queue(0, 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();
+}
Added: trunk/testing/dispatch_sema.c
===================================================================
--- trunk/testing/dispatch_sema.c (rev 0)
+++ trunk/testing/dispatch_sema.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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_global_queue(0, 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;
+}
Added: trunk/testing/dispatch_starfish.c
===================================================================
--- trunk/testing/dispatch_starfish.c (rev 0)
+++ trunk/testing/dispatch_starfish.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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();
+}
Added: trunk/testing/dispatch_test.c
===================================================================
--- trunk/testing/dispatch_test.c (rev 0)
+++ trunk/testing/dispatch_test.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,148 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/errno.h>
+#include <string.h>
+
+#include "dispatch_test.h"
+
+#define _test_print(_file, _line, _desc, \
+ _expr, _fmt1, _val1, _fmt2, _val2) do { \
+ const char* _exprstr = _expr ? "PASS" : "FAIL"; \
+ char _linestr[BUFSIZ]; \
+ if (!_expr) { \
+ snprintf(_linestr, sizeof(_linestr), \
+ " (%s:%ld)", _file, _line); \
+ } else { \
+ _linestr[0] = 0; \
+ } \
+ if (_fmt2 == 0) { \
+ printf("\tValue: " _fmt1 "\n" \
+ "[%s] %s%s\n", \
+ _val1, \
+ _exprstr, \
+ _desc, \
+ _linestr); \
+ } else { \
+ printf("\tActual: " _fmt1 "\n" \
+ "\tExpected: " _fmt2 "\n" \
+ "[%s] %s%s\n", \
+ _val1, \
+ _val2, \
+ _exprstr, \
+ _desc, \
+ _linestr); \
+ } \
+ if (!_expr) { \
+ printf("\t%s:%ld\n", _file, _line); \
+ } \
+ fflush(stdout); \
+} while (0);
+
+void
+test_start(const char* desc) {
+ printf("\n==================================================\n");
+ printf("[TEST] %s\n", desc);
+ printf("[PID] %d\n", getpid());
+ printf("==================================================\n\n");
+ usleep(100000); // give 'gdb --waitfor=' a chance to find this proc
+}
+
+#define test_ptr_null(a,b) _test_ptr_null(__FILE__, __LINE__, a, b)
+void
+_test_ptr_null(const char* file, long line, const char* desc, const void* ptr) {
+ _test_print(file, line, desc,
+ (ptr == NULL), "%p", ptr, "%p", (void*)0);
+}
+
+#define test_ptr_notnull(a,b) _test_ptr_notnull(__FILE__, __LINE__, a, b)
+void
+_test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr) {
+ _test_print(file, line, desc,
+ (ptr != NULL), "%p", ptr, "%p", ptr ?: (void*)~0);
+}
+
+#define test_ptr(a,b,c) _test_ptr(__FILE__, __LINE__, a, b, c)
+void
+_test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected) {
+ _test_print(file, line, desc,
+ (actual == expected), "%p", actual, "%p", expected);
+}
+
+#define test_long(a,b,c) _test_long(__FILE__, __LINE__, a, b, c)
+void
+_test_long(const char* file, long line, const char* desc, long actual, long expected) {
+ _test_print(file, line, desc,
+ (actual == expected), "%ld", actual, "%ld", expected);
+}
+
+#define test_long_less_than(a, b, c) _test_long_less_than(__FILE__, __LINE__, a, b, c)
+void
+_test_long_less_than(const char* file, long line, const char* desc, long actual, long expected_max) {
+ _test_print(file, line, desc, (actual < expected_max), "%ld", actual, "<%ld", expected_max);
+}
+
+#define test_double_less_than(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+void
+_test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected) {
+ _test_print(file, line, desc, (val < max_expected), "%f", val, "<%f", max_expected);
+}
+
+#define test_double_less_than_or_equal(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+void
+_test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected) {
+ _test_print(file, line, desc, (val <= max_expected), "%f", val, "<%f", max_expected);
+}
+
+#define test_errno(a,b,c) _test_errno(__FILE__, __LINE__, a, b, c)
+void
+_test_errno(const char* file, long line, const char* desc, long actual, long expected) {
+ char* actual_str;
+ char* expected_str;
+ asprintf(&actual_str, "%ld\t%s", actual, actual ? strerror(actual) : "");
+ asprintf(&expected_str, "%ld\t%s", expected, expected ? strerror(expected) : "");
+ _test_print(file, line, desc,
+ (actual == expected), "%s", actual_str, "%s", expected_str);
+ free(actual_str);
+ free(expected_str);
+}
+
+#include <spawn.h>
+
+extern char **environ;
+
+void
+test_stop(void) {
+ test_stop_after_delay((void *)(intptr_t)0);
+}
+
+void
+test_stop_after_delay(void *delay) {
+ int res;
+ pid_t pid;
+ char pidstr[10];
+
+ if (delay != NULL) {
+ sleep((int)(intptr_t)delay);
+ }
+
+ if (getenv("NOLEAKS")) _exit(EXIT_SUCCESS);
+
+ /* leaks doesn't work against debug variant malloc */
+ if (getenv("DYLD_IMAGE_SUFFIX")) _exit(EXIT_SUCCESS);
+
+ snprintf(pidstr, sizeof(pidstr), "%d", getpid());
+ char* args[] = { "./leaks-wrapper", pidstr, NULL };
+ res = posix_spawnp(&pid, args[0], NULL, NULL, args, environ);
+ if (res == 0 && pid > 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ test_long("Leaks", status, 0);
+ } else {
+ perror(args[0]);
+ }
+
+ _exit(EXIT_SUCCESS);
+}
Added: trunk/testing/dispatch_test.h
===================================================================
--- trunk/testing/dispatch_test.h (rev 0)
+++ trunk/testing/dispatch_test.h 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,33 @@
+#include <errno.h>
+
+__BEGIN_DECLS
+
+void test_start(const char* desc);
+void test_stop(void);
+void test_stop_after_delay(void *delay);
+
+void _test_ptr_null(const char* file, long line, const char* desc, const void* ptr);
+#define test_ptr_null(a,b) _test_ptr_null(__FILE__, __LINE__, a, b)
+
+void _test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr);
+#define test_ptr_notnull(a,b) _test_ptr_notnull(__FILE__, __LINE__, a, b)
+
+void _test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected);
+#define test_ptr(a,b,c) _test_ptr(__FILE__, __LINE__, a, b, c)
+
+void _test_long(const char* file, long line, const char* desc, long actual, long expected);
+#define test_long(a,b,c) _test_long(__FILE__, __LINE__, a, b, c)
+
+void _test_long_less_than(const char* file, long line, const char* desc, long actual, long max_expected);
+#define test_long_less_than(a,b,c) _test_long_less_than(__FILE__, __LINE__, a, b, c)
+
+void _test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected);
+#define test_double_less_than_or_equal(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+
+void _test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected);
+#define test_double_less_than(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+
+void _test_errno(const char* file, long line, const char* desc, long actual, long expected);
+#define test_errno(a,b,c) _test_errno(__FILE__, __LINE__, a, b, c)
+
+__END_DECLS
Added: trunk/testing/dispatch_timer_bit31.c
===================================================================
--- trunk/testing/dispatch_timer_bit31.c (rev 0)
+++ trunk/testing/dispatch_timer_bit31.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+//
+// There were several bugs related to sign extension / integer overflow that
+// were encountered during development. Create a timer whose interval has the
+// 31st bit of the word set to verify that it behaves correctly.
+//
+// 2 s < 0x80000000ull ns < 4 s
+//
+
+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());
+
+ dispatch_source_t timer;
+ struct timeval start_time;
+
+ gettimeofday(&start_time, NULL);
+
+ timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
+ test_ptr_notnull("DISPATCH_SOURCE_TYPE_TIMER", timer);
+
+ dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0x80000000ull), 0, 0);
+
+ dispatch_source_set_event_handler(timer, ^{
+ dispatch_source_cancel(timer);
+ });
+
+ dispatch_source_set_cancel_handler(timer, ^{
+ struct timeval end_time;
+ gettimeofday(&end_time, NULL);
+
+ test_long_less_than("elapsed time < 4s", end_time.tv_sec - start_time.tv_sec, 4);
+ test_long_less_than("elapsed time > 2s", 1, end_time.tv_sec - start_time.tv_sec);
+ test_stop();
+ });
+
+ dispatch_resume(timer);
+
+ dispatch_main();
+
+ return 0;
+}
Added: trunk/testing/dispatch_timer_bit63.c
===================================================================
--- trunk/testing/dispatch_timer_bit63.c (rev 0)
+++ trunk/testing/dispatch_timer_bit63.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+//
+// There were several bugs related to sign extension / integer overflow that
+// were encountered during development. Create a timer whose interval has the
+// 63rd bit of the word set to verify that it behaves correctly.
+//
+// The expected side-effect of integer overflow would be for the timer to
+// fire immediately since its interval has "elapsed." If the timer does not
+// fire within 1s we can assume the arithmetic was performed correctly.
+//
+
+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;
+}
Added: trunk/testing/dispatch_timer_set_time.c
===================================================================
--- trunk/testing/dispatch_timer_set_time.c (rev 0)
+++ trunk/testing/dispatch_timer_set_time.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,60 @@
+#include <sys/time.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+int main(void)
+{
+ test_start("Dispatch Update Timer");
+
+ dispatch_queue_t main_q = dispatch_get_main_queue();
+ test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
+
+ __block int i = 0;
+ struct timeval start_time;
+
+ gettimeofday(&start_time, NULL);
+ dispatch_source_attr_t attr = dispatch_source_attr_create();
+ dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) {
+ struct timeval end_time;
+ gettimeofday(&end_time, NULL);
+ // Make sure we actually managed to adjust the interval
+ // duration. Seven one second ticks would blow past
+ // this.
+ test_long_less_than("total duration", end_time.tv_sec - start_time.tv_sec, 3);
+ test_ptr_notnull("finalizer ran", ds);
+ test_stop();
+ });
+
+ dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+ 1000000000ull,
+ 0,
+ attr,
+ main_q,
+ ^(dispatch_event_t ev) {
+ long err;
+ if (dispatch_event_get_error(ev, &err)) {
+ test_errno("dispatch_event_get_error", err, ECANCELED);
+ dispatch_release(dispatch_event_get_source(ev));
+ } else {
+ fprintf(stderr, "%d\n", ++i);
+ if (i >= 7) {
+ dispatch_cancel(dispatch_event_get_source(ev));
+ } else if (i == 1) {
+ dispatch_source_timer_set_time(dispatch_event_get_source(ev), 100, 0);
+ }
+ }
+ });
+ test_ptr_notnull("dispatch_source_timer_create", s);
+
+ dispatch_release(attr);
+
+ dispatch_main();
+
+ return 0;
+}
Added: trunk/testing/fd_stress.c
===================================================================
--- trunk/testing/fd_stress.c (rev 0)
+++ trunk/testing/fd_stress.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ * 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;
+}
Added: trunk/testing/harness.c
===================================================================
--- trunk/testing/harness.c (rev 0)
+++ trunk/testing/harness.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <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 proc;
+ dispatch_source_t sig;
+ int res;
+ pid_t pid;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: harness [...]\n");
+ exit(1);
+ }
+
+ //
+ // Start the test in a suspended state. Will send SIGCONT once ready.
+ //
+
+ 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);
+
+ //
+ // Monitor the test process exit status.
+ //
+
+ dispatch_queue_t main_q = dispatch_get_main_queue();
+
+ proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, main_q);
+ assert(proc);
+
+ dispatch_source_set_event_handler(proc, ^{
+ int status;
+ int res2 = waitpid(pid, &status, 0);
+ assert(res2 != -1);
+ test_long("Process exited", WEXITSTATUS(status) | WTERMSIG(status), 0);
+ exit(0);
+ });
+
+ dispatch_resume(proc);
+
+ //
+ // Forcibly stop currently running test after 30 second timeout.
+ //
+
+ uint64_t timeout = 30LL * NSEC_PER_SEC;
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeout), main_q, ^{
+ kill(pid, SIGKILL);
+ fprintf(stderr, "Terminating unresponsive process (%0.1lfs)\n", (double)timeout/NSEC_PER_SEC);
+ });
+
+ //
+ // Control-c forcibly stops currently running test.
+ //
+
+ // Ignore the default signal handler so that dispatch can handle it.
+ signal(SIGINT, SIG_IGN);
+
+ sig = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGINT, 0, main_q);
+ assert(sig);
+
+ dispatch_source_set_event_handler(sig, ^{
+ fprintf(stderr, "Terminating process due to signal\n");
+ kill(pid, SIGKILL);
+ });
+ dispatch_resume(sig);
+
+ //
+ // Start the test.
+ //
+
+ kill(pid, SIGCONT);
+
+ dispatch_main();
+
+ return 0;
+}
Added: trunk/testing/leaks-wrapper
===================================================================
--- trunk/testing/leaks-wrapper (rev 0)
+++ trunk/testing/leaks-wrapper 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+/usr/bin/leaks "$@" 2>&1 | tee "$@.leakslog" | grep -q " 0 leaks for 0 total leaked bytes"
+
+if [ $? -eq 0 ]; then
+ rm "$@.leakslog"
+ exit 0
+else
+ exit $?
+fi
Property changes on: trunk/testing/leaks-wrapper
___________________________________________________________________
Added: svn:executable
+ *
Added: trunk/testing/nsoperation.m
===================================================================
--- trunk/testing/nsoperation.m (rev 0)
+++ trunk/testing/nsoperation.m 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <Foundation/Foundation.h>
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+ at interface MYOperation : NSOperation
+{
+}
+ at end
+
+ at implementation MYOperation
+
+- (id) init
+{
+ self = [super init];
+ return self;
+}
+
+- (void)main
+{
+ test_stop();
+}
+
+ at 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;
+}
Added: trunk/testing/queue_finalizer.c
===================================================================
--- trunk/testing/queue_finalizer.c (rev 0)
+++ trunk/testing/queue_finalizer.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "dispatch_test.h"
+
+void *ctxt_magic;
+
+static void
+finalizer(void *ctxt)
+{
+ test_ptr("finalizer ran", ctxt, ctxt_magic);
+ test_stop();
+}
+
+int
+main(void) {
+ test_start("Dispatch Queue Finalizer");
+
+#ifdef __LP64__
+ ctxt_magic = (void*)((uintptr_t)arc4random() << 32 | arc4random());
+#else
+ ctxt_magic = (void*)arc4random();
+#endif
+
+ dispatch_queue_t q = dispatch_queue_create(NULL, NULL);
+ test_ptr_notnull("dispatch_queue_new", q);
+
+ dispatch_set_context(q, ctxt_magic);
+
+ dispatch_set_finalizer_f(q, finalizer);
+
+ dispatch_release(q);
+
+ dispatch_main();
+
+ return 0;
+}
Added: trunk/testing/summarize.c
===================================================================
--- trunk/testing/summarize.c (rev 0)
+++ trunk/testing/summarize.c 2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <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);
+}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/libdispatch-changes/attachments/20090910/2aa74860/attachment-0001.html>
More information about the libdispatch-changes
mailing list