From e85f44377864e428703fb21503e29f422c11288f Mon Sep 17 00:00:00 2001 From: Apple Date: Mon, 15 Aug 2011 15:06:34 +0000 Subject: [PATCH] libdispatch-187.5.tar.gz --- EXPORT.APPLE | 21 - INSTALL | 81 + Makefile.am | 22 + PATCHES | 194 + autogen.sh | 2 + config/config.h | 203 + configure.ac | 266 ++ dispatch/Makefile.am | 19 + {src => dispatch}/base.h | 47 +- dispatch/data.h | 248 ++ {src => dispatch}/dispatch.h | 22 +- {src => dispatch}/group.h | 61 +- dispatch/io.h | 586 +++ {src => dispatch}/object.h | 60 +- {src => dispatch}/once.h | 51 +- {src => dispatch}/queue.h | 431 ++- {src => dispatch}/semaphore.h | 22 +- {src => dispatch}/source.h | 230 +- {src => dispatch}/time.h | 24 +- .../project.pbxproj | 609 --- examples/Dispatch Samples/ReadMe.txt | 93 - examples/Dispatch Samples/apply.c | 123 - examples/Dispatch Samples/nWide.c | 127 - examples/Dispatch Samples/netcat.c | 596 --- examples/Dispatch Samples/proc.c | 209 - examples/Dispatch Samples/readFile.c | 115 - examples/Dispatch Samples/readFileF.c | 117 - examples/Dispatch Samples/timers.c | 85 - examples/DispatchLife/DispatchLife.c | 392 -- .../DispatchLife.xcodeproj/project.pbxproj | 252 -- examples/DispatchLife/DispatchLifeGLView.h | 59 - examples/DispatchLife/DispatchLifeGLView.m | 203 - .../English.lproj/InfoPlist.strings | Bin 92 -> 0 bytes .../English.lproj/MainMenu.nib/designable.nib | 2651 ------------- .../MainMenu.nib/keyedobjects.nib | Bin 19575 -> 0 bytes examples/DispatchLife/Info.plist | 28 - examples/DispatchLife/ReadMe.txt | 37 - examples/DispatchLife/main.m | 49 - .../DispatchWebServer/DispatchWebServer.c | 956 ----- .../project.pbxproj | 203 - examples/DispatchWebServer/ReadMe.txt | 44 - install | 81 - install_Libsystem_pieces | 48 - libdispatch.xcodeproj/project.pbxproj | 1070 ++++- .../contents.xcworkspacedata | 6 + m4/atomic.m4 | 21 + m4/blocks.m4 | 112 + m4/pkg.m4 | 155 + man/Makefile.am | 89 + man/dispatch.3 | 18 +- man/dispatch_after.3 | 10 +- man/dispatch_api.3 | 2 +- man/dispatch_apply.3 | 33 +- man/dispatch_async.3 | 15 +- man/dispatch_benchmark.3 | 4 + man/dispatch_data_create.3 | 206 + man/dispatch_group_create.3 | 9 +- man/dispatch_io_create.3 | 238 ++ man/dispatch_io_read.3 | 151 + man/dispatch_object.3 | 22 +- man/dispatch_once.3 | 2 + man/dispatch_queue_create.3 | 127 +- man/dispatch_read.3 | 123 + man/dispatch_semaphore_create.3 | 21 +- man/dispatch_source_create.3 | 56 +- man/dispatch_time.3 | 1 + private/Makefile.am | 10 + {src => private}/benchmark.h | 19 +- {src => private}/private.h | 88 +- {src => private}/queue_private.h | 89 +- {src => private}/source_private.h | 62 +- resolver/resolved.h | 26 + resolver/resolver.c | 20 + resolver/resolver.h | 31 + src/Makefile.am | 73 + src/apply.c | 235 +- src/benchmark.c | 35 +- src/data.c | 429 ++ src/data_internal.h | 58 + src/hw_shims.h | 72 - src/init.c | 622 +++ src/internal.h | 404 +- src/io.c | 2155 +++++++++++ src/io_internal.h | 198 + src/legacy.c | 444 --- src/legacy.h | 748 ---- src/object.c | 130 +- src/object_internal.h | 84 +- src/once.c | 93 +- src/os_shims.h | 152 - src/protocol.defs | 49 +- src/provider.d | 42 + src/queue.c | 3438 ++++++++++------- src/queue_internal.h | 156 +- src/semaphore.c | 652 ++-- src/semaphore_internal.h | 27 +- src/shims.c | 65 - src/shims.h | 74 + src/shims/atomic.h | 157 + src/shims/getprogname.h | 37 + src/shims/hw_config.h | 106 + src/shims/malloc_zone.h | 98 + src/shims/perfmon.h | 97 + src/shims/time.h | 108 + src/shims/tsd.h | 104 + src/source.c | 2889 +++++++------- src/source_internal.h | 123 +- src/time.c | 106 +- src/trace.h | 152 + testing/Makefile | 127 - testing/apply_strtoull.c | 64 - testing/bench.mm | 488 --- testing/conc.c | 33 - testing/dispatch_after.c | 59 - testing/dispatch_api.c | 20 - testing/dispatch_apply.c | 29 - testing/dispatch_c99.c | 21 - testing/dispatch_cascade.c | 94 - testing/dispatch_cffd.c | 139 - testing/dispatch_deadname.c | 44 - testing/dispatch_debug.c | 30 - testing/dispatch_drift.c | 61 - testing/dispatch_group.c | 89 - testing/dispatch_overcommit.c | 45 - testing/dispatch_pingpong.c | 37 - testing/dispatch_plusplus.cpp | 17 - testing/dispatch_priority.c | 126 - testing/dispatch_proc.c | 89 - testing/dispatch_rdlock_bmark.c | 140 - testing/dispatch_read.c | 84 - testing/dispatch_read2.c | 69 - testing/dispatch_readsync.c | 59 - testing/dispatch_sema.c | 33 - testing/dispatch_starfish.c | 141 - testing/dispatch_suspend_timer.c | 76 - testing/dispatch_test.c | 148 - testing/dispatch_test.h | 33 - testing/dispatch_test_sync_on_main.c | 46 - testing/dispatch_timer.c | 65 - testing/dispatch_timer_bit31.c | 56 - testing/dispatch_timer_bit63.c | 42 - testing/dispatch_timer_oneshot.c | 43 - testing/dispatch_timer_set_time.c | 60 - testing/fast_apply_bench.c | 98 - testing/fd_stress.c | 457 --- testing/float_parsing.c | 136 - testing/fork-join.c | 40 - testing/func.c | 9 - testing/harness.c | 83 - testing/leaks-wrapper | 10 - testing/mach_server.c | 131 - testing/mig/Makefile | 17 - testing/mig/client.c | 34 - testing/mig/hello_logger.defs | 12 - testing/mig/hello_logger_types.h | 8 - testing/mig/server.c | 48 - testing/nsoperation.m | 52 - testing/queue_finalizer.c | 46 - testing/slice_benchmarks.c | 445 --- testing/summarize.c | 76 - testing/test.c | 45 - testing/yet-another-apply-test.c | 101 - tools/dispatch_trace.d | 76 + xcodeconfig/libdispatch-resolved.xcconfig | 25 + xcodeconfig/libdispatch-resolver.xcconfig | 20 + xcodeconfig/libdispatch.xcconfig | 67 + xcodescripts/install-manpages.sh | 107 + xcodescripts/mig-headers.sh | 29 + xcodescripts/postprocess-headers.sh | 21 + xcodescripts/symlink-headers.sh | 29 + 170 files changed, 14449 insertions(+), 17395 deletions(-) delete mode 100755 EXPORT.APPLE create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 PATCHES create mode 100644 autogen.sh create mode 100644 config/config.h create mode 100644 configure.ac create mode 100644 dispatch/Makefile.am rename {src => dispatch}/base.h (76%) create mode 100644 dispatch/data.h rename {src => dispatch}/dispatch.h (79%) rename {src => dispatch}/group.h (83%) create mode 100644 dispatch/io.h rename {src => dispatch}/object.h (74%) rename {src => dispatch}/once.h (60%) rename {src => dispatch}/queue.h (51%) rename {src => dispatch}/semaphore.h (87%) rename {src => dispatch}/source.h (75%) rename {src => dispatch}/time.h (84%) delete mode 100644 examples/Dispatch Samples/Dispatch Samples.xcodeproj/project.pbxproj delete mode 100644 examples/Dispatch Samples/ReadMe.txt delete mode 100644 examples/Dispatch Samples/apply.c delete mode 100644 examples/Dispatch Samples/nWide.c delete mode 100644 examples/Dispatch Samples/netcat.c delete mode 100644 examples/Dispatch Samples/proc.c delete mode 100644 examples/Dispatch Samples/readFile.c delete mode 100644 examples/Dispatch Samples/readFileF.c delete mode 100644 examples/Dispatch Samples/timers.c delete mode 100644 examples/DispatchLife/DispatchLife.c delete mode 100644 examples/DispatchLife/DispatchLife.xcodeproj/project.pbxproj delete mode 100644 examples/DispatchLife/DispatchLifeGLView.h delete mode 100644 examples/DispatchLife/DispatchLifeGLView.m delete mode 100644 examples/DispatchLife/English.lproj/InfoPlist.strings delete mode 100644 examples/DispatchLife/English.lproj/MainMenu.nib/designable.nib delete mode 100644 examples/DispatchLife/English.lproj/MainMenu.nib/keyedobjects.nib delete mode 100644 examples/DispatchLife/Info.plist delete mode 100644 examples/DispatchLife/ReadMe.txt delete mode 100644 examples/DispatchLife/main.m delete mode 100644 examples/DispatchWebServer/DispatchWebServer.c delete mode 100644 examples/DispatchWebServer/DispatchWebServer.xcodeproj/project.pbxproj delete mode 100644 examples/DispatchWebServer/ReadMe.txt delete mode 100755 install delete mode 100755 install_Libsystem_pieces create mode 100644 libdispatch.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 m4/atomic.m4 create mode 100644 m4/blocks.m4 create mode 100644 m4/pkg.m4 create mode 100644 man/Makefile.am create mode 100644 man/dispatch_data_create.3 create mode 100644 man/dispatch_io_create.3 create mode 100644 man/dispatch_io_read.3 create mode 100644 man/dispatch_read.3 create mode 100644 private/Makefile.am rename {src => private}/benchmark.h (91%) rename {src => private}/private.h (51%) rename {src => private}/queue_private.h (52%) rename {src => private}/source_private.h (62%) create mode 100644 resolver/resolved.h create mode 100644 resolver/resolver.c create mode 100644 resolver/resolver.h create mode 100644 src/Makefile.am create mode 100644 src/data.c create mode 100644 src/data_internal.h delete mode 100644 src/hw_shims.h create mode 100644 src/init.c create mode 100644 src/io.c create mode 100644 src/io_internal.h delete mode 100644 src/legacy.c delete mode 100644 src/legacy.h delete mode 100644 src/os_shims.h create mode 100644 src/provider.d delete mode 100644 src/shims.c create mode 100644 src/shims.h create mode 100644 src/shims/atomic.h create mode 100644 src/shims/getprogname.h create mode 100644 src/shims/hw_config.h create mode 100644 src/shims/malloc_zone.h create mode 100644 src/shims/perfmon.h create mode 100644 src/shims/time.h create mode 100644 src/shims/tsd.h create mode 100644 src/trace.h delete mode 100644 testing/Makefile delete mode 100644 testing/apply_strtoull.c delete mode 100644 testing/bench.mm delete mode 100644 testing/conc.c delete mode 100644 testing/dispatch_after.c delete mode 100644 testing/dispatch_api.c delete mode 100644 testing/dispatch_apply.c delete mode 100644 testing/dispatch_c99.c delete mode 100644 testing/dispatch_cascade.c delete mode 100644 testing/dispatch_cffd.c delete mode 100644 testing/dispatch_deadname.c delete mode 100644 testing/dispatch_debug.c delete mode 100644 testing/dispatch_drift.c delete mode 100644 testing/dispatch_group.c delete mode 100644 testing/dispatch_overcommit.c delete mode 100644 testing/dispatch_pingpong.c delete mode 100644 testing/dispatch_plusplus.cpp delete mode 100644 testing/dispatch_priority.c delete mode 100644 testing/dispatch_proc.c delete mode 100644 testing/dispatch_rdlock_bmark.c delete mode 100644 testing/dispatch_read.c delete mode 100644 testing/dispatch_read2.c delete mode 100644 testing/dispatch_readsync.c delete mode 100644 testing/dispatch_sema.c delete mode 100644 testing/dispatch_starfish.c delete mode 100644 testing/dispatch_suspend_timer.c delete mode 100644 testing/dispatch_test.c delete mode 100644 testing/dispatch_test.h delete mode 100644 testing/dispatch_test_sync_on_main.c delete mode 100644 testing/dispatch_timer.c delete mode 100644 testing/dispatch_timer_bit31.c delete mode 100644 testing/dispatch_timer_bit63.c delete mode 100644 testing/dispatch_timer_oneshot.c delete mode 100644 testing/dispatch_timer_set_time.c delete mode 100644 testing/fast_apply_bench.c delete mode 100644 testing/fd_stress.c delete mode 100644 testing/float_parsing.c delete mode 100644 testing/fork-join.c delete mode 100644 testing/func.c delete mode 100644 testing/harness.c delete mode 100755 testing/leaks-wrapper delete mode 100644 testing/mach_server.c delete mode 100644 testing/mig/Makefile delete mode 100644 testing/mig/client.c delete mode 100644 testing/mig/hello_logger.defs delete mode 100644 testing/mig/hello_logger_types.h delete mode 100644 testing/mig/server.c delete mode 100644 testing/nsoperation.m delete mode 100644 testing/queue_finalizer.c delete mode 100644 testing/slice_benchmarks.c delete mode 100644 testing/summarize.c delete mode 100644 testing/test.c delete mode 100644 testing/yet-another-apply-test.c create mode 100755 tools/dispatch_trace.d create mode 100644 xcodeconfig/libdispatch-resolved.xcconfig create mode 100644 xcodeconfig/libdispatch-resolver.xcconfig create mode 100644 xcodeconfig/libdispatch.xcconfig create mode 100755 xcodescripts/install-manpages.sh create mode 100755 xcodescripts/mig-headers.sh create mode 100755 xcodescripts/postprocess-headers.sh create mode 100755 xcodescripts/symlink-headers.sh diff --git a/EXPORT.APPLE b/EXPORT.APPLE deleted file mode 100755 index 2af942a..0000000 --- a/EXPORT.APPLE +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -x - -# copy cwd to SRCROOT like installsrc - -if [ -z "$SRCROOT" ]; then - PWD=`pwd` - SRCROOT=/tmp/`basename "$PWD"` -fi - -echo Exporting to $SRCROOT -mkdir -p "$SRCROOT" - -gnutar cf - \ - --exclude=.svn \ - --exclude=CVS \ - --exclude=build \ - --exclude=testing \ - --exclude=\*.APPLE \ - --exclude=install\* \ - . | (cd "$SRCROOT"; gnutar xf - ) - diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..69fd5a6 --- /dev/null +++ b/INSTALL @@ -0,0 +1,81 @@ +Grand Central Dispatch (GCD) + +GCD is a concurrent programming framework first shipped with Mac OS X Snow +Leopard. This package is an open source bundling of libdispatch, the core +user space library implementing GCD. At the time of writing, support for +the BSD kqueue API, and specifically extensions introduced in Mac OS X Snow +Leopard and FreeBSD 9-CURRENT, are required to use libdispatch. Other +systems are currently unsupported. + + Configuring and installing libdispatch + +GCD is built using autoconf, automake, and libtool, and has a number of +compile-time configuration options that should be reviewed before starting. +An uncustomized install requires: + + sh autogen.sh + ./configure + make + make install + +The following configure options may be of general interest: + +--with-apple-libc-source + + Specify the path to Apple's Libc package, so that appropriate headers + can be found and used. + +--with-apple-libclosure-source + + Specify the path to Apple's Libclosure package, so that appropriate headers + can be found and used. + +--with-apple-xnu-source + + Specify the path to Apple's XNU package, so that appropriate headers + can be found and used. + +--with-blocks-runtime + + On systems where -fblocks is supported, specify an additional library + path in which libBlocksRuntime can be found. This is not required on + Mac OS X, where the Blocks runtime is included in libSystem, but is + required on FreeBSD. + +The following options are likely to only be useful when building libdispatch +on Mac OS X as a replacement for /usr/lib/system/libdispatch.dylib: + +--disable-libdispatch-init-constructor + + Do not tag libdispatch's init routine as __constructor, in which case + it must be run manually before libdispatch routines can be called. + For the libdispatch library in /usr/lib/system, the init routine is called + automatically during process start. + +--enable-apple-tsd-optimizations + + Use a non-portable allocation scheme for pthread per-thread data (TSD) + keys when building libdispatch for /usr/lib/system on Mac OS X. This + should not be used on other OS's, or on Mac OS X when building a + stand-alone library. + + Typical configuration commands + +The following command lines create the configuration required to build +libdispatch for /usr/lib/system on Mac OS X Lion: + + sh autogen.sh + ./configure CFLAGS='-arch x86_64 -arch i386' \ + --prefix=/usr --libdir=/usr/lib/system \ + --disable-dependency-tracking --disable-static \ + --disable-libdispatch-init-constructor \ + --enable-apple-tsd-optimizations \ + --with-apple-libc-source=/path/to/10.7.0/Libc-763.11 \ + --with-apple-libclosure-source=/path/to/10.7.0/libclosure-53 \ + --with-apple-xnu-source=/path/to/10.7.0/xnu-1699.22.73 + +Typical configuration line for FreeBSD 8.x and 9.x to build libdispatch with +clang and blocks support: + + sh autogen.sh + ./configure CC=clang --with-blocks-runtime=/usr/local/lib diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..4e3167c --- /dev/null +++ b/Makefile.am @@ -0,0 +1,22 @@ +# +# +# + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS= \ + dispatch \ + man \ + private \ + src + +EXTRA_DIST= \ + LICENSE \ + PATCHES \ + autogen.sh \ + config/config.h \ + libdispatch.xcodeproj \ + resolver \ + tools \ + xcodeconfig \ + xcodescripts diff --git a/PATCHES b/PATCHES new file mode 100644 index 0000000..4f88387 --- /dev/null +++ b/PATCHES @@ -0,0 +1,194 @@ +The libdispatch project exists in a parallel open source repository at: + http://svn.macosforge.org/repository/libdispatch/trunk + +Externally committed revisions are periodically synchronized back to the +internal repository (this repository). + +Key: + APPLIED: change set was applied to internal repository. + INTERNAL: change set originated internally (i.e. already applied). + SKIPPED: change set was skipped. + +[ 1] SKIPPED +[ 2] SKIPPED +[ 3] INTERNAL rdar://problem/7148356 +[ 4] APPLIED rdar://problem/7323245 +[ 5] APPLIED rdar://problem/7323245 +[ 6] APPLIED rdar://problem/7323245 +[ 7] APPLIED rdar://problem/7323245 +[ 8] APPLIED rdar://problem/7323245 +[ 9] APPLIED rdar://problem/7323245 +[ 10] APPLIED rdar://problem/7323245 +[ 11] APPLIED rdar://problem/7323245 +[ 12] APPLIED rdar://problem/7323245 +[ 13] SKIPPED +[ 14] APPLIED rdar://problem/7323245 +[ 15] APPLIED rdar://problem/7323245 +[ 16] APPLIED rdar://problem/7323245 +[ 17] APPLIED rdar://problem/7323245 +[ 18] APPLIED rdar://problem/7323245 +[ 19] APPLIED rdar://problem/7323245 +[ 20] APPLIED rdar://problem/7323245 +[ 21] APPLIED rdar://problem/7323245 +[ 22] APPLIED rdar://problem/7323245 +[ 23] APPLIED rdar://problem/7323245 +[ 24] APPLIED rdar://problem/7323245 +[ 25] APPLIED rdar://problem/7323245 +[ 26] APPLIED rdar://problem/7323245 +[ 27] APPLIED rdar://problem/7323245 +[ 28] APPLIED rdar://problem/7323245 +[ 29] APPLIED rdar://problem/7323245 +[ 30] SKIPPED +[ 31] APPLIED rdar://problem/7323245 +[ 32] APPLIED rdar://problem/7323245 +[ 33] APPLIED rdar://problem/7323245 +[ 34] APPLIED rdar://problem/7323245 +[ 35] SKIPPED +[ 36] APPLIED rdar://problem/7323245 +[ 37] APPLIED rdar://problem/7323245 +[ 38] APPLIED rdar://problem/7323245 +[ 39] APPLIED rdar://problem/7323245 +[ 40] APPLIED rdar://problem/7323245 +[ 41] APPLIED rdar://problem/7323245 +[ 42] APPLIED rdar://problem/7323245 +[ 43] APPLIED rdar://problem/7323245 +[ 44] APPLIED rdar://problem/7323245 +[ 45] APPLIED rdar://problem/7323245 +[ 46] APPLIED rdar://problem/7323245 +[ 47] APPLIED rdar://problem/7323245 +[ 48] APPLIED rdar://problem/7323245 +[ 49] APPLIED rdar://problem/7323245 +[ 50] APPLIED rdar://problem/7323245 +[ 51] APPLIED rdar://problem/7323245 +[ 52] APPLIED rdar://problem/7323245 +[ 53] APPLIED rdar://problem/7323245 +[ 54] APPLIED rdar://problem/7323245 +[ 55] APPLIED rdar://problem/7323245 +[ 56] APPLIED rdar://problem/7323245 +[ 57] APPLIED rdar://problem/7323245 +[ 58] APPLIED rdar://problem/7323245 +[ 59] APPLIED rdar://problem/7323245 +[ 60] APPLIED rdar://problem/7323245 +[ 61] APPLIED rdar://problem/7323245 +[ 62] APPLIED rdar://problem/7323245 +[ 63] APPLIED rdar://problem/7323245 +[ 64] APPLIED rdar://problem/7323245 +[ 65] APPLIED rdar://problem/7323245 +[ 66] APPLIED rdar://problem/7323245 +[ 67] APPLIED rdar://problem/7323245 +[ 68] APPLIED rdar://problem/7323245 +[ 69] APPLIED rdar://problem/7323245 +[ 70] APPLIED rdar://problem/7323245 +[ 71] INTERNAL +[ 72] INTERNAL +[ 73] APPLIED rdar://problem/7531526 +[ 74] APPLIED rdar://problem/7531526 +[ 75] +[ 76] +[ 77] +[ 78] +[ 79] APPLIED rdar://problem/7531526 +[ 80] APPLIED rdar://problem/7531526 +[ 81] APPLIED rdar://problem/7531526 +[ 82] APPLIED rdar://problem/7531526 +[ 83] APPLIED rdar://problem/7531526 +[ 84] APPLIED rdar://problem/7531526 +[ 85] +[ 86] +[ 87] APPLIED rdar://problem/7531526 +[ 88] APPLIED rdar://problem/7531526 +[ 89] APPLIED rdar://problem/7531526 +[ 90] +[ 91] +[ 92] +[ 93] +[ 94] +[ 95] +[ 96] APPLIED rdar://problem/7531526 +[ 97] APPLIED rdar://problem/7531526 +[ 98] +[ 99] +[ 100] +[ 101] +[ 102] +[ 103] APPLIED rdar://problem/7531526 +[ 104] APPLIED rdar://problem/7531526 +[ 105] +[ 106] APPLIED rdar://problem/7531526 +[ 107] SKIPPED +[ 108] SKIPPED +[ 109] SKIPPED +[ 110] SKIPPED +[ 111] SKIPPED +[ 112] APPLIED rdar://problem/7531526 +[ 113] SKIPPED +[ 114] APPLIED rdar://problem/7531526 +[ 115] APPLIED rdar://problem/7531526 +[ 116] APPLIED rdar://problem/7531526 +[ 117] SKIPPED +[ 118] APPLIED rdar://problem/7531526 +[ 119] SKIPPED +[ 120] APPLIED rdar://problem/7531526 +[ 121] SKIPPED +[ 122] SKIPPED +[ 123] SKIPPED +[ 124] SKIPPED +[ 125] APPLIED rdar://problem/7531526 +[ 126] SKIPPED +[ 127] APPLIED rdar://problem/7531526 +[ 128] +[ 129] +[ 130] +[ 131] +[ 132] +[ 133] +[ 134] +[ 135] +[ 136] +[ 137] APPLIED rdar://problem/7647055 +[ 138] SKIPPED +[ 139] APPLIED rdar://problem/7531526 +[ 140] APPLIED rdar://problem/7531526 +[ 141] APPLIED rdar://problem/7531526 +[ 142] APPLIED rdar://problem/7531526 +[ 143] +[ 144] APPLIED rdar://problem/7531526 +[ 145] APPLIED rdar://problem/7531526 +[ 146] APPLIED rdar://problem/7531526 +[ 147] +[ 148] +[ 149] +[ 150] +[ 151] APPLIED rdar://problem/7531526 +[ 152] APPLIED rdar://problem/7531526 +[ 153] +[ 154] APPLIED rdar://problem/7531526 +[ 155] +[ 156] +[ 157] APPLIED rdar://problem/7531526 +[ 158] +[ 159] +[ 160] +[ 161] +[ 162] APPLIED rdar://problem/7531526 +[ 163] APPLIED rdar://problem/7531526 +[ 164] +[ 165] +[ 166] APPLIED rdar://problem/7531526 +[ 167] APPLIED rdar://problem/7531526 +[ 168] +[ 169] APPLIED rdar://problem/7531526 +[ 170] APPLIED rdar://problem/7531526 +[ 171] APPLIED rdar://problem/7531526 +[ 172] APPLIED rdar://problem/7531526 +[ 173] APPLIED rdar://problem/7531526 +[ 174] APPLIED rdar://problem/7531526 +[ 175] APPLIED rdar://problem/7531526 +[ 176] APPLIED rdar://problem/7531526 +[ 177] APPLIED rdar://problem/7531526 +[ 178] +[ 179] APPLIED rdar://problem/7531526 +[ 180] APPLIED rdar://problem/7531526 +[ 181] +[ 182] +[ 183] INTERNAL rdar://problem/7581831 diff --git a/autogen.sh b/autogen.sh new file mode 100644 index 0000000..3ebda42 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,2 @@ +#!/bin/sh +autoreconf -fvi diff --git a/config/config.h b/config/config.h new file mode 100644 index 0000000..040bf21 --- /dev/null +++ b/config/config.h @@ -0,0 +1,203 @@ +/* config/config.h. Generated from config.h.in by configure. */ +/* config/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the declaration of `CLOCK_MONOTONIC', and to 0 if + you don't. */ +#define HAVE_DECL_CLOCK_MONOTONIC 0 + +/* Define to 1 if you have the declaration of `CLOCK_UPTIME', and to 0 if you + don't. */ +#define HAVE_DECL_CLOCK_UPTIME 0 + +/* Define to 1 if you have the declaration of `FD_COPY', and to 0 if you + don't. */ +#define HAVE_DECL_FD_COPY 1 + +/* Define to 1 if you have the declaration of `NOTE_NONE', and to 0 if you + don't. */ +#define HAVE_DECL_NOTE_NONE 1 + +/* Define to 1 if you have the declaration of `NOTE_REAP', and to 0 if you + don't. */ +#define HAVE_DECL_NOTE_REAP 1 + +/* Define to 1 if you have the declaration of `NOTE_SIGNAL', and to 0 if you + don't. */ +#define HAVE_DECL_NOTE_SIGNAL 1 + +/* Define to 1 if you have the declaration of `POSIX_SPAWN_START_SUSPENDED', + and to 0 if you don't. */ +#define HAVE_DECL_POSIX_SPAWN_START_SUSPENDED 1 + +/* Define to 1 if you have the declaration of `program_invocation_short_name', + and to 0 if you don't. */ +#define HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME 0 + +/* Define to 1 if you have the declaration of `SIGEMT', and to 0 if you don't. + */ +#define HAVE_DECL_SIGEMT 1 + +/* Define to 1 if you have the declaration of `VQ_UPDATE', and to 0 if you + don't. */ +#define HAVE_DECL_VQ_UPDATE 1 + +/* Define to 1 if you have the declaration of `VQ_VERYLOWDISK', and to 0 if + you don't. */ +#define HAVE_DECL_VQ_VERYLOWDISK 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `getprogname' function. */ +#define HAVE_GETPROGNAME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if Apple leaks program is present */ +#define HAVE_LEAKS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIBKERN_OSATOMIC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIBKERN_OSCROSSENDIAN_H 1 + +/* Define if mach is present */ +#define HAVE_MACH 1 + +/* Define to 1 if you have the `mach_absolute_time' function. */ +#define HAVE_MACH_ABSOLUTE_TIME 1 + +/* Define to 1 if you have the `malloc_create_zone' function. */ +#define HAVE_MALLOC_CREATE_ZONE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_MALLOC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if __builtin_trap marked noreturn */ +#define HAVE_NORETURN_BUILTIN_TRAP 1 + +/* Define to 1 if you have the `pthread_key_init_np' function. */ +#define HAVE_PTHREAD_KEY_INIT_NP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PTHREAD_MACHDEP_H 1 + +/* Define to 1 if you have the `pthread_main_np' function. */ +#define HAVE_PTHREAD_MAIN_NP 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PTHREAD_NP_H */ + +/* Define if pthread work queues are present */ +#define HAVE_PTHREAD_WORKQUEUES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_CDEFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TARGETCONDITIONALS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libdispatch" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "libdispatch@macosforge.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libdispatch" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libdispatch 1.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libdispatch" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to use non-portable pthread TSD optimizations for Mac OS X) */ +#define USE_APPLE_TSD_OPTIMIZATIONS 1 + +/* Define to tag libdispatch_init as a constructor */ +/* #undef USE_LIBDISPATCH_INIT_CONSTRUCTOR */ + +/* Define to use Mach semaphores */ +#define USE_MACH_SEM 1 + +/* Define to use POSIX semaphores */ +/* #undef USE_POSIX_SEM */ + +/* Version number of package */ +#define VERSION "1.1" + +/* Define to 1 if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* # undef _ALL_SOURCE */ +#endif + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define if using Darwin $NOCANCEL */ +#define __DARWIN_NON_CANCELABLE 1 + +/* Enable extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..eeba91b --- /dev/null +++ b/configure.ac @@ -0,0 +1,266 @@ +# +# When this file changes, rerun autogen.sh. +# + +AC_PREREQ(2.59) +AC_INIT([libdispatch], [1.1], [libdispatch@macosforge.org], [libdispatch]) +AC_REVISION([$$]) +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_HEADER([config/config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AM_MAINTAINER_MODE + +# +# On Mac OS X, some required header files come from other source packages; +# allow specifying where those are. +# +AC_ARG_WITH([apple-libc-source], + [AS_HELP_STRING([--with-apple-libc-source], + [Specify path to Apple Libc source])], + [apple_libc_source_path=${withval}/pthreads + APPLE_LIBC_SOURCE_PATH=-I$apple_libc_source_path + CPPFLAGS="$CPPFLAGS -I$apple_libc_source_path"], + [APPLE_LIBC_SOURCE_PATH=] +) +AC_SUBST([APPLE_LIBC_SOURCE_PATH]) + +AC_ARG_WITH([apple-libclosure-source], + [AS_HELP_STRING([--with-apple-libclosure-source], + [Specify path to Apple libclosure source])], + [apple_libclosure_source_path=${withval} + APPLE_LIBCLOSURE_SOURCE_PATH=-I$apple_libclosure_source_path + CPPFLAGS="$CPPFLAGS -I$apple_libclosure_source_path"], + [APPLE_LIBCLOSURE_SOURCE_PATH=] +) +AC_SUBST([APPLE_LIBCLOSURE_SOURCE_PATH]) + +AC_ARG_WITH([apple-xnu-source], + [AS_HELP_STRING([--with-apple-xnu-source], + [Specify path to Apple XNU source])], + [apple_xnu_source_path=${withval}/libkern + APPLE_XNU_SOURCE_PATH=-I$apple_xnu_source_path + CPPFLAGS="$CPPFLAGS -I$apple_xnu_source_path" + apple_xnu_source_system_path=${withval}/osfmk + APPLE_XNU_SOURCE_SYSTEM_PATH=$apple_xnu_source_system_path], + [APPLE_XNU_SOURCE_PATH=] +) +AC_SUBST([APPLE_XNU_SOURCE_PATH]) +AC_SUBST([APPLE_XNU_SOURCE_SYSTEM_PATH]) +AM_CONDITIONAL(USE_XNU_SOURCE, [test -n "$apple_xnu_source_system_path"]) + +AC_CACHE_CHECK([for System.framework/PrivateHeaders], dispatch_cv_system_privateheaders, + [AS_IF([test -d /System/Library/Frameworks/System.framework/PrivateHeaders], + [dispatch_cv_system_privateheaders=yes], [dispatch_cv_system_privateheaders=no])] +) +AS_IF([test "x$dispatch_cv_system_privateheaders" != "xno"], + [CPPFLAGS="$CPPFLAGS -I/System/Library/Frameworks/System.framework/PrivateHeaders"] +) + +# +# On Mac OS X, libpispatch_init is automatically invoked during libSystem +# process initialization. On other systems, it is tagged as a library +# constructor to be run by automatically by the runtime linker. +# +AC_ARG_ENABLE([libdispatch-init-constructor], + [AS_HELP_STRING([--disable-libdispatch-init-constructor], + [Disable libdispatch_init as a constructor])] +) + +AS_IF([test "x$enable_libdispatch_init_constructor" != "xno"], + [AC_DEFINE(USE_LIBDISPATCH_INIT_CONSTRUCTOR, 1, + [Define to tag libdispatch_init as a constructor])] +) + +# +# On Mac OS X libdispatch can use the non-portable direct pthread TSD functions +# +AC_ARG_ENABLE([apple-tsd-optimizations], + [AS_HELP_STRING([--enable-apple-tsd-optimizations], + [Use non-portable pthread TSD optimizations for Mac OS X.])] +) + +AS_IF([test "x$enable_apple_tsd_optimizations" = "xyes"], + [AC_DEFINE(USE_APPLE_TSD_OPTIMIZATIONS, 1, + [Define to use non-portable pthread TSD optimizations for Mac OS X)])] +) + +AC_USE_SYSTEM_EXTENSIONS +AC_PROG_CC +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_LIBTOOL +AC_PATH_PROGS(MIG, mig) + +AC_PATH_PROG(LEAKS, leaks) +AS_IF([test "x$LEAKS" != "x"], + [AC_DEFINE(HAVE_LEAKS, 1, [Define if Apple leaks program is present])] +) + +AM_INIT_AUTOMAKE([foreign]) + +DISPATCH_C_ATOMIC_BUILTINS + +case $dispatch_cv_atomic in + yes) ;; + -march*) MARCH_FLAGS="$dispatch_cv_atomic" + AC_SUBST([MARCH_FLAGS]) ;; + *) AC_MSG_ERROR([No gcc builtin atomic operations available]) ;; +esac + +# +# Find libraries we will need +# +AC_SEARCH_LIBS(clock_gettime, rt) +AC_SEARCH_LIBS(pthread_create, pthread) + +# +# Prefer native kqueue(2); otherwise use libkqueue if present. +# +AC_CHECK_HEADER(sys/event.h, [], + [PKG_CHECK_MODULES(KQUEUE, libkqueue)] +) + +# +# Checks for header files. +# +AC_HEADER_STDC +AC_CHECK_HEADERS([TargetConditionals.h pthread_np.h malloc/malloc.h libkern/OSCrossEndian.h libkern/OSAtomic.h]) + +# hack for pthread_machdep.h's #include +AS_IF([test -n "$apple_xnu_source_system_path"], [ + saveCPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS -I." + ln -fsh "$apple_xnu_source_system_path" System +]) +AC_CHECK_HEADERS([pthread_machdep.h]) +AS_IF([test -n "$apple_xnu_source_system_path"], [ + rm -f System + CPPFLAGS="$saveCPPFLAGS" +]) + +# +# Core Services is tested in one of the GCD regression tests, so test for its +# presence using its header file. +# +AC_CHECK_HEADER([CoreServices/CoreServices.h], + [have_coreservices=true], + [have_coreservices=false] +) +AM_CONDITIONAL(HAVE_CORESERVICES, $have_coreservices) + +# +# We use the availability of mach.h to decide whether to compile in all sorts +# of Machisms, including using Mach ports as event sources, etc. +# +AC_CHECK_HEADER([mach/mach.h], [ + AC_DEFINE(HAVE_MACH, 1, [Define if mach is present]) + AC_DEFINE(__DARWIN_NON_CANCELABLE, 1, [Define if using Darwin $NOCANCEL]) + have_mach=true], + [have_mach=false] +) +AM_CONDITIONAL(USE_MIG, $have_mach) + +# +# We use the availability of pthread_workqueue.h to decide whether to compile +# in support for pthread work queues. +# +AC_CHECK_HEADER([pthread_workqueue.h], + [AC_DEFINE(HAVE_PTHREAD_WORKQUEUES, 1, [Define if pthread work queues are present])] +) + +# +# Find functions and declarations we care about. +# +AC_CHECK_DECLS([CLOCK_UPTIME, CLOCK_MONOTONIC], [], [], + [[#include ]]) +AC_CHECK_DECLS([NOTE_NONE, NOTE_REAP, NOTE_SIGNAL], [], [], + [[#include ]]) +AC_CHECK_DECLS([FD_COPY], [], [], [[#include ]]) +AC_CHECK_DECLS([SIGEMT], [], [], [[#include ]]) +AC_CHECK_DECLS([VQ_UPDATE, VQ_VERYLOWDISK], [], [], [[#include ]]) +AC_CHECK_DECLS([program_invocation_short_name], [], [], [[#include ]]) +AC_CHECK_FUNCS([pthread_key_init_np pthread_main_np mach_absolute_time malloc_create_zone sysconf getprogname]) + +AC_CHECK_DECLS([POSIX_SPAWN_START_SUSPENDED], + [have_posix_spawn_start_suspended=true], + [have_posix_spawn_start_suspended=false], + [[#include ]] +) +AM_CONDITIONAL(HAVE_POSIX_SPAWN_START_SUSPENDED, $have_posix_spawn_start_suspended) + +AC_CHECK_FUNC([sem_init], + [have_sem_init=true], + [have_sem_init=false] +) + +# +# We support both Mach semaphores and POSIX semaphores; if the former are +# available, prefer them. +# +AC_MSG_CHECKING([what semaphore type to use]); +AS_IF([test "x$have_mach" = "xtrue"], + [AC_DEFINE(USE_MACH_SEM, 1, [Define to use Mach semaphores]) + AC_MSG_RESULT([Mach semaphores])], + [test "x$have_sem_init" = "xtrue"], + [AC_DEFINE(USE_POSIX_SEM, 1, [Define to use POSIX semaphores]) + AC_MSG_RESULT([POSIX semaphores])], + [AC_MSG_ERROR([no supported semaphore type])] +) + +AC_CHECK_HEADERS([sys/cdefs.h], [], [], + [#ifdef HAVE_SYS_CDEFS_H + #include + #endif]) + +DISPATCH_C_BLOCKS + +AC_CACHE_CHECK([for -fvisibility=hidden], [dispatch_cv_cc_visibility_hidden], [ + saveCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fvisibility=hidden" + AC_LINK_IFELSE([AC_LANG_PROGRAM([ + extern __attribute__ ((visibility ("default"))) int foo; int foo;], [foo = 0;])], + [dispatch_cv_cc_visibility_hidden="yes"], [dispatch_cv_cc_visibility_hidden="no"]) + CFLAGS="$saveCFLAGS" +]) +AS_IF([test "x$dispatch_cv_cc_visibility_hidden" != "xno"], [ + VISIBILITY_FLAGS="-fvisibility=hidden" +]) +AC_SUBST([VISIBILITY_FLAGS]) + +AC_CACHE_CHECK([for -momit-leaf-frame-pointer], [dispatch_cv_cc_omit_leaf_fp], [ + saveCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -momit-leaf-frame-pointer" + AC_LINK_IFELSE([AC_LANG_PROGRAM([ + extern int foo(void); int foo(void) {return 1;}], [foo();])], + [dispatch_cv_cc_omit_leaf_fp="yes"], [dispatch_cv_cc_omit_leaf_fp="no"]) + CFLAGS="$saveCFLAGS" +]) +AS_IF([test "x$dispatch_cv_cc_omit_leaf_fp" != "xno"], [ + OMIT_LEAF_FP_FLAGS="-momit-leaf-frame-pointer" +]) +AC_SUBST([OMIT_LEAF_FP_FLAGS]) + +AC_CACHE_CHECK([for darwin linker], [dispatch_cv_ld_darwin], [ + saveLDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -dynamiclib -compatibility_version 1.2.3 -current_version 4.5.6" + AC_LINK_IFELSE([AC_LANG_PROGRAM([ + extern int foo; int foo;], [foo = 0;])], + [dispatch_cv_ld_darwin="yes"], [dispatch_cv_ld_darwin="no"]) + LDFLAGS="$saveLDFLAGS" +]) +AM_CONDITIONAL(HAVE_DARWIN_LD, [test "x$dispatch_cv_ld_darwin" != "xno"]) + +# +# Temporary: some versions of clang do not mark __builtin_trap() as +# __attribute__((__noreturn__)). Detect and add if required. +# +AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([void __attribute__((__noreturn__)) temp(void) { __builtin_trap(); }], [])], [ + AC_DEFINE(HAVE_NORETURN_BUILTIN_TRAP, 1, [Define if __builtin_trap marked noreturn]) + ], []) + +# +# Generate Makefiles. +# +AC_CONFIG_FILES([Makefile dispatch/Makefile man/Makefile private/Makefile src/Makefile]) +AC_OUTPUT diff --git a/dispatch/Makefile.am b/dispatch/Makefile.am new file mode 100644 index 0000000..5cba713 --- /dev/null +++ b/dispatch/Makefile.am @@ -0,0 +1,19 @@ +# +# +# + +dispatchdir=$(includedir)/dispatch + +dispatch_HEADERS= \ + base.h \ + data.h \ + dispatch.h \ + group.h \ + io.h \ + object.h \ + once.h \ + queue.h \ + semaphore.h \ + source.h \ + time.h + diff --git a/src/base.h b/dispatch/base.h similarity index 76% rename from src/base.h rename to dispatch/base.h index 3799a9a..029e3e0 100644 --- a/src/base.h +++ b/dispatch/base.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -47,19 +47,24 @@ typedef union { struct dispatch_source_s *_ds; struct dispatch_source_attr_s *_dsa; struct dispatch_semaphore_s *_dsema; + struct dispatch_data_s *_ddata; + struct dispatch_io_s *_dchannel; + struct dispatch_operation_s *_doperation; + struct dispatch_disk_s *_ddisk; } dispatch_object_t __attribute__((transparent_union)); #endif typedef void (*dispatch_function_t)(void *); #ifdef __cplusplus -#define DISPATCH_DECL(name) typedef struct name##_s : public dispatch_object_s {} *name##_t; +#define DISPATCH_DECL(name) \ + typedef struct name##_s : public dispatch_object_s {} *name##_t #else /*! @parseOnly */ -#define DISPATCH_DECL(name) typedef struct name##_s *name##_t; +#define DISPATCH_DECL(name) typedef struct name##_s *name##_t #endif -#ifdef __GNUC__ +#if __GNUC__ #define DISPATCH_NORETURN __attribute__((__noreturn__)) #define DISPATCH_NOTHROW __attribute__((__nothrow__)) #define DISPATCH_NONNULL1 __attribute__((__nonnull__(1))) @@ -69,7 +74,7 @@ typedef void (*dispatch_function_t)(void *); #define DISPATCH_NONNULL5 __attribute__((__nonnull__(5))) #define DISPATCH_NONNULL6 __attribute__((__nonnull__(6))) #define DISPATCH_NONNULL7 __attribute__((__nonnull__(7))) -#if __clang__ +#if __clang__ && __clang_major__ < 3 // rdar://problem/6857843 #define DISPATCH_NONNULL_ALL #else @@ -77,8 +82,10 @@ typedef void (*dispatch_function_t)(void *); #endif #define DISPATCH_SENTINEL __attribute__((__sentinel__)) #define DISPATCH_PURE __attribute__((__pure__)) +#define DISPATCH_CONST __attribute__((__const__)) #define DISPATCH_WARN_RESULT __attribute__((__warn_unused_result__)) #define DISPATCH_MALLOC __attribute__((__malloc__)) +#define DISPATCH_ALWAYS_INLINE __attribute__((__always_inline__)) #else /*! @parseOnly */ #define DISPATCH_NORETURN @@ -105,9 +112,31 @@ typedef void (*dispatch_function_t)(void *); /*! @parseOnly */ #define DISPATCH_PURE /*! @parseOnly */ +#define DISPATCH_CONST +/*! @parseOnly */ #define DISPATCH_WARN_RESULT /*! @parseOnly */ #define DISPATCH_MALLOC +/*! @parseOnly */ +#define DISPATCH_ALWAYS_INLINE +#endif + +#if __GNUC__ +#define DISPATCH_EXPORT extern __attribute__((visibility("default"))) +#else +#define DISPATCH_EXPORT extern +#endif + +#if __GNUC__ +#define DISPATCH_INLINE static __inline__ +#else +#define DISPATCH_INLINE static inline +#endif + +#if __GNUC__ +#define DISPATCH_EXPECT(x, v) __builtin_expect((x), (v)) +#else +#define DISPATCH_EXPECT(x, v) (x) #endif #endif diff --git a/dispatch/data.h b/dispatch/data.h new file mode 100644 index 0000000..2222e1b --- /dev/null +++ b/dispatch/data.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2009-2011 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@ + */ + +#ifndef __DISPATCH_DATA__ +#define __DISPATCH_DATA__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +__BEGIN_DECLS + +/*! @header + * Dispatch data objects describe contiguous or sparse regions of memory that + * may be managed by the system or by the application. + * Dispatch data objects are immutable, any direct access to memory regions + * represented by dispatch objects must not modify that memory. + */ + +/*! + * @typedef dispatch_data_t + * A dispatch object representing memory regions. + */ +DISPATCH_DECL(dispatch_data); + +/*! + * @var dispatch_data_empty + * @discussion The singleton dispatch data object representing a zero-length + * memory region. + */ +#define dispatch_data_empty (&_dispatch_data_empty) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT struct dispatch_data_s _dispatch_data_empty; + +#ifdef __BLOCKS__ + +/*! + * @const DISPATCH_DATA_DESTRUCTOR_DEFAULT + * @discussion The default destructor for dispatch data objects. + * Used at data object creation to indicate that the supplied buffer should + * be copied into internal storage managed by the system. + */ +#define DISPATCH_DATA_DESTRUCTOR_DEFAULT NULL + +/*! + * @const DISPATCH_DATA_DESTRUCTOR_FREE + * @discussion The destructor for dispatch data objects created from a malloc'd + * buffer. Used at data object creation to indicate that the supplied buffer + * was allocated by the malloc() family and should be destroyed with free(3). + */ +#define DISPATCH_DATA_DESTRUCTOR_FREE (_dispatch_data_destructor_free) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT const dispatch_block_t _dispatch_data_destructor_free; + +/*! + * @function dispatch_data_create + * Creates a dispatch data object from the given contiguous buffer of memory. If + * a non-default destructor is provided, ownership of the buffer remains with + * the caller (i.e. the bytes will not be copied). The last release of the data + * object will result in the invocation of the specified destructor on the + * specified queue to free the buffer. + * + * If the DISPATCH_DATA_DESTRUCTOR_FREE destructor is provided the buffer will + * be freed via free(3) and the queue argument ignored. + * + * If the DISPATCH_DATA_DESTRUCTOR_DEFAULT destructor is provided, data object + * creation will copy the buffer into internal memory managed by the system. + * + * @param buffer A contiguous buffer of data. + * @param size The size of the contiguous buffer of data. + * @param queue The queue to which the destructor should be submitted. + * @param destructor The destructor responsible for freeing the data when it + * is no longer needed. + * @result A newly created dispatch data object. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_data_t +dispatch_data_create(const void *buffer, + size_t size, + dispatch_queue_t queue, + dispatch_block_t destructor); + +/*! + * @function dispatch_data_get_size + * Returns the logical size of the memory region(s) represented by the specified + * dispatch data object. + * + * @param data The dispatch data object to query. + * @result The number of bytes represented by the data object. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_PURE DISPATCH_NONNULL1 DISPATCH_NOTHROW +size_t +dispatch_data_get_size(dispatch_data_t data); + +/*! + * @function dispatch_data_create_map + * Maps the memory represented by the specified dispatch data object as a single + * contiguous memory region and returns a new data object representing it. + * If non-NULL references to a pointer and a size variable are provided, they + * are filled with the location and extent of that region. These allow direct + * read access to the represented memory, but are only valid until the copy + * object is released. + * + * @param data The dispatch data object to map. + * @param buffer_ptr A pointer to a pointer variable to be filled with the + * location of the mapped contiguous memory region, or + * NULL. + * @param size_ptr A pointer to a size_t variable to be filled with the + * size of the mapped contiguous memory region, or NULL. + * @result A newly created dispatch data object. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_data_t +dispatch_data_create_map(dispatch_data_t data, + const void **buffer_ptr, + size_t *size_ptr); + +/*! + * @function dispatch_data_create_concat + * Returns a new dispatch data object representing the concatenation of the + * specified data objects. Those objects may be released by the application + * after the call returns (however, the system might not deallocate the memory + * region(s) described by them until the newly created object has also been + * released). + * + * @param data1 The data object representing the region(s) of memory to place + * at the beginning of the newly created object. + * @param data2 The data object representing the region(s) of memory to place + * at the end of the newly created object. + * @result A newly created object representing the concatenation of the + * data1 and data2 objects. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_data_t +dispatch_data_create_concat(dispatch_data_t data1, dispatch_data_t data2); + +/*! + * @function dispatch_data_create_subrange + * Returns a new dispatch data object representing a subrange of the specified + * data object, which may be released by the application after the call returns + * (however, the system might not deallocate the memory region(s) described by + * that object until the newly created object has also been released). + * + * @param data The data object representing the region(s) of memory to + * create a subrange of. + * @param offset The offset into the data object where the subrange + * starts. + * @param length The length of the range. + * @result A newly created object representing the specified + * subrange of the data object. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_data_t +dispatch_data_create_subrange(dispatch_data_t data, + size_t offset, + size_t length); + +/*! + * @typedef dispatch_data_applier_t + * A block to be invoked for every contiguous memory region in a data object. + * + * @param region A data object representing the current region. + * @param offset The logical offset of the current region to the start + * of the data object. + * @param buffer The location of the memory for the current region. + * @param size The size of the memory for the current region. + * @result A Boolean indicating whether traversal should continue. + */ +typedef bool (^dispatch_data_applier_t)(dispatch_data_t region, + size_t offset, + const void *buffer, + size_t size); + +/*! + * @function dispatch_data_apply + * Traverse the memory regions represented by the specified dispatch data object + * in logical order and invoke the specified block once for every contiguous + * memory region encountered. + * + * Each invocation of the block is passed a data object representing the current + * region and its logical offset, along with the memory location and extent of + * the region. These allow direct read access to the memory region, but are only + * valid until the passed-in region object is released. Note that the region + * object is released by the system when the block returns, it is the + * responsibility of the application to retain it if the region object or the + * associated memory location are needed after the block returns. + * + * @param data The data object to traverse. + * @param applier The block to be invoked for every contiguous memory + * region in the data object. + * @result A Boolean indicating whether traversal completed + * successfully. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +bool +dispatch_data_apply(dispatch_data_t data, dispatch_data_applier_t applier); + +/*! + * @function dispatch_data_copy_region + * Finds the contiguous memory region containing the specified location among + * the regions represented by the specified object and returns a copy of the + * internal dispatch data object representing that region along with its logical + * offset in the specified object. + * + * @param data The dispatch data object to query. + * @param location The logical position in the data object to query. + * @param offset_ptr A pointer to a size_t variable to be filled with the + * logical offset of the returned region object to the + * start of the queried data object. + * @result A newly created dispatch data object. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_WARN_RESULT +DISPATCH_NOTHROW +dispatch_data_t +dispatch_data_copy_region(dispatch_data_t data, + size_t location, + size_t *offset_ptr); + +#endif /* __BLOCKS__ */ + +__END_DECLS + +#endif /* __DISPATCH_DATA__ */ diff --git a/src/dispatch.h b/dispatch/dispatch.h similarity index 79% rename from src/dispatch.h rename to dispatch/dispatch.h index 95331d7..2ba2cce 100644 --- a/src/dispatch.h +++ b/dispatch/dispatch.h @@ -1,34 +1,42 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ #ifndef __DISPATCH_PUBLIC__ #define __DISPATCH_PUBLIC__ +#ifdef __APPLE__ #include +#include +#endif #include #include #include #include #include +#include + +#ifndef __OSX_AVAILABLE_STARTING +#define __OSX_AVAILABLE_STARTING(x, y) +#endif -#define DISPATCH_API_VERSION 20090501 +#define DISPATCH_API_VERSION 20110201 #ifndef __DISPATCH_BUILDING_DISPATCH__ @@ -44,6 +52,8 @@ #include #include #include +#include +#include #undef __DISPATCH_INDIRECT__ diff --git a/src/group.h b/dispatch/group.h similarity index 83% rename from src/group.h rename to dispatch/group.h index a282948..4e6e11d 100644 --- a/src/group.h +++ b/dispatch/group.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -40,7 +40,7 @@ __BEGIN_DECLS * * @abstract * Creates new group with which blocks may be associated. - * + * * @discussion * This function creates a new group with which blocks may be associated. * The dispatch group may be used to wait for the completion of the blocks it @@ -49,8 +49,8 @@ __BEGIN_DECLS * @result * The newly created group, or NULL on failure. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_WARN_RESULT +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_group_t dispatch_group_create(void); @@ -78,8 +78,8 @@ dispatch_group_create(void); * The block to perform asynchronously. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, @@ -112,8 +112,8 @@ dispatch_group_async(dispatch_group_t group, * parameter passed to this function is the context provided to * dispatch_group_async_f(). */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NONNULL4 +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t queue, @@ -124,11 +124,11 @@ dispatch_group_async_f(dispatch_group_t group, * @function dispatch_group_wait * * @abstract - * Wait synchronously for the previously submitted blocks to complete; - * returns if the blocks have not completed within the specified timeout. + * Wait synchronously until all the blocks associated with a group have + * completed or until the specified timeout has elapsed. * * @discussion - * This function waits for the completion of the blocks associated with the + * This function waits for the completion of the blocks associated with the * given dispatch group, and returns after all blocks have completed or when * the specified timeout has elapsed. When a timeout occurs, the group is * restored to its original state. @@ -155,8 +155,8 @@ dispatch_group_async_f(dispatch_group_t group, * Returns zero on success (all blocks associated with the group completed * within the specified timeout) or non-zero on error (i.e. timed out). */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); @@ -164,8 +164,8 @@ dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); * @function dispatch_group_notify * * @abstract - * Schedule a block to be submitted to a queue when a group of previously - * submitted blocks have completed. + * Schedule a block to be submitted to a queue when all the blocks associated + * with a group have completed. * * @discussion * This function schedules a notification block to be submitted to the specified @@ -173,7 +173,7 @@ dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); * * If no blocks are associated with the dispatch group (i.e. the group is empty) * then the notification block will be submitted immediately. - * + * * The group will be empty at the time the notification block is submitted to * the target queue. The group may either be released with dispatch_release() * or reused for additional operations. @@ -191,8 +191,8 @@ dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); * The block to submit when the group completes. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, @@ -203,8 +203,8 @@ dispatch_group_notify(dispatch_group_t group, * @function dispatch_group_notify_f * * @abstract - * Schedule a function to be submitted to a queue when a group of previously - * submitted functions have completed. + * Schedule a function to be submitted to a queue when all the blocks + * associated with a group have completed. * * @discussion * See dispatch_group_notify() for details. @@ -221,8 +221,9 @@ dispatch_group_notify(dispatch_group_t group, * parameter passed to this function is the context provided to * dispatch_group_notify_f(). */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NONNULL4 +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NONNULL4 +DISPATCH_NOTHROW void dispatch_group_notify_f(dispatch_group_t group, dispatch_queue_t queue, @@ -244,8 +245,8 @@ dispatch_group_notify_f(dispatch_group_t group, * The dispatch group to update. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW DISPATCH_NONNULL_ALL +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_group_enter(dispatch_group_t group); @@ -263,8 +264,8 @@ dispatch_group_enter(dispatch_group_t group); * The dispatch group to update. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW DISPATCH_NONNULL_ALL +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_group_leave(dispatch_group_t group); diff --git a/dispatch/io.h b/dispatch/io.h new file mode 100644 index 0000000..f8fb2ff --- /dev/null +++ b/dispatch/io.h @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2009-2010 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@ + */ + +#ifndef __DISPATCH_IO__ +#define __DISPATCH_IO__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +__BEGIN_DECLS + +/*! @header + * Dispatch I/O provides both stream and random access asynchronous read and + * write operations on file descriptors. One or more dispatch I/O channels may + * be created from a file descriptor as either the DISPATCH_IO_STREAM type or + * DISPATCH_IO_RANDOM type. Once a channel has been created the application may + * schedule asynchronous read and write operations. + * + * The application may set policies on the dispatch I/O channel to indicate the + * desired frequency of I/O handlers for long-running operations. + * + * Dispatch I/O also provides a memory managment model for I/O buffers that + * avoids unnecessary copying of data when pipelined between channels. Dispatch + * I/O monitors the overall memory pressure and I/O access patterns for the + * application to optimize resource utilization. + */ + +/*! + * @typedef dispatch_fd_t + * Native file descriptor type for the platform. + */ +typedef int dispatch_fd_t; + +#ifdef __BLOCKS__ + +/*! + * @functiongroup Dispatch I/O Convenience API + * Convenience wrappers around the dispatch I/O channel API, with simpler + * callback handler semantics and no explicit management of channel objects. + * File descriptors passed to the convenience API are treated as streams, and + * scheduling multiple operations on one file descriptor via the convenience API + * may incur more overhead than by using the dispatch I/O channel API directly. + */ + +/*! + * @function dispatch_read + * Schedule a read operation for asynchronous execution on the specified file + * descriptor. The specified handler is enqueued with the data read from the + * file descriptor when the operation has completed or an error occurs. + * + * The data object passed to the handler will be automatically released by the + * system when the handler returns. It is the responsibility of the application + * to retain, concatenate or copy the data object if it is needed after the + * handler returns. + * + * The data object passed to the handler will only contain as much data as is + * currently available from the file descriptor (up to the specified length). + * + * If an unrecoverable error occurs on the file descriptor, the handler will be + * enqueued with the appropriate error code along with a data object of any data + * that could be read successfully. + * + * An invocation of the handler with an error code of zero and an empty data + * object indicates that EOF was reached. + * + * The system takes control of the file descriptor until the handler is + * enqueued, and during this time file descriptor flags such as O_NONBLOCK will + * be modified by the system on behalf of the application. It is an error for + * the application to modify a file descriptor directly while it is under the + * control of the system, but it may create additional dispatch I/O convenience + * operations or dispatch I/O channels associated with that file descriptor. + * + * @param fd The file descriptor from which to read the data. + * @param length The length of data to read from the file descriptor, + * or SIZE_MAX to indicate that all of the data currently + * available from the file descriptor should be read. + * @param queue The dispatch queue to which the handler should be + * submitted. + * @param handler The handler to enqueue when data is ready to be + * delivered. + * @param data The data read from the file descriptor. + * @param error An errno condition for the read operation or + * zero if the read was successful. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL3 DISPATCH_NONNULL4 DISPATCH_NOTHROW +void +dispatch_read(dispatch_fd_t fd, + size_t length, + dispatch_queue_t queue, + void (^handler)(dispatch_data_t data, int error)); + +/*! + * @function dispatch_write + * Schedule a write operation for asynchronous execution on the specified file + * descriptor. The specified handler is enqueued when the operation has + * completed or an error occurs. + * + * If an unrecoverable error occurs on the file descriptor, the handler will be + * enqueued with the appropriate error code along with the data that could not + * be successfully written. + * + * An invocation of the handler with an error code of zero indicates that the + * data was fully written to the channel. + * + * The system takes control of the file descriptor until the handler is + * enqueued, and during this time file descriptor flags such as O_NONBLOCK will + * be modified by the system on behalf of the application. It is an error for + * the application to modify a file descriptor directly while it is under the + * control of the system, but it may create additional dispatch I/O convenience + * operations or dispatch I/O channels associated with that file descriptor. + * + * @param fd The file descriptor to which to write the data. + * @param data The data object to write to the file descriptor. + * @param queue The dispatch queue to which the handler should be + * submitted. + * @param handler The handler to enqueue when the data has been written. + * @param data The data that could not be written to the I/O + * channel, or NULL. + * @param error An errno condition for the write operation or + * zero if the write was successful. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_NONNULL3 DISPATCH_NONNULL4 +DISPATCH_NOTHROW +void +dispatch_write(dispatch_fd_t fd, + dispatch_data_t data, + dispatch_queue_t queue, + void (^handler)(dispatch_data_t data, int error)); + +/*! + * @functiongroup Dispatch I/O Channel API + */ + +/*! + * @typedef dispatch_io_t + * A dispatch I/O channel represents the asynchronous I/O policy applied to a + * file descriptor. I/O channels are first class dispatch objects and may be + * retained and released, suspended and resumed, etc. + */ +DISPATCH_DECL(dispatch_io); + +/*! + * @typedef dispatch_io_handler_t + * The prototype of I/O handler blocks for dispatch I/O operations. + * + * @param done A flag indicating whether the operation is complete. + * @param data The data object to be handled. + * @param error An errno condition for the operation. + */ +typedef void (^dispatch_io_handler_t)(bool done, dispatch_data_t data, + int error); + +/*! + * @typedef dispatch_io_type_t + * The type of a dispatch I/O channel: + * + * @const DISPATCH_IO_STREAM A dispatch I/O channel representing a stream of + * bytes. Read and write operations on a channel of this type are performed + * serially (in order of creation) and read/write data at the file pointer + * position that is current at the time the operation starts executing. + * Operations of different type (read vs. write) may be perfomed simultaneously. + * Offsets passed to operations on a channel of this type are ignored. + * + * @const DISPATCH_IO_RANDOM A dispatch I/O channel representing a random + * access file. Read and write operations on a channel of this type may be + * performed concurrently and read/write data at the specified offset. Offsets + * are interpreted relative to the file pointer position current at the time the + * I/O channel is created. Attempting to create a channel of this type for a + * file descriptor that is not seekable will result in an error. + */ +#define DISPATCH_IO_STREAM 0 +#define DISPATCH_IO_RANDOM 1 + +typedef unsigned long dispatch_io_type_t; + +/*! + * @function dispatch_io_create + * Create a dispatch I/O channel associated with a file descriptor. The system + * takes control of the file descriptor until the channel is closed, an error + * occurs on the file descriptor or all references to the channel are released. + * At that time the specified cleanup handler will be enqueued and control over + * the file descriptor relinquished. + * + * While a file descriptor is under the control of a dispatch I/O channel, file + * descriptor flags such as O_NONBLOCK will be modified by the system on behalf + * of the application. It is an error for the application to modify a file + * descriptor directly while it is under the control of a dispatch I/O channel, + * but it may create additional channels associated with that file descriptor. + * + * @param type The desired type of I/O channel (DISPATCH_IO_STREAM + * or DISPATCH_IO_RANDOM). + * @param fd The file descriptor to associate with the I/O channel. + * @param queue The dispatch queue to which the handler should be submitted. + * @param cleanup_handler The handler to enqueue when the system + * relinquishes control over the file descriptor. + * @param error An errno condition if control is relinquished + * because channel creation failed, zero otherwise. + * @result The newly created dispatch I/O channel or NULL if an error + * occurred. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_io_t +dispatch_io_create(dispatch_io_type_t type, + dispatch_fd_t fd, + dispatch_queue_t queue, + void (^cleanup_handler)(int error)); + +/*! +* @function dispatch_io_create_with_path +* Create a dispatch I/O channel associated with a path name. The specified +* path, oflag and mode parameters will be passed to open(2) when the first I/O +* operation on the channel is ready to execute and the resulting file +* descriptor will remain open and under the control of the system until the +* channel is closed, an error occurs on the file descriptor or all references +* to the channel are released. At that time the file descriptor will be closed +* and the specified cleanup handler will be enqueued. +* +* @param type The desired type of I/O channel (DISPATCH_IO_STREAM +* or DISPATCH_IO_RANDOM). +* @param path The path to associate with the I/O channel. +* @param oflag The flags to pass to open(2) when opening the file at +* path. +* @param mode The mode to pass to open(2) when creating the file at +* path (i.e. with flag O_CREAT), zero otherwise. +* @param queue The dispatch queue to which the handler should be +* submitted. +* @param cleanup_handler The handler to enqueue when the system +* has closed the file at path. +* @param error An errno condition if control is relinquished +* because channel creation or opening of the +* specified file failed, zero otherwise. +* @result The newly created dispatch I/O channel or NULL if an error +* occurred. +*/ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_NONNULL2 DISPATCH_WARN_RESULT +DISPATCH_NOTHROW +dispatch_io_t +dispatch_io_create_with_path(dispatch_io_type_t type, + const char *path, int oflag, mode_t mode, + dispatch_queue_t queue, + void (^cleanup_handler)(int error)); + +/*! + * @function dispatch_io_create_with_io + * Create a new dispatch I/O channel from an existing dispatch I/O channel. + * The new channel inherits the file descriptor or path name associated with + * the existing channel, but not its channel type or policies. + * + * If the existing channel is associated with a file descriptor, control by the + * system over that file descriptor is extended until the new channel is also + * closed, an error occurs on the file descriptor, or all references to both + * channels are released. At that time the specified cleanup handler will be + * enqueued and control over the file descriptor relinquished. + * + * While a file descriptor is under the control of a dispatch I/O channel, file + * descriptor flags such as O_NONBLOCK will be modified by the system on behalf + * of the application. It is an error for the application to modify a file + * descriptor directly while it is under the control of a dispatch I/O channel, + * but it may create additional channels associated with that file descriptor. + * + * @param type The desired type of I/O channel (DISPATCH_IO_STREAM + * or DISPATCH_IO_RANDOM). + * @param io The existing channel to create the new I/O channel from. + * @param queue The dispatch queue to which the handler should be submitted. + * @param cleanup_handler The handler to enqueue when the system + * relinquishes control over the file descriptor + * (resp. closes the file at path) associated with + * the existing channel. + * @param error An errno condition if control is relinquished + * because channel creation failed, zero otherwise. + * @result The newly created dispatch I/O channel or NULL if an error + * occurred. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_MALLOC DISPATCH_WARN_RESULT +DISPATCH_NOTHROW +dispatch_io_t +dispatch_io_create_with_io(dispatch_io_type_t type, + dispatch_io_t io, + dispatch_queue_t queue, + void (^cleanup_handler)(int error)); + +/*! + * @function dispatch_io_read + * Schedule a read operation for asynchronous execution on the specified I/O + * channel. The I/O handler is enqueued one or more times depending on the + * general load of the system and the policy specified on the I/O channel. + * + * Any data read from the channel is described by the dispatch data object + * passed to the I/O handler. This object will be automatically released by the + * system when the I/O handler returns. It is the responsibility of the + * application to retain, concatenate or copy the data object if it is needed + * after the I/O handler returns. + * + * Dispatch I/O handlers are not reentrant. The system will ensure that no new + * I/O handler instance is invoked until the previously enqueued handler block + * has returned. + * + * An invocation of the I/O handler with the done flag set indicates that the + * read operation is complete and that the handler will not be enqueued again. + * + * If an unrecoverable error occurs on the I/O channel's underlying file + * descriptor, the I/O handler will be enqueued with the done flag set, the + * appropriate error code and a NULL data object. + * + * An invocation of the I/O handler with the done flag set, an error code of + * zero and an empty data object indicates that EOF was reached. + * + * @param channel The dispatch I/O channel from which to read the data. + * @param offset The offset relative to the channel position from which + * to start reading (only for DISPATCH_IO_RANDOM). + * @param length The length of data to read from the I/O channel, or + * SIZE_MAX to indicate that data should be read until EOF + * is reached. + * @param queue The dispatch queue to which the I/O handler should be + * submitted. + * @param io_handler The I/O handler to enqueue when data is ready to be + * delivered. + * @param done A flag indicating whether the operation is complete. + * @param data An object with the data most recently read from the + * I/O channel as part of this read operation, or NULL. + * @param error An errno condition for the read operation or zero if + * the read was successful. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL4 DISPATCH_NONNULL5 +DISPATCH_NOTHROW +void +dispatch_io_read(dispatch_io_t channel, + off_t offset, + size_t length, + dispatch_queue_t queue, + dispatch_io_handler_t io_handler); + +/*! + * @function dispatch_io_write + * Schedule a write operation for asynchronous execution on the specified I/O + * channel. The I/O handler is enqueued one or more times depending on the + * general load of the system and the policy specified on the I/O channel. + * + * Any data remaining to be written to the I/O channel is described by the + * dispatch data object passed to the I/O handler. This object will be + * automatically released by the system when the I/O handler returns. It is the + * responsibility of the application to retain, concatenate or copy the data + * object if it is needed after the I/O handler returns. + * + * Dispatch I/O handlers are not reentrant. The system will ensure that no new + * I/O handler instance is invoked until the previously enqueued handler block + * has returned. + * + * An invocation of the I/O handler with the done flag set indicates that the + * write operation is complete and that the handler will not be enqueued again. + * + * If an unrecoverable error occurs on the I/O channel's underlying file + * descriptor, the I/O handler will be enqueued with the done flag set, the + * appropriate error code and an object containing the data that could not be + * written. + * + * An invocation of the I/O handler with the done flag set and an error code of + * zero indicates that the data was fully written to the channel. + * + * @param channel The dispatch I/O channel on which to write the data. + * @param offset The offset relative to the channel position from which + * to start writing (only for DISPATCH_IO_RANDOM). + * @param data The data to write to the I/O channel. The data object + * will be retained by the system until the write operation + * is complete. + * @param queue The dispatch queue to which the I/O handler should be + * submitted. + * @param io_handler The I/O handler to enqueue when data has been delivered. + * @param done A flag indicating whether the operation is complete. + * @param data An object of the data remaining to be + * written to the I/O channel as part of this write + * operation, or NULL. + * @param error An errno condition for the write operation or zero + * if the write was successful. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NONNULL4 +DISPATCH_NONNULL5 DISPATCH_NOTHROW +void +dispatch_io_write(dispatch_io_t channel, + off_t offset, + dispatch_data_t data, + dispatch_queue_t queue, + dispatch_io_handler_t io_handler); + +/*! + * @typedef dispatch_io_close_flags_t + * The type of flags you can set on a dispatch_io_close() call + * + * @const DISPATCH_IO_STOP Stop outstanding operations on a channel when + * the channel is closed. + */ +#define DISPATCH_IO_STOP 0x1 + +typedef unsigned long dispatch_io_close_flags_t; + +/*! + * @function dispatch_io_close + * Close the specified I/O channel to new read or write operations; scheduling + * operations on a closed channel results in their handler returning an error. + * + * If the DISPATCH_IO_STOP flag is provided, the system will make a best effort + * to interrupt any outstanding read and write operations on the I/O channel, + * otherwise those operations will run to completion normally. + * Partial results of read and write operations may be returned even after a + * channel is closed with the DISPATCH_IO_STOP flag. + * The final invocation of an I/O handler of an interrupted operation will be + * passed an ECANCELED error code, as will the I/O handler of an operation + * scheduled on a closed channel. + * + * @param channel The dispatch I/O channel to close. + * @param flags The flags for the close operation. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_io_close(dispatch_io_t channel, dispatch_io_close_flags_t flags); + +/*! + * @function dispatch_io_barrier + * Schedule a barrier operation on the specified I/O channel; all previously + * scheduled operations on the channel will complete before the provided + * barrier block is enqueued onto the global queue determined by the channel's + * target queue, and no subsequently scheduled operations will start until the + * barrier block has returned. + * + * If multiple channels are associated with the same file descriptor, a barrier + * operation scheduled on any of these channels will act as a barrier across all + * channels in question, i.e. all previously scheduled operations on any of the + * channels will complete before the barrier block is enqueued, and no + * operations subsequently scheduled on any of the channels will start until the + * barrier block has returned. + * + * While the barrier block is running, it may safely operate on the channel's + * underlying file descriptor with fsync(2), lseek(2) etc. (but not close(2)). + * + * @param channel The dispatch I/O channel to close. + * @param barrier The flags for the close operation. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_io_barrier(dispatch_io_t channel, dispatch_block_t barrier); + +/*! + * @function dispatch_io_get_descriptor + * Returns the file descriptor underlying a dispatch I/O channel. + * + * Will return -1 for a channel closed with dispatch_io_close() and for a + * channel associated with a path name that has not yet been open(2)ed. + * + * If called from a barrier block scheduled on a channel associated with a path + * name that has not yet been open(2)ed, this will trigger the channel open(2) + * operation and return the resulting file descriptor. + * + * @param channel The dispatch I/O channel to query. + * @result The file descriptor underlying the channel, or -1. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_fd_t +dispatch_io_get_descriptor(dispatch_io_t channel); + +/*! + * @function dispatch_io_set_high_water + * Set a high water mark on the I/O channel for all operations. + * + * The system will make a best effort to enqueue I/O handlers with partial + * results as soon the number of bytes processed by an operation (i.e. read or + * written) reaches the high water mark. + * + * The size of data objects passed to I/O handlers for this channel will never + * exceed the specified high water mark. + * + * The default value for the high water mark is unlimited (i.e. SIZE_MAX). + * + * @param channel The dispatch I/O channel on which to set the policy. + * @param high_water The number of bytes to use as a high water mark. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_io_set_high_water(dispatch_io_t channel, size_t high_water); + +/*! + * @function dispatch_io_set_low_water + * Set a low water mark on the I/O channel for all operations. + * + * The system will process (i.e. read or write) at least the low water mark + * number of bytes for an operation before enqueueing I/O handlers with partial + * results. + * + * The size of data objects passed to intermediate I/O handler invocations for + * this channel (i.e. excluding the final invocation) will never be smaller than + * the specified low water mark, except if the channel has an interval with the + * DISPATCH_IO_STRICT_INTERVAL flag set or if EOF or an error was encountered. + * + * I/O handlers should be prepared to receive amounts of data significantly + * larger than the low water mark in general. If an I/O handler requires + * intermediate results of fixed size, set both the low and and the high water + * mark to that size. + * + * The default value for the low water mark is unspecified, but must be assumed + * to be such that intermediate handler invocations may occur. + * If I/O handler invocations with partial results are not desired, set the + * low water mark to SIZE_MAX. + * + * @param channel The dispatch I/O channel on which to set the policy. + * @param low_water The number of bytes to use as a low water mark. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_io_set_low_water(dispatch_io_t channel, size_t low_water); + +/*! + * @typedef dispatch_io_interval_flags_t + * Type of flags to set on dispatch_io_set_interval() + * + * @const DISPATCH_IO_STRICT_INTERVAL Enqueue I/O handlers at a channel's + * interval setting even if the amount of data ready to be delivered is inferior + * to the low water mark (or zero). + */ +#define DISPATCH_IO_STRICT_INTERVAL 0x1 + +typedef unsigned long dispatch_io_interval_flags_t; + +/*! + * @function dispatch_io_set_interval + * Set a nanosecond interval at which I/O handlers are to be enqueued on the + * I/O channel for all operations. + * + * This allows an application to receive periodic feedback on the progress of + * read and write operations, e.g. for the purposes of displaying progress bars. + * + * If the amount of data ready to be delivered to an I/O handler at the interval + * is inferior to the channel low water mark, the handler will only be enqueued + * if the DISPATCH_IO_STRICT_INTERVAL flag is set. + * + * Note that the system may defer enqueueing interval I/O handlers by a small + * unspecified amount of leeway in order to align with other system activity for + * improved system performance or power consumption. + * + * @param channel The dispatch I/O channel on which to set the policy. + * @param interval The interval in nanoseconds at which delivery of the I/O + * handler is desired. + * @param flags Flags indicating desired data delivery behavior at + * interval time. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_io_set_interval(dispatch_io_t channel, + uint64_t interval, + dispatch_io_interval_flags_t flags); + +#endif /* __BLOCKS__ */ + +__END_DECLS + +#endif /* __DISPATCH_IO__ */ diff --git a/src/object.h b/dispatch/object.h similarity index 74% rename from src/object.h rename to dispatch/object.h index febc960..2ecf251 100644 --- a/src/object.h +++ b/dispatch/object.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 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@ */ @@ -34,19 +34,28 @@ __BEGIN_DECLS * @abstract * Programmatically log debug information about a dispatch object. * + * @discussion + * Programmatically log debug information about a dispatch object. By default, + * the log output is sent to syslog at notice level. In the debug version of + * the library, the log output is sent to a file in /var/tmp. + * The log output destination can be configured via the LIBDISPATCH_LOG + * environment variable, valid values are: YES, NO, syslog, stderr, file. + * * @param object * The object to introspect. * * @param message * The message to log above and beyond the introspection. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL2 DISPATCH_NOTHROW __attribute__((__format__(printf,2,3))) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_NOTHROW +__attribute__((__format__(printf,2,3))) void dispatch_debug(dispatch_object_t object, const char *message, ...); -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL2 DISPATCH_NOTHROW __attribute__((__format__(printf,2,0))) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_NOTHROW +__attribute__((__format__(printf,2,0))) void dispatch_debugv(dispatch_object_t object, const char *message, va_list ap); @@ -64,8 +73,8 @@ dispatch_debugv(dispatch_object_t object, const char *message, va_list ap); * The object to retain. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_retain(dispatch_object_t object); @@ -85,8 +94,8 @@ dispatch_retain(dispatch_object_t object); * The object to release. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_release(dispatch_object_t object); @@ -102,8 +111,9 @@ dispatch_release(dispatch_object_t object); * @result * The context of the object; may be NULL. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_PURE DISPATCH_WARN_RESULT +DISPATCH_NOTHROW void * dispatch_get_context(dispatch_object_t object); @@ -120,8 +130,8 @@ dispatch_get_context(dispatch_object_t object); * The new client defined context for the object. This may be NULL. * */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW //DISPATCH_NONNULL1 +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NOTHROW //DISPATCH_NONNULL1 void dispatch_set_context(dispatch_object_t object, void *context); @@ -146,8 +156,8 @@ dispatch_set_context(dispatch_object_t object, void *context); * The context parameter passed to the finalizer function is the current * context of the dispatch object at the time the finalizer call is made. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW //DISPATCH_NONNULL1 +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NOTHROW //DISPATCH_NONNULL1 void dispatch_set_finalizer_f(dispatch_object_t object, dispatch_function_t finalizer); @@ -166,12 +176,12 @@ dispatch_set_finalizer_f(dispatch_object_t object, * Calls to dispatch_suspend() must be balanced with calls * to dispatch_resume(). * - * @param object + * @param object * The object to be suspended. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_suspend(dispatch_object_t object); @@ -181,12 +191,12 @@ dispatch_suspend(dispatch_object_t object); * @abstract * Resumes the invocation of blocks on a dispatch object. * - * @param object + * @param object * The object to be resumed. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_resume(dispatch_object_t object); diff --git a/src/once.h b/dispatch/once.h similarity index 60% rename from src/once.h rename to dispatch/once.h index 8cd25d6..32cf2e8 100644 --- a/src/once.h +++ b/dispatch/once.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 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@ */ @@ -55,23 +55,42 @@ typedef long dispatch_once_t; * initialized by the block. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block); -#ifdef __GNUC__ -#define dispatch_once(x, ...) do { if (__builtin_expect(*(x), ~0l) != ~0l) dispatch_once((x), (__VA_ARGS__)); } while (0) -#endif -#endif -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void -dispatch_once_f(dispatch_once_t *predicate, void *context, void (*function)(void *)); -#ifdef __GNUC__ -#define dispatch_once_f(x, y, z) do { if (__builtin_expect(*(x), ~0l) != ~0l) dispatch_once_f((x), (y), (z)); } while (0) +_dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) +{ + if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) { + dispatch_once(predicate, block); + } +} +#undef dispatch_once +#define dispatch_once _dispatch_once #endif +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_once_f(dispatch_once_t *predicate, void *context, + dispatch_function_t function); + +DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL1 DISPATCH_NONNULL3 +DISPATCH_NOTHROW +void +_dispatch_once_f(dispatch_once_t *predicate, void *context, + dispatch_function_t function) +{ + if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) { + dispatch_once_f(predicate, context, function); + } +} +#undef dispatch_once_f +#define dispatch_once_f _dispatch_once_f + __END_DECLS #endif diff --git a/src/queue.h b/dispatch/queue.h similarity index 51% rename from src/queue.h rename to dispatch/queue.h index 6b55696..d767771 100644 --- a/src/queue.h +++ b/dispatch/queue.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -30,7 +30,7 @@ * @header * * Dispatch is an abstract model for expressing concurrency via simple but - * powerful API. + * powerful API. * * At the core, dispatch provides serial FIFO queues to which blocks may be * submitted. Blocks submitted to these dispatch queues are invoked on a pool @@ -70,7 +70,7 @@ DISPATCH_DECL(dispatch_queue); * @typedef dispatch_queue_attr_t * * @abstract - * Attribute and policy extensions for dispatch queues. + * Attribute for dispatch queues. */ DISPATCH_DECL(dispatch_queue_attr); @@ -128,8 +128,8 @@ __BEGIN_DECLS * * The target queue determines whether the block will be invoked serially or * concurrently with respect to other blocks submitted to that same queue. - * Serial queues are processed concurrently with with respect to each other. - * + * Serial queues are processed concurrently with respect to each other. + * * @param queue * The target dispatch queue to which the block is submitted. * The system will hold a reference on the target queue until the block @@ -142,8 +142,8 @@ __BEGIN_DECLS * The result of passing NULL in this parameter is undefined. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); #endif @@ -156,7 +156,7 @@ dispatch_async(dispatch_queue_t queue, dispatch_block_t block); * * @discussion * See dispatch_async() for details. - * + * * @param queue * The target dispatch queue to which the function is submitted. * The system will hold a reference on the target queue until the function @@ -172,8 +172,8 @@ dispatch_async(dispatch_queue_t queue, dispatch_block_t block); * dispatch_async_f(). * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW void dispatch_async_f(dispatch_queue_t queue, void *context, @@ -210,8 +210,8 @@ dispatch_async_f(dispatch_queue_t queue, * The result of passing NULL in this parameter is undefined. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); #endif @@ -238,8 +238,8 @@ dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); * dispatch_sync_f(). * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW void dispatch_sync_f(dispatch_queue_t queue, void *context, @@ -254,9 +254,9 @@ dispatch_sync_f(dispatch_queue_t queue, * @discussion * Submits a block to a dispatch queue for multiple invocations. This function * waits for the task block to complete before returning. If the target queue - * is a concurrent queue returned by dispatch_get_concurrent_queue(), the block - * may be invoked concurrently, and it must therefore be reentrant safe. - * + * is concurrent, the block may be invoked concurrently, and it must therefore + * be reentrant safe. + * * Each invocation of the block will be passed the current index of iteration. * * @param iterations @@ -271,10 +271,11 @@ dispatch_sync_f(dispatch_queue_t queue, * The result of passing NULL in this parameter is undefined. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void -dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); +dispatch_apply(size_t iterations, dispatch_queue_t queue, + void (^block)(size_t)); #endif /*! @@ -303,8 +304,8 @@ dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)) * current index of iteration. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW void dispatch_apply_f(size_t iterations, dispatch_queue_t queue, void *context, @@ -315,18 +316,25 @@ dispatch_apply_f(size_t iterations, dispatch_queue_t queue, * * @abstract * Returns the queue on which the currently executing block is running. - * + * * @discussion * Returns the queue on which the currently executing block is running. * * When dispatch_get_current_queue() is called outside of the context of a * submitted block, it will return the default concurrent queue. * + * Recommended for debugging and logging purposes only: + * The code must not make any assumptions about the queue returned, unless it + * is one of the global queues or a queue the code has itself created. + * The code must not assume that synchronous execution onto a queue is safe + * from deadlock if that queue is not the one returned by + * dispatch_get_current_queue(). + * * @result * Returns the current queue. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_queue_t dispatch_get_current_queue(void); @@ -345,12 +353,13 @@ dispatch_get_current_queue(void); * Returns the main queue. This queue is created automatically on behalf of * the main thread before main() is called. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern struct dispatch_queue_s _dispatch_main_q; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT struct dispatch_queue_s _dispatch_main_q; #define dispatch_get_main_queue() (&_dispatch_main_q) /*! - * @enum dispatch_queue_priority_t + * @typedef dispatch_queue_priority_t + * Type of dispatch_queue_priority * * @constant DISPATCH_QUEUE_PRIORITY_HIGH * Items dispatched to the queue will run at high priority, @@ -368,12 +377,20 @@ extern struct dispatch_queue_s _dispatch_main_q; * i.e. the queue will be scheduled for execution after all * default priority and high priority queues have been * scheduled. + * + * @constant DISPATCH_QUEUE_PRIORITY_BACKGROUND + * Items dispatched to the queue will run at background priority, i.e. the queue + * will be scheduled for execution after all higher priority queues have been + * scheduled and the system will run items on this queue on a thread with + * background status as per setpriority(2) (i.e. disk I/O is throttled and the + * thread's scheduling priority is set to lowest value). */ -enum { - DISPATCH_QUEUE_PRIORITY_HIGH = 2, - DISPATCH_QUEUE_PRIORITY_DEFAULT = 0, - DISPATCH_QUEUE_PRIORITY_LOW = -2, -}; +#define DISPATCH_QUEUE_PRIORITY_HIGH 2 +#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 +#define DISPATCH_QUEUE_PRIORITY_LOW (-2) +#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN + +typedef long dispatch_queue_priority_t; /*! * @function dispatch_get_global_queue @@ -396,10 +413,27 @@ enum { * @result * Returns the requested global queue. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_CONST DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_queue_t -dispatch_get_global_queue(long priority, unsigned long flags); +dispatch_get_global_queue(dispatch_queue_priority_t priority, + unsigned long flags); + +/*! + * @const DISPATCH_QUEUE_SERIAL + * @discussion A dispatch queue that invokes blocks serially in FIFO order. + */ +#define DISPATCH_QUEUE_SERIAL NULL + +/*! + * @const DISPATCH_QUEUE_CONCURRENT + * @discussion A dispatch queue that may invoke blocks concurrently and supports + * barrier blocks submitted with the dispatch barrier API. + */ +#define DISPATCH_QUEUE_CONCURRENT (&_dispatch_queue_attr_concurrent) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT +struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent; /*! * @function dispatch_queue_create @@ -408,25 +442,35 @@ dispatch_get_global_queue(long priority, unsigned long flags); * Creates a new dispatch queue to which blocks may be submitted. * * @discussion - * Dispatch queues invoke blocks serially in FIFO order. + * Dispatch queues created with the DISPATCH_QUEUE_SERIAL or a NULL attribute + * invoke blocks serially in FIFO order. + * + * Dispatch queues created with the DISPATCH_QUEUE_CONCURRENT attribute may + * invoke blocks concurrently (similarly to the global concurrent queues, but + * potentially with more overhead), and support barrier blocks submitted with + * the dispatch barrier API, which e.g. enables the implementation of efficient + * reader-writer schemes. + * + * When a dispatch queue is no longer needed, it should be released with + * dispatch_release(). Note that any pending blocks submitted to a queue will + * hold a reference to that queue. Therefore a queue will not be deallocated + * until all pending blocks have finished. * - * When the dispatch queue is no longer needed, it should be released - * with dispatch_release(). Note that any pending blocks submitted - * to a queue will hold a reference to that queue. Therefore a queue - * will not be deallocated until all pending blocks have finished. + * The target queue of a newly created dispatch queue is the default priority + * global concurrent queue. * * @param label * A string label to attach to the queue. * This parameter is optional and may be NULL. * * @param attr - * Unused. Pass NULL for now. + * DISPATCH_QUEUE_SERIAL or DISPATCH_QUEUE_CONCURRENT. * * @result * The newly created dispatch queue. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); @@ -443,11 +487,20 @@ dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); * @result * The label of the queue. The result may be NULL. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_PURE DISPATCH_WARN_RESULT +DISPATCH_NOTHROW const char * dispatch_queue_get_label(dispatch_queue_t queue); +/*! + * @const DISPATCH_TARGET_QUEUE_DEFAULT + * @discussion Constant to pass to the dispatch_set_target_queue() and + * dispatch_source_create() functions to indicate that the default target queue + * for the given object type should be used. + */ +#define DISPATCH_TARGET_QUEUE_DEFAULT NULL + /*! * @function dispatch_set_target_queue * @@ -457,27 +510,38 @@ dispatch_queue_get_label(dispatch_queue_t queue); * @discussion * An object's target queue is responsible for processing the object. * - * A dispatch queue's priority is inherited by its target queue. Use the + * A dispatch queue's priority is inherited from its target queue. Use the * dispatch_get_global_queue() function to obtain suitable target queue * of the desired priority. * + * Blocks submitted to a serial queue whose target queue is another serial + * queue will not be invoked concurrently with blocks submitted to the target + * queue or to any other queue with that same target queue. + * + * The result of introducing a cycle into the hierarchy of target queues is + * undefined. + * * A dispatch source's target queue specifies where its event handler and * cancellation handler blocks will be submitted. * - * The result of calling dispatch_set_target_queue() on any other type of - * dispatch object is undefined. + * A dispatch I/O channel's target queue specifies where where its I/O + * operations are executed. * - * @param object + * For all other dispatch object types, the only function of the target queue + * is to determine where an object's finalizer function is invoked. + * + * @param object * The object to modify. * The result of passing NULL in this parameter is undefined. * - * @param queue + * @param queue * The new target queue for the object. The queue is retained, and the - * previous one, if any, is released. - * The result of passing NULL in this parameter is undefined. + * previous target queue, if any, is released. + * If queue is DISPATCH_TARGET_QUEUE_DEFAULT, set the object's target queue + * to the default target queue for the given object type. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NOTHROW // DISPATCH_NONNULL1 void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue); @@ -494,8 +558,8 @@ dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue); * Applications that call NSApplicationMain() or CFRunLoopRun() on the * main thread do not need to call dispatch_main(). */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW DISPATCH_NORETURN +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NOTHROW DISPATCH_NORETURN void dispatch_main(void); @@ -522,8 +586,8 @@ dispatch_main(void); * The result of passing NULL in this parameter is undefined. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL2 DISPATCH_NONNULL3 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_NONNULL3 DISPATCH_NOTHROW void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, @@ -555,14 +619,251 @@ dispatch_after(dispatch_time_t when, * dispatch_after_f(). * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW void dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *context, dispatch_function_t work); +/*! + * @functiongroup Dispatch Barrier API + * The dispatch barrier API is a mechanism for submitting barrier blocks to a + * dispatch queue, analogous to the dispatch_async()/dispatch_sync() API. + * It enables the implementation of efficient reader/writer schemes. + * Barrier blocks only behave specially when submitted to queues created with + * the DISPATCH_QUEUE_CONCURRENT attribute; on such a queue, a barrier block + * will not run until all blocks submitted to the queue earlier have completed, + * and any blocks submitted to the queue after a barrier block will not run + * until the barrier block has completed. + * When submitted to a a global queue or to a queue not created with the + * DISPATCH_QUEUE_CONCURRENT attribute, barrier blocks behave identically to + * blocks submitted with the dispatch_async()/dispatch_sync() API. + */ + +/*! + * @function dispatch_barrier_async + * + * @abstract + * Submits a barrier block for asynchronous execution on a dispatch queue. + * + * @discussion + * Submits a block to a dispatch queue like dispatch_async(), but marks that + * block as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_async() for details. + * + * @param queue + * The target dispatch queue to which the block is submitted. + * The system will hold a reference on the target queue until the block + * has finished. + * The result of passing NULL in this parameter is undefined. + * + * @param block + * The block to submit to the target dispatch queue. This function performs + * Block_copy() and Block_release() on behalf of callers. + * The result of passing NULL in this parameter is undefined. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); +#endif + +/*! + * @function dispatch_barrier_async_f + * + * @abstract + * Submits a barrier function for asynchronous execution on a dispatch queue. + * + * @discussion + * Submits a function to a dispatch queue like dispatch_async_f(), but marks + * that function as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT + * queues). + * + * See dispatch_async_f() for details. + * + * @param queue + * The target dispatch queue to which the function is submitted. + * The system will hold a reference on the target queue until the function + * has returned. + * The result of passing NULL in this parameter is undefined. + * + * @param context + * The application-defined context parameter to pass to the function. + * + * @param work + * The application-defined function to invoke on the target queue. The first + * parameter passed to this function is the context provided to + * dispatch_barrier_async_f(). + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_barrier_async_f(dispatch_queue_t queue, + void *context, + dispatch_function_t work); + +/*! + * @function dispatch_barrier_sync + * + * @abstract + * Submits a barrier block for synchronous execution on a dispatch queue. + * + * @discussion + * Submits a block to a dispatch queue like dispatch_sync(), but marks that + * block as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_sync() for details. + * + * @param queue + * The target dispatch queue to which the block is submitted. + * The result of passing NULL in this parameter is undefined. + * + * @param block + * The block to be invoked on the target dispatch queue. + * The result of passing NULL in this parameter is undefined. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); +#endif + +/*! + * @function dispatch_barrier_sync_f + * + * @abstract + * Submits a barrier function for synchronous execution on a dispatch queue. + * + * @discussion + * Submits a function to a dispatch queue like dispatch_sync_f(), but marks that + * fuction as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_sync_f() for details. + * + * @param queue + * The target dispatch queue to which the function is submitted. + * The result of passing NULL in this parameter is undefined. + * + * @param context + * The application-defined context parameter to pass to the function. + * + * @param work + * The application-defined function to invoke on the target queue. The first + * parameter passed to this function is the context provided to + * dispatch_barrier_sync_f(). + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_barrier_sync_f(dispatch_queue_t queue, + void *context, + dispatch_function_t work); + +/*! + * @functiongroup Dispatch queue-specific contexts + * This API allows different subsystems to associate context to a shared queue + * without risk of collision and to retrieve that context from blocks executing + * on that queue or any of its child queues in the target queue hierarchy. + */ + +/*! + * @function dispatch_queue_set_specific + * + * @abstract + * Associates a subsystem-specific context with a dispatch queue, for a key + * unique to the subsystem. + * + * @discussion + * The specified destructor will be invoked with the context on the default + * priority global concurrent queue when a new context is set for the same key, + * or after all references to the queue have been released. + * + * @param queue + * The dispatch queue to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param key + * The key to set the context for, typically a pointer to a static variable + * specific to the subsystem. Keys are only compared as pointers and never + * dereferenced. Passing a string constant directly is not recommended. + * The NULL key is reserved and attemps to set a context for it are ignored. + * + * @param context + * The new subsystem-specific context for the object. This may be NULL. + * + * @param destructor + * The destructor function pointer. This may be NULL and is ignored if context + * is NULL. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NOTHROW +void +dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, + void *context, dispatch_function_t destructor); + +/*! + * @function dispatch_queue_get_specific + * + * @abstract + * Returns the subsystem-specific context associated with a dispatch queue, for + * a key unique to the subsystem. + * + * @discussion + * Returns the context for the specified key if it has been set on the specified + * queue. + * + * @param queue + * The dispatch queue to query. + * The result of passing NULL in this parameter is undefined. + * + * @param key + * The key to get the context for, typically a pointer to a static variable + * specific to the subsystem. Keys are only compared as pointers and never + * dereferenced. Passing a string constant directly is not recommended. + * + * @result + * The context for the specified key or NULL if no context was found. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_PURE DISPATCH_WARN_RESULT +DISPATCH_NOTHROW +void * +dispatch_queue_get_specific(dispatch_queue_t queue, const void *key); + +/*! + * @function dispatch_get_specific + * + * @abstract + * Returns the current subsystem-specific context for a key unique to the + * subsystem. + * + * @discussion + * When called from a block executing on a queue, returns the context for the + * specified key if it has been set on the queue, otherwise returns the result + * of dispatch_get_specific() executed on the queue's target queue or NULL + * if the current queue is a global concurrent queue. + * + * @param key + * The key to get the context for, typically a pointer to a static variable + * specific to the subsystem. Keys are only compared as pointers and never + * dereferenced. Passing a string constant directly is not recommended. + * + * @result + * The context for the specified key or NULL if no context was found. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_PURE DISPATCH_WARN_RESULT +DISPATCH_NOTHROW +void * +dispatch_get_specific(const void *key); + __END_DECLS #endif diff --git a/src/semaphore.h b/dispatch/semaphore.h similarity index 87% rename from src/semaphore.h rename to dispatch/semaphore.h index 882b567..19b50af 100644 --- a/src/semaphore.h +++ b/dispatch/semaphore.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -55,8 +55,8 @@ __BEGIN_DECLS * @result * The newly created semaphore, or NULL on failure. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_semaphore_t dispatch_semaphore_create(long value); @@ -80,8 +80,8 @@ dispatch_semaphore_create(long value); * @result * Returns zero on success, or non-zero if the timeout occurred. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); @@ -102,8 +102,8 @@ dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); * This function returns non-zero if a thread is woken. Otherwise, zero is * returned. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW long dispatch_semaphore_signal(dispatch_semaphore_t dsema); diff --git a/src/source.h b/dispatch/source.h similarity index 75% rename from src/source.h rename to dispatch/source.h index 867ba86..4c9f601 100644 --- a/src/source.h +++ b/dispatch/source.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -26,8 +26,10 @@ #include // for HeaderDoc #endif +#if TARGET_OS_MAC #include #include +#endif #include /*! @@ -70,8 +72,9 @@ typedef const struct dispatch_source_type_s *dispatch_source_type_t; * The mask is unused (pass zero for now). */ #define DISPATCH_SOURCE_TYPE_DATA_ADD (&_dispatch_source_type_data_add) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_data_add; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_data_add; /*! * @const DISPATCH_SOURCE_TYPE_DATA_OR @@ -82,8 +85,9 @@ extern const struct dispatch_source_type_s _dispatch_source_type_data_add; * dispatch_source_merge_data(). */ #define DISPATCH_SOURCE_TYPE_DATA_OR (&_dispatch_source_type_data_or) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_data_or; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_data_or; /*! * @const DISPATCH_SOURCE_TYPE_MACH_SEND @@ -93,8 +97,9 @@ extern const struct dispatch_source_type_s _dispatch_source_type_data_or; * The mask is a mask of desired events from dispatch_source_mach_send_flags_t. */ #define DISPATCH_SOURCE_TYPE_MACH_SEND (&_dispatch_source_type_mach_send) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_mach_send; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_mach_send; /*! * @const DISPATCH_SOURCE_TYPE_MACH_RECV @@ -103,8 +108,9 @@ extern const struct dispatch_source_type_s _dispatch_source_type_mach_send; * The mask is unused (pass zero for now). */ #define DISPATCH_SOURCE_TYPE_MACH_RECV (&_dispatch_source_type_mach_recv) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_mach_recv; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_mach_recv; /*! * @const DISPATCH_SOURCE_TYPE_PROC @@ -114,8 +120,9 @@ extern const struct dispatch_source_type_s _dispatch_source_type_mach_recv; * The mask is a mask of desired events from dispatch_source_proc_flags_t. */ #define DISPATCH_SOURCE_TYPE_PROC (&_dispatch_source_type_proc) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_proc; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_proc; /*! * @const DISPATCH_SOURCE_TYPE_READ @@ -125,8 +132,9 @@ extern const struct dispatch_source_type_s _dispatch_source_type_proc; * The mask is unused (pass zero for now). */ #define DISPATCH_SOURCE_TYPE_READ (&_dispatch_source_type_read) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_read; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_read; /*! * @const DISPATCH_SOURCE_TYPE_SIGNAL @@ -135,8 +143,9 @@ extern const struct dispatch_source_type_s _dispatch_source_type_read; * The mask is unused (pass zero for now). */ #define DISPATCH_SOURCE_TYPE_SIGNAL (&_dispatch_source_type_signal) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_signal; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_signal; /*! * @const DISPATCH_SOURCE_TYPE_TIMER @@ -146,8 +155,9 @@ extern const struct dispatch_source_type_s _dispatch_source_type_signal; * The mask is unused (pass zero for now). */ #define DISPATCH_SOURCE_TYPE_TIMER (&_dispatch_source_type_timer) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_timer; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_timer; /*! * @const DISPATCH_SOURCE_TYPE_VNODE @@ -157,8 +167,9 @@ extern const struct dispatch_source_type_s _dispatch_source_type_timer; * The mask is a mask of desired events from dispatch_source_vnode_flags_t. */ #define DISPATCH_SOURCE_TYPE_VNODE (&_dispatch_source_type_vnode) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_vnode; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_vnode; /*! * @const DISPATCH_SOURCE_TYPE_WRITE @@ -168,21 +179,24 @@ extern const struct dispatch_source_type_s _dispatch_source_type_vnode; * The mask is unused (pass zero for now). */ #define DISPATCH_SOURCE_TYPE_WRITE (&_dispatch_source_type_write) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_write; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +const struct dispatch_source_type_s _dispatch_source_type_write; /*! - * @enum dispatch_source_mach_send_flags_t + * @typedef dispatch_source_mach_send_flags_t + * Type of dispatch_source_mach_send flags * * @constant DISPATCH_MACH_SEND_DEAD * The receive right corresponding to the given send right was destroyed. */ -enum { - DISPATCH_MACH_SEND_DEAD = 0x1, -}; +#define DISPATCH_MACH_SEND_DEAD 0x1 + +typedef unsigned long dispatch_source_mach_send_flags_t; /*! - * @enum dispatch_source_proc_flags_t + * @typedef dispatch_source_proc_flags_t + * Type of dispatch_source_proc flags * * @constant DISPATCH_PROC_EXIT * The process has exited (perhaps cleanly, perhaps not). @@ -197,15 +211,16 @@ enum { * @constant DISPATCH_PROC_SIGNAL * A Unix signal was delivered to the process. */ -enum { - DISPATCH_PROC_EXIT = 0x80000000, - DISPATCH_PROC_FORK = 0x40000000, - DISPATCH_PROC_EXEC = 0x20000000, - DISPATCH_PROC_SIGNAL = 0x08000000, -}; +#define DISPATCH_PROC_EXIT 0x80000000 +#define DISPATCH_PROC_FORK 0x40000000 +#define DISPATCH_PROC_EXEC 0x20000000 +#define DISPATCH_PROC_SIGNAL 0x08000000 + +typedef unsigned long dispatch_source_proc_flags_t; /*! - * @enum dispatch_source_vnode_flags_t + * @typedef dispatch_source_vnode_flags_t + * Type of dispatch_source_vnode flags * * @constant DISPATCH_VNODE_DELETE * The filesystem object was deleted from the namespace. @@ -228,15 +243,16 @@ enum { * @constant DISPATCH_VNODE_REVOKE * The filesystem object was revoked. */ -enum { - DISPATCH_VNODE_DELETE = 0x1, - DISPATCH_VNODE_WRITE = 0x2, - DISPATCH_VNODE_EXTEND = 0x4, - DISPATCH_VNODE_ATTRIB = 0x8, - DISPATCH_VNODE_LINK = 0x10, - DISPATCH_VNODE_RENAME = 0x20, - DISPATCH_VNODE_REVOKE = 0x40, -}; + +#define DISPATCH_VNODE_DELETE 0x1 +#define DISPATCH_VNODE_WRITE 0x2 +#define DISPATCH_VNODE_EXTEND 0x4 +#define DISPATCH_VNODE_ATTRIB 0x8 +#define DISPATCH_VNODE_LINK 0x10 +#define DISPATCH_VNODE_RENAME 0x20 +#define DISPATCH_VNODE_REVOKE 0x40 + +typedef unsigned long dispatch_source_vnode_flags_t; __BEGIN_DECLS @@ -267,10 +283,12 @@ __BEGIN_DECLS * A mask of flags specifying which events are desired. The interpretation of * this argument is determined by the constant provided in the type parameter. * @param queue - * The dispatch queue to which the event handler block will be submited. + * The dispatch queue to which the event handler block will be submitted. + * If queue is DISPATCH_TARGET_QUEUE_DEFAULT, the source will submit the event + * handler block to the default priority global queue. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, @@ -286,13 +304,13 @@ dispatch_source_create(dispatch_source_type_t type, * @param source * The dispatch source to modify. * The result of passing NULL in this parameter is undefined. - * + * * @param handler * The event handler block to submit to the source's target queue. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW void dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t handler); @@ -314,8 +332,8 @@ dispatch_source_set_event_handler(dispatch_source_t source, * context of the dispatch source at the time the handler call is made. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW void dispatch_source_set_event_handler_f(dispatch_source_t source, dispatch_function_t handler); @@ -343,13 +361,13 @@ dispatch_source_set_event_handler_f(dispatch_source_t source, * @param source * The dispatch source to modify. * The result of passing NULL in this parameter is undefined. - * + * * @param handler * The cancellation handler block to submit to the source's target queue. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t cancel_handler); @@ -373,8 +391,8 @@ dispatch_source_set_cancel_handler(dispatch_source_t source, * The context parameter passed to the event handler function is the current * context of the dispatch source at the time the handler call is made. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW void dispatch_source_set_cancel_handler_f(dispatch_source_t source, dispatch_function_t cancel_handler); @@ -401,8 +419,8 @@ dispatch_source_set_cancel_handler_f(dispatch_source_t source, * The dispatch source to be canceled. * The result of passing NULL in this parameter is undefined. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_source_cancel(dispatch_source_t source); @@ -419,8 +437,9 @@ dispatch_source_cancel(dispatch_source_t source); * @result * Non-zero if canceled and zero if not canceled. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE +DISPATCH_NOTHROW long dispatch_source_testcancel(dispatch_source_t source); @@ -443,13 +462,14 @@ dispatch_source_testcancel(dispatch_source_t source); * DISPATCH_SOURCE_TYPE_MACH_RECV: mach port (mach_port_t) * DISPATCH_SOURCE_TYPE_PROC: process identifier (pid_t) * DISPATCH_SOURCE_TYPE_READ: file descriptor (int) - * DISPATCH_SOURCE_TYPE_SIGNAL: signal number (int) + * DISPATCH_SOURCE_TYPE_SIGNAL: signal number (int) * DISPATCH_SOURCE_TYPE_TIMER: n/a * DISPATCH_SOURCE_TYPE_VNODE: file descriptor (int) * DISPATCH_SOURCE_TYPE_WRITE: file descriptor (int) */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE +DISPATCH_NOTHROW uintptr_t dispatch_source_get_handle(dispatch_source_t source); @@ -477,8 +497,9 @@ dispatch_source_get_handle(dispatch_source_t source); * DISPATCH_SOURCE_TYPE_VNODE: dispatch_source_vnode_flags_t * DISPATCH_SOURCE_TYPE_WRITE: n/a */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE +DISPATCH_NOTHROW unsigned long dispatch_source_get_mask(dispatch_source_t source); @@ -513,8 +534,9 @@ dispatch_source_get_mask(dispatch_source_t source); * DISPATCH_SOURCE_TYPE_VNODE: dispatch_source_vnode_flags_t * DISPATCH_SOURCE_TYPE_WRITE: estimated buffer space available */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE +DISPATCH_NOTHROW unsigned long dispatch_source_get_data(dispatch_source_t source); @@ -534,8 +556,8 @@ dispatch_source_get_data(dispatch_source_t source); * as specified by the dispatch source type. A value of zero has no effect * and will not result in the submission of the event handler block. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_source_merge_data(dispatch_source_t source, unsigned long value); @@ -547,14 +569,15 @@ dispatch_source_merge_data(dispatch_source_t source, unsigned long value); * * @discussion * Calling this function has no effect if the timer source has already been - * canceled. - * + * canceled. Once this function returns, any pending timer data accumulated + * for the previous timer values has been cleared + * * The start time argument also determines which clock will be used for the * timer. If the start time is DISPATCH_TIME_NOW or created with * dispatch_time() then the timer is based on mach_absolute_time(). Otherwise, * if the start time of the timer is created with dispatch_walltime() then the * timer is based on gettimeofday(3). - * + * * @param start * The start time of the timer. See dispatch_time() and dispatch_walltime() * for more information. @@ -570,14 +593,67 @@ dispatch_source_merge_data(dispatch_source_t source, unsigned long value); * a leeway of up to 30 seconds.) Note that some latency is to be expected for * all timers even when a leeway value of zero is specified. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway); +/*! + * @function dispatch_source_set_registration_handler + * + * @abstract + * Sets the registration handler block for the given dispatch source. + * + * @discussion + * The registration handler (if specified) will be submitted to the source's + * target queue once the corresponding kevent() has been registered with the + * system, following the initial dispatch_resume() of the source. + * + * If a source is already registered when the registration handler is set, the + * registration handler will be invoked immediately. + * + * @param source + * The dispatch source to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param handler + * The registration handler block to submit to the source's target queue. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_source_set_registration_handler(dispatch_source_t source, + dispatch_block_t registration_handler); +#endif /* __BLOCKS__ */ + +/*! + * @function dispatch_source_set_registration_handler_f + * + * @abstract + * Sets the registration handler function for the given dispatch source. + * + * @discussion + * See dispatch_source_set_registration_handler() for more details. + * + * @param source + * The dispatch source to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param handler + * The registration handler function to submit to the source's target queue. + * The context parameter passed to the registration handler function is the + * current context of the dispatch source at the time the handler call is made. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_source_set_registration_handler_f(dispatch_source_t source, + dispatch_function_t registration_handler); + __END_DECLS #endif diff --git a/src/time.h b/dispatch/time.h similarity index 84% rename from src/time.h rename to dispatch/time.h index 510d6d7..d39578d 100644 --- a/src/time.h +++ b/dispatch/time.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -42,7 +42,11 @@ struct timespec; #ifdef NSEC_PER_USEC #undef NSEC_PER_USEC #endif +#ifdef NSEC_PER_MSEC +#undef NSEC_PER_MSEC +#endif #define NSEC_PER_SEC 1000000000ull +#define NSEC_PER_MSEC 1000000ull #define USEC_PER_SEC 1000000ull #define NSEC_PER_USEC 1000ull @@ -50,7 +54,7 @@ struct timespec; * @typedef dispatch_time_t * * @abstract - * An somewhat abstract representation of time; where zero means "now" and + * A somewhat abstract representation of time; where zero means "now" and * DISPATCH_TIME_FOREVER means "infinity" and every value in between is an * opaque encoding. */ @@ -79,8 +83,8 @@ typedef uint64_t dispatch_time_t; * @result * A new dispatch_time_t. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta); @@ -103,8 +107,8 @@ dispatch_time(dispatch_time_t when, int64_t delta); * @result * A new dispatch_time_t. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_time_t dispatch_walltime(const struct timespec *when, int64_t delta); diff --git a/examples/Dispatch Samples/Dispatch Samples.xcodeproj/project.pbxproj b/examples/Dispatch Samples/Dispatch Samples.xcodeproj/project.pbxproj deleted file mode 100644 index 15482f3..0000000 --- a/examples/Dispatch Samples/Dispatch Samples.xcodeproj/project.pbxproj +++ /dev/null @@ -1,609 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 45; - objects = { - -/* Begin PBXAggregateTarget section */ - 4C96F87F0F8288070051687B /* Samples */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 4C96F88F0F8288290051687B /* Build configuration list for PBXAggregateTarget "Samples" */; - buildPhases = ( - ); - dependencies = ( - 4C96F88B0F82881B0051687B /* PBXTargetDependency */, - 4C96F8890F8288190051687B /* PBXTargetDependency */, - 4C96F8870F8288170051687B /* PBXTargetDependency */, - 4C96F8850F8288140051687B /* PBXTargetDependency */, - 4C96F8830F82880E0051687B /* PBXTargetDependency */, - ); - name = Samples; - productName = Samples; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 4CBAB02C0F780242006D97F1 /* apply.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB02A0F780242006D97F1 /* apply.c */; }; - 4CBAB04C0F7802DA006D97F1 /* netcat.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB04A0F7802DA006D97F1 /* netcat.c */; }; - 4CBAB0530F7802F1006D97F1 /* proc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB0510F7802F1006D97F1 /* proc.c */; }; - 4CBAB0560F780314006D97F1 /* readFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB0540F780314006D97F1 /* readFile.c */; }; - 4CBAB0590F780327006D97F1 /* timers.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB0570F780327006D97F1 /* timers.c */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 4C96F8820F82880E0051687B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CBAB0240F7801C6006D97F1; - remoteInfo = "dispatch-apply"; - }; - 4C96F8840F8288140051687B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CBAB0300F780272006D97F1; - remoteInfo = "dispatch-netcat"; - }; - 4C96F8860F8288170051687B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CBAB0370F78028E006D97F1; - remoteInfo = "dispatch-proc"; - }; - 4C96F8880F8288190051687B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CBAB03E0F7802A6006D97F1; - remoteInfo = "dispatch-readFile"; - }; - 4C96F88A0F82881B0051687B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CBAB0450F7802BA006D97F1; - remoteInfo = "dispatch-timers"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 4CBAB0250F7801C6006D97F1 /* dispatch-apply */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-apply"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CBAB02A0F780242006D97F1 /* apply.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apply.c; sourceTree = ""; }; - 4CBAB0310F780272006D97F1 /* dispatch-netcat */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-netcat"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CBAB0380F78028E006D97F1 /* dispatch-proc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-proc"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CBAB03F0F7802A6006D97F1 /* dispatch-readFile */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-readFile"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CBAB0460F7802BA006D97F1 /* dispatch-timers */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-timers"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CBAB04A0F7802DA006D97F1 /* netcat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = netcat.c; sourceTree = ""; }; - 4CBAB0510F7802F1006D97F1 /* proc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = proc.c; sourceTree = ""; }; - 4CBAB0540F780314006D97F1 /* readFile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readFile.c; sourceTree = ""; }; - 4CBAB0570F780327006D97F1 /* timers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = timers.c; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 4CBAB0230F7801C6006D97F1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CBAB02F0F780272006D97F1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CBAB0360F78028E006D97F1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CBAB03D0F7802A6006D97F1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CBAB0440F7802BA006D97F1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* Dispatch Samples */ = { - isa = PBXGroup; - children = ( - 08FB7795FE84155DC02AAC07 /* Source */, - C6A0FF2B0290797F04C91782 /* Documentation */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - ); - name = "Dispatch Samples"; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 4CBAB0570F780327006D97F1 /* timers.c */, - 4CBAB0540F780314006D97F1 /* readFile.c */, - 4CBAB0510F7802F1006D97F1 /* proc.c */, - 4CBAB04A0F7802DA006D97F1 /* netcat.c */, - 4CBAB02A0F780242006D97F1 /* apply.c */, - ); - name = Source; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 4CBAB0250F7801C6006D97F1 /* dispatch-apply */, - 4CBAB0310F780272006D97F1 /* dispatch-netcat */, - 4CBAB0380F78028E006D97F1 /* dispatch-proc */, - 4CBAB03F0F7802A6006D97F1 /* dispatch-readFile */, - 4CBAB0460F7802BA006D97F1 /* dispatch-timers */, - ); - name = Products; - sourceTree = ""; - }; - C6A0FF2B0290797F04C91782 /* Documentation */ = { - isa = PBXGroup; - children = ( - ); - name = Documentation; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 4CBAB0240F7801C6006D97F1 /* dispatch-apply */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4CBAB0290F7801E5006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-apply" */; - buildPhases = ( - 4CBAB0220F7801C6006D97F1 /* Sources */, - 4CBAB0230F7801C6006D97F1 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "dispatch-apply"; - productName = "dispatch-apply"; - productReference = 4CBAB0250F7801C6006D97F1 /* dispatch-apply */; - productType = "com.apple.product-type.tool"; - }; - 4CBAB0300F780272006D97F1 /* dispatch-netcat */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4CBAB04D0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-netcat" */; - buildPhases = ( - 4CBAB02E0F780272006D97F1 /* Sources */, - 4CBAB02F0F780272006D97F1 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "dispatch-netcat"; - productName = "dispatch-netcat"; - productReference = 4CBAB0310F780272006D97F1 /* dispatch-netcat */; - productType = "com.apple.product-type.tool"; - }; - 4CBAB0370F78028E006D97F1 /* dispatch-proc */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4CBAB04E0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-proc" */; - buildPhases = ( - 4CBAB0350F78028E006D97F1 /* Sources */, - 4CBAB0360F78028E006D97F1 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "dispatch-proc"; - productName = "dispatch-proc"; - productReference = 4CBAB0380F78028E006D97F1 /* dispatch-proc */; - productType = "com.apple.product-type.tool"; - }; - 4CBAB03E0F7802A6006D97F1 /* dispatch-readFile */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4CBAB04F0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-readFile" */; - buildPhases = ( - 4CBAB03C0F7802A6006D97F1 /* Sources */, - 4CBAB03D0F7802A6006D97F1 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "dispatch-readFile"; - productName = "dispatch-readFile"; - productReference = 4CBAB03F0F7802A6006D97F1 /* dispatch-readFile */; - productType = "com.apple.product-type.tool"; - }; - 4CBAB0450F7802BA006D97F1 /* dispatch-timers */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4CBAB0500F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-timers" */; - buildPhases = ( - 4CBAB0430F7802BA006D97F1 /* Sources */, - 4CBAB0440F7802BA006D97F1 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "dispatch-timers"; - productName = "dispatch-timers"; - productReference = 4CBAB0460F7802BA006D97F1 /* dispatch-timers */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "Dispatch Samples" */; - compatibilityVersion = "Xcode 3.1"; - hasScannedForEncodings = 1; - mainGroup = 08FB7794FE84155DC02AAC07 /* Dispatch Samples */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 4C96F87F0F8288070051687B /* Samples */, - 4CBAB0240F7801C6006D97F1 /* dispatch-apply */, - 4CBAB0300F780272006D97F1 /* dispatch-netcat */, - 4CBAB0370F78028E006D97F1 /* dispatch-proc */, - 4CBAB03E0F7802A6006D97F1 /* dispatch-readFile */, - 4CBAB0450F7802BA006D97F1 /* dispatch-timers */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 4CBAB0220F7801C6006D97F1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CBAB02C0F780242006D97F1 /* apply.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CBAB02E0F780272006D97F1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CBAB04C0F7802DA006D97F1 /* netcat.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CBAB0350F78028E006D97F1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CBAB0530F7802F1006D97F1 /* proc.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CBAB03C0F7802A6006D97F1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CBAB0560F780314006D97F1 /* readFile.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CBAB0430F7802BA006D97F1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CBAB0590F780327006D97F1 /* timers.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 4C96F8830F82880E0051687B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CBAB0240F7801C6006D97F1 /* dispatch-apply */; - targetProxy = 4C96F8820F82880E0051687B /* PBXContainerItemProxy */; - }; - 4C96F8850F8288140051687B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CBAB0300F780272006D97F1 /* dispatch-netcat */; - targetProxy = 4C96F8840F8288140051687B /* PBXContainerItemProxy */; - }; - 4C96F8870F8288170051687B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CBAB0370F78028E006D97F1 /* dispatch-proc */; - targetProxy = 4C96F8860F8288170051687B /* PBXContainerItemProxy */; - }; - 4C96F8890F8288190051687B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CBAB03E0F7802A6006D97F1 /* dispatch-readFile */; - targetProxy = 4C96F8880F8288190051687B /* PBXContainerItemProxy */; - }; - 4C96F88B0F82881B0051687B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CBAB0450F7802BA006D97F1 /* dispatch-timers */; - targetProxy = 4C96F88A0F82881B0051687B /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 1DEB928A08733DD80010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - ONLY_ACTIVE_ARCH = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Debug; - }; - 1DEB928B08733DD80010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Release; - }; - 4C96F8800F8288080051687B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - PRODUCT_NAME = Samples; - }; - name = Debug; - }; - 4C96F8810F8288080051687B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - PRODUCT_NAME = Samples; - ZERO_LINK = NO; - }; - name = Release; - }; - 4CBAB0270F7801C7006D97F1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-apply"; - }; - name = Debug; - }; - 4CBAB0280F7801C7006D97F1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-apply"; - ZERO_LINK = NO; - }; - name = Release; - }; - 4CBAB0330F780273006D97F1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-netcat"; - }; - name = Debug; - }; - 4CBAB0340F780273006D97F1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-netcat"; - ZERO_LINK = NO; - }; - name = Release; - }; - 4CBAB03A0F78028F006D97F1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-proc"; - }; - name = Debug; - }; - 4CBAB03B0F78028F006D97F1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-proc"; - ZERO_LINK = NO; - }; - name = Release; - }; - 4CBAB0410F7802A7006D97F1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-readFile"; - }; - name = Debug; - }; - 4CBAB0420F7802A7006D97F1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-readFile"; - ZERO_LINK = NO; - }; - name = Release; - }; - 4CBAB0480F7802BB006D97F1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-timers"; - }; - name = Debug; - }; - 4CBAB0490F7802BB006D97F1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; - PRODUCT_NAME = "dispatch-timers"; - ZERO_LINK = NO; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "Dispatch Samples" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB928A08733DD80010E9CD /* Debug */, - 1DEB928B08733DD80010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4C96F88F0F8288290051687B /* Build configuration list for PBXAggregateTarget "Samples" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4C96F8800F8288080051687B /* Debug */, - 4C96F8810F8288080051687B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4CBAB0290F7801E5006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-apply" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4CBAB0270F7801C7006D97F1 /* Debug */, - 4CBAB0280F7801C7006D97F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4CBAB04D0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-netcat" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4CBAB0330F780273006D97F1 /* Debug */, - 4CBAB0340F780273006D97F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4CBAB04E0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-proc" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4CBAB03A0F78028F006D97F1 /* Debug */, - 4CBAB03B0F78028F006D97F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4CBAB04F0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-readFile" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4CBAB0410F7802A7006D97F1 /* Debug */, - 4CBAB0420F7802A7006D97F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4CBAB0500F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-timers" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4CBAB0480F7802BB006D97F1 /* Debug */, - 4CBAB0490F7802BB006D97F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/examples/Dispatch Samples/ReadMe.txt b/examples/Dispatch Samples/ReadMe.txt deleted file mode 100644 index 3214db1..0000000 --- a/examples/Dispatch Samples/ReadMe.txt +++ /dev/null @@ -1,93 +0,0 @@ -### DispatchProcessMonitor ### - -=========================================================================== -DESCRIPTION: - -Sample code showing how to: monitor process, do file and network I/O, -create and manage timers, and use dispatch_apply - -=========================================================================== -BUILD REQUIREMENTS: - -Mac OS X version 10.6 Snow Leopard - -=========================================================================== -RUNTIME REQUIREMENTS: - -Mac OS X version 10.6 Snow Leopard - -=========================================================================== -PACKAGING LIST: - -apply.c - dispatch_apply examples -netcat.c - network I/O examples -nWide.c - use of dispatch_semaphore to limit number of in-flight blocks -proc.c - process monitoring example -readFile.c - file I/O examples -readFileF.c - file I/O examples without Blocks -timers.c - create and manage timers - -=========================================================================== -SAMPLE USAGE: - -dispatch-apply - -dispatch-apply takes no arguments. When run it will display some status -messages and timing information. - -dispatch-netcat - -Open two terminal windows. In one window run the "server": - -cat ReadMe.txt | dispatch-netcat -l localhost 5050 - -In the other run the "client": - -dispatch-netcat localhost 5050 - -Your server will send the contents of ReadMe.txt to the client, the server -will close it's connection and exit. The client will display whatever -the server sent (the ReadMe.txt file). See the main function in netcat.c -for more options. - -dispatch-nWide - -dispatch-nWide takes no arguments. When run it will display explanatory -text. - -dispatch-proc - -dispatch-proc takes no arguments. When run it will display output from -some processes it runs, and it will display information from the -process lifecycle events dispatch generates. - -dispatch-readFile - -Run dispatch-readFile with a filename as an argument: - -dispatch-readFile ReadMe.txt - -It will read the file 10 (or fewer) bytes at a time and display how many -bytes dispatch thinks are remaining to read. - -dispatch-readFileF - -Exactly the same as dispatch-readFile, but written without the use of Blocks. - -dispatch-timers - -dispatch-timers takes no arguments, running it display timer ticks for -a timer with an initial interval of one second, changing to one half second -after the first three events. It will exit after six events. - -=========================================================================== -CHANGES FROM PREVIOUS VERSIONS: - -Version 1.1 -- Updated to current libdispatch API, and added samples readFileF.c and -nWide.c -Version 1.0 -- First version - -=========================================================================== -Copyright (C) 2009 Apple Inc. All rights reserved. diff --git a/examples/Dispatch Samples/apply.c b/examples/Dispatch Samples/apply.c deleted file mode 100644 index 3eb39a5..0000000 --- a/examples/Dispatch Samples/apply.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include - -#define kIT 10 - -uint64_t elapsed_time; - -void timer_start() { - elapsed_time = mach_absolute_time(); -} - -double timer_milePost() { - static dispatch_once_t justOnce; - static double scale; - - dispatch_once(&justOnce, ^{ - mach_timebase_info_data_t tbi; - mach_timebase_info(&tbi); - scale = tbi.numer; - scale = scale/tbi.denom; - printf("Scale is %10.4f Just computed once courtesy of dispatch_once()\n", scale); - }); - - uint64_t now = mach_absolute_time()-elapsed_time; - double fTotalT = now; - fTotalT = fTotalT * scale; // convert this to nanoseconds... - fTotalT = fTotalT / 1000000000.0; - return fTotalT; -} - -int -main(void) -{ - dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL); - dispatch_group_t myGroup = dispatch_group_create(); - -// dispatch_apply on a serial queue finishes each block in order so the following code will take a little more than a second - timer_start(); - dispatch_apply(kIT, myQueue, ^(size_t current){ - printf("Block #%ld of %d is being run\n", - current+1, // adjusting the zero based current iteration we get passed in - kIT); - usleep(USEC_PER_SEC/10); - }); - printf("and dispatch_apply( serial queue ) returned after %10.4lf seconds\n",timer_milePost()); - -// dispatch_apply on a concurrent queue returns after all blocks are finished, however it can execute them concurrently with each other -// so this will take quite a bit less time - timer_start(); - dispatch_apply(kIT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t current){ - printf("Block #%ld of %d is being run\n",current+1, kIT); - usleep(USEC_PER_SEC/10); - }); - printf("and dispatch_apply( concurrent queue) returned after %10.4lf seconds\n",timer_milePost()); - -// To execute all blocks in a dispatch_apply asynchonously, you will need to perform the dispatch_apply -// asynchonously, like this (NOTE the nested dispatch_apply inside of the async block.) -// Also note the use of the dispatch_group so that we can ultimatly know when the work is -// all completed - - timer_start(); - dispatch_group_async(myGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - dispatch_apply(kIT, myQueue, ^(size_t current){ - printf("Block #%ld of %d is being run\n",current+1, kIT); - usleep(USEC_PER_SEC/10); - }); - }); - - printf("and dispatch_group_async( dispatch_apply( )) returned after %10.4lf seconds\n",timer_milePost()); - printf("Now to wait for the dispatch group to finish...\n"); - dispatch_group_wait(myGroup, UINT64_MAX); - printf("and we are done with dispatch_group_async( dispatch_apply( )) after %10.4lf seconds\n",timer_milePost()); - return 0; -} - diff --git a/examples/Dispatch Samples/nWide.c b/examples/Dispatch Samples/nWide.c deleted file mode 100644 index 92914a9..0000000 --- a/examples/Dispatch Samples/nWide.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2009 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -/* - * nWide.c - * Samples project - * - * Created by Mensch on 5/1/09. - * Copyright 2009 Apple, Inc. All rights reserved. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#import -#include - - -/* - * Demonstrate using dispatch_semaphore to create a concurrent queue that - * allows only a fixed number of blocks to be in flight at any given time - */ - -int main (int argc, const char * argv[]) { - dispatch_group_t mg = dispatch_group_create(); - dispatch_semaphore_t ds; - __block int numRunning = 0; - int qWidth = 5; - int numWorkBlocks = 100; - - if (argc >= 2) { - qWidth = atoi(argv[1]); // use the command 1st line parameter as the queue width - if (qWidth==0) qWidth==1; // protect against bad values - } - - if (argc >=3) { - numWorkBlocks = atoi(argv[2]); // use the 2nd command line parameter as the queue width - if (numWorkBlocks==0) numWorkBlocks==1; // protect against bad values - } - - printf("Starting dispatch semaphore test to simulate a %d wide dispatch queue\n", qWidth ); - ds = dispatch_semaphore_create(qWidth); - - int i; - for (i=0; i -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// #define DEBUG 1 - -#if DEBUG -#define dlog(a) dispatch_debug(a, #a) -#else -#define dlog(a) do { } while(0) -#endif - -void usage(void); -void *run_block(void *); -void setup_fd_relay(int netfd /* bidirectional */, - int infd /* local input */, - int outfd /* local output */, - void (^finalizer_block)(void)); -void doreadwrite(int fd1, int fd2, char *buffer, size_t len); - -#define BUFFER_SIZE 1099 - -int main(int argc, char *argv[]) { - - int ch; - bool use_v4_only = false, use_v6_only = false; - bool debug = false, no_stdin = false; - bool keep_listening = false, do_listen = false; - bool do_loookups = true, verbose = false; - bool do_udp = false, do_bind_ip = false, do_bind_port = false; - const char *hostname, *servname; - int ret; - struct addrinfo hints, *aires, *aires0; - const char *bind_hostname, *bind_servname; - - dispatch_queue_t dq; - dispatch_group_t listen_group = NULL; - - while ((ch = getopt(argc, argv, "46Ddhklnvup:s:")) != -1) { - switch (ch) { - case '4': - use_v4_only = true; - break; - case '6': - use_v6_only = true; - break; - case 'D': - debug = true; - break; - case 'd': - no_stdin = true; - break; - case 'h': - usage(); - break; - case 'k': - keep_listening = true; - break; - case 'l': - do_listen = true; - break; - case 'n': - do_loookups = false; - break; - case 'v': - verbose = true; - break; - case 'u': - do_udp = true; - break; - case 'p': - do_bind_port = true; - bind_servname = optarg; - break; - case 's': - do_bind_ip = true; - bind_hostname = optarg; - break; - case '?': - default: - usage(); - break; - } - } - - argc -= optind; - argv += optind; - - if (use_v4_only && use_v6_only) { - errx(EX_USAGE, "-4 and -6 specified"); - } - - if (keep_listening && !do_listen) { - errx(EX_USAGE, "-k specified but no -l"); - } - - if (do_listen && (do_bind_ip || do_bind_port)) { - errx(EX_USAGE, "-p or -s option with -l"); - } - - if (do_listen) { - if (argc >= 2) { - hostname = argv[0]; - servname = argv[1]; - } else if (argc >= 1) { - hostname = NULL; - servname = argv[0]; - } else { - errx(EX_USAGE, "No service name provided"); - } - } else { - if (argc >= 2) { - hostname = argv[0]; - servname = argv[1]; - } else { - errx(EX_USAGE, "No hostname and service name provided"); - } - } - - if (do_bind_ip || do_bind_port) { - if (!do_bind_ip) { - bind_hostname = NULL; - } - if (!do_bind_port) { - bind_servname = NULL; - } - } - - openlog(getprogname(), LOG_PERROR|LOG_CONS, LOG_DAEMON); - setlogmask(debug ? LOG_UPTO(LOG_DEBUG) : verbose ? LOG_UPTO(LOG_INFO) : LOG_UPTO(LOG_ERR)); - - dq = dispatch_queue_create("netcat", NULL); - listen_group = dispatch_group_create(); - - bzero(&hints, sizeof(hints)); - hints.ai_family = use_v4_only ? PF_INET : (use_v6_only ? PF_INET6 : PF_UNSPEC); - hints.ai_socktype = do_udp ? SOCK_DGRAM : SOCK_STREAM; - hints.ai_protocol = do_udp ? IPPROTO_UDP : IPPROTO_TCP; - hints.ai_flags = (!do_loookups ? AI_NUMERICHOST | AI_NUMERICSERV : 0) | (do_listen ? AI_PASSIVE : 0); - - ret = getaddrinfo(hostname, servname, &hints, &aires0); - if (ret) { - errx(1, "getaddrinfo(%s, %s): %s", hostname, servname, gai_strerror(ret)); - } - - for (aires = aires0; aires; aires = aires->ai_next) { - if (do_listen) { - // asynchronously set up the socket - dispatch_retain(dq); - dispatch_group_async(listen_group, - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), - ^{ - int s, val = 1; - dispatch_source_t ds; - - s = socket(aires->ai_family, aires->ai_socktype, aires->ai_protocol); - if (s < 0) { - warn("socket"); - return; - } - - if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)) < 0) { - warn("Could not set SO_REUSEADDR"); - } - - if(setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (const char *)&val, sizeof(val)) < 0) { - warn("Could not set SO_REUSEPORT"); - } - - if(setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) < 0) { - warn("Could not set SO_NOSIGPIPE"); - } - - if (bind(s, aires->ai_addr, aires->ai_addrlen) < 0) { - warn("bind"); - close(s); - return; - } - - listen(s, 2); - syslog(LOG_DEBUG, "listening on socket %d", s); - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, s, 0, dq); - dispatch_source_set_event_handler(ds, ^{ - // got an incoming connection - int s2, lfd = dispatch_source_get_handle(ds); - dispatch_queue_t listen_queue = dispatch_get_current_queue(); - - // prevent further accept(2)s across multiple sources - dispatch_retain(listen_queue); - dispatch_suspend(listen_queue); - - if (do_udp) { - // lfd is our socket, but let's connect in the reverse - // direction to set up the connection fully - char udpbuf[4]; - struct sockaddr_storage sockin; - socklen_t socklen; - ssize_t peeklen; - int cret; - - socklen = sizeof(sockin); - peeklen = recvfrom(lfd, udpbuf, sizeof(udpbuf), - MSG_PEEK, (struct sockaddr *)&sockin, &socklen); - if (peeklen < 0) { - warn("recvfrom"); - dispatch_resume(listen_queue); - dispatch_release(listen_queue); - return; - } - - cret = connect(lfd, (struct sockaddr *)&sockin, socklen); - if (cret < 0) { - warn("connect"); - dispatch_resume(listen_queue); - dispatch_release(listen_queue); - return; - } - - s2 = lfd; - syslog(LOG_DEBUG, "accepted socket %d", s2); - } else { - s2 = accept(lfd, NULL, NULL); - if (s2 < 0) { - warn("accept"); - dispatch_resume(listen_queue); - dispatch_release(listen_queue); - return; - } - syslog(LOG_DEBUG, "accepted socket %d -> %d", lfd, s2); - } - - - setup_fd_relay(s2, no_stdin ? -1 : STDIN_FILENO, STDOUT_FILENO, ^{ - if (!do_udp) { - close(s2); - } - dispatch_resume(listen_queue); - dispatch_release(listen_queue); - if (!keep_listening) { - exit(0); - } - }); - }); - dispatch_resume(ds); - dispatch_release(dq); - }); - } else { - // synchronously try each address to try to connect - __block bool did_connect = false; - - dispatch_sync(dq, ^{ - int s, val = 1; - - s = socket(aires->ai_family, aires->ai_socktype, aires->ai_protocol); - if (s < 0) { - warn("socket"); - return; - } - - if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)) < 0) { - warn("Could not set SO_REUSEADDR"); - } - - if(setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (const char *)&val, sizeof(val)) < 0) { - warn("Could not set SO_REUSEPORT"); - } - - if(setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) < 0) { - warn("Could not set SO_NOSIGPIPE"); - } - - if (do_bind_port || do_bind_ip) { - struct addrinfo bhints, *bind_aires; - int bret; - in_port_t bport; - - bzero(&bhints, sizeof(bhints)); - bhints.ai_family = aires->ai_family; - bhints.ai_socktype = aires->ai_socktype; - bhints.ai_protocol = aires->ai_protocol; - bhints.ai_flags = (do_bind_ip ? AI_NUMERICHOST : 0) | (do_bind_port ? AI_NUMERICSERV : 0) | AI_PASSIVE; - - bret = getaddrinfo(bind_hostname, bind_servname, &bhints, &bind_aires); - if (bret) { - warnx("getaddrinfo(%s, %s): %s", bind_hostname, bind_servname, gai_strerror(bret)); - close(s); - freeaddrinfo(bind_aires); - return; - } - - switch(bind_aires->ai_family) { - case PF_INET: - bport = ((struct sockaddr_in *)bind_aires->ai_addr)->sin_port; - break; - case PF_INET6: - bport = ((struct sockaddr_in6 *)bind_aires->ai_addr)->sin6_port; - break; - default: - bport = htons(0); - break; - } - - if (ntohs(bport) > 0 && ntohs(bport) < IPPORT_RESERVED) { - bret = bindresvport_sa(s, (struct sockaddr *)bind_aires->ai_addr); - } else { - bret = bind(s, bind_aires->ai_addr, bind_aires->ai_addrlen); - } - - if (bret < 0) { - warn("bind"); - close(s); - freeaddrinfo(bind_aires); - return; - } - - freeaddrinfo(bind_aires); - } - - if (connect(s, aires->ai_addr, aires->ai_addrlen) < 0) { - syslog(LOG_INFO, "connect to %s port %s (%s) failed: %s", - hostname, - servname, - aires->ai_protocol == IPPROTO_TCP ? "tcp" : aires->ai_protocol == IPPROTO_UDP ? "udp" : "unknown", - strerror(errno)); - close(s); - return; - } - - syslog(LOG_INFO, "Connection to %s %s port [%s] succeeded!", - hostname, - servname, - aires->ai_protocol == IPPROTO_TCP ? "tcp" : aires->ai_protocol == IPPROTO_UDP ? "udp" : "unknown"); - did_connect = true; - - if (do_udp) { - // netcat sends a few bytes to set up the connection - doreadwrite(-1, s, "XXXX", 4); - } - - setup_fd_relay(s, no_stdin ? -1 : STDIN_FILENO, STDOUT_FILENO, ^{ - close(s); - exit(0); - }); - }); - - if (did_connect) { - break; - } - } - } - - dispatch_group_wait(listen_group, DISPATCH_TIME_FOREVER); - freeaddrinfo(aires0); - - if (!do_listen && aires == NULL) { - // got to the end of the address list without connecting - exit(1); - } - - dispatch_main(); - - return 0; -} - -void usage(void) -{ - fprintf(stderr, "Usage: %s [-4] [-6] [-D] [-d] [-h] [-k] [-l] [-n] [-v]\n", getprogname()); - fprintf(stderr, " \t[-u] [-p ] [-s ]\n"); - exit(EX_USAGE); -} - -void *run_block(void *arg) -{ - void (^b)(void) = (void (^)(void))arg; - - b(); - - _Block_release(arg); - - return NULL; -} - -/* - * Read up-to as much as is requested, and write - * that to the other fd, taking into account exceptional - * conditions and re-trying - */ -void doreadwrite(int fd1, int fd2, char *buffer, size_t len) { - ssize_t readBytes, writeBytes, totalWriteBytes; - - if (fd1 != -1) { - syslog(LOG_DEBUG, "trying to read %ld bytes from fd %d", len, fd1); - readBytes = read(fd1, buffer, len); - if (readBytes < 0) { - if (errno == EINTR || errno == EAGAIN) { - /* can't do anything now, hope we get called again */ - syslog(LOG_DEBUG, "error read fd %d: %s (%d)", fd1, strerror(errno), errno); - return; - } else { - err(1, "read fd %d", fd1); - } - } else if (readBytes == 0) { - syslog(LOG_DEBUG, "EOF on fd %d", fd1); - return; - } - syslog(LOG_DEBUG, "read %ld bytes from fd %d", readBytes, fd1); - } else { - readBytes = len; - syslog(LOG_DEBUG, "read buffer has %ld bytes", readBytes); - } - - totalWriteBytes = 0; - do { - writeBytes = write(fd2, buffer+totalWriteBytes, readBytes-totalWriteBytes); - if (writeBytes < 0) { - if (errno == EINTR || errno == EAGAIN) { - continue; - } else { - err(1, "write fd %d", fd2); - } - } - syslog(LOG_DEBUG, "wrote %ld bytes to fd %d", writeBytes, fd2); - totalWriteBytes += writeBytes; - - } while (totalWriteBytes < readBytes); - - return; -} - -/* - * We set up dispatch sources for netfd and infd. - * Since only one callback is called at a time per-source, - * we don't need any additional serialization, and the network - * and infd could be read from at the same time. - */ -void setup_fd_relay(int netfd /* bidirectional */, - int infd /* local input */, - int outfd /* local output */, - void (^finalizer_block)(void)) -{ - dispatch_source_t netsource = NULL, insource = NULL; - - dispatch_queue_t teardown_queue = dispatch_queue_create("teardown_queue", NULL); - - void (^finalizer_block_copy)(void) = _Block_copy(finalizer_block); // release after calling - void (^cancel_hander)(dispatch_source_t source) = ^(dispatch_source_t source){ - dlog(source); - dlog(teardown_queue); - - /* - * allowing the teardown queue to become runnable will get - * the teardown block scheduled, which will cancel all other - * sources and call the client-supplied finalizer - */ - dispatch_resume(teardown_queue); - dispatch_release(teardown_queue); - }; - void (^event_handler)(dispatch_source_t source, int wfd) = ^(dispatch_source_t source, int wfd) { - int rfd = dispatch_source_get_handle(source); - size_t bytesAvail = dispatch_source_get_data(source); - char *buffer; - - syslog(LOG_DEBUG, "dispatch source %d -> %d has %lu bytes available", - rfd, wfd, bytesAvail); - if (bytesAvail == 0) { - dlog(source); - dispatch_source_cancel(source); - return; - } - buffer = malloc(BUFFER_SIZE); - doreadwrite(rfd,wfd, buffer, MIN(BUFFER_SIZE, bytesAvail+2)); - free(buffer); - }; - - /* - * Suspend this now twice so that neither source can accidentally resume it - * while we're still setting up the teardown block. When either source - * gets an EOF, the queue is resumed so that it can teardown the other source - * and call the client-supplied finalizer - */ - dispatch_suspend(teardown_queue); - dispatch_suspend(teardown_queue); - - if (infd != -1) { - dispatch_retain(teardown_queue); // retain so that we can resume in this block later - - dlog(teardown_queue); - - // since the event handler serializes, put this on a concurrent queue - insource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); - dispatch_source_set_event_handler(insource, ^{ event_handler(insource, netfd); }); - dispatch_source_set_cancel_handler(insource, ^{ cancel_hander(insource); }); - dispatch_resume(insource); - dlog(insource); - } - - dispatch_retain(teardown_queue); // retain so that we can resume in this block later - - dlog(teardown_queue); - - // since the event handler serializes, put this on a concurrent queue - netsource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, netfd, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); - dispatch_source_set_event_handler(netsource, ^{ event_handler(netsource, outfd); }); - dispatch_source_set_cancel_handler(netsource, ^{ cancel_hander(netsource); }); - dispatch_resume(netsource); - dlog(netsource); - - dispatch_async(teardown_queue, ^{ - syslog(LOG_DEBUG, "Closing connection on fd %d -> %d -> %d", infd, netfd, outfd); - - if (insource) { - dlog(insource); - dispatch_source_cancel(insource); - dispatch_release(insource); // matches initial create - dlog(insource); - } - - dlog(netsource); - dispatch_source_cancel(netsource); - dispatch_release(netsource); // matches initial create - dlog(netsource); - - dlog(teardown_queue); - - finalizer_block_copy(); - _Block_release(finalizer_block_copy); - }); - - /* Resume this once so their either source can do the second resume - * to start the teardown block running - */ - dispatch_resume(teardown_queue); - dispatch_release(teardown_queue); // matches initial create - dlog(teardown_queue); -} - diff --git a/examples/Dispatch Samples/proc.c b/examples/Dispatch Samples/proc.c deleted file mode 100644 index 511b42f..0000000 --- a/examples/Dispatch Samples/proc.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -extern char **environ; -dispatch_queue_t qpf; -volatile int exitcount = 0; - -// maximum value for exitcount before we quit -#define proccount 2 - - -struct qp_msg { - FILE *f; - char *str; -}; - -void qpf_puts(void *m_) { - struct qp_msg *m = m_; - fputs(m->str, m->f); - free(m->str); - free(m); -} - -void qfprintf(FILE *f, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - struct qp_msg *m = malloc(sizeof(struct qp_msg)); - assert(m); - vasprintf(&m->str, fmt, ap); - m->f = f; - dispatch_async(qpf, ^(void) { qpf_puts(m); }); - va_end(ap); -} - -#define qprintf(fmt...) qfprintf(stdout, ## fmt) - -/* context structure, contains a process id and the - * command line arguments used to launch it. Used to - * provide context info to the block associated - * with a process event source. - */ -struct pinfo { - pid_t pid; - dispatch_source_t source; - char **argv; -}; - -/* pid_finalize() is called when the dispatch source is released. - * this block is attached to the attribute that is passed to dispatch_source_proc_create(), - * and is thus associated with the dispatch source. */ -void pid_finalize(struct pinfo *pi) { - qprintf("process %d is done watching %s (%d)\n", getpid(), pi->argv[0], pi->pid); - dispatch_release(pi->source); - if (OSAtomicIncrement32(&exitcount) == proccount) { - qprintf("both processes exited\n"); - dispatch_sync(qpf,^{}); - exit(0); - } -} - - -/* pid_event() is called from a block that is associated with a process event - * source for a specific process id (via dispatch_source_proc_create()). When - * such an event occurs, pid_event() calls dispatch_source_get_context() to - * gain access to the pid and process name that were stored in the context at - * the time the block was attached to the event source. - */ -#define FLAG(X) ((dispatch_source_get_data(src) & DISPATCH_PROC_##X) ? #X" " : "") - -void pid_event(struct pinfo *pi) { - dispatch_source_t src = pi->source; - - qprintf("process %d %s, flags: %x %s%s%s%s\n", pi->pid, pi->argv[0], dispatch_source_get_data(src), FLAG(EXIT), FLAG(FORK), FLAG(EXEC), FLAG(SIGNAL)); - if (dispatch_source_get_data(src) & DISPATCH_PROC_EXIT) { - int s; - waitpid(dispatch_source_get_handle(src), &s, WNOHANG); - qprintf(" %s exit status %d\n", pi->argv[0], s); - dispatch_source_cancel(src); - } -} - -/* proc_start() takes a context pointer (ppi), and a dispatch queue (pq), - * and spawns the process named in ppi->argv[0]. The resulting process id - * is stored in the context (ppi->pid). On successfully spawning the process, - * it creates a dispatch source for the purpose of executing the routine pid_event(pi,ev) - * when certain events (exit, fork, exec, reap, or signal) occur to the process. - */ -void proc_start(void *ppi, dispatch_queue_t pq) { - struct pinfo *pi = ppi; - - int rc = posix_spawnp(&pi->pid, pi->argv[0], NULL, NULL, pi->argv, environ); - if (rc) { - int e = errno; - qprintf("Can't spawn %s (rc=%d, e=%d %s)\n", pi->argv[0], rc, e, strerror(e)); - } else { - - dispatch_source_t dsp = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pi->pid, DISPATCH_PROC_EXIT|DISPATCH_PROC_FORK|DISPATCH_PROC_EXEC|DISPATCH_PROC_SIGNAL, pq); - dispatch_source_set_event_handler_f(dsp, (dispatch_function_t)pid_event); - dispatch_source_set_cancel_handler_f(dsp, (dispatch_function_t)pid_finalize); - pi->source = dsp; - dispatch_set_context(dsp, pi); - dispatch_resume(dsp); - - qprintf("process %d spawned %s: %d, watching with event source: %p\n", getpid(), pi->argv[0], pi->pid, dsp); - - } -} - -int main(int argc, char *argv[]) { - struct pinfo pi, pi2, pi3; - struct pinfo *ppi2 = & pi2, *ppi3 = &pi3; - - char *av[] = {argv[0], NULL}; // set up context info (struct pinfo) for this process. - pi.pid = getpid(); - pi.argv = av; - - char *av2[] = {"sleep", "3", NULL}; // set up context info (struct pinfo) for the sleep tool - pi2.argv = av2; - - char *av3[] = {"script", "/tmp/LOG", "banner", "-w80", "!", NULL}; // set up context info (struct pinfo) for the script tool - pi3.argv = av3; - - dispatch_queue_t pq = dispatch_queue_create("PQ", NULL); // create our main processing queue - - qpf = dispatch_queue_create("qprintf", NULL); // create a separate queue for printf - - /* create a dispatch source that will call the routine pid_event(pi,ev) - * when certain events occur to the specified process (pi->pid). The dispatch source is - * associated with the dispatch queue that was created in this routine (pq). This example - * requests the block be executed whenever one of the following events occurs: - * exit, fork, exec, reap, or signal. - */ - dispatch_source_t procSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pi.pid, DISPATCH_PROC_EXIT|DISPATCH_PROC_FORK|DISPATCH_PROC_EXEC|DISPATCH_PROC_SIGNAL, pq); - - dispatch_source_set_event_handler_f(procSource, (dispatch_function_t)pid_event); - dispatch_source_set_cancel_handler_f(procSource, (dispatch_function_t)pid_finalize); - pi.source = procSource; - dispatch_set_context(procSource, &pi); - dispatch_resume(procSource); - - /* create a block (which simply calls proc_start()), and dispatch it to the queue. - * proc_start() will spawn the process named by ppiX->argv[0], and set up - * another block (containing a call to pid_event()) on an event source that - * will recieve process events... - */ - dispatch_async(pq, ^(void) { proc_start( ppi2, pq ); }); // launch the sleep tool, and create the process watcher for it - dispatch_async(pq, ^(void) { proc_start( ppi3, pq ); }); // launch the script tool, and create the process watcher for it - - - dispatch_main(); // wait for all the queued and spawned items to finish... -} diff --git a/examples/Dispatch Samples/readFile.c b/examples/Dispatch Samples/readFile.c deleted file mode 100644 index 9c537c5..0000000 --- a/examples/Dispatch Samples/readFile.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -int main(int argc, char* argv[]) -{ - int infd; - dispatch_source_t fileSource; - - if (argc != 2) { - fprintf(stderr, "usage: %s file ...\n", argv[0]); - exit(1); - } - - - infd = open(argv[1], O_RDONLY); - if (infd == -1) { - perror(argv[1]); - exit(1); - } - - if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) { - perror(argv[1]); - exit(1); - } - - fileSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, dispatch_queue_create("read source queue",NULL)); - - dispatch_source_set_event_handler( fileSource, ^{ - char buffer[10]; - size_t estimated = dispatch_source_get_data(fileSource); - printf("Estimated bytes available: %ld\n", estimated); - ssize_t actual = read(infd, buffer, sizeof(buffer)); - if (actual == -1) { - if (errno != EAGAIN) { - perror("read"); - exit(-1); - } - } else { - if (estimated>actual) { - printf(" bytes read: %ld\n", actual); - } else { - // end of file has been reached. - printf(" last bytes read: %ld\n", actual); - dispatch_source_cancel(fileSource); - } - } - }); - - dispatch_source_set_cancel_handler( fileSource, ^{ - // release all our associated dispatch data structures - dispatch_release(fileSource); - dispatch_release(dispatch_get_current_queue()); - // close the file descriptor because we are done reading it - close(infd); - // and since we have nothing left to do, exit the tool - exit(0); - - }); - - dispatch_resume(fileSource); - - dispatch_main(); - - return 0; -} diff --git a/examples/Dispatch Samples/readFileF.c b/examples/Dispatch Samples/readFileF.c deleted file mode 100644 index 6546714..0000000 --- a/examples/Dispatch Samples/readFileF.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - - -void readFileData(void* theSource) { - char buffer[10]; - size_t estimated = dispatch_source_get_data(theSource); - printf("Estimated bytes available: %ld\n", estimated); - ssize_t actual = read(dispatch_source_get_handle(theSource), buffer, sizeof(buffer)); - if (actual == -1) { - if (errno != EAGAIN) { - perror("read"); - exit(-1); - } - } else { - if (estimated>actual) { - printf(" bytes read: %ld\n", actual); - } else { - // end of file has been reached. - printf(" last bytes read: %ld\n", actual); - dispatch_source_cancel(theSource); - } - } -} - -void cancelSource(void* theSource) { - close(dispatch_source_get_handle(theSource)); - dispatch_release(theSource); - dispatch_release(dispatch_get_current_queue()); - printf("Everything is finished, goodbye.\n"); - exit(0); -} - -int main(int argc, char* argv[]) -{ - int infd; - dispatch_source_t fileSource; - - if (argc != 2) { - fprintf(stderr, "usage: %s file ...\n", argv[0]); - exit(1); - } - - - infd = open(argv[1], O_RDONLY); - if (infd == -1) { - perror(argv[1]); - exit(1); - } - - if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) { - perror(argv[1]); - exit(1); - } - - fileSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, dispatch_queue_create("read source queue",NULL)); - dispatch_source_set_event_handler_f( fileSource, readFileData); - dispatch_source_set_cancel_handler_f( fileSource, cancelSource); - // setting the context pointer to point to the source itself means the functions will get the source - // as a paremeter, from there they can get all the information they need. - dispatch_set_context(fileSource, fileSource); - dispatch_resume(fileSource); - - dispatch_main(); - - return 0; -} diff --git a/examples/Dispatch Samples/timers.c b/examples/Dispatch Samples/timers.c deleted file mode 100644 index 7dc9f8c..0000000 --- a/examples/Dispatch Samples/timers.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include - -#include - -int main(int argc, char* argv[]) -{ - dispatch_source_t theTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_queue_create("timer queue",NULL)); - - __block int i = 0; - - printf("Starting to count by seconds\n"); - - dispatch_source_set_event_handler(theTimer, ^{ - printf("%d\n", ++i); - if (i >= 6) { - printf("i>6\n"); - dispatch_source_cancel(theTimer); - } - if (i == 3) { - printf("switching to half seconds\n"); - dispatch_source_set_timer(theTimer, DISPATCH_TIME_NOW, NSEC_PER_SEC / 2, 0); - } - }); - - dispatch_source_set_cancel_handler(theTimer, ^{ - printf("dispatch source canceled OK\n"); - dispatch_release(theTimer); - exit(0); - }); - - dispatch_source_set_timer(theTimer, dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC) , NSEC_PER_SEC, 0); - - dispatch_resume(theTimer); - dispatch_main(); - - return 0; -} diff --git a/examples/DispatchLife/DispatchLife.c b/examples/DispatchLife/DispatchLife.c deleted file mode 100644 index 0871e4a..0000000 --- a/examples/DispatchLife/DispatchLife.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ -/*! - @header Life - An asynchronous variation of Conway's Game of Life implemented with - GCD. Like the classic version, the game board consists of a grid of - cells that can live, die or multiply by the following rules[1]: - - 1. Survivals. Every [living cell] with two or three neighboring - [living cells] survives for the next generation. - 2. Deaths. Each [living cell] wiht four or more neighbors dies (is - removed) from overpopulation. Every [living cell] with one - neighbor or none dies from isolation. - 3. Births. Each empty cell adjacent to exactly three neighbors--no - more, no fewer--is a birth cell. A [living cell] is placed on it - at the next move. - - However, unlike the classic version, not all deaths and births occur - simultaneously in a single, synchronous, "move" of the game board. - Instead the rules are applies to each cell independently based on its - observations of the cells around it. - - Each cell is backed by a GCD queue which manages the synchronization - of the cells internal state (living or dead). When a cell's state - changes, a notification in the form of a dispatch_call() of the - cell_needs_update() work function is sent to all adjacent cells so - that the state of those cells may be re-evaluated. - - To re-evaluate the state of a cell, a request of the current state of - all adjecent cells is sent in the form of a dispatch_call() of the - _cell_is_alive() work function. The state of the adjacent cells is - returned to the requestor via the _cell_is_alive_callback() completion - callback. Once all outstanding completion callbacks have been - received, the cell updates its state according to the aforementioned - rules. If the application of these rules results in another state - change, the update_cell() notification is once again sent out, - repeating the process. - - Due to the highly asynchronous nature of this implementation, the - simulation's results may differ from the classic version for the same - set of initial conditions. In particular, due to non-deterministic - scheduling factors, the same set of initial condiitions is likely to - produce dramatically different results on subsequent simulations. - - [1] Martin Gardner. "MATHEMATICAL GAMES: The fantastic combinations of - John Conway's new solitaire game 'life'" Scientific American 223 - (October 1970): 120-123. - - @copyright Copyright (c) 2008-2009 Apple Inc. All rights reserved. - @updated 2009-03-31 -*/ -//////////////////////////////////////////////////////////////////////////////// - -// Adjustable parameters -unsigned long grid_x_size = 40; -unsigned long grid_y_size = 20; - -int use_curses = 1; - -//////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include - -#define CELL_MAX_NEIGHBORS 8 - -struct cell { - dispatch_queue_t q; - int alive; - char display; - - // tracks whether a update_cell() notification arrived while - // an update was already in progress - int needs_update; - int living_neighbors; - int queries_outstanding; - - struct cell* neighbors[CELL_MAX_NEIGHBORS]; - char* label; -} __attribute__((aligned(64))); - -//////////////////////////////////////////////////////////////////////////////// - -/*! @function init_grid - Initializes the grid data structure based on the global variables - grid_x_size and grid_y_size. Must be called before any calls to - cell_set_alive. */ -struct cell* init_grid(size_t grid_x_size, size_t grid_y_size); - -/*! @function init_display - Initializes the display subsystem. Starts a periodic timer to update the - display based on the current contents of the cell grid. - */ -void init_display(struct cell* grid); - -//////////////////////////////////////////////////////////////////////////////// - -// Macro to test whether x,y coordinates are within bounds of the grid -#define GRID_VALID(u,v) (((u) >= 0) && ((v) >= 0) && \ - ((u) < grid_x_size) && ((v) < grid_y_size)) -// Macro to translate from 2d grid coordinates to array offest -#define GRID_OFF(u,v) ((v) * grid_x_size + (u)) - -#if !defined(DISPATCH_LIFE_GL) -int main(int argc, char* argv[]) { - - struct ttysize tsz; - int res; - - res = ioctl(STDIN_FILENO, TIOCGWINSZ, &tsz); - if (res == 0) { - grid_x_size = tsz.ts_cols; - grid_y_size = tsz.ts_lines; - } - - int dispflag = 1; - int ch; - - while ((ch = getopt(argc, argv, "x:y:q")) != -1) { - char* endptr; - switch (ch) { - case 'x': - grid_x_size = strtol(optarg, &endptr, 10); - if (grid_x_size < 0 || (endptr && *endptr != 0)) { - fprintf(stderr, "life: invalid x size\n"); - exit(1); - } - break; - case 'y': - grid_y_size = strtol(optarg, &endptr, 10); - if (grid_y_size < 0 || (endptr && *endptr != 0)) { - fprintf(stderr, "life: invalid y size\n"); - exit(1); - } - break; - case 'q': - dispflag = 0; - break; - case '?': - default: - fprintf(stderr, "usage: life [-q] [-x size] [-y size]\n"); - fprintf(stderr, "\t-x: grid x size (default is terminal columns)\n"); - fprintf(stderr, "\t-y: grid y size (default is terminal rows)\n"); - fprintf(stderr, "\t-q: suppress display output\n"); - exit(1); - } - } - - struct cell* grid = init_grid(grid_x_size, grid_y_size); - - if (dispflag) { - init_display(grid); - if (use_curses) { - initscr(); cbreak(); noecho(); - nonl(); - intrflush(stdscr, FALSE); - keypad(stdscr, TRUE); - } - } - - dispatch_main(); - - if (dispflag && use_curses) { - endwin(); - } - - return 0; -} -#endif /* defined(DISPATCH_LIFE_GL) */ - -//////////////////////////////////////////////////////////////////////////////// - -static void cell_set_alive(struct cell*, int alive); - -/*! @function update_cell - GCD work function. Begins the update process for a cell by - sending cell_is_alive() messages with cell_is_alive_callback() - completion callbacks to all adjacent cells. If an update is already - in progress, simply sets the needs_update flag of the cell. */ -static void update_cell(struct cell*); - -/*! @function cell_is_alive_callback - GCD completion callback. Receives the result from cell_is_alive. When - all _cell_is_alive_callback() completion callbacks have been received - from an update, recalculates the internal state of the cell. If the - state changes, sends update_cell() to all adjacent cells. */ -static void update_cell_response(struct cell*, int); - -//////////////////////////////////////////////////////////////////////////////// - -void -foreach_neighbor(struct cell* self, void (^action)(struct cell* other)) { - int i; - for (i = 0; i < CELL_MAX_NEIGHBORS; ++i) { - struct cell* other = self->neighbors[i]; - if (other) { - action(other); - } - } -} - - -// Change cell state, update the screen, and update neighbors. -void -cell_set_alive(struct cell* self, int alive) { - if (alive == self->alive) return; // nothing to do - - dispatch_async(self->q, ^{ - self->alive = alive; - self->display = (self->alive) ? '#' : ' '; - - foreach_neighbor(self, ^(struct cell* other) { - dispatch_async(other->q, ^{ update_cell(other); }); - }); - }); -} - -void -update_cell(struct cell* self) { - if (self->queries_outstanding == 0) { - self->needs_update = 0; - self->living_neighbors = 0; - - foreach_neighbor(self, ^(struct cell* other) { - ++self->queries_outstanding; - dispatch_async(other->q, ^{ - dispatch_async(self->q, ^{ update_cell_response(self, other->alive); }); - }); - }); - - // '.' indicates the cell is not alive but needs an update - if (!self->alive) self->display = '.'; - } else { - self->needs_update = 1; - } -} - -void -update_cell_response(struct cell* self, int response) { - if (response) ++self->living_neighbors; - --self->queries_outstanding; - - // when all neighbors have replied with their state, - // recalculate our internal state - if (self->queries_outstanding == 0) { - const int living_neighbors = self->living_neighbors; - int alive = self->alive; - - // Conway's Game of Life - if (living_neighbors < 2 || living_neighbors > 3) { - alive = 0; - } else if (living_neighbors == 3) { - alive = 1; - } - - // Notify neighbors of state change - cell_set_alive(self, alive); - - // if a request for an update came in while we were - // already processing one, kick off the next update - if (self->needs_update) { - dispatch_async(self->q, ^{ update_cell(self); }); - } else { - // otherwise clear the '.' character that was - // displayed during the update - if (!self->alive) { - self->display = ' '; - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// - -struct cell* -init_grid(size_t grid_x_size, size_t grid_y_size) { - struct cell* grid = calloc(sizeof(struct cell),grid_x_size*grid_y_size); - - int i,j; - for (i = 0; i < grid_x_size; ++i) { - for (j = 0; j < grid_y_size; ++j) { - struct cell* ptr = &grid[GRID_OFF(i,j)]; - - asprintf(&ptr->label, "x%dy%d", i, j); - - ptr->q = dispatch_queue_create(ptr->label, NULL); - dispatch_set_target_queue(ptr->q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)); - dispatch_queue_set_context(ptr->q, ptr); - - ptr->neighbors[0] = GRID_VALID(i ,j-1) ? - &grid[GRID_OFF(i ,j-1)] : NULL; // N - ptr->neighbors[1] = GRID_VALID(i+1,j-1) ? - &grid[GRID_OFF(i+1,j-1)] : NULL; // NE - ptr->neighbors[2] = GRID_VALID(i+1,j ) ? - &grid[GRID_OFF(i+1,j )] : NULL; // E - ptr->neighbors[3] = GRID_VALID(i+1,j+1) ? - &grid[GRID_OFF(i+1,j+1)] : NULL; // SE - ptr->neighbors[4] = GRID_VALID(i ,j+1) ? - &grid[GRID_OFF(i ,j+1)] : NULL; // S - ptr->neighbors[5] = GRID_VALID(i-1,j+1) ? - &grid[GRID_OFF(i-1,j+1)] : NULL; // SW - ptr->neighbors[6] = GRID_VALID(i-1,j ) ? - &grid[GRID_OFF(i-1,j )] : NULL; // W - ptr->neighbors[7] = GRID_VALID(i-1,j-1) ? - &grid[GRID_OFF(i-1,j-1)] : NULL; // NW - } - } - - srandomdev(); - for (i = 0; i < grid_x_size; ++i) { - for (j = 0; j < grid_y_size; ++j) { - if (random() & 1) { - cell_set_alive(&grid[GRID_OFF(i,j)], 1); - } - } - } - - return grid; -} - -#if defined(DISPATCH_LIFE_GL) -char -get_grid_display_char(struct cell* grid, size_t x, size_t y) { - return grid[GRID_OFF(x,y)].display; -} -#endif /* defined(DISPATCH_LIFE_GL) */ - -#if !defined(DISPATCH_LIFE_GL) -void -init_display(struct cell* grid) -{ - dispatch_source_t timer; - - timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - dispatch_source_set_timer(dispatch_time(DISPATCH_TIME_NOW, 0). 10000000, 1000); - dispatch_source_set_event_handler(^{ - int x,y; - x = 0; - for (x = 0; x < grid_x_size; ++x) { - for (y = 0; y < grid_y_size; ++y) { - mvaddnstr(y, x, &grid[GRID_OFF(x,y)].display, 1); - } - } - refresh(); - }); - dispatch_resume(timer); -} -#endif /* defined(DISPATCH_LIFE_GL) */ diff --git a/examples/DispatchLife/DispatchLife.xcodeproj/project.pbxproj b/examples/DispatchLife/DispatchLife.xcodeproj/project.pbxproj deleted file mode 100644 index 6897278..0000000 --- a/examples/DispatchLife/DispatchLife.xcodeproj/project.pbxproj +++ /dev/null @@ -1,252 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 45; - objects = { - -/* Begin PBXBuildFile section */ - 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; }; - 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; - 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; - 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; - FC0615200DF53162002BF852 /* DispatchLifeGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = FC06151F0DF53162002BF852 /* DispatchLifeGLView.m */; }; - FC0615450DF535BD002BF852 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC0615440DF535BD002BF852 /* OpenGL.framework */; }; - FC787BF60DF67AAF009415DA /* DispatchLife.c in Sources */ = {isa = PBXBuildFile; fileRef = FC787BF50DF67AAF009415DA /* DispatchLife.c */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; - 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; - 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; - 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; - 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 8D1107320486CEB800E47090 /* DispatchLife.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DispatchLife.app; sourceTree = BUILT_PRODUCTS_DIR; }; - FC06151E0DF53162002BF852 /* DispatchLifeGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DispatchLifeGLView.h; sourceTree = ""; }; - FC06151F0DF53162002BF852 /* DispatchLifeGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DispatchLifeGLView.m; sourceTree = ""; }; - FC0615440DF535BD002BF852 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; - FC787BF50DF67AAF009415DA /* DispatchLife.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DispatchLife.c; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8D11072E0486CEB800E47090 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, - FC0615450DF535BD002BF852 /* OpenGL.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 080E96DDFE201D6D7F000001 /* Classes */ = { - isa = PBXGroup; - children = ( - FC06151E0DF53162002BF852 /* DispatchLifeGLView.h */, - FC06151F0DF53162002BF852 /* DispatchLifeGLView.m */, - ); - name = Classes; - sourceTree = ""; - }; - 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { - isa = PBXGroup; - children = ( - 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, - FC0615440DF535BD002BF852 /* OpenGL.framework */, - ); - name = "Linked Frameworks"; - sourceTree = ""; - }; - 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { - isa = PBXGroup; - children = ( - 29B97324FDCFA39411CA2CEA /* AppKit.framework */, - 29B97325FDCFA39411CA2CEA /* Foundation.framework */, - ); - name = "Other Frameworks"; - sourceTree = ""; - }; - 19C28FACFE9D520D11CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8D1107320486CEB800E47090 /* DispatchLife.app */, - ); - name = Products; - sourceTree = ""; - }; - 29B97314FDCFA39411CA2CEA /* DispatchLife */ = { - isa = PBXGroup; - children = ( - 080E96DDFE201D6D7F000001 /* Classes */, - 29B97315FDCFA39411CA2CEA /* Other Sources */, - 29B97317FDCFA39411CA2CEA /* Resources */, - 29B97323FDCFA39411CA2CEA /* Frameworks */, - 19C28FACFE9D520D11CA2CBB /* Products */, - ); - name = DispatchLife; - sourceTree = ""; - }; - 29B97315FDCFA39411CA2CEA /* Other Sources */ = { - isa = PBXGroup; - children = ( - FC787BF50DF67AAF009415DA /* DispatchLife.c */, - 29B97316FDCFA39411CA2CEA /* main.m */, - ); - name = "Other Sources"; - sourceTree = ""; - }; - 29B97317FDCFA39411CA2CEA /* Resources */ = { - isa = PBXGroup; - children = ( - 8D1107310486CEB800E47090 /* Info.plist */, - 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, - 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, - ); - name = Resources; - sourceTree = ""; - }; - 29B97323FDCFA39411CA2CEA /* Frameworks */ = { - isa = PBXGroup; - children = ( - 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, - 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8D1107260486CEB800E47090 /* DispatchLife */ = { - isa = PBXNativeTarget; - buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "DispatchLife" */; - buildPhases = ( - 8D1107290486CEB800E47090 /* Resources */, - 8D11072C0486CEB800E47090 /* Sources */, - 8D11072E0486CEB800E47090 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DispatchLife; - productInstallPath = "$(HOME)/Applications"; - productName = DispatchLife; - productReference = 8D1107320486CEB800E47090 /* DispatchLife.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 29B97313FDCFA39411CA2CEA /* Project object */ = { - isa = PBXProject; - buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "DispatchLife" */; - compatibilityVersion = "Xcode 3.1"; - hasScannedForEncodings = 1; - mainGroup = 29B97314FDCFA39411CA2CEA /* DispatchLife */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8D1107260486CEB800E47090 /* DispatchLife */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 8D1107290486CEB800E47090 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */, - 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 8D11072C0486CEB800E47090 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8D11072D0486CEB800E47090 /* main.m in Sources */, - FC0615200DF53162002BF852 /* DispatchLifeGLView.m in Sources */, - FC787BF60DF67AAF009415DA /* DispatchLife.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 089C165DFE840E0CC02AAC07 /* English */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - 29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = { - isa = PBXVariantGroup; - children = ( - 29B97319FDCFA39411CA2CEA /* English */, - ); - name = MainMenu.nib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - C01FCF4C08A954540054247B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - INFOPLIST_FILE = Info.plist; - PRODUCT_NAME = DispatchLife; - }; - name = Release; - }; - C01FCF5008A954540054247B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; - GCC_C_LANGUAGE_STANDARD = c99; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - OTHER_CFLAGS = ( - "-DDISPATCH_LIFE_GL", - "-fblocks", - ); - PREBINDING = NO; - SDKROOT = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "DispatchLife" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C01FCF4C08A954540054247B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C01FCF4E08A954540054247B /* Build configuration list for PBXProject "DispatchLife" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C01FCF5008A954540054247B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; -} diff --git a/examples/DispatchLife/DispatchLifeGLView.h b/examples/DispatchLife/DispatchLifeGLView.h deleted file mode 100644 index 7ed6bbd..0000000 --- a/examples/DispatchLife/DispatchLifeGLView.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2009-2009 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -#import - -struct cell; - -// From DispatchLife.c -extern struct cell* init_grid(size_t grid_x_size, size_t grid_y_size); -extern char get_grid_display_char(struct cell* grid, size_t x, size_t y); - -@interface DispatchLifeGLView : NSOpenGLView { - struct cell* grid; - uint32_t* image; -} - -- (void)adjustGLViewBounds; - -@end diff --git a/examples/DispatchLife/DispatchLifeGLView.m b/examples/DispatchLife/DispatchLifeGLView.m deleted file mode 100644 index 5aa843b..0000000 --- a/examples/DispatchLife/DispatchLifeGLView.m +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2009-2009 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -#import "DispatchLifeGLView.h" - -#import - -#include -#include -#include - -#include -#include - -extern size_t grid_x_size; -extern size_t grid_y_size; - -@implementation DispatchLifeGLView - -#define CELL_WIDTH 8 -#define CELL_HEIGHT 8 - -- (void)goFullScreen:(NSOpenGLView*)view { - NSOpenGLPixelFormatAttribute attrs[] = - { - NSOpenGLPFAFullScreen, - - NSOpenGLPFAScreenMask, CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), - - NSOpenGLPFAAccelerated, - NSOpenGLPFANoRecovery, - NSOpenGLPFADoubleBuffer, - 0 - }; - NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; - - NSOpenGLContext* screen = [[NSOpenGLContext alloc] initWithFormat:pixFmt shareContext:[view openGLContext]]; - - CGDisplayErr err = CGCaptureAllDisplays(); - if (err != CGDisplayNoErr) { - [screen release]; - return; - } - - [screen setFullScreen]; - [screen makeCurrentContext]; - - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - [screen flushBuffer]; - glClear(GL_COLOR_BUFFER_BIT); - [screen flushBuffer]; -} - - -- (id)initWithFrame:(NSRect)frame { - NSOpenGLPixelFormatAttribute attrs[] = - { - NSOpenGLPFAAccelerated, - NSOpenGLPFANoRecovery, - NSOpenGLPFADoubleBuffer, - 0 - }; - NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; - - self = [super initWithFrame:frame pixelFormat:pixFmt]; - if (self) { - - [[self openGLContext] makeCurrentContext]; - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glClearColor(1.0, 1.0, 1.0, 1.0); - glColor4f(1.0, 1.0, 1.0, 1.0); - glEnable(GL_RASTER_POSITION_UNCLIPPED_IBM); - glDisable(GL_DITHER); - - grid_x_size = 128; - grid_y_size = 96; - - self->grid = init_grid(grid_x_size, grid_y_size); - size_t image_size = grid_x_size * grid_y_size * sizeof(uint32_t); - self->image = malloc(image_size); - memset(self->image, 0xFF, image_size); - - [self adjustGLViewBounds]; - - [[NSTimer scheduledTimerWithTimeInterval: (1.0f / 15.0) target: self selector:@selector(drawRect:) userInfo:self repeats:true] retain]; - - } - return self; -} - -- (void)drawRect:(NSRect)rect { - [[self openGLContext] makeCurrentContext]; - - glClear(GL_COLOR_BUFFER_BIT); - - NSRect bounds = [self bounds]; - glRasterPos2f(-bounds.size.width/2, -bounds.size.height/2); - glPixelZoom(bounds.size.width/grid_x_size, bounds.size.height/grid_y_size); - - const int width = grid_x_size; - const int height = grid_y_size; - - int x, y; - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - int i = y * width + x; - switch (get_grid_display_char(grid, x, y)) { - case '.': - image[i] = 0xCCCCCCFF; - break; - case '#': - image[i] = 0x000000FF; - break; - case ' ': - image[i] = 0xFFFFFFFF; - break; - default: - image[i] = 0x0000FFFF; - break; - } - } - } - - glDrawPixels(width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, image); - - glFinish(); - - [[self openGLContext] flushBuffer]; - -} - -- (void)adjustGLViewBounds -{ - [[self openGLContext] makeCurrentContext]; - [[self openGLContext] update]; - - NSRect rect = [self bounds]; - - glViewport(0, 0, (GLint) rect.size.width, (GLint) rect.size.height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluOrtho2D(-(rect.size.width/2), rect.size.width/2, -(rect.size.height/2), rect.size.height/2); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - [self setNeedsDisplay:true]; -} - -- (void)update // moved or resized -{ - [super update]; - [self adjustGLViewBounds]; -} - -- (void)reshape // scrolled, moved or resized -{ - [super reshape]; - [self adjustGLViewBounds]; -} - -@end diff --git a/examples/DispatchLife/English.lproj/InfoPlist.strings b/examples/DispatchLife/English.lproj/InfoPlist.strings deleted file mode 100644 index 5e45963c382ba690b781b953a00585212b898ac5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcmW-XQ3`+{5C!MkQ~2$No+IcIkqMDxWCV8j>LCj|yTg2Mz+o9F%uHlf9u}h9EuK`F a!Y*1dX%G66ZqL#C$|bw0ZoP5@jOGW1ArT7z diff --git a/examples/DispatchLife/English.lproj/MainMenu.nib/designable.nib b/examples/DispatchLife/English.lproj/MainMenu.nib/designable.nib deleted file mode 100644 index 0cdc4e1..0000000 --- a/examples/DispatchLife/English.lproj/MainMenu.nib/designable.nib +++ /dev/null @@ -1,2651 +0,0 @@ - - - - 0 - 10A219 - 708 - 994.4 - 404.00 - - com.apple.InterfaceBuilder.CocoaPlugin - 708 - - - YES - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - - YES - - NSApplication - - - FirstResponder - - - NSApplication - - - AMainMenu - - YES - - - DispatchLife - - 1048576 - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - submenuAction: - - DispatchLife - - YES - - - About DispatchLife - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - UHJlZmVyZW5jZXPigKY - , - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Services - - 1048576 - 2147483647 - - - submenuAction: - - Services - - YES - - _NSServicesMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Hide DispatchLife - h - 1048576 - 2147483647 - - - - - - Hide Others - h - 1572864 - 2147483647 - - - - - - Show All - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Quit DispatchLife - q - 1048576 - 2147483647 - - - - - _NSAppleMenu - - - - - File - - 1048576 - 2147483647 - - - submenuAction: - - File - - YES - - - New - n - 1048576 - 2147483647 - - - - - - T3BlbuKApg - o - 1048576 - 2147483647 - - - - - - Open Recent - - 1048576 - 2147483647 - - - submenuAction: - - Open Recent - - YES - - - Clear Menu - - 1048576 - 2147483647 - - - - - _NSRecentDocumentsMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Close - w - 1048576 - 2147483647 - - - - - - Save - s - 1048576 - 2147483647 - - - - - - U2F2ZSBBc+KApg - S - 1179648 - 2147483647 - - - - - - Revert to Saved - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Page Setup... - P - 1179648 - 2147483647 - - - - - - - UHJpbnTigKY - p - 1048576 - 2147483647 - - - - - - - - - Edit - - 1048576 - 2147483647 - - - submenuAction: - - Edit - - YES - - - Undo - z - 1048576 - 2147483647 - - - - - - Redo - Z - 1179648 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Cut - x - 1048576 - 2147483647 - - - - - - Copy - c - 1048576 - 2147483647 - - - - - - Paste - v - 1048576 - 2147483647 - - - - - - Delete - - 1048576 - 2147483647 - - - - - - Select All - a - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Find - - 1048576 - 2147483647 - - - submenuAction: - - Find - - YES - - - RmluZOKApg - f - 1048576 - 2147483647 - - - 1 - - - - Find Next - g - 1048576 - 2147483647 - - - 2 - - - - Find Previous - G - 1179648 - 2147483647 - - - 3 - - - - Use Selection for Find - e - 1048576 - 2147483647 - - - 7 - - - - Jump to Selection - j - 1048576 - 2147483647 - - - - - - - - - Spelling and Grammar - - 1048576 - 2147483647 - - - submenuAction: - - Spelling and Grammar - - YES - - - U2hvdyBTcGVsbGluZ+KApg - : - 1048576 - 2147483647 - - - - - - Check Spelling - ; - 1048576 - 2147483647 - - - - - - Check Spelling While Typing - - 1048576 - 2147483647 - - - - - - Check Grammar With Spelling - - 1048576 - 2147483647 - - - - - - - - - Substitutions - - 1048576 - 2147483647 - - - submenuAction: - - Substitutions - - YES - - - Smart Copy/Paste - f - 1048576 - 2147483647 - - - 1 - - - - Smart Quotes - g - 1048576 - 2147483647 - - - 2 - - - - Smart Links - G - 1179648 - 2147483647 - - - 3 - - - - - - - Speech - - 1048576 - 2147483647 - - - submenuAction: - - Speech - - YES - - - Start Speaking - - 1048576 - 2147483647 - - - - - - Stop Speaking - - 1048576 - 2147483647 - - - - - - - - - - - - Format - - 1048576 - 2147483647 - - - submenuAction: - - Format - - YES - - - Show Fonts - t - 1048576 - 2147483647 - - - - - - Show Colors - C - 1179648 - 2147483647 - - - - - - - - - View - - 1048576 - 2147483647 - - - submenuAction: - - View - - YES - - - Show Toolbar - t - 1572864 - 2147483647 - - - - - - Q3VzdG9taXplIFRvb2xiYXLigKY - - 1048576 - 2147483647 - - - - - - - - - Window - - 1048576 - 2147483647 - - - submenuAction: - - Window - - YES - - - Minimize - m - 1048576 - 2147483647 - - - - - - Zoom - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Bring All to Front - - 1048576 - 2147483647 - - - - - _NSWindowsMenu - - - - - Help - - 1048576 - 2147483647 - - - submenuAction: - - Help - - YES - - - DispatchLife Help - ? - 1048576 - 2147483647 - - - - - - - - _NSMainMenu - - - 15 - 2 - {{384, 348}, {512, 384}} - 1954021376 - DispatchLife - NSWindow - - {3.40282e+38, 3.40282e+38} - - - 256 - - YES - - - 4415 - {512, 384} - - - DispatchLifeGLView - - - {512, 384} - - - - {{0, 0}, {1280, 1002}} - {3.40282e+38, 3.40282e+38} - - - - - YES - - - performMiniaturize: - - - - 37 - - - - arrangeInFront: - - - - 39 - - - - print: - - - - 86 - - - - runPageLayout: - - - - 87 - - - - clearRecentDocuments: - - - - 127 - - - - orderFrontStandardAboutPanel: - - - - 142 - - - - performClose: - - - - 193 - - - - toggleContinuousSpellChecking: - - - - 222 - - - - undo: - - - - 223 - - - - copy: - - - - 224 - - - - checkSpelling: - - - - 225 - - - - paste: - - - - 226 - - - - stopSpeaking: - - - - 227 - - - - cut: - - - - 228 - - - - showGuessPanel: - - - - 230 - - - - redo: - - - - 231 - - - - selectAll: - - - - 232 - - - - startSpeaking: - - - - 233 - - - - delete: - - - - 235 - - - - performZoom: - - - - 240 - - - - performFindPanelAction: - - - - 241 - - - - centerSelectionInVisibleArea: - - - - 245 - - - - toggleGrammarChecking: - - - - 347 - - - - toggleSmartInsertDelete: - - - - 355 - - - - toggleAutomaticQuoteSubstitution: - - - - 356 - - - - toggleAutomaticLinkDetection: - - - - 357 - - - - showHelp: - - - - 360 - - - - orderFrontColorPanel: - - - - 361 - - - - saveDocument: - - - - 362 - - - - saveDocumentAs: - - - - 363 - - - - revertDocumentToSaved: - - - - 364 - - - - runToolbarCustomizationPalette: - - - - 365 - - - - toggleToolbarShown: - - - - 366 - - - - hide: - - - - 367 - - - - hideOtherApplications: - - - - 368 - - - - terminate: - - - - 369 - - - - unhideAllApplications: - - - - 370 - - - - newDocument: - - - - 373 - - - - openDocument: - - - - 374 - - - - - YES - - 0 - - YES - - - - - - -2 - - - RmlsZSdzIE93bmVyA - - - -1 - - - First Responder - - - -3 - - - Application - - - 29 - - - YES - - - - - - - - - - MainMenu - - - 19 - - - YES - - - - - - 56 - - - YES - - - - - - 103 - - - YES - - - - 1 - - - 217 - - - YES - - - - - - 83 - - - YES - - - - - - 81 - - - YES - - - - - - - - - - - - - - - - 75 - - - 3 - - - 80 - - - 8 - - - 78 - - - 6 - - - 72 - - - - - 82 - - - 9 - - - 124 - - - YES - - - - - - 77 - - - 5 - - - 73 - - - 1 - - - 79 - - - 7 - - - 112 - - - 10 - - - 74 - - - 2 - - - 125 - - - YES - - - - - - 126 - - - - - 205 - - - YES - - - - - - - - - - - - - - - - - - 202 - - - - - 198 - - - - - 207 - - - - - 214 - - - - - 199 - - - - - 203 - - - - - 197 - - - - - 206 - - - - - 215 - - - - - 218 - - - YES - - - - - - 216 - - - YES - - - - - - 200 - - - YES - - - - - - - - - 219 - - - - - 201 - - - - - 204 - - - - - 220 - - - YES - - - - - - - - - - 213 - - - - - 210 - - - - - 221 - - - - - 208 - - - - - 209 - - - - - 106 - - - YES - - - - 2 - - - 111 - - - - - 57 - - - YES - - - - - - - - - - - - - - - - 58 - - - - - 134 - - - - - 150 - - - - - 136 - - - 1111 - - - 144 - - - - - 129 - - - 121 - - - 143 - - - - - 236 - - - - - 131 - - - YES - - - - - - 149 - - - - - 145 - - - - - 130 - - - - - 24 - - - YES - - - - - - - - - 92 - - - - - 5 - - - - - 239 - - - - - 23 - - - - - 295 - - - YES - - - - - - 296 - - - YES - - - - - - - 297 - - - - - 298 - - - - - 299 - - - YES - - - - - - 300 - - - YES - - - - - - - 344 - - - - - 345 - - - - - 211 - - - YES - - - - - - 212 - - - YES - - - - - - - 195 - - - - - 196 - - - - - 346 - - - - - 348 - - - YES - - - - - - 349 - - - YES - - - - - - - - 350 - - - - - 351 - - - - - 354 - - - - - 371 - - - YES - - - - - - 372 - - - YES - - - - - - 377 - - - - - - - YES - - YES - 103.IBPluginDependency - 103.ImportedFromIB2 - 106.IBEditorWindowLastContentRect - 106.IBPluginDependency - 106.ImportedFromIB2 - 106.editorWindowContentRectSynchronizationRect - 111.IBPluginDependency - 111.ImportedFromIB2 - 112.IBPluginDependency - 112.ImportedFromIB2 - 124.IBPluginDependency - 124.ImportedFromIB2 - 125.IBPluginDependency - 125.ImportedFromIB2 - 125.editorWindowContentRectSynchronizationRect - 126.IBPluginDependency - 126.ImportedFromIB2 - 129.IBPluginDependency - 129.ImportedFromIB2 - 130.IBPluginDependency - 130.ImportedFromIB2 - 130.editorWindowContentRectSynchronizationRect - 131.IBPluginDependency - 131.ImportedFromIB2 - 134.IBPluginDependency - 134.ImportedFromIB2 - 136.IBPluginDependency - 136.ImportedFromIB2 - 143.IBPluginDependency - 143.ImportedFromIB2 - 144.IBPluginDependency - 144.ImportedFromIB2 - 145.IBPluginDependency - 145.ImportedFromIB2 - 149.IBPluginDependency - 149.ImportedFromIB2 - 150.IBPluginDependency - 150.ImportedFromIB2 - 19.IBPluginDependency - 19.ImportedFromIB2 - 195.IBPluginDependency - 195.ImportedFromIB2 - 196.IBPluginDependency - 196.ImportedFromIB2 - 197.IBPluginDependency - 197.ImportedFromIB2 - 198.IBPluginDependency - 198.ImportedFromIB2 - 199.IBPluginDependency - 199.ImportedFromIB2 - 200.IBPluginDependency - 200.ImportedFromIB2 - 200.editorWindowContentRectSynchronizationRect - 201.IBPluginDependency - 201.ImportedFromIB2 - 202.IBPluginDependency - 202.ImportedFromIB2 - 203.IBPluginDependency - 203.ImportedFromIB2 - 204.IBPluginDependency - 204.ImportedFromIB2 - 205.IBEditorWindowLastContentRect - 205.IBPluginDependency - 205.ImportedFromIB2 - 205.editorWindowContentRectSynchronizationRect - 206.IBPluginDependency - 206.ImportedFromIB2 - 207.IBPluginDependency - 207.ImportedFromIB2 - 208.IBPluginDependency - 208.ImportedFromIB2 - 209.IBPluginDependency - 209.ImportedFromIB2 - 210.IBPluginDependency - 210.ImportedFromIB2 - 211.IBPluginDependency - 211.ImportedFromIB2 - 212.IBPluginDependency - 212.ImportedFromIB2 - 212.editorWindowContentRectSynchronizationRect - 213.IBPluginDependency - 213.ImportedFromIB2 - 214.IBPluginDependency - 214.ImportedFromIB2 - 215.IBPluginDependency - 215.ImportedFromIB2 - 216.IBPluginDependency - 216.ImportedFromIB2 - 217.IBPluginDependency - 217.ImportedFromIB2 - 218.IBPluginDependency - 218.ImportedFromIB2 - 219.IBPluginDependency - 219.ImportedFromIB2 - 220.IBPluginDependency - 220.ImportedFromIB2 - 220.editorWindowContentRectSynchronizationRect - 221.IBPluginDependency - 221.ImportedFromIB2 - 23.IBPluginDependency - 23.ImportedFromIB2 - 236.IBPluginDependency - 236.ImportedFromIB2 - 239.IBPluginDependency - 239.ImportedFromIB2 - 24.IBEditorWindowLastContentRect - 24.IBPluginDependency - 24.ImportedFromIB2 - 24.editorWindowContentRectSynchronizationRect - 29.IBEditorWindowLastContentRect - 29.IBPluginDependency - 29.ImportedFromIB2 - 29.WindowOrigin - 29.editorWindowContentRectSynchronizationRect - 295.IBPluginDependency - 296.IBEditorWindowLastContentRect - 296.IBPluginDependency - 296.editorWindowContentRectSynchronizationRect - 297.IBPluginDependency - 298.IBPluginDependency - 299.IBPluginDependency - 300.IBEditorWindowLastContentRect - 300.IBPluginDependency - 300.editorWindowContentRectSynchronizationRect - 344.IBPluginDependency - 345.IBPluginDependency - 346.IBPluginDependency - 346.ImportedFromIB2 - 348.IBPluginDependency - 348.ImportedFromIB2 - 349.IBPluginDependency - 349.ImportedFromIB2 - 349.editorWindowContentRectSynchronizationRect - 350.IBPluginDependency - 350.ImportedFromIB2 - 351.IBPluginDependency - 351.ImportedFromIB2 - 354.IBPluginDependency - 354.ImportedFromIB2 - 371.IBEditorWindowLastContentRect - 371.IBPluginDependency - 371.IBWindowTemplateEditedContentRect - 371.NSWindowTemplate.visibleAtLaunch - 371.editorWindowContentRectSynchronizationRect - 371.windowTemplate.maxSize - 372.IBPluginDependency - 377.IBPluginDependency - 377.IBViewIntegration.shadowBlurRadius - 377.IBViewIntegration.shadowColor - 377.IBViewIntegration.shadowOffsetHeight - 377.IBViewIntegration.shadowOffsetWidth - 5.IBPluginDependency - 5.ImportedFromIB2 - 56.IBPluginDependency - 56.ImportedFromIB2 - 57.IBEditorWindowLastContentRect - 57.IBPluginDependency - 57.ImportedFromIB2 - 57.editorWindowContentRectSynchronizationRect - 58.IBPluginDependency - 58.ImportedFromIB2 - 72.IBPluginDependency - 72.ImportedFromIB2 - 73.IBPluginDependency - 73.ImportedFromIB2 - 74.IBPluginDependency - 74.ImportedFromIB2 - 75.IBPluginDependency - 75.ImportedFromIB2 - 77.IBPluginDependency - 77.ImportedFromIB2 - 78.IBPluginDependency - 78.ImportedFromIB2 - 79.IBPluginDependency - 79.ImportedFromIB2 - 80.IBPluginDependency - 80.ImportedFromIB2 - 81.IBEditorWindowLastContentRect - 81.IBPluginDependency - 81.ImportedFromIB2 - 81.editorWindowContentRectSynchronizationRect - 82.IBPluginDependency - 82.ImportedFromIB2 - 83.IBPluginDependency - 83.ImportedFromIB2 - 92.IBPluginDependency - 92.ImportedFromIB2 - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - - {{394, 713}, {191, 23}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{596, 852}, {216, 23}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{522, 812}, {146, 23}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{436, 809}, {64, 6}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{608, 612}, {275, 83}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{163, 493}, {240, 243}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{187, 434}, {243, 243}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{608, 612}, {167, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{608, 612}, {241, 103}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{323, 663}, {194, 73}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{525, 802}, {197, 73}} - {{0, 736}, {455, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - - {74, 862} - {{6, 978}, {478, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - {{273, 693}, {231, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - {{475, 832}, {234, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{207, 693}, {173, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - {{231, 634}, {176, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{608, 612}, {215, 63}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{314, 134}, {512, 384}} - com.apple.InterfaceBuilder.CocoaPlugin - {{314, 134}, {512, 384}} - - {{33, 99}, {480, 360}} - {3.40282e+38, 3.40282e+38} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - 3 - MAA - - - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{12, 553}, {220, 183}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{23, 794}, {245, 183}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{121, 533}, {196, 203}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{145, 474}, {199, 203}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 377 - - - - YES - - DispatchLifeGLView - NSOpenGLView - - IBProjectSource - DispatchLifeGLView.h - - - - - YES - - NSApplication - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSApplication.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSApplicationScripting.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSColorPanel.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSHelpManager.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSPageLayout.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSUserInterfaceItemSearching.h - - - - NSBrowser - NSControl - - IBFrameworkSource - AppKit.framework/Headers/NSBrowser.h - - - - NSControl - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSControl.h - - - - NSDocument - NSObject - - YES - - YES - printDocument: - revertDocumentToSaved: - runPageLayout: - saveDocument: - saveDocumentAs: - saveDocumentTo: - - - YES - id - id - id - id - id - id - - - - IBFrameworkSource - AppKit.framework/Headers/NSDocument.h - - - - NSDocument - - IBFrameworkSource - AppKit.framework/Headers/NSDocumentScripting.h - - - - NSDocumentController - NSObject - - YES - - YES - clearRecentDocuments: - newDocument: - openDocument: - saveAllDocuments: - - - YES - id - id - id - id - - - - IBFrameworkSource - AppKit.framework/Headers/NSDocumentController.h - - - - NSFormatter - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFormatter.h - - - - NSMatrix - NSControl - - IBFrameworkSource - AppKit.framework/Headers/NSMatrix.h - - - - NSMenu - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSMenu.h - - - - NSMenuItem - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSMenuItem.h - - - - NSMovieView - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSMovieView.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSAccessibility.h - - - - NSObject - - - - NSObject - - - - NSObject - - - - NSObject - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSDictionaryController.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSDragging.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSFontManager.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSFontPanel.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSKeyValueBinding.h - - - - NSObject - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSNibLoading.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSOutlineView.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSPasteboard.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSSavePanel.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSTableView.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSToolbarItem.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSView.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSClassDescription.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSError.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFileManager.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueObserving.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyedArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObject.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObjectScripting.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSPortCoder.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSRunLoop.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptClassDescription.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptObjectSpecifiers.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptWhoseTests.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSThread.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURL.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLConnection.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLDownload.h - - - - NSOpenGLView - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSOpenGLView.h - - - - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSInterfaceStyle.h - - - - NSResponder - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSResponder.h - - - - NSTableView - NSControl - - - - NSText - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSText.h - - - - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSClipView.h - - - - NSView - - - - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSRulerView.h - - - - NSView - NSResponder - - - - NSWindow - - IBFrameworkSource - AppKit.framework/Headers/NSDrawer.h - - - - NSWindow - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSWindow.h - - - - NSWindow - - IBFrameworkSource - AppKit.framework/Headers/NSWindowScripting.h - - - - - 0 - ../DispatchLifeGL.xcodeproj - 3 - - diff --git a/examples/DispatchLife/English.lproj/MainMenu.nib/keyedobjects.nib b/examples/DispatchLife/English.lproj/MainMenu.nib/keyedobjects.nib deleted file mode 100644 index 05cdecfbf52574b74d6f15bb75a4b6cfa9159b00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19575 zcmb7r2YgfI*Z=d}o7{Vwj@+#7CTY`|Cf!Yw6qLPJX(?sajzC+w>;THVSN28(1OZvf z5(E(tkRKvj5Rkn$$QBh*hQR;aqzx(U@BP1T{b24q-*e7$p0l3k=9U*1R#wHuokAE9 zh(sbJW@?yW%|1Hu@FaN57$e&@=QLy}$@F*b{qUDOTV>ti{1N1nY1Fj>GXd0Vm=V+zdCz zEpSWR7I(+#I0N^Kqh!}-{T3vmUm#8tQ&*Wi!v7(5n_!;|nVJQsh8=i&Ky5&jad zz$@`rcoW`+x8v{e5BMPd2_M3T@iBZ3pT`&QMSK%ZL@kH^gRQ3-KMXmDoY-BlZ&qh=at> z#A)ITah5noTq150zY=$d2gF~*Q{r#p1xb=L$&hZOg!CePNMF*A3?joxJsC|JNFy0f zTFF$h3E7NnPj)7|kX^}ivM>`!Ksd1MJ$N|ur3k!DrzHYD{3d|D(WrjBkC*46_toeMP;HYQMIT>G)y#BG($8~ z^s#8MXpQJw(I(Mm(Js+p(a)kIq7$OiqBElFqKBeKqGzJ#q8DODtPm^3DzTr~UmPZm z6Q_!sh?|OAh&zfqiF=B(#J$A*#f9P`@gQ-9c%*ogc#?Rsc#3$sc!v06@m;DJHIJH4 zeMT*y7E+6-&#A@K5^5>6j9N~8L48TBpjJ|=sMXXO>MLq3wT}9lT2F1DzM(c!-%^{X z&D0j^J8CPnjoMCqPwk-oL+zw~pmtF|QoE@=)Lv>IwVygb9i)Du4pE1xpQ$6%QR*1= z3w4}2L7k*dQKzXh)LH5rb)LFFU8F8im#Hh%Rq7gbow`BYq;65Ssb8r()LrTxb)R}b zJ)|B{zf(`>3i=QFPx=Y{7yXp}oBoG>Mn9)t&@btK=~wh?`VE5^%n%I8h!`A#Iwq6}W5SsT zCX$I_^h`8kV2n%*6U)Rg@k|1f$RshzjEPBM%#4MxGO0`xrYZ9t^FGszY0k7@S~9Je z)=V3wEz^!^&valqGM$*tOc$mr(~U`Ex-;oa2GfJdWO_1LOfRN4(}(HH^ke!n*~|w_ z4wK8|G5L&*8Ndu=3YbEsh#ABbGbKzpQ^^eO5n5VZTzr+4BL=x4335js$P;-XDPoZf z$&msnkqUVuALNVtkUt7QfhY*6kp^i|FbY9B6pF%7IEp}#C<^IObe}HiU2;lnl^tz0 zmHoP;w=B-7taO~goi-J4Ye-qeplsH!OM3IXJX>XDVQyh@VO34n!u-n2oZ@O*W$!NO z!ihj2o0YIJ;n zhE@UfY?keko>>Us#kQV>rTJw;1ZM4wJ8^1ph4=e9SaRIT49ydSvh!9ep5sPCp=y~c ztElAhdpw@QiFv|@$Di`JDTlZKp77=f86M72Jbs`1mB;gV!k5Q$c|vspG;TyOC>FG4 z2N8JVL>vihHZO-Dk(VRLd;%q)M3jV*vm8u;_8bSL@c1&6j7J4)Cy~rT+YrF* znbD>=XJ92LQ@CKqVWpfe=f+7mh9x*3&Ykm+R}Dpo^W#u9>pOgSqB+T+O-wS6G-!t> z$0xwCIcem`zO4!?%X6yo3OW@Iuz@liyk)cg!xLkY;u6dWw&+ANJp1-!IVcz9p?qXRIiTwF>fFJ=l*6C|`|NJ3EH5j~w^abU>DA@73gI?j4X%^H zK4O9%oeXeZoF}J}VZ?cJO3u?Uk_D(}DJtZooZ>71*RhZdc2x|+l!nSsIj~Z}v786z zA(Oq&$$^Nh_giVRS-GpYZ956dbC}#{pTwRp|Q?GeNMNUmmuxR1e1=#ThhlVb9AY`+u;ltw$+Bjh_;uFkpn37^1 zIr4whyMxGBG;SFhhfMYvUu$%>lJer5Dx2eh325SSG!ad*%Tdc$x~-}Q=-yW4P~;R; zw4RMXQ_(au9nC;9(Z^^O`UK5JbI@G$DVm4oqtDO+v=A*qpQFWS30jJlq2=fc^d(w> zR-#pCHCltdLTk}F^fg+KHlT0NMlO)kbFo|!XXW1KT5;{UE?hd7#r5ZMxq)0U_aRr! z)o`P@kGP54G;S6*k6XwsS5RcFM`M6@z`GmsMBf*|K*o=TciqS!rQjr?NczR0c2D`orqN!JW(Ug{cex_Nfa~t7~cd67a=f$aT+v zJ#;P{YRk7@=hX7iZ{EC-Lp(v8h70DjTo@N|3V1jS_&=i~=qNgdegR~`!-3fd479JZ zm4M4k;IWB&&f_F7#1<7W`L)f9i?vl{+BOw1+q<1ar+|bJoknNSS@3gUaCTwsu2RQ^ zav{K`oe8@e5eT;|u;mRZ$*CCR82gLp(lT_Z);JtCTt}pi_!WUT7cNkL+?jBacge@B0yKVcaE5{9v!&JCb*qoDJw&V{9gCEzvEO0ro6 zxRn8A6(z#8oT};yxNJRzUe+3s4!r`nH(&>?Y?W1orS@3E5nLj`$DOrXk<*4S#snto zEQpJ@4>y<4V5G4dmY`$UgU9YX4iJXb6Na^UZdrAe*44{ovq5DQ;QZ`b10|Q{=Tzj| zAM2J=YAd$ZDg?7=E0%K>;3&C)La-96us8NW`>~&0Ato0iOaa!+f$X{)ll>JkVoPUKKP4(HwjR!b`wr7rP5{tF06+&|j%SpW6=xKdb9HKz!(46yU~L63 zPTQ{=H~=lU%j)u&n3%L~{VS?Vg-e}sYG8Doa%BQ=7S|boJ2WO&AKVxB1K8{aa&-i# zP6E^bNH8i;Au2^xFg)d%<=_ zRzcE=9t>TZLU zM3vyC1uKM9+q=SkrB23-GW&HW)+)eS!wmqeyoRE##q03bc)b%Z-$hhg9jG&!;LUgo zh`W`?!8{HXB-ZtAS!~Ox&Z@a>LTRnf4R?wpWt~tg18BmUL<$zMs7|L;c0-pqw(+yBc z0i~=KC9S%!zSm6quy>WMqC{}jZ=o&$)D^A@peh=Jx{hz)n+*i=1q-dL6R3|}oUT;` z&~k!-6a=xKxRfAo^#>seKjMZ0+~CIGe#d`cAw=0TjoLWj(BF`{fy_z=LllBMU{#k2 zXmH#p)(gP;mm3LK!y00}#%~BjU?7p?hj!NW=I|7{*}UM-I)_75OCDo z1i%^70Ef^L!9)n5L;Hy^yP=GA5pf*gjCXMXL==b^&E4aka}P3FmX+6}<@LyeA16{A zASG~90clcWNXdkWNC6~E1Ek4-G(|w_oo*|JbVV4tw4C0R_7gaEA~XYp7Tm{xFugH^ z)r@pKw4`5JRxVSJ9kQy58wm11pq$3A-I+(B#MYZ zPAhO7ozDQ=?kY1|ftsppJ@f59PQsNyxSCr6gcmhNcqmar3xMd<_8W z1ppSju00(jHi9v1;vR!B{9%JOhag@)h&KS*OSS1*8|WC>1#E6_JURK<2K;=_Z3cce zHWYIev6J|L*ah6|wu|Y?#kau4CV>kGARVep%I(3^K8c{mlU4*Nu@JFr6^>a3*V>oX zHm{mj*m9isKLP$>Zad(A*AQPz93_qszX1M;dTJIDpsj$v&93I|Hc)f#9u@H8Bzqpn zUgUNH*&PkZW)N41tHd>@sk_dz{{Y$AtRSHNw9@u0C@i*VGiu6Vj%Bmzsv$mD(N@xv`x$sR)CdnGOUg*OQ@%PLgpBfVoqT1` z$ZZKLJi~6vl>XzYk6?gy@kWD+mX>A3Wfhfa zEg>;`Tk_d2)^i_Z3@{PLodG6JHDH3sAQQsZO_80 z0;iLQY{M}swRhh&JhCY;@IH4D7&zC6qamA+Lu;BHth%%PD6pnM|S6~0Y8@;=cfmmNkZ%+ z?XeGasmIS1;OD9^GJPG$Y1L&_w#qb^hQSWp=GD+H25ZQ(-+}fdv;eBAg?6)(fe(Oz zT<$h7aHDYsY~%oPATUrU7zSDFWI!;Do4~*=fdQ86<6xmvVdohMgB;r%dULb6quig|T^>j9(6_k3#c^vnHIMzdv0OekhR4xdDvuQ$&0}va zo}13&K<*RnHIJ>_B3CdAP+)ttZf>SKY_LnlRrZa8dW#z$dk||084>BNi{Sb z9dVJn>oN<*LjGAB4Y>Qxf{i>)o`KnLj>im-Ey9d&%V?cnSk=GML8~^vItKm{Dq2U( zAr6z9h!%vJs3r!$P;P_4Tn@v!9n|q1h&G)Y!z}{QCUK8IZ7&235}cw|4Cs1-8015t z_enk?wB%#*cc@1WfT7wOXXIbdyKG+xC9=?+&WHY2`^t1%c}_(RbmBPIEKUo7E!a&k zVz>WB`vv*3KJEVxf%-Zov?8&Hf*fXJosLDG^$4tk6s1m~zh?+3Nn=VA$xzXk#FsEb zR*_YZoU8>K{0r=HBd2I&;u5Pw{-OX;po`~lO;euKd5&HJoK{$?Xq-r>C`=UYGW+X@ z2($lb-R$pYC(;d?1BDRr8z&SaiWSA7{h|cHjfj%#rEdQKp=WkCLW2YEQ7Lqwgdzm| zlonc>T7i6;&E9quSwz-lA}cc0HnEL9dS3M@qL*S@o zg2$+zt9Pz!6uciQS`IF(JNWB!FxPMVci?=H4ZntZxK8@wdcfcw$hXL%OpxA|SI{VA zIVxHPe)L~(O~W`vhR|8E&tTDDSdjvk*o#y2YTh}guvF;EIPMHbMJvfQIt_A;F0FHUE;K)my?A7-(7>kyQmc8Q(T>D2Z z&ZI$p123-?trL9>lVF2A$#Qv-$1DepE}1Y1itVjY8yW!lUIqLsg%w5{5?YvsMjaH; z{Z6#?f5%j#(*+3Z7VRM?ikz!QqC>7-9f)H*R=(A`Dl1)B$%G~UcQoz{2BLkUW9XRZ zIK0MVUm+OyIE>>YDq6xRpuEf$7Udj71k?F%f}Inc|33-VV7P9IZvF2F^aYND{*=vy zo%$0RZ1b_`cj&4IIc@WwdbVj2Rvw%)S`D7O*!GTw99nn@Hi?H~Ib;_ba5y&M0=$si zjoUy^J%fX=4Z%B%OND6PmCJ@84l%uh(;(~fgq326*d4FMo9lQI%bmS_p`OTx2I^a$ zLi%)tWe}~SZQ2-XVsBwyuYr^V2FV*#@=k}bsVu!4GEkit6G`goj}=~!ZK z6s{P;AbLdHCx1lSAcfrp$tL8gb-48{1ML=9iL2ikSl5!?#WMPX#W`RrT`aA^IK$=} zJQ;uDlw)E&IeH6ABu=H7-w~h>K)elD_N_VMX`G11sgNVOG^p*_hIl3#xlGvTV4t_* zS&n+81}lkGumcda4xeL-!HTVe(_r{ZJ;H(C65iQa|o~?xh zcP2aqp%JLOgkDDm)Hhm`iZOl-HA)&#se$J*$SF|$2(=9s=)<29p4AHP2q$p#8s1b2 z6oF=QK!am)yYLiNLRMdcCv$h9f>9O$NYskDIGL1 z?dVb!g8rc{guG!H?6A?fR)$bNX4Te)P!~c+ZJDz!-1kmh2s(zkP*9(vFcci}+tN@N ziFb;_u7blq2D!bXIvfTCp->-w=co@iLa|rjC=0n4!f=@&S2`_Tq=*sr)<;!Y9LiW6;egiAgY)up-QPT zs+{_es-P;VDyo_qObwxiQZ>{tYB)858cB_!MpGQcQy)=dsIk;IYCJW8nn+EeCR0$AfrW%;ORsm-4ub$K^c!kjE7~uHZJRZhl(EJD7~p^@JnT7@=f6>4Dz=l{^&M^V{qh~Qqe z!sZ-pWaIT#R9XVG+U$=-HdY=15g!*KE?vN=K90A9U=@{B=WAhyh|A-S*c;WyS&|zQ z-=Sx3rP;elfTVVLwc$RKs4`aeU)11i`U^s{E>wipge!bfEz|bv^&hp%QU8+A;sb)- zE(G6(`l$4HV|<;skuC`ryrFb`qc;DA%PL3E(?+^3>BDuY{qe}UOc|~y99J5B&M};@ zx>h#=Lfs7SxXLZ$q;SdKg^&9Ee7Nc7a|lXE(vQgu2z>^esJ}Ahzo?b=d>} zcU-4c!(DmsK%fi$yUlI5?P@6XT0n5mzE7jp!~7Tj;p;-ZPS1jx--N@sR9Xsj=UuMa=Wp#g3cTQ|dAri;F<5J|j#=wE zpr&3_O8*?>$yPU!_O*7R>;Uwc~D6VeH#Y-oo0S~UaaaD_b4-&A`aIBmL z;JR2CWl#2~-^l$-oGHj3t|xy9`N#-+Li`d6<%zIi_e-=9(!V$;oR<@GpvN>GnxPZP zJ!m;3b4p=zE-VhbPYgicklSGc?;~jG4<#4DR^8obDkN;4Lwd66(oRaG?N^P z3d!A&rZ0m6JQqKLE_8D=4-&*_P}wX&^U1xikT4t)$IsA0G7vTePeU7s?_ulkEa+Kl zp$Vuap2K$FH&B3Q5#3QLT7`F_rO?U#7Pbt3j6Q)yi|Md!_)~HiS_joqcanodb24l! zUW?X4D|akf0j2*HXaHbHGdJNiXbx_JW&>5W1T@n)OQBasI<1&Q2@d)JDfslY7f)sofY)B4* zbagaz*R7CdN`-tAlK*&EJGcRh43i)^&xQW`YoVr_$U(A`A}mhGq5oG5JDHEdUS>V) zRNe?Xo?AhCek^oAk3rS>0~ZH{K?mrF(y*_&0CqY*64KCxuqC>^upDBA{mzh6MZi|) zUEF0ziiPC0H*AXD0~?>$3U&4h=yp^L_;PE#+-pJ!`dAy0on|Zv2$53Ew zzpY!+yk6-fmUmm~W@oOHx;R(bOm?z+iK$0g!JR#-@iYI8EV0hRrMo4(VohLka z!jmVwctXk(EKkUILe3Kko=}46v6t8&8IEMTvSl(nnEh3TZR~j&?#+&1&#}kYbao7T zn*B(IBiILQJ2s8I!(L$z$Z&u5kPPRuC)sY?ut^u_xH+Y)|$udxX6x!v-0SVt;3?>`1l?`;!b;vQOFZ>@D^T`;fgM z!$oW++g<^i`Gvn%>>n~*EW^<N$qhz?6?JUEC*oo`~87{DUa;DVY+;tqo(k_^#W7aZNbtmwN6ElPvx{MiW|8}H( z3&*ESt#YbzBvOT}+qKm}PHdnLyuCne`3mnByv*CjV4 zwapBom&b0810Fwl9QHWkam?em#|@9$9(O(NdpzxwSJ(E2% zJ+nOfdFFcNdk*j{@GSBy_N?(7;W^5a_nhiE-E*PmYR|7c*Lkk@{KfNx=ULB7o>x4t zdEW56<@u}UL(ktl|Mhb7a`*D|l6u8?C3vNHHSv1StC?2|uU1}dygGVy_UhwR;8o;R z>{aUZiPs{p#a>@{t@irLYn|76uW!7*_1f&U$7`S0DX;5ZH@$9q-H|dR-xxYM6u9j=%A@WeUS#FiLmUowD$TQ_x@*4SYIVT?{pCF$kpCX?opCMl$ z-z5K0zDK@Ken5U#eqa7r{+Il3`7`+o`M>hl3R6SE6yk`DlRLoDy}PTDsC(OQj$tmDOW0$ z-pUlEMfskxrLwiMt+Ktcqq4KIx3aIYzw!g6O*v3mq8z3ip&X^;luMP%m8+ENl zD8E&1R(_}4t=y|Tro5!QqP(WOp(0gcl|&^~$y5rJO68;SQw6Css!&zDDp8fJN>OF0 zdaJTk`Kke`0#%W!SXHVTt{SNtt>RTPRkKt}RbQ(%s5YuLsZOiTsxGOnt8S`ptL~`o zsUE2QRK4(~z1_Usy*<5SyyLu+y{+C&yx;R~=H0@(m3Ig4PTuL>A9&|_=X($Ep5Q&n zd%E{0-gCS^^`7s&zw~cQ*-$LI(zU98vzC(O#e24pv z^d0Rx)_1(`RNwi&3w#&(F81B!yW97G@6WzReSh&i;d{#WjPGB5D!&lFP`_}$NWb=e z9sRob_3-QI*UPVuUq8QWzkI(B{YLq5ejoXb_50FqrQce=Z~VUX+wAw9-!{MR{SNva z_B-PDi{E9xtA0=Y34f74<`A7T5`6v6Q_*?u_{hRu~??1qQqW>KKPyOfnFYw>t zztew@|3Uvl{y+O4_5a2Hg#T6l8~(Ta@AyCQe;VK!;2#hepbpRmG!JMQ&@P~JK-Yk@ zfb@VK0X+k%1EvQo2v`)bIACeO{(yr4M*>a+oC-J-a4z6Nz@>nD0S^Km1^gaJ2C4(2 z1C4>Pf$@Qvfmwn50&@fN0|x{a1QrDr2i61*4_q9$Ht_4f4S^d2&jy|kyb^db@OI#x zzgMW}>elME>h|hR>aOZEb-KETx~ICAy05yw`U7>I+NK_;E>sUv zm#E9s73wPWVD(V-F!c!aX!S?xaq5ZcDe7tJnd)uoJ?fv;N7X0Q=hPR}m(*9(*VH%E zx75F?|53kCzf!-^5E@$Jth?HFq`lH4invX&!6-&^*!nr47<*w4vH4 zZM4>?jn&3$6Sc|O6m1i2M{R~STbrZJ)7rEHwT0S2+L_u}+E29$wV!L3XqRcf(5}#K z(4N+w)n3wG*WT3L*51+H(>~BX4^{_jgTsRL!G_?N;JDy~;G|$vaM$3p;NHR6!2^P4 z1b-YnH+VttqTt2BOM{mOe;K?ocy;iu;A6q(g0BbP489$FC-`3QgAiqicSt~pHY6k@ zG$cGEGDII@2#F188qz7GM@Zk0{vjWP7obz?w7T}Xj=FBT z9=e{o-nzcJ{<;rzxjLJ!LdWYS>z3-4>sIJi>(=Vl>o)2(>9**$>b}?gNB4v7uI|3> zvFGXMChr|Goj}~FNWR{8Ozp&h}{ICIG1!09@gThL}O2a-4n;-Uh*z&M1!&Zi^4qFqpHtg%L^dd*pslo!=8n`2>UnebvO>E!d2nw@Z|86a7%cT@b|-;hqnxG9o{y)LwKj~ zF5%t6Yr=*zd5v?OSM0AZvi%5^?5s?{@712AQPsG@W@exxZW=714m=p17#QcZ_ z5sM<0M68V167fUC-iQMcKSdmlco^|p#FL0;5icWNMZAf`kz}M-WJ;tZ^1aBGk*y=! zMRthn6xk)RTV#44cSRnE zJQH~}@^0k)$cK@SBmane5)~Mwj?zU%M(LvrQ87`mQSniUQAtrPql%+Sqbj3@Mh%M^ z88teJj~Wv-E^1=br%}tJ)<%6D^-a_dQ3s=rM4i#|`Z4-(`U(2U`l6IGT(W zN7K=6(eBZn(b8yHv?5v+?Gx=69S|K9t%(kf)lhp(ilJj9F`hAOj3UN6#xEu?MiUbf6BZL06Ahnf#>XVZq{LWb zn#MGXX&KWdrhQDOn65G1V|v77#q^2kACnW4AG084SIq91YcVfksaR=jXlz_;huA@} zGh>&>u8;jb_Gs+&*cY*{;}miFxE664aYb<>;wHu|h}#->D(+g`^LUSVU3^%4RD4Q& z*Z8#fnei**_rza_f095Y_$7oTv`ENJn2@kEVRypygjb2$#MH#LiJcM$Ck{=VlDIf= zY2uE=ONsXr|4u?lq9nhh@TA0~c1amYg-K(RCL~QtT9ULZX+zSXq%%q9lAa}pBu6J3 zl4Fz2$(H0M$*oPzOf5{UOl?fsjn&9lxxa24Kx**icO`a za#Mw=$~4$C)HKXA!Zg~%o5q;NnI@Pfo2Htkn`WA3nP!{jn&z24Gc7cIZdzhmX8OXk z!nDftm1&)6gK48_lWB`-t7*GwhiRv2muZh_pXq?{5}p#7qE9iT#HPfjB&H;%q@-96VWzvn`)mKC>*cEU_%Ntgx)MthKDSY_x2)Y_)uE*=hOFve$CJa>(+t<*4Pj<)r1b z<(%b$<+A0f<%Z>!9L)tH?@Q-K_3bPpi}_ zvns6KR$r^XHPEWD23vL3aBHMhZ#7tBtZ~)^YqB-PYPB}8zGrQ2ZE0<7ZD;LZ?PTq0 zO|zz3Gp$+HKGuHLY-_I7W-YK5S&OY@)(@?f)@thzYmIfdb(EF2jdd+&ndfR%(de8dM`qcWj^;s%TB~rzyUa8Vld1^qaHZ?XiDYZ>% W$JAcP{znjwUuKl!x9 - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - - CFBundleIdentifier - com.apple.example.DispatchLife - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleVersion - 1.1 - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/examples/DispatchLife/ReadMe.txt b/examples/DispatchLife/ReadMe.txt deleted file mode 100644 index 2beea13..0000000 --- a/examples/DispatchLife/ReadMe.txt +++ /dev/null @@ -1,37 +0,0 @@ -### DispatchLife ### - -=========================================================================== -DESCRIPTION: - -The classic game of Life showing use of dispatch queues as lightweight threads (each cell is a queue), and an example of how to avoid overloading a slow queue (OpenGL or curses screen updates) with many requests (cell updates) by using a timer to drive the screen updates and allowing the cells to update as fast as they can. - -=========================================================================== -BUILD REQUIREMENTS: - -Mac OS X version 10.6 Snow Leopard - -=========================================================================== -RUNTIME REQUIREMENTS: - -Mac OS X version 10.6 Snow Leopard - -=========================================================================== -PACKAGING LIST: - -DispatchLife.c - Simulation engine using GCD. -DispatchLifeGLView.h - OpenGL view for visualization. -DispatchLifeGLView.m - OpenGL view for visualization. - -=========================================================================== -CHANGES FROM PREVIOUS VERSIONS: - -Version 1.2 -- Updated to use current GCD source API. -Version 1.1 -- Updated to use current GCD API. -- Added OpenGL view for visualization. -Version 1.0 -- First version (WWDC 2008). - -=========================================================================== -Copyright (C) 2008-2009 Apple Inc. All rights reserved. diff --git a/examples/DispatchLife/main.m b/examples/DispatchLife/main.m deleted file mode 100644 index 59eb37a..0000000 --- a/examples/DispatchLife/main.m +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2009-2009 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -#import - -int main(int argc, char *argv[]) -{ - return NSApplicationMain(argc, (const char **) argv); -} diff --git a/examples/DispatchWebServer/DispatchWebServer.c b/examples/DispatchWebServer/DispatchWebServer.c deleted file mode 100644 index d839d3b..0000000 --- a/examples/DispatchWebServer/DispatchWebServer.c +++ /dev/null @@ -1,956 +0,0 @@ -/* - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_DTS_LICENSE_HEADER_START@ - * - * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - * ("Apple") in consideration of your agreement to the following terms, and your - * use, installation, modification or redistribution of this Apple software - * constitutes acceptance of these terms. If you do not agree with these terms, - * please do not use, install, modify or redistribute this Apple software. - * - * In consideration of your agreement to abide by the following terms, and - * subject to these terms, Apple grants you a personal, non-exclusive license, - * under Apple's copyrights in this original Apple software (the "Apple Software"), - * to use, reproduce, modify and redistribute the Apple Software, with or without - * modifications, in source and/or binary forms; provided that if you redistribute - * the Apple Software in its entirety and without modifications, you must retain - * this notice and the following text and disclaimers in all such redistributions - * of the Apple Software. Neither the name, trademarks, service marks or logos of - * Apple Computer, Inc. may be used to endorse or promote products derived from - * the Apple Software without specific prior written permission from Apple. Except - * as expressly stated in this notice, no other rights or licenses, express or - * implied, are granted by Apple herein, including but not limited to any patent - * rights that may be infringed by your derivative works or by other works in - * which the Apple Software may be incorporated. - * - * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - * COMBINATION WITH YOUR PRODUCTS. - * - * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @APPLE_DTS_LICENSE_HEADER_END@ - */ - -/* A tiny web server that does as much stuff the "dispatch way" as it can, like a queue per connection... */ - -/**************************************************************************** -overview of dispatch related operations: - -main() { - have dump_reqs() called every 5 to 6 seconds, and on every SIGINFO - and SIGPIPE - - have accept_cb() called when there are new connections on our port - - have reopen_logfile_when_needed() called whenever our logfile is - renamed, deleted, or forcibly closed -} - -reopen_logfile_when_needed() { - call ourself whenever our logfile is renamed, deleted, or forcibly - closed -} - -accept_cb() { - allocate a new queue to handle network and file I/O, and timers - for a series of HTTP requests coming from a new network connection - - have read_req() called (on the new queue) when there - is network traffic for the new connection - - have req_free(new_req) called when the connection is "done" (no - pending work to be executed on the queue, an no sources left to - generate new work for the queue) -} - -req_free() { - uses dispatch_get_current_queue() and dispatch_async() to call itself - "on the right queue" -} - -read_req() { - If there is a timeout source delete_source() it - - if (we have a whole request) { - make a new dispatch source (req->fd_rd.ds) for the - content file - - have clean up fd, req->fd and req->fd_rd (if - appropriate) when the content file source is canceled - - have read_filedata called when the content file is - read to be read - - if we already have a dispatch source for "network - socket ready to be written", enable it. Otherwise - make one, and have write_filedata called when it - time to write to it. - - disable the call to read_req - } - - close the connection if something goes wrong -} - -write_filedata() { - close the connection if anything goes wrong - - if (we have written the whole HTTP document) { - timeout in a little bit, closing the connection if we - haven't received a new command - - enable the call to read_req - } - - if (we have written all the buffered data) { - disable the call to write_filedata() - } -} - -read_filedata() { - if (nothing left to read) { - delete the content file dispatch source - } else { - enable the call to write_filedata() - } -} - -qprintf, qfprintf, qflush - schedule stdio calls on a single queue - -disable_source, enable_source - implements a binary enable/disable on top of dispatch's - counted suspend/resume - -delete_source - cancels the source (this example program uses source - cancelation to schedule any source cleanup it needs, - so "delete" needs a cancel). - - ensure the source isn't suspended - - release the reference, which _should_ be the last - reference (this example program never has more - then one reference to a source) - -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -char *DOC_BASE = NULL; -char *log_name = NULL; -FILE *logfile = NULL; -char *argv0 = "a.out"; -char *server_port = "8080"; -const int re_request_nmatch = 4; -regex_t re_first_request, re_nth_request, re_accept_deflate, re_host; - - -// qpf is the queue that we schedule our "stdio file I/O", which serves as a lock, -// and orders the output, and also gets it "out of the way" of our main line execution -dispatch_queue_t qpf; - -void qfprintf(FILE *f, const char *fmt, ...) __attribute__((format(printf, 2, 3))); - -void qfprintf(FILE *f, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - char *str; - /* We gennerate the formatted string on the same queue (or - thread) that calls qfprintf, that way the values can change - while the fputs call is being sent to the qpf queue, or waiting - for other work to complete ont he qpf queue. */ - - vasprintf(&str, fmt, ap); - dispatch_async(qpf, ^{ fputs(str, f); free(str); }); - if ('*' == *fmt) { - dispatch_sync(qpf, ^{ fflush(f); }); - } - va_end(ap); -} - -void qfflush(FILE *f) { - dispatch_sync(qpf, ^{ fflush(f); }); -} - -void reopen_logfile_when_needed() { - // We don't want to use a fd with a lifetime managed by something else - // because we need to close it inside the cancel handler (see below) - int lf_dup = dup(fileno(logfile)); - FILE **lf = &logfile; - - // We register the vnode callback on the qpf queue since that is where - // we do all our logfile printing. (we set up to reopen the logfile - // if the "old one" has been deleted or renamed (or revoked). This - // makes it pretty safe to mv the file to a new name, delay breifly, - // then gzip it. Safer to move the file to a new name, wait for the - // "old" file to reappear, then gzip. Niftier then doing the move, - // sending a SIGHUP to the right process (somehow) and then doing - // as above. Well, maybe it'll never catch on as "the new right - /// thing", but it makes a nifty demo. - dispatch_source_t vn = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, lf_dup, DISPATCH_VNODE_REVOKE|DISPATCH_VNODE_RENAME|DISPATCH_VNODE_DELETE, qpf); - - dispatch_source_set_event_handler(vn, ^{ - printf("lf_dup is %d (logfile's fileno=%d), closing it\n", lf_dup, fileno(logfile)); - fprintf(logfile, "# flush n' roll!\n"); - dispatch_cancel(vn); - dispatch_release(vn); - fflush(logfile); - *lf = freopen(log_name, "a", logfile); - - // The new logfile has (or may have) a diffrent fd from the old one, so - // we have to register it again - reopen_logfile_when_needed(); - }); - - dispatch_source_set_cancel_handler(vn, ^{ close(lf_dup); }); - - dispatch_resume(vn); -} - -#define qprintf(fmt...) qfprintf(stdout, ## fmt); - -struct buffer { - // Manage a buffer, currently at sz bytes, but will realloc if needed - // The buffer has a part that we read data INTO, and a part that we - // write data OUT OF. - // - // Best use of the space would be a circular buffer (and we would - // use readv/writev and pass around iovec structs), but we use a - // simpler layout: - // data from buf to outof is wasted. From outof to into is - // "ready to write data OUT OF", from into until buf+sz is - // "ready to read data IN TO". - size_t sz; - unsigned char *buf; - unsigned char *into, *outof; -}; - -struct request_source { - // libdispatch gives suspension a counting behaviour, we want a simple on/off behaviour, so we use - // this struct to provide track suspensions - dispatch_source_t ds; - bool suspended; -}; - -// The request struct manages an actiave HTTP request/connection. It gets reused for pipelined HTTP clients. -// Every request has it's own queue where all of it's network traffic, and source file I/O as well as -// compression (when requested by the HTTP client) is done. -struct request { - struct sockaddr_in r_addr; - z_stream *deflate; - // cmd_buf holds the HTTP request - char cmd_buf[8196], *cb; - char chunk_num[13], *cnp; // Big enough for 8 digits plus \r\n\r\n\0 - bool needs_zero_chunk; - bool reuse_guard; - short status_number; - size_t chunk_bytes_remaining; - char *q_name; - int req_num; // For debugging - int files_served; // For this socket - dispatch_queue_t q; - // "sd" is the socket descriptor, where the network I/O for this request goes. "fd" is the source file (or -1) - int sd, fd; - // fd_rd is for read events from the source file (say /Users/YOU/Sites/index.html for a GET /index.html request) - // sd_rd is for read events from the network socket (we suspend it after we read an HTTP request header, and - // resume it when we complete a request) - // sd_wr is for write events to the network socket (we suspend it when we have no buffered source data to send, - // and resume it when we have data ready to send) - // timeo is the timeout event waiting for a new client request header. - struct request_source fd_rd, sd_rd, sd_wr, timeo; - uint64_t timeout_at; - struct stat sb; - - // file_b is where we read data from fd into. - // For compressed GET requests: - // - data is compressed from file_b into deflate_b - // - data is written to the network socket from deflate_b - // For uncompressed GET requests - // - data is written to the network socket from file_b - // - deflate_b is unused - struct buffer file_b, deflate_b; - - ssize_t total_written; -}; - -void req_free(struct request *req); - -void disable_source(struct request *req, struct request_source *rs) { - // we want a binary suspend state, not a counted state. Our - // suspend flag is "locked" by only being used on req->q, this - // assert makes sure we are in a valid context to write the new - // suspend value. - assert(req->q == dispatch_get_current_queue()); - if (!rs->suspended) { - rs->suspended = true; - dispatch_suspend(rs->ds); - } -} - -void enable_source(struct request *req, struct request_source *rs) { - assert(req->q == dispatch_get_current_queue()); - if (rs->suspended) { - rs->suspended = false; - dispatch_resume(rs->ds); - } -} - -void delete_source(struct request *req, struct request_source *rs) { - assert(req->q == dispatch_get_current_queue()); - if (rs->ds) { - /* sources need to be resumed before they can be deleted - (otherwise an I/O and/or cancel block might be stranded - waiting for a resume that will never come, causing - leaks) */ - - enable_source(req, rs); - dispatch_cancel(rs->ds); - dispatch_release(rs->ds); - } - rs->ds = NULL; - rs->suspended = false; -} - -size_t buf_into_sz(struct buffer *b) { - return (b->buf + b->sz) - b->into; -} - -void buf_need_into(struct buffer *b, size_t cnt) { - // resize buf so into has at least cnt bytes ready to use - size_t sz = buf_into_sz(b); - if (cnt <= sz) { - return; - } - sz = malloc_good_size(cnt - sz + b->sz); - unsigned char *old = b->buf; - // We could special case b->buf == b->into && b->into == b->outof to - // do a free & malloc rather then realloc, but after testing it happens - // only for the 1st use of the buffer, where realloc is the same cost as - // malloc anyway. - b->buf = reallocf(b->buf, sz); - assert(b->buf); - b->sz = sz; - b->into = b->buf + (b->into - old); - b->outof = b->buf + (b->outof - old); -} - -void buf_used_into(struct buffer *b, size_t used) { - b->into += used; - assert(b->into <= b->buf + b->sz); -} - -size_t buf_outof_sz(struct buffer *b) { - return b->into - b->outof; -} - -int buf_sprintf(struct buffer *b, char *fmt, ...) __attribute__((format(printf,2,3))); - -int buf_sprintf(struct buffer *b, char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - size_t s = buf_into_sz(b); - int l = vsnprintf((char *)(b->into), s, fmt, ap); - if (l < s) { - buf_used_into(b, l); - } else { - // Reset ap -- vsnprintf has already used it. - va_end(ap); - va_start(ap, fmt); - buf_need_into(b, l); - s = buf_into_sz(b); - l = vsnprintf((char *)(b->into), s, fmt, ap); - assert(l <= s); - buf_used_into(b, l); - } - va_end(ap); - - return l; -} - -void buf_used_outof(struct buffer *b, size_t used) { - b->outof += used; - //assert(b->into <= b->outof); - assert(b->outof <= b->into); - if (b->into == b->outof) { - b->into = b->outof = b->buf; - } -} - -char *buf_debug_str(struct buffer *b) { - char *ret = NULL; - asprintf(&ret, "S%d i#%d o#%d", b->sz, buf_into_sz(b), buf_outof_sz(b)); - return ret; -} - -uint64_t getnanotime() { - struct timeval tv; - gettimeofday(&tv, NULL); - - return tv.tv_sec * NSEC_PER_SEC + tv.tv_usec * NSEC_PER_USEC; -} - -int n_req; -struct request **debug_req; - -void dump_reqs() { - int i = 0; - static last_reported = -1; - - // We want to see the transition into n_req == 0, but we don't need to - // keep seeing it. - if (n_req == 0 && n_req == last_reported) { - return; - } else { - last_reported = n_req; - } - - qprintf("%d actiave requests to dump\n", n_req); - uint64_t now = getnanotime(); - /* Because we iterate over the debug_req array in this queue - ("the main queue"), it has to "own" that array. All manipulation - of the array as a whole will have to be done on this queue. */ - - for(i = 0; i < n_req; i++) { - struct request *req = debug_req[i]; - qprintf("%s sources: fd_rd %p%s, sd_rd %p%s, sd_wr %p%s, timeo %p%s\n", req->q_name, req->fd_rd.ds, req->fd_rd.suspended ? " (SUSPENDED)" : "", req->sd_rd.ds, req->sd_rd.suspended ? " (SUSPENDED)" : "", req->sd_wr.ds, req->sd_wr.suspended ? " (SUSPENDED)" : "", req->timeo.ds, req->timeo.suspended ? " (SUSPENDED)" : ""); - if (req->timeout_at) { - double when = req->timeout_at - now; - when /= NSEC_PER_SEC; - if (when < 0) { - qprintf(" timeout %f seconds ago\n", -when); - } else { - qprintf(" timeout in %f seconds\n", when); - } - } else { - qprintf(" timeout_at not set\n"); - } - char *file_bd = buf_debug_str(&req->file_b), *deflate_bd = buf_debug_str(&req->deflate_b); - qprintf(" file_b %s; deflate_b %s\n cmd_buf used %ld; fd#%d; files_served %d\n", file_bd, deflate_bd, (long)(req->cb - req->cmd_buf), req->fd, req->files_served); - if (req->deflate) { - qprintf(" deflate total in: %ld ", req->deflate->total_in); - } - qprintf("%s total_written %lu, file size %lld\n", req->deflate ? "" : " ", req->total_written, req->sb.st_size); - free(file_bd); - free(deflate_bd); - } -} - -void req_free(struct request *req) { - assert(!req->reuse_guard); - if (dispatch_get_main_queue() != dispatch_get_current_queue()) { - /* dispatch_set_finalizer_f arranges to have us "invoked - asynchronously on req->q's target queue". However, - we want to manipulate the debug_req array in ways - that are unsafe anywhere except the same queue that - dump_reqs runs on (which happens to be the main queue). - So if we are running anywhere but the main queue, we - just arrange to be called there */ - - dispatch_async(dispatch_get_main_queue(), ^{ req_free(req); }); - return; - } - - req->reuse_guard = true; - *(req->cb) = '\0'; - qprintf("$$$ req_free %s; fd#%d; buf: %s\n", dispatch_queue_get_label(req->q), req->fd, req->cmd_buf); - assert(req->sd_rd.ds == NULL && req->sd_wr.ds == NULL); - close(req->sd); - assert(req->fd_rd.ds == NULL); - if (req->fd >= 0) close(req->fd); - free(req->file_b.buf); - free(req->deflate_b.buf); - free(req->q_name); - free(req->deflate); - free(req); - - int i; - bool found = false; - for(i = 0; i < n_req; i++) { - if (found) { - debug_req[i -1] = debug_req[i]; - } else { - found = (debug_req[i] == req); - } - } - debug_req = reallocf(debug_req, sizeof(struct request *) * --n_req); - assert(n_req >= 0); -} - -void close_connection(struct request *req) { - qprintf("$$$ close_connection %s, served %d files -- canceling all sources\n", dispatch_queue_get_label(req->q), req->files_served); - delete_source(req, &req->fd_rd); - delete_source(req, &req->sd_rd); - delete_source(req, &req->sd_wr); - delete_source(req, &req->timeo); -} - -// We have some "content data" (either from the file, or from -// compressing the file), and the network socket is ready for us to -// write it -void write_filedata(struct request *req, size_t avail) { - /* We always attempt to write as much data as we have. This - is safe becuase we use non-blocking I/O. It is a good idea - becuase the amount of buffer space that dispatch tells us may - be stale (more space could have opened up, or memory presure - may have caused it to go down). */ - - struct buffer *w_buf = req->deflate ? &req->deflate_b : &req->file_b; - ssize_t sz = buf_outof_sz(w_buf); - if (req->deflate) { - struct iovec iov[2]; - if (!req->chunk_bytes_remaining) { - req->chunk_bytes_remaining = sz; - req->needs_zero_chunk = sz != 0; - req->cnp = req->chunk_num; - int n = snprintf(req->chunk_num, sizeof(req->chunk_num), "\r\n%lx\r\n%s", sz, sz ? "" : "\r\n"); - assert(n <= sizeof(req->chunk_num)); - } - iov[0].iov_base = req->cnp; - iov[0].iov_len = req->cnp ? strlen(req->cnp) : 0; - iov[1].iov_base = w_buf->outof; - iov[1].iov_len = (req->chunk_bytes_remaining < sz) ? req->chunk_bytes_remaining : sz; - sz = writev(req->sd, iov, 2); - if (sz > 0) { - if (req->cnp) { - if (sz >= strlen(req->cnp)) { - req->cnp = NULL; - } else { - req->cnp += sz; - } - } - sz -= iov[0].iov_len; - sz = (sz < 0) ? 0 : sz; - req->chunk_bytes_remaining -= sz; - } - } else { - sz = write(req->sd, w_buf->outof, sz); - } - if (sz > 0) { - buf_used_outof(w_buf, sz); - } else if (sz < 0) { - int e = errno; - qprintf("write_filedata %s write error: %d %s\n", dispatch_queue_get_label(req->q), e, strerror(e)); - close_connection(req); - return; - } - - req->total_written += sz; - off_t bytes = req->total_written; - if (req->deflate) { - bytes = req->deflate->total_in - buf_outof_sz(w_buf); - if (req->deflate->total_in < buf_outof_sz(w_buf)) { - bytes = 0; - } - } - if (bytes == req->sb.st_size) { - if (req->needs_zero_chunk && req->deflate && (sz || req->cnp)) { - return; - } - - // We have transfered the file, time to write the log entry. - - // We don't deal with " in the request string, this is an example of how - // to use dispatch, not how to do C string manipulation, eh? - size_t rlen = strcspn(req->cmd_buf, "\r\n"); - char tstr[45], astr[45]; - struct tm tm; - time_t clock; - time(&clock); - strftime(tstr, sizeof(tstr), "%d/%b/%Y:%H:%M:%S +0", gmtime_r(&clock, &tm)); - addr2ascii(AF_INET, &req->r_addr.sin_addr, sizeof(struct in_addr), astr); - qfprintf(logfile, "%s - - [%s] \"%.*s\" %hd %zd\n", astr, tstr, (int)rlen, req->cmd_buf, req->status_number, req->total_written); - - int64_t t_offset = 5 * NSEC_PER_SEC + req->files_served * NSEC_PER_SEC / 10; - int64_t timeout_at = req->timeout_at = getnanotime() + t_offset; - - req->timeo.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, req->q); - dispatch_source_set_timer(req->timeo.ds, dispatch_time(DISPATCH_TIME_NOW, t_offset), NSEC_PER_SEC, NSEC_PER_SEC); - dispatch_source_set_event_handler(req->timeo.ds, ^{ - if (req->timeout_at == timeout_at) { - qfprintf(stderr, "$$$ -- timeo fire (delta=%f) -- close connection: q=%s\n", (getnanotime() - (double)timeout_at) / NSEC_PER_SEC, dispatch_queue_get_label(req->q)); - close_connection(req); - } else { - // This happens if the timeout value has been updated, but a pending timeout event manages to race in before the cancel - } - }); - dispatch_resume(req->timeo.ds); - - req->files_served++; - qprintf("$$$ wrote whole file (%s); timeo %p, about to enable %p and close %d, total_written=%zd, this is the %d%s file served\n", dispatch_queue_get_label(req->q), req->timeo.ds, req->sd_rd.ds, req->fd, req->total_written, req->files_served, (1 == req->files_served) ? "st" : (2 == req->files_served) ? "nd" : "th"); - enable_source(req, &req->sd_rd); - if (req->fd_rd.ds) { - delete_source(req, &req->fd_rd); - } - req->cb = req->cmd_buf; - } else { - assert(bytes <= req->sb.st_size); - } - - if (0 == buf_outof_sz(w_buf)) { - // The write buffer is now empty, so we don't need to know when sd is ready for us to write to it. - disable_source(req, &req->sd_wr); - } -} - -// Our "content file" has some data ready for us to read. -void read_filedata(struct request *req, size_t avail) { - if (avail == 0) { - delete_source(req, &req->fd_rd); - return; - } - - /* We make sure we can read at least as many bytes as dispatch - says are avilable, but if our buffer is bigger we will read as - much as we have space for. We have the file opened in non-blocking - mode so this is safe. */ - - buf_need_into(&req->file_b, avail); - size_t rsz = buf_into_sz(&req->file_b); - ssize_t sz = read(req->fd, req->file_b.into, rsz); - if (sz >= 0) { - assert(req->sd_wr.ds); - size_t sz0 = buf_outof_sz(&req->file_b); - buf_used_into(&req->file_b, sz); - assert(sz == buf_outof_sz(&req->file_b) - sz0); - } else { - int e = errno; - qprintf("read_filedata %s read error: %d %s\n", dispatch_queue_get_label(req->q), e, strerror(e)); - close_connection(req); - return; - } - if (req->deflate) { - // Note:: deflateBound is "worst case", we could try with any non-zero - // buffer, and alloc more if we get Z_BUF_ERROR... - buf_need_into(&req->deflate_b, deflateBound(req->deflate, buf_outof_sz(&req->file_b))); - req->deflate->next_in = (req->file_b.outof); - size_t o_sz = buf_outof_sz(&req->file_b); - req->deflate->avail_in = o_sz; - req->deflate->next_out = req->deflate_b.into; - size_t i_sz = buf_into_sz(&req->deflate_b); - req->deflate->avail_out = i_sz; - assert(req->deflate->avail_in + req->deflate->total_in <= req->sb.st_size); - // at EOF we want to use Z_FINISH, otherwise we pass Z_NO_FLUSH so we get maximum compression - int rc = deflate(req->deflate, (req->deflate->avail_in + req->deflate->total_in >= req->sb.st_size) ? Z_FINISH : Z_NO_FLUSH); - assert(rc == Z_OK || rc == Z_STREAM_END); - buf_used_outof(&req->file_b, o_sz - req->deflate->avail_in); - buf_used_into(&req->deflate_b, i_sz - req->deflate->avail_out); - if (i_sz != req->deflate->avail_out) { - enable_source(req, &req->sd_wr); - } - } else { - enable_source(req, &req->sd_wr); - } -} - -// We are waiting to for an HTTP request (we eitther havn't gotten -// the first request, or pipelneing is on, and we finished a request), -// and there is data to read on the network socket. -void read_req(struct request *req, size_t avail) { - if (req->timeo.ds) { - delete_source(req, &req->timeo); - } - - // -1 to account for the trailing NUL - int s = (sizeof(req->cmd_buf) - (req->cb - req->cmd_buf)) -1; - if (s == 0) { - qprintf("read_req fd#%d command overflow\n", req->sd); - close_connection(req); - return; - } - int rd = read(req->sd, req->cb, s); - if (rd > 0) { - req->cb += rd; - if (req->cb > req->cmd_buf + 4) { - int i; - for(i = -4; i != 0; i++) { - char ch = *(req->cb + i); - if (ch != '\n' && ch != '\r') { - break; - } - } - if (i == 0) { - *(req->cb) = '\0'; - - assert(buf_outof_sz(&req->file_b) == 0); - assert(buf_outof_sz(&req->deflate_b) == 0); - regmatch_t pmatch[re_request_nmatch]; - regex_t *rex = req->files_served ? &re_first_request : &re_nth_request; - int rc = regexec(rex, req->cmd_buf, re_request_nmatch, pmatch, 0); - if (rc) { - char ebuf[1024]; - regerror(rc, rex, ebuf, sizeof(ebuf)); - qprintf("\n$$$ regexec error: %s, ditching request: '%s'\n", ebuf, req->cmd_buf); - close_connection(req); - return; - } else { - if (!strncmp("GET", req->cmd_buf + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so)) { - rc = regexec(&re_accept_deflate, req->cmd_buf, 0, NULL, 0); - assert(rc == 0 || rc == REG_NOMATCH); - // to disable deflate code: - // rc = REG_NOMATCH; - if (req->deflate) { - deflateEnd(req->deflate); - free(req->deflate); - } - req->deflate = (0 == rc) ? calloc(1, sizeof(z_stream)) : NULL; - char path_buf[4096]; - strlcpy(path_buf, DOC_BASE, sizeof(path_buf)); - // WARNING: this doesn't avoid use of .. in the path - // do get outside of DOC_ROOT, a real web server would - // really have to avoid that. - char ch = *(req->cmd_buf + pmatch[2].rm_eo); - *(req->cmd_buf + pmatch[2].rm_eo) = '\0'; - strlcat(path_buf, req->cmd_buf + pmatch[2].rm_so, sizeof(path_buf)); - *(req->cmd_buf + pmatch[2].rm_eo) = ch; - req->fd = open(path_buf, O_RDONLY|O_NONBLOCK); - qprintf("GET req for %s, path: %s, deflate: %p; fd#%d\n", dispatch_queue_get_label(req->q), path_buf, req->deflate, req->fd); - size_t n; - if (req->fd < 0) { - const char *msg = "404 Page not here

You step in the stream,
but the water has moved on.
This page is not here.
"; - req->status_number = 404; - n = buf_sprintf(&req->file_b, "HTTP/1.1 404 Not Found\r\nContent-Length: %zu\r\nExpires: now\r\nServer: %s\r\n\r\n%s", strlen(msg), argv0, msg); - req->sb.st_size = 0; - } else { - rc = fstat(req->fd, &req->sb); - assert(rc >= 0); - if (req->sb.st_mode & S_IFDIR) { - req->status_number = 301; - regmatch_t hmatch[re_request_nmatch]; - rc = regexec(&re_host, req->cmd_buf, re_request_nmatch, hmatch, 0); - assert(rc == 0 || rc == REG_NOMATCH); - if (rc == REG_NOMATCH) { - hmatch[1].rm_so = hmatch[1].rm_eo = 0; - } - n = buf_sprintf(&req->file_b, "HTTP/1.1 301 Redirect\r\nContent-Length: 0\r\nExpires: now\r\nServer: %s\r\nLocation: http://%*.0s/%*.0s/index.html\r\n\r\n", argv0, (int)(hmatch[1].rm_eo - hmatch[1].rm_so), req->cmd_buf + hmatch[1].rm_so, (int)(pmatch[2].rm_eo - pmatch[2].rm_so), req->cmd_buf + pmatch[2].rm_so); - req->sb.st_size = 0; - close(req->fd); - req->fd = -1; - } else { - req->status_number = 200; - if (req->deflate) { - n = buf_sprintf(&req->deflate_b, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Encoding: deflate\r\nExpires: now\r\nServer: %s\r\n", argv0); - req->chunk_bytes_remaining = buf_outof_sz(&req->deflate_b); - } else { - n = buf_sprintf(req->deflate ? &req->deflate_b : &req->file_b, "HTTP/1.1 200 OK\r\nContent-Length: %lld\r\nExpires: now\r\nServer: %s\r\n\r\n", req->sb.st_size, argv0); - } - } - } - - if (req->status_number != 200) { - free(req->deflate); - req->deflate = NULL; - } - - if (req->deflate) { - rc = deflateInit(req->deflate, Z_BEST_COMPRESSION); - assert(rc == Z_OK); - } - - // Cheat: we don't count the header bytes as part of total_written - req->total_written = -buf_outof_sz(&req->file_b); - if (req->fd >= 0) { - req->fd_rd.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, req->fd, 0, req->q); - // Cancelation is async, so we capture the fd and read sources we will want to operate on as the req struct may have moved on to a new set of values - int fd = req->fd; - dispatch_source_t fd_rd = req->fd_rd.ds; - dispatch_source_set_cancel_handler(req->fd_rd.ds, ^{ - close(fd); - if (req->fd == fd) { - req->fd = -1; - } - if (req->fd_rd.ds == fd_rd) { - req->fd_rd.ds = NULL; - } - }); - dispatch_source_set_event_handler(req->fd_rd.ds, ^{ - if (req->fd_rd.ds) { - read_filedata(req, dispatch_source_get_data(req->fd_rd.ds)); - } - }); - dispatch_resume(req->fd_rd.ds); - } else { - req->fd_rd.ds = NULL; - } - - if (req->sd_wr.ds) { - enable_source(req, &req->sd_wr); - } else { - req->sd_wr.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, req->sd, 0, req->q); - dispatch_source_set_event_handler(req->sd_wr.ds, ^{ write_filedata(req, dispatch_source_get_data(req->sd_wr.ds)); }); - dispatch_resume(req->sd_wr.ds); - } - disable_source(req, &req->sd_rd); - } - } - } - } - } else if (rd == 0) { - qprintf("### (%s) read_req fd#%d rd=0 (%s); %d files served\n", dispatch_queue_get_label(req->q), req->sd, (req->cb == req->cmd_buf) ? "no final request" : "incomplete request", req->files_served); - close_connection(req); - return; - } else { - int e = errno; - qprintf("reqd_req fd#%d rd=%d err=%d %s\n", req->sd, rd, e, strerror(e)); - close_connection(req); - return; - } -} - -// We have a new connection, allocate a req struct & set up a read event handler -void accept_cb(int fd) { - static int req_num = 0; - struct request *new_req = calloc(1, sizeof(struct request)); - assert(new_req); - new_req->cb = new_req->cmd_buf; - socklen_t r_len = sizeof(new_req->r_addr); - int s = accept(fd, (struct sockaddr *)&(new_req->r_addr), &r_len); - if (s < 0) { - qfprintf(stderr, "accept failure (rc=%d, errno=%d %s)\n", s, errno, strerror(errno)); - return; - } - assert(s >= 0); - new_req->sd = s; - new_req->req_num = req_num; - asprintf(&(new_req->q_name), "req#%d s#%d", req_num++, s); - qprintf("accept_cb fd#%d; made: %s\n", fd, new_req->q_name); - - // All further work for this request will happen "on" new_req->q, - // except the final tear down (see req_free()) - new_req->q = dispatch_queue_create(new_req->q_name, NULL); - dispatch_set_context(new_req->q, new_req); - dispatch_set_finalizer_f(new_req->q, (dispatch_function_t)req_free); - - debug_req = reallocf(debug_req, sizeof(struct request *) * ++n_req); - debug_req[n_req -1] = new_req; - - - new_req->sd_rd.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, new_req->sd, 0, new_req->q); - dispatch_source_set_event_handler(new_req->sd_rd.ds, ^{ - read_req(new_req, dispatch_source_get_data(new_req->sd_rd.ds)); - }); - - // We want our queue to go away when all of it's sources do, so we - // drop the reference dispatch_queue_create gave us & rely on the - // references each source holds on the queue to keep it alive. - dispatch_release(new_req->q); - dispatch_resume(new_req->sd_rd.ds); -} - -int main(int argc, char *argv[]) { - int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - assert(sock > 0); - int rc; - struct addrinfo ai_hints, *my_addr; - - qpf = dispatch_queue_create("printf", NULL); - - argv0 = basename(argv[0]); - struct passwd *pw = getpwuid(getuid()); - assert(pw); - asprintf(&DOC_BASE, "%s/Sites/", pw->pw_dir); - asprintf(&log_name, "%s/Library/Logs/%s-transfer.log", pw->pw_dir, argv0); - logfile = fopen(log_name, "a"); - reopen_logfile_when_needed(logfile, log_name); - - bzero(&ai_hints, sizeof(ai_hints)); - ai_hints.ai_flags = AI_PASSIVE; - ai_hints.ai_family = PF_INET; - ai_hints.ai_socktype = SOCK_STREAM; - ai_hints.ai_protocol = IPPROTO_TCP; - rc = getaddrinfo(NULL, server_port, &ai_hints, &my_addr); - assert(rc == 0); - - qprintf("Serving content from %s on port %s, logging transfers to %s\n", DOC_BASE, server_port, log_name); - - int yes = 1; - rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - assert(rc == 0); - yes = 1; - rc = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)); - assert(rc == 0); - - rc = bind(sock, my_addr->ai_addr, my_addr->ai_addr->sa_len); - assert(rc >= 0); - - rc = listen(sock, 25); - assert(rc >= 0); - - rc = regcomp(&re_first_request, "^([A-Z]+)[ \t]+([^ \t\n]+)[ \t]+HTTP/1\\.1[\r\n]+", REG_EXTENDED); - assert(rc == 0); - - rc = regcomp(&re_nth_request, "^([A-Z]+)[ \t]+([^ \t\n]+)([ \t]+HTTP/1\\.1)?[\r\n]+", REG_EXTENDED); - assert(rc == 0); - - rc = regcomp(&re_accept_deflate, "[\r\n]+Accept-Encoding:(.*,)? *deflate[,\r\n]+", REG_EXTENDED); - assert(rc == 0); - - rc = regcomp(&re_host, "[\r\n]+Host: *([^ \r\n]+)[ \r\n]+", REG_EXTENDED); - assert(rc == 0); - - dispatch_source_t accept_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sock, 0, dispatch_get_main_queue()); - dispatch_source_set_event_handler(accept_ds, ^{ accept_cb(sock); }); - assert(accept_ds); - dispatch_resume(accept_ds); - - sigset_t sigs; - sigemptyset(&sigs); - sigaddset(&sigs, SIGINFO); - sigaddset(&sigs, SIGPIPE); - - int s; - for(s = 0; s < NSIG; s++) { - if (sigismember(&sigs, s)) { - dispatch_source_t sig_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, s, 0, dispatch_get_main_queue()); - assert(sig_ds); - dispatch_source_set_event_handler(sig_ds, ^{ dump_reqs(); }); - dispatch_resume(sig_ds); - } - } - - rc = sigprocmask(SIG_BLOCK, &sigs, NULL); - assert(rc == 0); - - dispatch_source_t dump_timer_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - dispatch_source_set_timer(dump_timer_ds, DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC, NSEC_PER_SEC); - dispatch_source_set_event_handler(dump_timer_ds, ^{ dump_reqs(); }); - dispatch_resume(dump_timer_ds); - - dispatch_main(); - printf("dispatch_main returned\n"); - - return 1; -} diff --git a/examples/DispatchWebServer/DispatchWebServer.xcodeproj/project.pbxproj b/examples/DispatchWebServer/DispatchWebServer.xcodeproj/project.pbxproj deleted file mode 100644 index 444288a..0000000 --- a/examples/DispatchWebServer/DispatchWebServer.xcodeproj/project.pbxproj +++ /dev/null @@ -1,203 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 45; - objects = { - -/* Begin PBXBuildFile section */ - 4CDA1C1F0F795F5B00E0869E /* DispatchWebServer.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA1C1E0F795F5B00E0869E /* DispatchWebServer.c */; }; - 4CDA1C400F79786E00E0869E /* libz.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CDA1C3F0F79786E00E0869E /* libz.1.dylib */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 8DD76FAF0486AB0100D96B5E /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 4CDA1C1E0F795F5B00E0869E /* DispatchWebServer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DispatchWebServer.c; sourceTree = ""; }; - 4CDA1C3F0F79786E00E0869E /* libz.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.dylib; path = /usr/lib/libz.1.dylib; sourceTree = ""; }; - 8DD76FB20486AB0100D96B5E /* DispatchWebServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = DispatchWebServer; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8DD76FAD0486AB0100D96B5E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CDA1C400F79786E00E0869E /* libz.1.dylib in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* DispatchWebServer */ = { - isa = PBXGroup; - children = ( - 4CDA1C3F0F79786E00E0869E /* libz.1.dylib */, - 08FB7795FE84155DC02AAC07 /* Source */, - C6A0FF2B0290797F04C91782 /* Documentation */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - ); - name = DispatchWebServer; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 4CDA1C1E0F795F5B00E0869E /* DispatchWebServer.c */, - ); - name = Source; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8DD76FB20486AB0100D96B5E /* DispatchWebServer */, - ); - name = Products; - sourceTree = ""; - }; - C6A0FF2B0290797F04C91782 /* Documentation */ = { - isa = PBXGroup; - children = ( - ); - name = Documentation; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8DD76FA90486AB0100D96B5E /* DispatchWebServer */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "DispatchWebServer" */; - buildPhases = ( - 8DD76FAB0486AB0100D96B5E /* Sources */, - 8DD76FAD0486AB0100D96B5E /* Frameworks */, - 8DD76FAF0486AB0100D96B5E /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DispatchWebServer; - productInstallPath = "$(HOME)/bin"; - productName = DispatchWebServer; - productReference = 8DD76FB20486AB0100D96B5E /* DispatchWebServer */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "DispatchWebServer" */; - compatibilityVersion = "Xcode 3.1"; - hasScannedForEncodings = 1; - mainGroup = 08FB7794FE84155DC02AAC07 /* DispatchWebServer */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8DD76FA90486AB0100D96B5E /* DispatchWebServer */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 8DD76FAB0486AB0100D96B5E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CDA1C1F0F795F5B00E0869E /* DispatchWebServer.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 1DEB928608733DD80010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = DispatchWebServer; - }; - name = Debug; - }; - 1DEB928708733DD80010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = DispatchWebServer; - }; - name = Release; - }; - 1DEB928A08733DD80010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - ONLY_ACTIVE_ARCH = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Debug; - }; - 1DEB928B08733DD80010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "DispatchWebServer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB928608733DD80010E9CD /* Debug */, - 1DEB928708733DD80010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "DispatchWebServer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB928A08733DD80010E9CD /* Debug */, - 1DEB928B08733DD80010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/examples/DispatchWebServer/ReadMe.txt b/examples/DispatchWebServer/ReadMe.txt deleted file mode 100644 index 4a63596..0000000 --- a/examples/DispatchWebServer/ReadMe.txt +++ /dev/null @@ -1,44 +0,0 @@ -### DispatchWebServer ### - -=========================================================================== -DESCRIPTION: - -Sample code showing how to: Use dispatch in a real world setting, -schedule file and network I/O, use vnode sources, create and manage -timers. - -=========================================================================== -BUILD REQUIREMENTS: - -Mac OS X version 10.6 Snow Leopard - -=========================================================================== -RUNTIME REQUIREMENTS: - -Mac OS X version 10.6 Snow Leopard - -=========================================================================== -PACKAGING LIST: - -DispatchWebServer.c - the web server - -=========================================================================== -RUNNING: - -Running the program will start a web server on port 8080, it will read -content from ~/Sites and write ~/Library/Logs/DispatchWebServer-transfer.log -each time complets a request. - -It will write some to stdout when it makes new connections, recieves -requests, completes requests, and when it closes connections. It also -shows the state of each actiave request once evey five seconds and any -time you send a SIGINFO signal to it. - -=========================================================================== -CHANGES FROM PREVIOUS VERSIONS: - -Version 1.0 -- First version - -=========================================================================== -Copyright (C) 2009 Apple Inc. All rights reserved. diff --git a/install b/install deleted file mode 100755 index 221a93e..0000000 --- a/install +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash - -if [ $EUID -ne 0 ]; then - echo "install script must be run as root" 2>&1 - exit 1 -fi - -## Copy Libsystem pieces from ~rc -copylibs=0 -## Install results in / -noinstall=0 -build=$(sw_vers -buildVersion) -train=$(~rc/bin/getTrainForBuild --quiet "$build") - -while [ $# -gt 0 ]; do - if [ "${1/=*/}" = "--build" ]; then - build="${1/*=/}" - elif [ "$1" = "--noinstall" ]; then - noinstall=1 - elif [ "$1" = "--copylibs" ]; then - copylibs=1 - else - echo "install: [--build=10A400] [--noinstall] [--copylibs]" 2>&1 - exit 1 - fi - shift -done - -ROOTS=/var/tmp/GCDRoots."$build" - -# Building for another version implies copylibs and noinstall -if [ "$build" != "$(sw_vers -buildVersion)" ]; then - copylibs=1 - noinstall=1 -fi - -set -ex -mkdir -p "$ROOTS" - -function BUILDIT() { - ~rc/bin/buildit -arch i386 -arch ppc -arch x86_64 -arch armv6 \ - -release "$train" -rootsDirectory "$ROOTS" "$@" . -} - -BUILDIT -project libdispatch -merge / -noverify - -if [ $copylibs = 1 ]; then - ALTUSRLOCALLIBSYSTEM="$ROOTS"/system - mkdir -p "$ALTUSRLOCALLIBSYSTEM" - export ALTUSRLOCALLIBSYSTEM - ./install_Libsystem_pieces "$build" - cp /usr/local/lib/system/libdispatch* "$ALTUSRLOCALLIBSYSTEM" -fi - -LIBSYSTEM=$(~rc/bin/getvers "$train$build" Libsystem) -if [ -z "$LIBSYSTEM" ]; then - exit 1 -fi -SRCROOT="/var/tmp/$LIBSYSTEM" -if [ ! -e "$SRCROOT" ]; then - cd $(dirname "$SRCROOT") - svn co http://src.apple.com/svn/BSD/Libsystem/tags/"$LIBSYSTEM" -fi -cd "$SRCROOT" -BUILDIT - -if [ $noinstall -eq 1 ]; then - exit 0 -fi - -if [ ! -e /usr/lib/libSystem.B.dylib.orig ]; then - cp /usr/lib/libSystem.B.dylib /usr/lib/libSystem.B.dylib.orig -fi -if [ ! -e /usr/lib/libSystem.B_debug.dylib.orig ] ; then - cp /usr/lib/libSystem.B_debug.dylib /usr/lib/libSystem.B_debug.dylib.orig -fi -if [ ! -e /usr/lib/libSystem.B_profile.dylib.orig ] ; then - cp /usr/lib/libSystem.B_profile.dylib /usr/lib/libSystem.B_profile.dylib.orig -fi -cp -R "$ROOTS"/"$LIBSYSTEM".roots/"$LIBSYSTEM"~sym/libSystem* /usr/lib/ -update_dyld_shared_cache diff --git a/install_Libsystem_pieces b/install_Libsystem_pieces deleted file mode 100755 index 39015d9..0000000 --- a/install_Libsystem_pieces +++ /dev/null @@ -1,48 +0,0 @@ -#!/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 diff --git a/libdispatch.xcodeproj/project.pbxproj b/libdispatch.xcodeproj/project.pbxproj index 01b065c..e36948c 100644 --- a/libdispatch.xcodeproj/project.pbxproj +++ b/libdispatch.xcodeproj/project.pbxproj @@ -3,12 +3,41 @@ archiveVersion = 1; classes = { }; - objectVersion = 45; + objectVersion = 46; objects = { +/* Begin PBXAggregateTarget section */ + 3F3C9326128E637B0042B1F7 /* libdispatch_Sim */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 3F3C9356128E637B0042B1F7 /* Build configuration list for PBXAggregateTarget "libdispatch_Sim" */; + buildPhases = ( + ); + dependencies = ( + E4128E4A13B94BCE00ABB2CB /* PBXTargetDependency */, + ); + name = libdispatch_Sim; + productName = libdispatch_Sim; + }; + C927F35A10FD7F0600C5AB8B /* libdispatch_tools */ = { + isa = PBXAggregateTarget; + buildConfigurationList = C927F35E10FD7F0B00C5AB8B /* Build configuration list for PBXAggregateTarget "libdispatch_tools" */; + buildPhases = ( + ); + dependencies = ( + C927F36910FD7F1A00C5AB8B /* PBXTargetDependency */, + ); + name = libdispatch_tools; + productName = ddt; + }; +/* End PBXAggregateTarget section */ + /* Begin PBXBuildFile section */ - 2EC9C9B80E8809EF00E2499A /* legacy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2EC9C9B70E8809EF00E2499A /* legacy.c */; }; + 5A0095A210F274B0000E2A31 /* io_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A0095A110F274B0000E2A31 /* io_internal.h */; }; + 5A27262610F26F1900751FBC /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 5A27262510F26F1900751FBC /* io.c */; }; 5A5D13AC0F6B280500197CC3 /* semaphore_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A5D13AB0F6B280500197CC3 /* semaphore_internal.h */; }; + 5AAB45C010D30B79004407EA /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 5AAB45BF10D30B79004407EA /* data.c */; }; + 5AAB45C410D30CC7004407EA /* io.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AAB45C310D30CC7004407EA /* io.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5AAB45C610D30D0C004407EA /* data.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AAB45C510D30D0C004407EA /* data.h */; settings = {ATTRIBUTES = (Public, ); }; }; 721F5C5D0F15520500FF03A6 /* semaphore.h in Headers */ = {isa = PBXBuildFile; fileRef = 721F5C5C0F15520500FF03A6 /* semaphore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 721F5CCF0F15553500FF03A6 /* semaphore.c in Sources */ = {isa = PBXBuildFile; fileRef = 721F5CCE0F15553500FF03A6 /* semaphore.c */; }; 72CC94300ECCD8750031B751 /* base.h in Headers */ = {isa = PBXBuildFile; fileRef = 72CC942F0ECCD8750031B751 /* base.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -20,74 +49,227 @@ 965ECC210F3EAB71004DDD89 /* object_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 965ECC200F3EAB71004DDD89 /* object_internal.h */; }; 9661E56B0F3E7DDF00749F3E /* object.c in Sources */ = {isa = PBXBuildFile; fileRef = 9661E56A0F3E7DDF00749F3E /* object.c */; }; 9676A0E10F3E755D00713ADB /* apply.c in Sources */ = {isa = PBXBuildFile; fileRef = 9676A0E00F3E755D00713ADB /* apply.c */; }; - 96929D840F3EA1020041FF5D /* hw_shims.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D820F3EA1020041FF5D /* hw_shims.h */; }; - 96929D850F3EA1020041FF5D /* os_shims.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D830F3EA1020041FF5D /* os_shims.h */; }; + 96929D840F3EA1020041FF5D /* atomic.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D820F3EA1020041FF5D /* atomic.h */; }; + 96929D850F3EA1020041FF5D /* shims.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D830F3EA1020041FF5D /* shims.h */; }; 96929D960F3EA2170041FF5D /* queue_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D950F3EA2170041FF5D /* queue_internal.h */; }; 96A8AA870F41E7A400CD570B /* source.c in Sources */ = {isa = PBXBuildFile; fileRef = 96A8AA860F41E7A400CD570B /* source.c */; }; 96BC39BD0F3EBAB100C59689 /* queue_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 96BC39BC0F3EBAB100C59689 /* queue_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 96C9553B0F3EAEDD000D2CA4 /* once.h in Headers */ = {isa = PBXBuildFile; fileRef = 96C9553A0F3EAEDD000D2CA4 /* once.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96DF70BE0F38FE3C0074BD99 /* once.c in Sources */ = {isa = PBXBuildFile; fileRef = 96DF70BD0F38FE3C0074BD99 /* once.c */; }; + E4128ED613BA9A1700ABB2CB /* hw_config.h in Headers */ = {isa = PBXBuildFile; fileRef = E4128ED513BA9A1700ABB2CB /* hw_config.h */; }; + E4128ED713BA9A1700ABB2CB /* hw_config.h in Headers */ = {isa = PBXBuildFile; fileRef = E4128ED513BA9A1700ABB2CB /* hw_config.h */; }; + E417A38412A472C4004D659D /* provider.d in Sources */ = {isa = PBXBuildFile; fileRef = E43570B8126E93380097AB9F /* provider.d */; }; + E417A38512A472C5004D659D /* provider.d in Sources */ = {isa = PBXBuildFile; fileRef = E43570B8126E93380097AB9F /* provider.d */; }; + E422A0D512A557B5005E5BDB /* trace.h in Headers */ = {isa = PBXBuildFile; fileRef = E422A0D412A557B5005E5BDB /* trace.h */; }; + E422A0D612A557B5005E5BDB /* trace.h in Headers */ = {isa = PBXBuildFile; fileRef = E422A0D412A557B5005E5BDB /* trace.h */; }; + E43570B9126E93380097AB9F /* provider.d in Sources */ = {isa = PBXBuildFile; fileRef = E43570B8126E93380097AB9F /* provider.d */; }; + E43570BA126E93380097AB9F /* provider.d in Sources */ = {isa = PBXBuildFile; fileRef = E43570B8126E93380097AB9F /* provider.d */; }; + E44EBE3E1251659900645D88 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = E44EBE3B1251659900645D88 /* init.c */; }; + E44EBE5412517EBE00645D88 /* protocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED950E8361E600161930 /* protocol.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + E44EBE5512517EBE00645D88 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = E44EBE3B1251659900645D88 /* init.c */; }; + E44EBE5612517EBE00645D88 /* protocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED950E8361E600161930 /* protocol.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + E44EBE5712517EBE00645D88 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = E44EBE3B1251659900645D88 /* init.c */; }; + E49F2423125D3C960057C971 /* resolver.c in Sources */ = {isa = PBXBuildFile; fileRef = E44EBE371251656400645D88 /* resolver.c */; }; + E49F2424125D3C970057C971 /* resolver.c in Sources */ = {isa = PBXBuildFile; fileRef = E44EBE371251656400645D88 /* resolver.c */; }; + E49F2499125D48D80057C971 /* resolver.c in Sources */ = {isa = PBXBuildFile; fileRef = E44EBE371251656400645D88 /* resolver.c */; }; + E49F24AB125D57FA0057C971 /* dispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED960E8361E600161930 /* dispatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24AC125D57FA0057C971 /* base.h in Headers */ = {isa = PBXBuildFile; fileRef = 72CC942F0ECCD8750031B751 /* base.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24AD125D57FA0057C971 /* object.h in Headers */ = {isa = PBXBuildFile; fileRef = 961B994F0F3E85C30006BC96 /* object.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24AE125D57FA0057C971 /* queue.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8B0E8361E600161930 /* queue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24AF125D57FA0057C971 /* source.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8D0E8361E600161930 /* source.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24B0125D57FA0057C971 /* semaphore.h in Headers */ = {isa = PBXBuildFile; fileRef = 721F5C5C0F15520500FF03A6 /* semaphore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24B1125D57FA0057C971 /* group.h in Headers */ = {isa = PBXBuildFile; fileRef = FC5C9C1D0EADABE3006E462D /* group.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24B2125D57FA0057C971 /* once.h in Headers */ = {isa = PBXBuildFile; fileRef = 96C9553A0F3EAEDD000D2CA4 /* once.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24B3125D57FA0057C971 /* io.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AAB45C310D30CC7004407EA /* io.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24B4125D57FA0057C971 /* data.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AAB45C510D30D0C004407EA /* data.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24B5125D57FA0057C971 /* time.h in Headers */ = {isa = PBXBuildFile; fileRef = 96032E4C0F5CC8D100241C5F /* time.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49F24B6125D57FA0057C971 /* private.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED930E8361E600161930 /* private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E49F24B7125D57FA0057C971 /* queue_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 96BC39BC0F3EBAB100C59689 /* queue_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E49F24B8125D57FA0057C971 /* source_private.h in Headers */ = {isa = PBXBuildFile; fileRef = FCEF047F0F5661960067401F /* source_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E49F24B9125D57FA0057C971 /* benchmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 961B99350F3E83980006BC96 /* benchmark.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E49F24BA125D57FA0057C971 /* internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8F0E8361E600161930 /* internal.h */; settings = {ATTRIBUTES = (); }; }; + E49F24BB125D57FA0057C971 /* queue_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D950F3EA2170041FF5D /* queue_internal.h */; }; + E49F24BC125D57FA0057C971 /* object_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 965ECC200F3EAB71004DDD89 /* object_internal.h */; }; + E49F24BD125D57FA0057C971 /* semaphore_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A5D13AB0F6B280500197CC3 /* semaphore_internal.h */; }; + E49F24BE125D57FA0057C971 /* source_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FC0B34780FA2851C0080FFA0 /* source_internal.h */; }; + E49F24BF125D57FA0057C971 /* io_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A0095A110F274B0000E2A31 /* io_internal.h */; }; + E49F24C1125D57FA0057C971 /* tsd.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1832A4109923C7003403D5 /* tsd.h */; }; + E49F24C2125D57FA0057C971 /* atomic.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D820F3EA1020041FF5D /* atomic.h */; }; + E49F24C3125D57FA0057C971 /* shims.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D830F3EA1020041FF5D /* shims.h */; }; + E49F24C4125D57FA0057C971 /* time.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1832A3109923C7003403D5 /* time.h */; }; + E49F24C5125D57FA0057C971 /* perfmon.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1832A2109923C7003403D5 /* perfmon.h */; }; + E49F24C6125D57FA0057C971 /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = FC9C70E7105EC9620074F9CA /* config.h */; }; + E49F24C8125D57FA0057C971 /* protocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED950E8361E600161930 /* protocol.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + E49F24C9125D57FA0057C971 /* resolver.c in Sources */ = {isa = PBXBuildFile; fileRef = E44EBE371251656400645D88 /* resolver.c */; }; + E49F24CA125D57FA0057C971 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = E44EBE3B1251659900645D88 /* init.c */; }; + E49F24CB125D57FA0057C971 /* queue.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED8A0E8361E600161930 /* queue.c */; }; + E49F24CC125D57FA0057C971 /* semaphore.c in Sources */ = {isa = PBXBuildFile; fileRef = 721F5CCE0F15553500FF03A6 /* semaphore.c */; }; + E49F24CD125D57FA0057C971 /* once.c in Sources */ = {isa = PBXBuildFile; fileRef = 96DF70BD0F38FE3C0074BD99 /* once.c */; }; + E49F24CE125D57FA0057C971 /* apply.c in Sources */ = {isa = PBXBuildFile; fileRef = 9676A0E00F3E755D00713ADB /* apply.c */; }; + E49F24CF125D57FA0057C971 /* object.c in Sources */ = {isa = PBXBuildFile; fileRef = 9661E56A0F3E7DDF00749F3E /* object.c */; }; + E49F24D0125D57FA0057C971 /* benchmark.c in Sources */ = {isa = PBXBuildFile; fileRef = 965CD6340F3E806200D4E28D /* benchmark.c */; }; + E49F24D1125D57FA0057C971 /* source.c in Sources */ = {isa = PBXBuildFile; fileRef = 96A8AA860F41E7A400CD570B /* source.c */; }; + E49F24D2125D57FA0057C971 /* time.c in Sources */ = {isa = PBXBuildFile; fileRef = 96032E4A0F5CC8C700241C5F /* time.c */; }; + E49F24D3125D57FA0057C971 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 5AAB45BF10D30B79004407EA /* data.c */; }; + E49F24D4125D57FA0057C971 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 5A27262510F26F1900751FBC /* io.c */; }; + E4BA743B13A8911B0095BDF1 /* getprogname.h in Headers */ = {isa = PBXBuildFile; fileRef = E4BA743913A8911B0095BDF1 /* getprogname.h */; }; + E4BA743C13A8911B0095BDF1 /* getprogname.h in Headers */ = {isa = PBXBuildFile; fileRef = E4BA743913A8911B0095BDF1 /* getprogname.h */; }; + E4BA743F13A8911B0095BDF1 /* malloc_zone.h in Headers */ = {isa = PBXBuildFile; fileRef = E4BA743A13A8911B0095BDF1 /* malloc_zone.h */; }; + E4BA744013A8911B0095BDF1 /* malloc_zone.h in Headers */ = {isa = PBXBuildFile; fileRef = E4BA743A13A8911B0095BDF1 /* malloc_zone.h */; }; + E4C1ED6F1263E714000D3C8B /* data_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C1ED6E1263E714000D3C8B /* data_internal.h */; }; + E4C1ED701263E714000D3C8B /* data_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C1ED6E1263E714000D3C8B /* data_internal.h */; }; + E4EC11AE12514302000DDBD1 /* queue.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED8A0E8361E600161930 /* queue.c */; }; + E4EC11AF12514302000DDBD1 /* semaphore.c in Sources */ = {isa = PBXBuildFile; fileRef = 721F5CCE0F15553500FF03A6 /* semaphore.c */; }; + E4EC11B012514302000DDBD1 /* once.c in Sources */ = {isa = PBXBuildFile; fileRef = 96DF70BD0F38FE3C0074BD99 /* once.c */; }; + E4EC11B112514302000DDBD1 /* apply.c in Sources */ = {isa = PBXBuildFile; fileRef = 9676A0E00F3E755D00713ADB /* apply.c */; }; + E4EC11B212514302000DDBD1 /* object.c in Sources */ = {isa = PBXBuildFile; fileRef = 9661E56A0F3E7DDF00749F3E /* object.c */; }; + E4EC11B312514302000DDBD1 /* benchmark.c in Sources */ = {isa = PBXBuildFile; fileRef = 965CD6340F3E806200D4E28D /* benchmark.c */; }; + E4EC11B412514302000DDBD1 /* source.c in Sources */ = {isa = PBXBuildFile; fileRef = 96A8AA860F41E7A400CD570B /* source.c */; }; + E4EC11B512514302000DDBD1 /* time.c in Sources */ = {isa = PBXBuildFile; fileRef = 96032E4A0F5CC8C700241C5F /* time.c */; }; + E4EC11B712514302000DDBD1 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 5AAB45BF10D30B79004407EA /* data.c */; }; + E4EC11B812514302000DDBD1 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 5A27262510F26F1900751FBC /* io.c */; }; + E4EC121A12514715000DDBD1 /* queue.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED8A0E8361E600161930 /* queue.c */; }; + E4EC121B12514715000DDBD1 /* semaphore.c in Sources */ = {isa = PBXBuildFile; fileRef = 721F5CCE0F15553500FF03A6 /* semaphore.c */; }; + E4EC121C12514715000DDBD1 /* once.c in Sources */ = {isa = PBXBuildFile; fileRef = 96DF70BD0F38FE3C0074BD99 /* once.c */; }; + E4EC121D12514715000DDBD1 /* apply.c in Sources */ = {isa = PBXBuildFile; fileRef = 9676A0E00F3E755D00713ADB /* apply.c */; }; + E4EC121E12514715000DDBD1 /* object.c in Sources */ = {isa = PBXBuildFile; fileRef = 9661E56A0F3E7DDF00749F3E /* object.c */; }; + E4EC121F12514715000DDBD1 /* benchmark.c in Sources */ = {isa = PBXBuildFile; fileRef = 965CD6340F3E806200D4E28D /* benchmark.c */; }; + E4EC122012514715000DDBD1 /* source.c in Sources */ = {isa = PBXBuildFile; fileRef = 96A8AA860F41E7A400CD570B /* source.c */; }; + E4EC122112514715000DDBD1 /* time.c in Sources */ = {isa = PBXBuildFile; fileRef = 96032E4A0F5CC8C700241C5F /* time.c */; }; + E4EC122312514715000DDBD1 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 5AAB45BF10D30B79004407EA /* data.c */; }; + E4EC122412514715000DDBD1 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 5A27262510F26F1900751FBC /* io.c */; }; FC0B34790FA2851C0080FFA0 /* source_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FC0B34780FA2851C0080FFA0 /* source_internal.h */; }; + FC1832A6109923C7003403D5 /* perfmon.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1832A2109923C7003403D5 /* perfmon.h */; }; + FC1832A7109923C7003403D5 /* time.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1832A3109923C7003403D5 /* time.h */; }; + FC1832A8109923C7003403D5 /* tsd.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1832A4109923C7003403D5 /* tsd.h */; }; FC5C9C1E0EADABE3006E462D /* group.h in Headers */ = {isa = PBXBuildFile; fileRef = FC5C9C1D0EADABE3006E462D /* group.h */; settings = {ATTRIBUTES = (Public, ); }; }; FC7BED990E8361E600161930 /* queue.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED8A0E8361E600161930 /* queue.c */; }; FC7BED9A0E8361E600161930 /* queue.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8B0E8361E600161930 /* queue.h */; settings = {ATTRIBUTES = (Public, ); }; }; FC7BED9C0E8361E600161930 /* source.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8D0E8361E600161930 /* source.h */; settings = {ATTRIBUTES = (Public, ); }; }; FC7BED9E0E8361E600161930 /* internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8F0E8361E600161930 /* internal.h */; settings = {ATTRIBUTES = (); }; }; - FC7BED9F0E8361E600161930 /* legacy.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED900E8361E600161930 /* legacy.h */; settings = {ATTRIBUTES = (Private, ); }; }; FC7BEDA20E8361E600161930 /* private.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED930E8361E600161930 /* private.h */; settings = {ATTRIBUTES = (Private, ); }; }; FC7BEDA40E8361E600161930 /* protocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED950E8361E600161930 /* protocol.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; FC7BEDA50E8361E600161930 /* dispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED960E8361E600161930 /* dispatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FC7BEDA60E8361E600161930 /* shims.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED970E8361E600161930 /* shims.c */; }; + FC9C70E8105EC9620074F9CA /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = FC9C70E7105EC9620074F9CA /* config.h */; }; FCEF04800F5661960067401F /* source_private.h in Headers */ = {isa = PBXBuildFile; fileRef = FCEF047F0F5661960067401F /* source_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + C927F36610FD7F1000C5AB8B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C927F35F10FD7F1000C5AB8B /* ddt.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = FCFA5AA010D1AE050074F59A; + remoteInfo = ddt; + }; + C927F36810FD7F1A00C5AB8B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C927F35F10FD7F1000C5AB8B /* ddt.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = FCFA5A9F10D1AE050074F59A; + remoteInfo = ddt; + }; + E4128E4913B94BCE00ABB2CB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D2AAC045055464E500DB518D; + remoteInfo = libdispatch; + }; + E47D6ECA125FEB9D0070D91C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E4EC118F12514302000DDBD1; + remoteInfo = "libdispatch up resolved"; + }; + E47D6ECC125FEBA10070D91C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E4EC121612514715000DDBD1; + remoteInfo = "libdispatch mp resolved"; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ - 2EC9C9B70E8809EF00E2499A /* legacy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = legacy.c; path = src/legacy.c; sourceTree = ""; }; - 5A5D13AB0F6B280500197CC3 /* semaphore_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = semaphore_internal.h; path = src/semaphore_internal.h; sourceTree = ""; }; - 721F5C5C0F15520500FF03A6 /* semaphore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = semaphore.h; path = src/semaphore.h; sourceTree = ""; }; - 721F5CCE0F15553500FF03A6 /* semaphore.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = semaphore.c; path = src/semaphore.c; sourceTree = ""; }; - 72B54F690EB169EB00DBECBA /* dispatch_source_create.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch_source_create.3; path = man/dispatch_source_create.3; sourceTree = ""; }; - 72CC940C0ECCD5720031B751 /* dispatch_object.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch_object.3; path = man/dispatch_object.3; sourceTree = ""; }; - 72CC940D0ECCD5720031B751 /* dispatch.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch.3; path = man/dispatch.3; sourceTree = ""; }; - 72CC942F0ECCD8750031B751 /* base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = base.h; path = src/base.h; sourceTree = ""; }; - 96032E4A0F5CC8C700241C5F /* time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = time.c; path = src/time.c; sourceTree = ""; }; - 96032E4C0F5CC8D100241C5F /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = time.h; path = src/time.h; sourceTree = ""; }; - 960F0E7D0F3FB232000D88BF /* dispatch_apply.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_apply.3; path = man/dispatch_apply.3; sourceTree = ""; }; - 960F0E7E0F3FB232000D88BF /* dispatch_once.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_once.3; path = man/dispatch_once.3; sourceTree = ""; }; - 961B99350F3E83980006BC96 /* benchmark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = benchmark.h; path = src/benchmark.h; sourceTree = ""; }; - 961B994F0F3E85C30006BC96 /* object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = object.h; path = src/object.h; sourceTree = ""; }; - 963FDDE50F3FB6BD00BF2D00 /* dispatch_semaphore_create.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_semaphore_create.3; path = man/dispatch_semaphore_create.3; sourceTree = ""; }; - 965CD6340F3E806200D4E28D /* benchmark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = benchmark.c; path = src/benchmark.c; sourceTree = ""; }; - 965ECC200F3EAB71004DDD89 /* object_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = object_internal.h; path = src/object_internal.h; sourceTree = ""; }; - 9661E56A0F3E7DDF00749F3E /* object.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = object.c; path = src/object.c; sourceTree = ""; }; - 9676A0E00F3E755D00713ADB /* apply.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = apply.c; path = src/apply.c; sourceTree = ""; }; - 96859A3D0EF71BAD003EB3FB /* dispatch_benchmark.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch_benchmark.3; path = man/dispatch_benchmark.3; sourceTree = ""; }; - 96929D820F3EA1020041FF5D /* hw_shims.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hw_shims.h; path = src/hw_shims.h; sourceTree = ""; }; - 96929D830F3EA1020041FF5D /* os_shims.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = os_shims.h; path = src/os_shims.h; sourceTree = ""; }; - 96929D950F3EA2170041FF5D /* queue_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = queue_internal.h; path = src/queue_internal.h; sourceTree = ""; }; - 96A8AA860F41E7A400CD570B /* source.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = source.c; path = src/source.c; sourceTree = ""; }; - 96BC39BC0F3EBAB100C59689 /* queue_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = queue_private.h; path = src/queue_private.h; sourceTree = ""; }; - 96C9553A0F3EAEDD000D2CA4 /* once.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = once.h; path = src/once.h; sourceTree = ""; }; - 96DF70BD0F38FE3C0074BD99 /* once.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = once.c; path = src/once.c; sourceTree = ""; }; - D2AAC046055464E500DB518D /* libdispatch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdispatch.a; sourceTree = BUILT_PRODUCTS_DIR; }; - FC0B34780FA2851C0080FFA0 /* source_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = source_internal.h; path = src/source_internal.h; sourceTree = ""; }; - FC36279C0E933ED80054F1A3 /* dispatch_queue_create.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch_queue_create.3; path = man/dispatch_queue_create.3; sourceTree = ""; }; - FC5C9C1D0EADABE3006E462D /* group.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = group.h; path = src/group.h; sourceTree = ""; }; - FC678DE80F97E0C300AB5993 /* dispatch_after.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_after.3; path = man/dispatch_after.3; sourceTree = ""; }; - FC678DE90F97E0C300AB5993 /* dispatch_api.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_api.3; path = man/dispatch_api.3; sourceTree = ""; }; - FC678DEA0F97E0C300AB5993 /* dispatch_async.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_async.3; path = man/dispatch_async.3; sourceTree = ""; }; - FC678DEB0F97E0C300AB5993 /* dispatch_group_create.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_group_create.3; path = man/dispatch_group_create.3; sourceTree = ""; }; - FC678DEC0F97E0C300AB5993 /* dispatch_time.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_time.3; path = man/dispatch_time.3; sourceTree = ""; }; - FC7BED8A0E8361E600161930 /* queue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = queue.c; path = src/queue.c; sourceTree = ""; }; - FC7BED8B0E8361E600161930 /* queue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = queue.h; path = src/queue.h; sourceTree = ""; }; - FC7BED8D0E8361E600161930 /* source.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = source.h; path = src/source.h; sourceTree = ""; }; - FC7BED8F0E8361E600161930 /* internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = internal.h; path = src/internal.h; sourceTree = ""; }; - FC7BED900E8361E600161930 /* legacy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = legacy.h; path = src/legacy.h; sourceTree = ""; }; - FC7BED930E8361E600161930 /* private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = private.h; path = src/private.h; sourceTree = ""; }; - FC7BED950E8361E600161930 /* protocol.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = protocol.defs; path = src/protocol.defs; sourceTree = ""; }; - FC7BED960E8361E600161930 /* dispatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dispatch.h; path = src/dispatch.h; sourceTree = ""; }; - FC7BED970E8361E600161930 /* shims.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shims.c; path = src/shims.c; sourceTree = ""; }; - FCEF047F0F5661960067401F /* source_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = source_private.h; path = src/source_private.h; sourceTree = ""; }; + 5A0095A110F274B0000E2A31 /* io_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_internal.h; sourceTree = ""; }; + 5A27262510F26F1900751FBC /* io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = io.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + 5A5D13AB0F6B280500197CC3 /* semaphore_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = semaphore_internal.h; sourceTree = ""; }; + 5AAB45BF10D30B79004407EA /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = data.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + 5AAB45C310D30CC7004407EA /* io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io.h; sourceTree = ""; tabWidth = 8; }; + 5AAB45C510D30D0C004407EA /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = ""; tabWidth = 8; }; + 721F5C5C0F15520500FF03A6 /* semaphore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = semaphore.h; sourceTree = ""; }; + 721F5CCE0F15553500FF03A6 /* semaphore.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = semaphore.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + 72B54F690EB169EB00DBECBA /* dispatch_source_create.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = dispatch_source_create.3; sourceTree = ""; }; + 72CC940C0ECCD5720031B751 /* dispatch_object.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = dispatch_object.3; sourceTree = ""; }; + 72CC940D0ECCD5720031B751 /* dispatch.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = dispatch.3; sourceTree = ""; }; + 72CC942F0ECCD8750031B751 /* base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base.h; sourceTree = ""; }; + 96032E4A0F5CC8C700241C5F /* time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = time.c; sourceTree = ""; }; + 96032E4C0F5CC8D100241C5F /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = time.h; sourceTree = ""; }; + 960F0E7D0F3FB232000D88BF /* dispatch_apply.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dispatch_apply.3; sourceTree = ""; }; + 960F0E7E0F3FB232000D88BF /* dispatch_once.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dispatch_once.3; sourceTree = ""; }; + 961B99350F3E83980006BC96 /* benchmark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = benchmark.h; sourceTree = ""; }; + 961B994F0F3E85C30006BC96 /* object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = object.h; sourceTree = ""; }; + 963FDDE50F3FB6BD00BF2D00 /* dispatch_semaphore_create.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dispatch_semaphore_create.3; sourceTree = ""; }; + 965CD6340F3E806200D4E28D /* benchmark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = benchmark.c; sourceTree = ""; }; + 965ECC200F3EAB71004DDD89 /* object_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = object_internal.h; sourceTree = ""; }; + 9661E56A0F3E7DDF00749F3E /* object.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = object.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + 9676A0E00F3E755D00713ADB /* apply.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = apply.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + 96859A3D0EF71BAD003EB3FB /* dispatch_benchmark.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = dispatch_benchmark.3; sourceTree = ""; }; + 96929D820F3EA1020041FF5D /* atomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = atomic.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 96929D830F3EA1020041FF5D /* shims.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shims.h; sourceTree = ""; }; + 96929D950F3EA2170041FF5D /* queue_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = queue_internal.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 96A8AA860F41E7A400CD570B /* source.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = source.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + 96BC39BC0F3EBAB100C59689 /* queue_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = queue_private.h; sourceTree = ""; }; + 96C9553A0F3EAEDD000D2CA4 /* once.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = once.h; sourceTree = ""; }; + 96DF70BD0F38FE3C0074BD99 /* once.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = once.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + C927F35F10FD7F1000C5AB8B /* ddt.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ddt.xcodeproj; path = tools/ddt/ddt.xcodeproj; sourceTree = ""; }; + D2AAC046055464E500DB518D /* libdispatch.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdispatch.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + E40041A9125D70590022B135 /* libdispatch-resolved.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "libdispatch-resolved.xcconfig"; sourceTree = ""; }; + E40041AA125D705F0022B135 /* libdispatch-resolver.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "libdispatch-resolver.xcconfig"; sourceTree = ""; }; + E4128ED513BA9A1700ABB2CB /* hw_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hw_config.h; sourceTree = ""; }; + E422A0D412A557B5005E5BDB /* trace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trace.h; sourceTree = ""; }; + E43570B8126E93380097AB9F /* provider.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = provider.d; sourceTree = ""; }; + E43D93F11097917E004F6A62 /* libdispatch.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdispatch.xcconfig; sourceTree = ""; }; + E44EBE331251654000645D88 /* resolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resolver.h; sourceTree = ""; }; + E44EBE371251656400645D88 /* resolver.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = resolver.c; sourceTree = ""; }; + E44EBE3B1251659900645D88 /* init.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = init.c; sourceTree = ""; }; + E47D6BB5125F0F800070D91C /* resolved.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resolved.h; sourceTree = ""; }; + E482F1CD12DBAB590030614D /* postprocess-headers.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "postprocess-headers.sh"; sourceTree = ""; }; + E49F24DF125D57FA0057C971 /* libdispatch.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdispatch.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + E49F251C125D629F0057C971 /* symlink-headers.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "symlink-headers.sh"; sourceTree = ""; }; + E49F251D125D630A0057C971 /* install-manpages.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "install-manpages.sh"; sourceTree = ""; }; + E49F251E125D631D0057C971 /* mig-headers.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "mig-headers.sh"; sourceTree = ""; }; + E4BA743513A88FE10095BDF1 /* dispatch_data_create.3 */ = {isa = PBXFileReference; lastKnownFileType = text; path = dispatch_data_create.3; sourceTree = ""; }; + E4BA743613A88FF30095BDF1 /* dispatch_io_create.3 */ = {isa = PBXFileReference; lastKnownFileType = text; path = dispatch_io_create.3; sourceTree = ""; }; + E4BA743713A88FF30095BDF1 /* dispatch_io_read.3 */ = {isa = PBXFileReference; lastKnownFileType = text; path = dispatch_io_read.3; sourceTree = ""; }; + E4BA743813A8900B0095BDF1 /* dispatch_read.3 */ = {isa = PBXFileReference; lastKnownFileType = text; path = dispatch_read.3; sourceTree = ""; }; + E4BA743913A8911B0095BDF1 /* getprogname.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = getprogname.h; sourceTree = ""; }; + E4BA743A13A8911B0095BDF1 /* malloc_zone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = malloc_zone.h; sourceTree = ""; }; + E4C1ED6E1263E714000D3C8B /* data_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data_internal.h; sourceTree = ""; }; + E4EC11C312514302000DDBD1 /* libdispatch_up.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdispatch_up.a; sourceTree = BUILT_PRODUCTS_DIR; }; + E4EC122D12514715000DDBD1 /* libdispatch_mp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdispatch_mp.a; sourceTree = BUILT_PRODUCTS_DIR; }; + FC0B34780FA2851C0080FFA0 /* source_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = source_internal.h; sourceTree = ""; }; + FC1832A2109923C7003403D5 /* perfmon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = perfmon.h; sourceTree = ""; }; + FC1832A3109923C7003403D5 /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = time.h; sourceTree = ""; }; + FC1832A4109923C7003403D5 /* tsd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tsd.h; sourceTree = ""; }; + FC36279C0E933ED80054F1A3 /* dispatch_queue_create.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = dispatch_queue_create.3; sourceTree = ""; }; + FC5C9C1D0EADABE3006E462D /* group.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = group.h; sourceTree = ""; }; + FC678DE80F97E0C300AB5993 /* dispatch_after.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dispatch_after.3; sourceTree = ""; }; + FC678DE90F97E0C300AB5993 /* dispatch_api.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dispatch_api.3; sourceTree = ""; }; + FC678DEA0F97E0C300AB5993 /* dispatch_async.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dispatch_async.3; sourceTree = ""; }; + FC678DEB0F97E0C300AB5993 /* dispatch_group_create.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dispatch_group_create.3; sourceTree = ""; }; + FC678DEC0F97E0C300AB5993 /* dispatch_time.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dispatch_time.3; sourceTree = ""; }; + FC7BED8A0E8361E600161930 /* queue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = queue.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; + FC7BED8B0E8361E600161930 /* queue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = queue.h; sourceTree = ""; }; + FC7BED8D0E8361E600161930 /* source.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = source.h; sourceTree = ""; }; + FC7BED8F0E8361E600161930 /* internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal.h; sourceTree = ""; }; + FC7BED930E8361E600161930 /* private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = private.h; sourceTree = ""; }; + FC7BED950E8361E600161930 /* protocol.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; path = protocol.defs; sourceTree = ""; }; + FC7BED960E8361E600161930 /* dispatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dispatch.h; sourceTree = ""; }; + FC9C70E7105EC9620074F9CA /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = ""; }; + FCEF047F0F5661960067401F /* source_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = source_private.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -98,12 +280,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E49F24D5125D57FA0057C971 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 08FB7794FE84155DC02AAC07 /* libdispatch */ = { isa = PBXGroup; children = ( + E44DB71E11D2FF080074F2AD /* Build Support */, FC7BEDAA0E83625200161930 /* Public Headers */, FC7BEDAF0E83626100161930 /* Private Headers */, FC7BEDB60E8363DC00161930 /* Project Headers */, @@ -111,31 +301,40 @@ C6A0FF2B0290797F04C91782 /* Documentation */, 1AB674ADFE9D54B511CA2CBB /* Products */, ); + indentWidth = 4; name = libdispatch; sourceTree = ""; + tabWidth = 4; + usesTabs = 1; }; 08FB7795FE84155DC02AAC07 /* Source */ = { isa = PBXGroup; children = ( + 9676A0E00F3E755D00713ADB /* apply.c */, 965CD6340F3E806200D4E28D /* benchmark.c */, + 5AAB45BF10D30B79004407EA /* data.c */, + E44EBE3B1251659900645D88 /* init.c */, + 5A27262510F26F1900751FBC /* io.c */, 9661E56A0F3E7DDF00749F3E /* object.c */, - 9676A0E00F3E755D00713ADB /* apply.c */, - FC7BED8A0E8361E600161930 /* queue.c */, 96DF70BD0F38FE3C0074BD99 /* once.c */, - 96032E4A0F5CC8C700241C5F /* time.c */, + FC7BED8A0E8361E600161930 /* queue.c */, 721F5CCE0F15553500FF03A6 /* semaphore.c */, 96A8AA860F41E7A400CD570B /* source.c */, - FC7BED970E8361E600161930 /* shims.c */, - 2EC9C9B70E8809EF00E2499A /* legacy.c */, + 96032E4A0F5CC8C700241C5F /* time.c */, FC7BED950E8361E600161930 /* protocol.defs */, + E43570B8126E93380097AB9F /* provider.d */, ); name = Source; + path = src; sourceTree = ""; }; 1AB674ADFE9D54B511CA2CBB /* Products */ = { isa = PBXGroup; children = ( - D2AAC046055464E500DB518D /* libdispatch.a */, + D2AAC046055464E500DB518D /* libdispatch.dylib */, + E4EC11C312514302000DDBD1 /* libdispatch_up.a */, + E4EC122D12514715000DDBD1 /* libdispatch_mp.a */, + E49F24DF125D57FA0057C971 /* libdispatch.dylib */, ); name = Products; sourceTree = ""; @@ -143,38 +342,118 @@ C6A0FF2B0290797F04C91782 /* Documentation */ = { isa = PBXGroup; children = ( + 72CC940D0ECCD5720031B751 /* dispatch.3 */, FC678DE80F97E0C300AB5993 /* dispatch_after.3 */, FC678DE90F97E0C300AB5993 /* dispatch_api.3 */, - FC678DEA0F97E0C300AB5993 /* dispatch_async.3 */, - FC678DEB0F97E0C300AB5993 /* dispatch_group_create.3 */, - FC678DEC0F97E0C300AB5993 /* dispatch_time.3 */, - 72CC940D0ECCD5720031B751 /* dispatch.3 */, 960F0E7D0F3FB232000D88BF /* dispatch_apply.3 */, + FC678DEA0F97E0C300AB5993 /* dispatch_async.3 */, 96859A3D0EF71BAD003EB3FB /* dispatch_benchmark.3 */, + E4BA743513A88FE10095BDF1 /* dispatch_data_create.3 */, + FC678DEB0F97E0C300AB5993 /* dispatch_group_create.3 */, + E4BA743613A88FF30095BDF1 /* dispatch_io_create.3 */, + E4BA743713A88FF30095BDF1 /* dispatch_io_read.3 */, 72CC940C0ECCD5720031B751 /* dispatch_object.3 */, 960F0E7E0F3FB232000D88BF /* dispatch_once.3 */, + FC36279C0E933ED80054F1A3 /* dispatch_queue_create.3 */, + E4BA743813A8900B0095BDF1 /* dispatch_read.3 */, 963FDDE50F3FB6BD00BF2D00 /* dispatch_semaphore_create.3 */, 72B54F690EB169EB00DBECBA /* dispatch_source_create.3 */, - FC36279C0E933ED80054F1A3 /* dispatch_queue_create.3 */, + FC678DEC0F97E0C300AB5993 /* dispatch_time.3 */, ); name = Documentation; + path = man; + sourceTree = ""; + }; + C927F36010FD7F1000C5AB8B /* Products */ = { + isa = PBXGroup; + children = ( + C927F36710FD7F1000C5AB8B /* ddt */, + ); + name = Products; + sourceTree = ""; + }; + E40041E4125E71150022B135 /* xcodeconfig */ = { + isa = PBXGroup; + children = ( + E43D93F11097917E004F6A62 /* libdispatch.xcconfig */, + E40041AA125D705F0022B135 /* libdispatch-resolver.xcconfig */, + E40041A9125D70590022B135 /* libdispatch-resolved.xcconfig */, + ); + path = xcodeconfig; + sourceTree = ""; + }; + E44DB71E11D2FF080074F2AD /* Build Support */ = { + isa = PBXGroup; + children = ( + E4BA743413A88D390095BDF1 /* config */, + E40041E4125E71150022B135 /* xcodeconfig */, + E49F259C125D664F0057C971 /* xcodescripts */, + E47D6BCA125F10F70070D91C /* resolver */, + C927F35F10FD7F1000C5AB8B /* ddt.xcodeproj */, + ); + name = "Build Support"; + sourceTree = ""; + }; + E47D6BCA125F10F70070D91C /* resolver */ = { + isa = PBXGroup; + children = ( + E47D6BB5125F0F800070D91C /* resolved.h */, + E44EBE371251656400645D88 /* resolver.c */, + E44EBE331251654000645D88 /* resolver.h */, + ); + path = resolver; + sourceTree = ""; + }; + E49F259C125D664F0057C971 /* xcodescripts */ = { + isa = PBXGroup; + children = ( + E49F251D125D630A0057C971 /* install-manpages.sh */, + E49F251E125D631D0057C971 /* mig-headers.sh */, + E482F1CD12DBAB590030614D /* postprocess-headers.sh */, + E49F251C125D629F0057C971 /* symlink-headers.sh */, + ); + path = xcodescripts; + sourceTree = ""; + }; + E4BA743413A88D390095BDF1 /* config */ = { + isa = PBXGroup; + children = ( + FC9C70E7105EC9620074F9CA /* config.h */, + ); + path = config; + sourceTree = ""; + }; + FC1832A0109923B3003403D5 /* shims */ = { + isa = PBXGroup; + children = ( + 96929D820F3EA1020041FF5D /* atomic.h */, + E4BA743913A8911B0095BDF1 /* getprogname.h */, + E4128ED513BA9A1700ABB2CB /* hw_config.h */, + E4BA743A13A8911B0095BDF1 /* malloc_zone.h */, + FC1832A2109923C7003403D5 /* perfmon.h */, + FC1832A3109923C7003403D5 /* time.h */, + FC1832A4109923C7003403D5 /* tsd.h */, + ); + path = shims; sourceTree = ""; }; FC7BEDAA0E83625200161930 /* Public Headers */ = { isa = PBXGroup; children = ( - 96032E4C0F5CC8D100241C5F /* time.h */, - 721F5C5C0F15520500FF03A6 /* semaphore.h */, - 961B994F0F3E85C30006BC96 /* object.h */, - 96C9553A0F3EAEDD000D2CA4 /* once.h */, - 961B99350F3E83980006BC96 /* benchmark.h */, 72CC942F0ECCD8750031B751 /* base.h */, + 5AAB45C510D30D0C004407EA /* data.h */, FC7BED960E8361E600161930 /* dispatch.h */, + FC5C9C1D0EADABE3006E462D /* group.h */, + 5AAB45C310D30CC7004407EA /* io.h */, + 961B994F0F3E85C30006BC96 /* object.h */, + 96C9553A0F3EAEDD000D2CA4 /* once.h */, FC7BED8B0E8361E600161930 /* queue.h */, + 721F5C5C0F15520500FF03A6 /* semaphore.h */, FC7BED8D0E8361E600161930 /* source.h */, - FC5C9C1D0EADABE3006E462D /* group.h */, + 96032E4C0F5CC8D100241C5F /* time.h */, ); name = "Public Headers"; + path = dispatch; sourceTree = ""; }; FC7BEDAF0E83626100161930 /* Private Headers */ = { @@ -182,24 +461,29 @@ children = ( FC7BED930E8361E600161930 /* private.h */, 96BC39BC0F3EBAB100C59689 /* queue_private.h */, - FC7BED900E8361E600161930 /* legacy.h */, + FCEF047F0F5661960067401F /* source_private.h */, + 961B99350F3E83980006BC96 /* benchmark.h */, ); name = "Private Headers"; + path = private; sourceTree = ""; }; FC7BEDB60E8363DC00161930 /* Project Headers */ = { isa = PBXGroup; children = ( + FC7BED8F0E8361E600161930 /* internal.h */, + E4C1ED6E1263E714000D3C8B /* data_internal.h */, + 5A0095A110F274B0000E2A31 /* io_internal.h */, 965ECC200F3EAB71004DDD89 /* object_internal.h */, 96929D950F3EA2170041FF5D /* queue_internal.h */, 5A5D13AB0F6B280500197CC3 /* semaphore_internal.h */, FC0B34780FA2851C0080FFA0 /* source_internal.h */, - FCEF047F0F5661960067401F /* source_private.h */, - 96929D820F3EA1020041FF5D /* hw_shims.h */, - 96929D830F3EA1020041FF5D /* os_shims.h */, - FC7BED8F0E8361E600161930 /* internal.h */, + E422A0D412A557B5005E5BDB /* trace.h */, + 96929D830F3EA1020041FF5D /* shims.h */, + FC1832A0109923B3003403D5 /* shims */, ); name = "Project Headers"; + path = src; sourceTree = ""; }; /* End PBXGroup section */ @@ -209,81 +493,157 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 72CC94300ECCD8750031B751 /* base.h in Headers */, FC7BEDA50E8361E600161930 /* dispatch.h in Headers */, + 72CC94300ECCD8750031B751 /* base.h in Headers */, + 961B99500F3E85C30006BC96 /* object.h in Headers */, FC7BED9A0E8361E600161930 /* queue.h in Headers */, FC7BED9C0E8361E600161930 /* source.h in Headers */, - FC5C9C1E0EADABE3006E462D /* group.h in Headers */, - FC7BEDA20E8361E600161930 /* private.h in Headers */, - FC7BED9F0E8361E600161930 /* legacy.h in Headers */, - FC7BED9E0E8361E600161930 /* internal.h in Headers */, 721F5C5D0F15520500FF03A6 /* semaphore.h in Headers */, - 961B99360F3E83980006BC96 /* benchmark.h in Headers */, - 961B99500F3E85C30006BC96 /* object.h in Headers */, - 96929D840F3EA1020041FF5D /* hw_shims.h in Headers */, - 96929D850F3EA1020041FF5D /* os_shims.h in Headers */, - 96929D960F3EA2170041FF5D /* queue_internal.h in Headers */, - 965ECC210F3EAB71004DDD89 /* object_internal.h in Headers */, + FC5C9C1E0EADABE3006E462D /* group.h in Headers */, 96C9553B0F3EAEDD000D2CA4 /* once.h in Headers */, + 5AAB45C410D30CC7004407EA /* io.h in Headers */, + 5AAB45C610D30D0C004407EA /* data.h in Headers */, + 96032E4D0F5CC8D100241C5F /* time.h in Headers */, + FC7BEDA20E8361E600161930 /* private.h in Headers */, 96BC39BD0F3EBAB100C59689 /* queue_private.h in Headers */, FCEF04800F5661960067401F /* source_private.h in Headers */, - 96032E4D0F5CC8D100241C5F /* time.h in Headers */, - 5A5D13AC0F6B280500197CC3 /* semaphore_internal.h in Headers */, + 961B99360F3E83980006BC96 /* benchmark.h in Headers */, + FC7BED9E0E8361E600161930 /* internal.h in Headers */, + 965ECC210F3EAB71004DDD89 /* object_internal.h in Headers */, + 96929D960F3EA2170041FF5D /* queue_internal.h in Headers */, FC0B34790FA2851C0080FFA0 /* source_internal.h in Headers */, + 5A5D13AC0F6B280500197CC3 /* semaphore_internal.h in Headers */, + E4C1ED6F1263E714000D3C8B /* data_internal.h in Headers */, + 5A0095A210F274B0000E2A31 /* io_internal.h in Headers */, + FC1832A8109923C7003403D5 /* tsd.h in Headers */, + 96929D840F3EA1020041FF5D /* atomic.h in Headers */, + 96929D850F3EA1020041FF5D /* shims.h in Headers */, + FC1832A7109923C7003403D5 /* time.h in Headers */, + FC1832A6109923C7003403D5 /* perfmon.h in Headers */, + FC9C70E8105EC9620074F9CA /* config.h in Headers */, + E422A0D512A557B5005E5BDB /* trace.h in Headers */, + E4BA743B13A8911B0095BDF1 /* getprogname.h in Headers */, + E4BA743F13A8911B0095BDF1 /* malloc_zone.h in Headers */, + E4128ED613BA9A1700ABB2CB /* hw_config.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E49F24AA125D57FA0057C971 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + E49F24AB125D57FA0057C971 /* dispatch.h in Headers */, + E49F24AC125D57FA0057C971 /* base.h in Headers */, + E49F24AD125D57FA0057C971 /* object.h in Headers */, + E49F24AE125D57FA0057C971 /* queue.h in Headers */, + E49F24AF125D57FA0057C971 /* source.h in Headers */, + E49F24B0125D57FA0057C971 /* semaphore.h in Headers */, + E49F24B1125D57FA0057C971 /* group.h in Headers */, + E49F24B2125D57FA0057C971 /* once.h in Headers */, + E49F24B3125D57FA0057C971 /* io.h in Headers */, + E49F24B4125D57FA0057C971 /* data.h in Headers */, + E49F24B5125D57FA0057C971 /* time.h in Headers */, + E49F24B6125D57FA0057C971 /* private.h in Headers */, + E49F24B7125D57FA0057C971 /* queue_private.h in Headers */, + E49F24B8125D57FA0057C971 /* source_private.h in Headers */, + E49F24B9125D57FA0057C971 /* benchmark.h in Headers */, + E49F24BA125D57FA0057C971 /* internal.h in Headers */, + E49F24BC125D57FA0057C971 /* object_internal.h in Headers */, + E49F24BB125D57FA0057C971 /* queue_internal.h in Headers */, + E49F24BE125D57FA0057C971 /* source_internal.h in Headers */, + E49F24BD125D57FA0057C971 /* semaphore_internal.h in Headers */, + E4C1ED701263E714000D3C8B /* data_internal.h in Headers */, + E49F24BF125D57FA0057C971 /* io_internal.h in Headers */, + E49F24C1125D57FA0057C971 /* tsd.h in Headers */, + E49F24C2125D57FA0057C971 /* atomic.h in Headers */, + E49F24C3125D57FA0057C971 /* shims.h in Headers */, + E49F24C4125D57FA0057C971 /* time.h in Headers */, + E49F24C5125D57FA0057C971 /* perfmon.h in Headers */, + E49F24C6125D57FA0057C971 /* config.h in Headers */, + E422A0D612A557B5005E5BDB /* trace.h in Headers */, + E4BA743C13A8911B0095BDF1 /* getprogname.h in Headers */, + E4BA744013A8911B0095BDF1 /* malloc_zone.h in Headers */, + E4128ED713BA9A1700ABB2CB /* hw_config.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ -/* Begin PBXLegacyTarget section */ - 721EB4790F69D26F00845379 /* testbots */ = { - isa = PBXLegacyTarget; - buildArgumentsString = testbots; - buildConfigurationList = 721EB4850F69D2A600845379 /* Build configuration list for PBXLegacyTarget "testbots" */; +/* Begin PBXNativeTarget section */ + D2AAC045055464E500DB518D /* libdispatch */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB91EB08733DB70010E9CD /* Build configuration list for PBXNativeTarget "libdispatch" */; buildPhases = ( + D2AAC043055464E500DB518D /* Headers */, + D2AAC044055464E500DB518D /* Sources */, + D289987405E68DCB004EDB86 /* Frameworks */, + E482F1C512DBAA110030614D /* Postprocess Headers */, + 2EC9C9800E846B5200E2499A /* Symlink Headers */, + 4CED8B9D0EEDF8B600AF99AB /* Install Manpages */, + ); + buildRules = ( ); - buildToolPath = /usr/bin/make; - buildWorkingDirectory = testing; dependencies = ( + E47D6ECB125FEB9D0070D91C /* PBXTargetDependency */, + E47D6ECD125FEBA10070D91C /* PBXTargetDependency */, ); - name = testbots; - passBuildSettingsInEnvironment = 0; - productName = testbots; + name = libdispatch; + productName = libdispatch; + productReference = D2AAC046055464E500DB518D /* libdispatch.dylib */; + productType = "com.apple.product-type.library.dynamic"; }; - 7276FCBA0EB10E0F00F7F487 /* test */ = { - isa = PBXLegacyTarget; - buildArgumentsString = test; - buildConfigurationList = 7276FCC80EB10E2300F7F487 /* Build configuration list for PBXLegacyTarget "test" */; + E49F24A9125D57FA0057C971 /* libdispatch no resolver */ = { + isa = PBXNativeTarget; + buildConfigurationList = E49F24D8125D57FA0057C971 /* Build configuration list for PBXNativeTarget "libdispatch no resolver" */; buildPhases = ( + E49F24AA125D57FA0057C971 /* Headers */, + E49F24C7125D57FA0057C971 /* Sources */, + E49F24D5125D57FA0057C971 /* Frameworks */, + E4128EB213B9612700ABB2CB /* Postprocess Headers */, + E49F24D6125D57FA0057C971 /* Symlink Headers */, + E49F24D7125D57FA0057C971 /* Install Manpages */, + ); + buildRules = ( ); - buildToolPath = /usr/bin/make; - buildWorkingDirectory = testing; dependencies = ( ); - name = test; - passBuildSettingsInEnvironment = 0; - productName = test; + name = "libdispatch no resolver"; + productName = libdispatch; + productReference = E49F24DF125D57FA0057C971 /* libdispatch.dylib */; + productType = "com.apple.product-type.library.dynamic"; }; -/* End PBXLegacyTarget section */ - -/* Begin PBXNativeTarget section */ - D2AAC045055464E500DB518D /* libdispatch */ = { + E4EC118F12514302000DDBD1 /* libdispatch up resolved */ = { isa = PBXNativeTarget; - buildConfigurationList = 1DEB91EB08733DB70010E9CD /* Build configuration list for PBXNativeTarget "libdispatch" */; + buildConfigurationList = E4EC11BC12514302000DDBD1 /* Build configuration list for PBXNativeTarget "libdispatch up resolved" */; buildPhases = ( - D2AAC043055464E500DB518D /* Headers */, - D2AAC044055464E500DB518D /* Sources */, - D289987405E68DCB004EDB86 /* Frameworks */, - 2EC9C9800E846B5200E2499A /* ShellScript */, - 4CED8B9D0EEDF8B600AF99AB /* ShellScript */, + E4EC12141251461A000DDBD1 /* Mig Headers */, + E4EC11AC12514302000DDBD1 /* Sources */, + E4EC121212514613000DDBD1 /* Symlink normal variant */, ); buildRules = ( ); dependencies = ( ); - name = libdispatch; + name = "libdispatch up resolved"; productName = libdispatch; - productReference = D2AAC046055464E500DB518D /* libdispatch.a */; + productReference = E4EC11C312514302000DDBD1 /* libdispatch_up.a */; + productType = "com.apple.product-type.library.static"; + }; + E4EC121612514715000DDBD1 /* libdispatch mp resolved */ = { + isa = PBXNativeTarget; + buildConfigurationList = E4EC122612514715000DDBD1 /* Build configuration list for PBXNativeTarget "libdispatch mp resolved" */; + buildPhases = ( + E4EC121712514715000DDBD1 /* Mig Headers */, + E4EC121812514715000DDBD1 /* Sources */, + E4EC122512514715000DDBD1 /* Symlink normal variant */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "libdispatch mp resolved"; + productName = libdispatch; + productReference = E4EC122D12514715000DDBD1 /* libdispatch_mp.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -291,46 +651,216 @@ /* Begin PBXProject section */ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 0420; + }; buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "libdispatch" */; - compatibilityVersion = "Xcode 3.1"; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); mainGroup = 08FB7794FE84155DC02AAC07 /* libdispatch */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = C927F36010FD7F1000C5AB8B /* Products */; + ProjectRef = C927F35F10FD7F1000C5AB8B /* ddt.xcodeproj */; + }, + ); projectRoot = ""; targets = ( D2AAC045055464E500DB518D /* libdispatch */, - 7276FCBA0EB10E0F00F7F487 /* test */, - 721EB4790F69D26F00845379 /* testbots */, + E49F24A9125D57FA0057C971 /* libdispatch no resolver */, + E4EC118F12514302000DDBD1 /* libdispatch up resolved */, + E4EC121612514715000DDBD1 /* libdispatch mp resolved */, + 3F3C9326128E637B0042B1F7 /* libdispatch_Sim */, + C927F35A10FD7F0600C5AB8B /* libdispatch_tools */, ); }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + C927F36710FD7F1000C5AB8B /* ddt */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = ddt; + remoteRef = C927F36610FD7F1000C5AB8B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXShellScriptBuildPhase section */ - 2EC9C9800E846B5200E2499A /* ShellScript */ = { + 2EC9C9800E846B5200E2499A /* Symlink Headers */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/xcodescripts/symlink-headers.sh", + ); + name = "Symlink Headers"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/bash -e"; + shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; + showEnvVarsInLog = 0; + }; + 4CED8B9D0EEDF8B600AF99AB /* Install Manpages */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/xcodescripts/install-manpages.sh", + ); + name = "Install Manpages"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = "/bin/bash -e"; + shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; + showEnvVarsInLog = 0; + }; + E4128EB213B9612700ABB2CB /* Postprocess Headers */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/xcodescripts/postprocess-headers.sh", + ); + name = "Postprocess Headers"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = "/bin/bash -e"; + shellScript = ". \"${SCRIPT_INPUT_FILE_0}\" "; + showEnvVarsInLog = 0; + }; + E482F1C512DBAA110030614D /* Postprocess Headers */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( + "$(SRCROOT)/xcodescripts/postprocess-headers.sh", ); + name = "Postprocess Headers"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "# private.h supersedes dispatch.h where available\nmv \"$DSTROOT\"/usr/local/include/dispatch/private.h \"$DSTROOT\"/usr/local/include/dispatch/dispatch.h\nln -sf dispatch.h \"$DSTROOT\"/usr/local/include/dispatch/private.h\n\n# keep events.h around for a little while\nln -sf ../../../include/dispatch/source.h \"$DSTROOT\"/usr/local/include/dispatch/events.h"; + shellPath = "/bin/bash -e"; + shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; + showEnvVarsInLog = 0; + }; + E49F24D6125D57FA0057C971 /* Symlink Headers */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/xcodescripts/symlink-headers.sh", + ); + name = "Symlink Headers"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/bash -e"; + shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; + showEnvVarsInLog = 0; }; - 4CED8B9D0EEDF8B600AF99AB /* ShellScript */ = { + E49F24D7125D57FA0057C971 /* Install Manpages */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( + "$(SRCROOT)/xcodescripts/install-manpages.sh", ); + name = "Install Manpages"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "#!/bin/sh\n\nmkdir -p $DSTROOT/usr/share/man/man3 || true\nmkdir -p $DSTROOT/usr/local/share/man/man3 || true\n\n# Copy man pages\ncd $SRCROOT/man\nBASE_PAGES=\"dispatch.3 dispatch_after.3 dispatch_api.3 dispatch_apply.3 dispatch_async.3 dispatch_group_create.3 dispatch_object.3 dispatch_once.3 dispatch_queue_create.3 dispatch_semaphore_create.3 dispatch_source_create.3 dispatch_time.3\"\n\nPRIVATE_PAGES=\"dispatch_benchmark.3\"\n\ncp ${BASE_PAGES} $DSTROOT/usr/share/man/man3\ncp ${PRIVATE_PAGES} $DSTROOT/usr/local/share/man/man3\n\n# Make hard links (lots of hard links)\n\ncd $DSTROOT/usr/local/share/man/man3\nln -f dispatch_benchmark.3 dispatch_benchmark_f.3\nchown ${INSTALL_OWNER}:${INSTALL_GROUP} $PRIVATE_PAGES\nchmod $INSTALL_MODE_FLAG $PRIVATE_PAGES\n\n\ncd $DSTROOT/usr/share/man/man3\n\nchown ${INSTALL_OWNER}:${INSTALL_GROUP} $BASE_PAGES\nchmod $INSTALL_MODE_FLAG $BASE_PAGES\n\nln -f dispatch_after.3 dispatch_after_f.3\nln -f dispatch_apply.3 dispatch_apply_f.3\nln -f dispatch_once.3 dispatch_once_f.3\n\nfor m in dispatch_async_f dispatch_sync dispatch_sync_f; do\n\tln -f dispatch_async.3 ${m}.3\ndone\n\nfor m in dispatch_group_enter dispatch_group_leave dispatch_group_wait dispatch_group_async dispatch_group_async_f dispatch_group_notify dispatch_group_notify_f; do\n\tln -f dispatch_group_create.3 ${m}.3\ndone\n\nfor m in dispatch_retain dispatch_release dispatch_suspend dispatch_resume dispatch_get_context dispatch_set_context dispatch_set_finalizer_f; do\n\tln -f dispatch_object.3 ${m}.3\ndone\n\nfor m in dispatch_semaphore_signal dispatch_semaphore_wait; do\n\tln -f dispatch_semaphore_create.3 ${m}.3\ndone\n\nfor m in dispatch_get_current_queue dispatch_main dispatch_get_main_queue dispatch_get_global_queue dispatch_queue_get_label dispatch_set_target_queue; do\n\tln -f dispatch_queue_create.3 ${m}.3\ndone\n\nfor m in dispatch_source_set_event_handler dispatch_source_set_event_handler_f dispatch_source_set_cancel_handler dispatch_source_set_cancel_handler_f dispatch_source_cancel dispatch_source_testcancel dispatch_source_get_handle dispatch_source_get_mask dispatch_source_get_data dispatch_source_merge_data dispatch_source_set_timer; do\n\tln -f dispatch_source_create.3 ${m}.3\ndone\n\nln -f dispatch_time.3 dispatch_walltime.3"; + shellPath = "/bin/bash -e"; + shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; + showEnvVarsInLog = 0; + }; + E4EC121212514613000DDBD1 /* Symlink normal variant */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Symlink normal variant"; + outputPaths = ( + "$(CONFIGURATION_BUILD_DIR)/$(PRODUCT_NAME)_normal.a", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/bash -e"; + shellScript = "ln -fs \"${PRODUCT_NAME}.a\" \"${SCRIPT_OUTPUT_FILE_0}\""; + showEnvVarsInLog = 0; + }; + E4EC12141251461A000DDBD1 /* Mig Headers */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/src/protocol.defs", + "$(SRCROOT)/xcodescripts/mig-headers.sh", + ); + name = "Mig Headers"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/protocol.h", + "$(DERIVED_FILE_DIR)/protocolServer.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/bash -e"; + shellScript = ". \"${SCRIPT_INPUT_FILE_1}\""; + showEnvVarsInLog = 0; + }; + E4EC121712514715000DDBD1 /* Mig Headers */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/src/protocol.defs", + "$(SRCROOT)/xcodescripts/mig-headers.sh", + ); + name = "Mig Headers"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/protocol.h", + "$(DERIVED_FILE_DIR)/protocolServer.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/bash -e"; + shellScript = ". \"${SCRIPT_INPUT_FILE_1}\""; + showEnvVarsInLog = 0; + }; + E4EC122512514715000DDBD1 /* Symlink normal variant */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Symlink normal variant"; + outputPaths = ( + "$(CONFIGURATION_BUILD_DIR)/$(PRODUCT_NAME)_normal.a", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/bash -e"; + shellScript = "ln -fs \"${PRODUCT_NAME}.a\" \"${SCRIPT_OUTPUT_FILE_0}\""; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -339,10 +869,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E43570B9126E93380097AB9F /* provider.d in Sources */, FC7BEDA40E8361E600161930 /* protocol.defs in Sources */, + E49F2499125D48D80057C971 /* resolver.c in Sources */, + E44EBE3E1251659900645D88 /* init.c in Sources */, FC7BED990E8361E600161930 /* queue.c in Sources */, - FC7BEDA60E8361E600161930 /* shims.c in Sources */, - 2EC9C9B80E8809EF00E2499A /* legacy.c in Sources */, 721F5CCF0F15553500FF03A6 /* semaphore.c in Sources */, 96DF70BE0F38FE3C0074BD99 /* once.c in Sources */, 9676A0E10F3E755D00713ADB /* apply.c in Sources */, @@ -350,112 +881,198 @@ 965CD6350F3E806200D4E28D /* benchmark.c in Sources */, 96A8AA870F41E7A400CD570B /* source.c in Sources */, 96032E4B0F5CC8C700241C5F /* time.c in Sources */, + 5AAB45C010D30B79004407EA /* data.c in Sources */, + 5A27262610F26F1900751FBC /* io.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E49F24C7125D57FA0057C971 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E43570BA126E93380097AB9F /* provider.d in Sources */, + E49F24C8125D57FA0057C971 /* protocol.defs in Sources */, + E49F24C9125D57FA0057C971 /* resolver.c in Sources */, + E49F24CA125D57FA0057C971 /* init.c in Sources */, + E49F24CB125D57FA0057C971 /* queue.c in Sources */, + E49F24CC125D57FA0057C971 /* semaphore.c in Sources */, + E49F24CD125D57FA0057C971 /* once.c in Sources */, + E49F24CE125D57FA0057C971 /* apply.c in Sources */, + E49F24CF125D57FA0057C971 /* object.c in Sources */, + E49F24D0125D57FA0057C971 /* benchmark.c in Sources */, + E49F24D1125D57FA0057C971 /* source.c in Sources */, + E49F24D2125D57FA0057C971 /* time.c in Sources */, + E49F24D3125D57FA0057C971 /* data.c in Sources */, + E49F24D4125D57FA0057C971 /* io.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E4EC11AC12514302000DDBD1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E417A38412A472C4004D659D /* provider.d in Sources */, + E44EBE5412517EBE00645D88 /* protocol.defs in Sources */, + E49F2424125D3C970057C971 /* resolver.c in Sources */, + E44EBE5512517EBE00645D88 /* init.c in Sources */, + E4EC11AE12514302000DDBD1 /* queue.c in Sources */, + E4EC11AF12514302000DDBD1 /* semaphore.c in Sources */, + E4EC11B012514302000DDBD1 /* once.c in Sources */, + E4EC11B112514302000DDBD1 /* apply.c in Sources */, + E4EC11B212514302000DDBD1 /* object.c in Sources */, + E4EC11B312514302000DDBD1 /* benchmark.c in Sources */, + E4EC11B412514302000DDBD1 /* source.c in Sources */, + E4EC11B512514302000DDBD1 /* time.c in Sources */, + E4EC11B712514302000DDBD1 /* data.c in Sources */, + E4EC11B812514302000DDBD1 /* io.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E4EC121812514715000DDBD1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E417A38512A472C5004D659D /* provider.d in Sources */, + E44EBE5612517EBE00645D88 /* protocol.defs in Sources */, + E49F2423125D3C960057C971 /* resolver.c in Sources */, + E44EBE5712517EBE00645D88 /* init.c in Sources */, + E4EC121A12514715000DDBD1 /* queue.c in Sources */, + E4EC121B12514715000DDBD1 /* semaphore.c in Sources */, + E4EC121C12514715000DDBD1 /* once.c in Sources */, + E4EC121D12514715000DDBD1 /* apply.c in Sources */, + E4EC121E12514715000DDBD1 /* object.c in Sources */, + E4EC121F12514715000DDBD1 /* benchmark.c in Sources */, + E4EC122012514715000DDBD1 /* source.c in Sources */, + E4EC122112514715000DDBD1 /* time.c in Sources */, + E4EC122312514715000DDBD1 /* data.c in Sources */, + E4EC122412514715000DDBD1 /* io.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + C927F36910FD7F1A00C5AB8B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = ddt; + targetProxy = C927F36810FD7F1A00C5AB8B /* PBXContainerItemProxy */; + }; + E4128E4A13B94BCE00ABB2CB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D2AAC045055464E500DB518D /* libdispatch */; + targetProxy = E4128E4913B94BCE00ABB2CB /* PBXContainerItemProxy */; + }; + E47D6ECB125FEB9D0070D91C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E4EC118F12514302000DDBD1 /* libdispatch up resolved */; + targetProxy = E47D6ECA125FEB9D0070D91C /* PBXContainerItemProxy */; + }; + E47D6ECD125FEBA10070D91C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E4EC121612514715000DDBD1 /* libdispatch mp resolved */; + targetProxy = E47D6ECC125FEBA10070D91C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 1DEB91ED08733DB70010E9CD /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = E40041AA125D705F0022B135 /* libdispatch-resolver.xcconfig */; buildSettings = { - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - EXECUTABLE_PREFIX = ""; - GCC_CW_ASM_SYNTAX = NO; - GCC_ENABLE_CPP_EXCEPTIONS = NO; - GCC_ENABLE_CPP_RTTI = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = "__DARWIN_NON_CANCELABLE=1"; - GENERATE_MASTER_OBJECT_FILE = NO; - INSTALL_PATH = /usr/local/lib/system; - LINK_WITH_STANDARD_LIBRARIES = NO; - OTHER_CFLAGS = ( - "-fno-unwind-tables", - "-fno-exceptions", - "-I$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", - "-fdiagnostics-show-option", - "-fsched-interblock", - "-freorder-blocks", - "-Xarch_x86_64", - "-momit-leaf-frame-pointer", - "-Xarch_i386", - "-momit-leaf-frame-pointer", - ); - OTHER_CFLAGS_debug = "-O0 -fstack-protector -fno-inline -DDISPATCH_DEBUG=1"; - PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/dispatch; - PRODUCT_NAME = libdispatch; - PUBLIC_HEADERS_FOLDER_PATH = /usr/include/dispatch; - SEPARATE_STRIP = NO; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = __; }; name = Release; }; 1DEB91F108733DB70010E9CD /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E43D93F11097917E004F6A62 /* libdispatch.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 3F3C9357128E637B0042B1F7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - BUILD_VARIANTS = ( - normal, - debug, - profile, - ); - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_PASCAL_STRINGS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_STRICT_ALIASING = YES; - GCC_SYMBOLS_PRIVATE_EXTERN = YES; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_SHADOW = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LINK_WITH_STANDARD_LIBRARIES = YES; - ONLY_ACTIVE_ARCH = NO; - OTHER_CFLAGS = ( - "-I$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", - "-fdiagnostics-show-option", - "-fsched-interblock", - "-freorder-blocks", - "-Xarch_x86_64", - "-momit-leaf-frame-pointer", - "-Xarch_i386", - "-momit-leaf-frame-pointer", - ); - OTHER_CFLAGS_debug = "-O0 -fstack-protector -fno-inline -DDISPATCH_DEBUG=1"; - PREBINDING = NO; - STRIP_INSTALLED_PRODUCT = NO; - WARNING_CFLAGS = ( - "-Wall", - "-Wextra", - "-Waggregate-return", - "-Wfloat-equal", - "-Wpacked", - "-Wmissing-declarations", - "-Wstrict-overflow=4", - "-Wstrict-aliasing=2", - ); }; name = Release; }; - 721EB47A0F69D26F00845379 /* Release */ = { + 3F3C9358128E637B0042B1F7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Debug; + }; + C927F35B10FD7F0600C5AB8B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Release; }; - 7276FCBB0EB10E0F00F7F487 /* Release */ = { + C927F35C10FD7F0600C5AB8B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Debug; + }; + E49F24D9125D57FA0057C971 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Release; }; + E49F24DA125D57FA0057C971 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Debug; + }; + E4EB382D1089033000C33AD4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E43D93F11097917E004F6A62 /* libdispatch.xcconfig */; + buildSettings = { + BUILD_VARIANTS = debug; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + E4EB382E1089033000C33AD4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E40041AA125D705F0022B135 /* libdispatch-resolver.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + E4EC11BD12514302000DDBD1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E40041A9125D70590022B135 /* libdispatch-resolved.xcconfig */; + buildSettings = { + DISPATCH_RESOLVED_VARIANT = up; + }; + name = Release; + }; + E4EC11BE12514302000DDBD1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E40041A9125D70590022B135 /* libdispatch-resolved.xcconfig */; + buildSettings = { + DISPATCH_RESOLVED_VARIANT = up; + }; + name = Debug; + }; + E4EC122712514715000DDBD1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E40041A9125D70590022B135 /* libdispatch-resolved.xcconfig */; + buildSettings = { + DISPATCH_RESOLVED_VARIANT = mp; + }; + name = Release; + }; + E4EC122812514715000DDBD1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E40041A9125D70590022B135 /* libdispatch-resolved.xcconfig */; + buildSettings = { + DISPATCH_RESOLVED_VARIANT = mp; + }; + name = Debug; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -463,6 +1080,7 @@ isa = XCConfigurationList; buildConfigurations = ( 1DEB91ED08733DB70010E9CD /* Release */, + E4EB382E1089033000C33AD4 /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -471,22 +1089,52 @@ isa = XCConfigurationList; buildConfigurations = ( 1DEB91F108733DB70010E9CD /* Release */, + E4EB382D1089033000C33AD4 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3F3C9356128E637B0042B1F7 /* Build configuration list for PBXAggregateTarget "libdispatch_Sim" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F3C9357128E637B0042B1F7 /* Release */, + 3F3C9358128E637B0042B1F7 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C927F35E10FD7F0B00C5AB8B /* Build configuration list for PBXAggregateTarget "libdispatch_tools" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C927F35B10FD7F0600C5AB8B /* Release */, + C927F35C10FD7F0600C5AB8B /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E49F24D8125D57FA0057C971 /* Build configuration list for PBXNativeTarget "libdispatch no resolver" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E49F24D9125D57FA0057C971 /* Release */, + E49F24DA125D57FA0057C971 /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 721EB4850F69D2A600845379 /* Build configuration list for PBXLegacyTarget "testbots" */ = { + E4EC11BC12514302000DDBD1 /* Build configuration list for PBXNativeTarget "libdispatch up resolved" */ = { isa = XCConfigurationList; buildConfigurations = ( - 721EB47A0F69D26F00845379 /* Release */, + E4EC11BD12514302000DDBD1 /* Release */, + E4EC11BE12514302000DDBD1 /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 7276FCC80EB10E2300F7F487 /* Build configuration list for PBXLegacyTarget "test" */ = { + E4EC122612514715000DDBD1 /* Build configuration list for PBXNativeTarget "libdispatch mp resolved" */ = { isa = XCConfigurationList; buildConfigurations = ( - 7276FCBB0EB10E0F00F7F487 /* Release */, + E4EC122712514715000DDBD1 /* Release */, + E4EC122812514715000DDBD1 /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/libdispatch.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/libdispatch.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..23ad996 --- /dev/null +++ b/libdispatch.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,6 @@ + + + + + diff --git a/m4/atomic.m4 b/m4/atomic.m4 new file mode 100644 index 0000000..ba85004 --- /dev/null +++ b/m4/atomic.m4 @@ -0,0 +1,21 @@ +AC_DEFUN([DISPATCH_C_ATOMIC_BUILTINS], [ +# +# This is a bit subtle: on i386 systems without at least -march=i486 defined, +# certain built-in atomics fall back to depending on undefined symbols if +# their return values are used. +# +AC_CACHE_CHECK([for gcc atomic builtins],[dispatch_cv_atomic], +[AC_LINK_IFELSE([AC_LANG_PROGRAM([],[[ +int i, x =0; +i = __sync_add_and_fetch(&x,1); +return x;]])],[dispatch_cv_atomic=yes], + [saveCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -march=i486" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[[ + int i, x =0; + i = __sync_add_and_fetch(&x,1); + return x;]])],[CFLAGS="$saveCFLAGS" +dispatch_cv_atomic="-march=i486" +])])]) + +]) diff --git a/m4/blocks.m4 b/m4/blocks.m4 new file mode 100644 index 0000000..49ee2a3 --- /dev/null +++ b/m4/blocks.m4 @@ -0,0 +1,112 @@ +AC_DEFUN([DISPATCH_C_BLOCKS], [ +# +# Allow configure to be passed a path to the directory where it should look +# for the Blocks runtime library, if any. +# +AC_ARG_WITH([blocks-runtime], + [AS_HELP_STRING([--with-blocks-runtime], + [Specify path to the blocks runtime])], + [blocks_runtime=${withval} + LIBS="$LIBS -L$blocks_runtime"] +) + +# +# Detect compiler support for Blocks; perhaps someday -fblocks won't be +# required, in which case we'll need to change this. +# +AC_CACHE_CHECK([for C Blocks support], [dispatch_cv_cblocks], [ + saveCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fblocks" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[(void)^{int i; i = 0; }();])], [ + CFLAGS="$saveCFLAGS" + dispatch_cv_cblocks="-fblocks" + ], [ + CFLAGS="$saveCFLAGS" + dispatch_cv_cblocks="no" + ]) +]) + +AS_IF([test "x$dispatch_cv_cblocks" != "xno"], [ + CBLOCKS_FLAGS="$dispatch_cv_cblocks" + + # + # It may be necessary to directly link the Blocks runtime on some + # systems, so give it a try if we can't link a C program that uses + # Blocks. We will want to remove this at somepoint, as really -fblocks + # should force that linkage already. + # + saveCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fblocks -O0" + AC_MSG_CHECKING([whether additional libraries are required for the Blocks runtime]) + AC_TRY_LINK([], [ + ^{ int j; j=0; }(); + ], [ + AC_MSG_RESULT([no]); + ], [ + saveLIBS="$LIBS" + LIBS="$LIBS -lBlocksRuntime" + AC_TRY_LINK([], [ + ^{ int k; k=0; }(); + ], [ + AC_MSG_RESULT([-lBlocksRuntime]) + ], [ + AC_MSG_ERROR([can't find Blocks runtime]) + ]) + ]) + CFLAGS="$saveCFLAGS" + have_cblocks=true +], [ + CBLOCKS_FLAGS="" + have_cblocks=false +]) +AM_CONDITIONAL(HAVE_CBLOCKS, $have_cblocks) +AC_SUBST([CBLOCKS_FLAGS]) + +# +# Because a different C++ compiler may be specified than C compiler, we have +# to do it again for C++. +# +AC_LANG_PUSH([C++]) +AC_CACHE_CHECK([for C++ Blocks support], [dispatch_cv_cxxblocks], [ + saveCXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -fblocks" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[(void)^{int i; i = 0; }();])], [ + CXXFLAGS="$saveCXXFLAGS" + dispatch_cv_cxxblocks="-fblocks" + ], [ + CXXFLAGS="$saveCXXFLAGS" + dispatch_cv_cxxblocks="no" + ]) +]) + +AS_IF([test "x$dispatch_cv_cxxblocks" != "xno"], [ + CXXBLOCKS_FLAGS="$dispatch_cv_cxxblocks" + + saveCXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -fblocks -O0" + AC_MSG_CHECKING([whether additional libraries are required for the Blocks runtime]) + AC_TRY_LINK([], [ + ^{ int j; j=0; }(); + ], [ + AC_MSG_RESULT([no]); + ], [ + saveLIBS="$LIBS" + LIBS="$LIBS -lBlocksRuntime" + AC_TRY_LINK([], [ + ^{ int k; k=0; }(); + ], [ + AC_MSG_RESULT([-lBlocksRuntime]) + ], [ + AC_MSG_ERROR([can't find Blocks runtime]) + ]) + ]) + CXXFLAGS="$saveCXXFLAGS" + have_cxxblocks=true +], [ + CXXBLOCKS_FLAGS="" + have_cxxblocks=false +]) +AC_LANG_POP([C++]) +AM_CONDITIONAL(HAVE_CXXBLOCKS, $have_cxxblocks) +AC_SUBST([CXXBLOCKS_FLAGS]) +]) diff --git a/m4/pkg.m4 b/m4/pkg.m4 new file mode 100644 index 0000000..a0b9cd4 --- /dev/null +++ b/m4/pkg.m4 @@ -0,0 +1,155 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..f57453a --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,89 @@ +# +# +# + +dist_man3_MANS= \ + dispatch.3 \ + dispatch_after.3 \ + dispatch_api.3 \ + dispatch_apply.3 \ + dispatch_async.3 \ + dispatch_data_create.3 \ + dispatch_group_create.3 \ + dispatch_io_create.3 \ + dispatch_io_read.3 \ + dispatch_object.3 \ + dispatch_once.3 \ + dispatch_queue_create.3 \ + dispatch_read.3 \ + dispatch_semaphore_create.3 \ + dispatch_source_create.3 \ + dispatch_time.3 + +EXTRA_DIST= \ + dispatch_benchmark.3 + +# +# Install man page hardlinks. Is there a better way to do this in automake? +# + +LN=ln + +install-data-hook: + cd $(DESTDIR)$(mandir)/man3 && \ + $(LN) -f dispatch_after.3 dispatch_after_f.3 && \ + $(LN) -f dispatch_apply.3 dispatch_apply_f.3 && \ + $(LN) -f dispatch_async.3 dispatch_sync.3 && \ + $(LN) -f dispatch_async.3 dispatch_async_f.3 && \ + $(LN) -f dispatch_async.3 dispatch_sync_f.3 && \ + $(LN) -f dispatch_group_create.3 dispatch_group_enter.3 && \ + $(LN) -f dispatch_group_create.3 dispatch_group_leave.3 && \ + $(LN) -f dispatch_group_create.3 dispatch_group_wait.3 && \ + $(LN) -f dispatch_group_create.3 dispatch_group_notify.3 && \ + $(LN) -f dispatch_group_create.3 dispatch_group_notify_f.3 && \ + $(LN) -f dispatch_group_create.3 dispatch_group_async.3 && \ + $(LN) -f dispatch_group_create.3 dispatch_group_async_f.3 && \ + $(LN) -f dispatch_object.3 dispatch_retain.3 && \ + $(LN) -f dispatch_object.3 dispatch_release.3 && \ + $(LN) -f dispatch_object.3 dispatch_suspend.3 && \ + $(LN) -f dispatch_object.3 dispatch_resume.3 && \ + $(LN) -f dispatch_object.3 dispatch_get_context.3 && \ + $(LN) -f dispatch_object.3 dispatch_set_context.3 && \ + $(LN) -f dispatch_object.3 dispatch_set_finalizer_f.3 && \ + $(LN) -f dispatch_once.3 dispatch_once_f.3 && \ + $(LN) -f dispatch_queue_create.3 dispatch_queue_get_label.3 && \ + $(LN) -f dispatch_queue_create.3 dispatch_get_current_queue.3 && \ + $(LN) -f dispatch_queue_create.3 dispatch_get_global_queue.3 && \ + $(LN) -f dispatch_queue_create.3 dispatch_get_main_queue.3 && \ + $(LN) -f dispatch_queue_create.3 dispatch_main.3 && \ + $(LN) -f dispatch_queue_create.3 dispatch_set_target_queue.3 && \ + $(LN) -f dispatch_semaphore_create.3 dispatch_semaphore_signal.3 && \ + $(LN) -f dispatch_semaphore_create.3 dispatch_semaphore_wait.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_set_event_handler.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_set_event_handler_f.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_set_cancel_handler.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_set_cancel_handler_f.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_cancel.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_testcancel.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_get_handle.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_get_mask.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_get_data.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_merge_data.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_set_timer.3 && \ + $(LN) -f dispatch_time.3 dispatch_walltime.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_create_concat.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_create_subrange.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_create_map.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_apply.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_copy_subrange.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_copy_region.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_get_size.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_copy_subrange.3 && \ + $(LN) -f dispatch_data_create.3 dispatch_data_empty.3 && \ + $(LN) -f dispatch_io_create.3 dispatch_io_create_with_path.3 && \ + $(LN) -f dispatch_io_create.3 dispatch_io_set_high_water.3 && \ + $(LN) -f dispatch_io_create.3 dispatch_io_set_low_water.3 && \ + $(LN) -f dispatch_io_create.3 dispatch_io_set_interval.3 && \ + $(LN) -f dispatch_io_create.3 dispatch_io_close.3 && \ + $(LN) -f dispatch_io_read.3 dispatch_io_write.3 && \ + $(LN) -f dispatch_read.3 dispatch_write.3 diff --git a/man/dispatch.3 b/man/dispatch.3 index c361863..c55be96 100644 --- a/man/dispatch.3 +++ b/man/dispatch.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. .Dd May 1, 2009 .Dt dispatch 3 .Os Darwin @@ -27,12 +27,18 @@ for more information. The dispatch framework also provides functions to monitor underlying system events and automatically submit event handler blocks to dispatch queues. .Sh SEE ALSO +.Xr dispatch_after 3 , +.Xr dispatch_api 3 , +.Xr dispatch_apply 3 , .Xr dispatch_async 3 , +.Xr dispatch_benchmark 3 , +.Xr dispatch_data_create 3, +.Xr dispatch_group_create 3 , +.Xr dispatch_io_create 3 , +.Xr dispatch_io_read 3 , .Xr dispatch_object 3 , +.Xr dispatch_once 3 , .Xr dispatch_queue_create 3 , -.Xr dispatch_group_create 3 , +.Xr dispatch_semaphore_create 3 , .Xr dispatch_source_create 3 , -.Xr dispatch_benchmark 3 , -.Xr dispatch_time 3 , -.Xr dispatch_apply 3 , -.Xr dispatch_once 3 . +.Xr dispatch_time 3 diff --git a/man/dispatch_after.3 b/man/dispatch_after.3 index 404aefb..4c55214 100644 --- a/man/dispatch_after.3 +++ b/man/dispatch_after.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. .Dd May 1, 2009 .Dt dispatch_after 3 .Os Darwin @@ -13,7 +13,7 @@ .Fc .Ft void .Fo dispatch_after_f -.Fa "dispatch_time_t when" "dispatch_queue_t queue" "void *context" "void (^function)(void *)" +.Fa "dispatch_time_t when" "dispatch_queue_t queue" "void *context" "void (*function)(void *)" .Fc .Sh DESCRIPTION The @@ -35,6 +35,9 @@ or For a more detailed description about submitting blocks to queues, see .Xr dispatch_async 3 . .Sh CAVEATS +.Fn dispatch_after +retains the passed queue. +.Pp Specifying .Vt DISPATCH_TIME_NOW as the @@ -42,16 +45,19 @@ as the parameter is supported, but is not as efficient as calling .Fn dispatch_async . +.Pp The result of passing .Vt DISPATCH_TIME_FOREVER as the .Fa when parameter is undefined. +.Pp .Sh FUNDAMENTALS The .Fn dispatch_after function is a wrapper around .Fn dispatch_after_f . .Sh SEE ALSO +.Xr dispatch 3 , .Xr dispatch_async 3 , .Xr dispatch_time 3 diff --git a/man/dispatch_api.3 b/man/dispatch_api.3 index a39fa64..9123386 100644 --- a/man/dispatch_api.3 +++ b/man/dispatch_api.3 @@ -22,7 +22,7 @@ which the block will be submitted should immediately precede the block argument .Pp .Bd -literal -offset indent read_async(file, callback_queue, ^{ - printf("received callback.\n"); + printf("received callback.\\n"); }); .Ed .Pp diff --git a/man/dispatch_apply.3 b/man/dispatch_apply.3 index 48fb395..5a43a0a 100644 --- a/man/dispatch_apply.3 +++ b/man/dispatch_apply.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. .Dd May 1, 2009 .Dt dispatch_apply 3 .Os Darwin @@ -20,7 +20,7 @@ The .Fn dispatch_apply function provides data-level concurrency through a "for (;;)" loop like primitive: .Bd -literal -dispatch_queue_t the_queue = dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT); +dispatch_queue_t the_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); size_t iterations = 10; // 'idx' is zero indexed, just like: @@ -47,7 +47,7 @@ Calculating the optimal stride is best left to experimentation. Start with a stride of one and work upwards until the desired performance is achieved (perhaps using a power of two search): .Bd -literal -#define STRIDE 3 +#define STRIDE 3 dispatch_apply(count / STRIDE, queue, ^(size_t idx) { size_t j = idx * STRIDE; @@ -62,6 +62,17 @@ for (i = count - (count % STRIDE); i < count; i++) { printf("%zu\\n", i); } .Ed +.Sh IMPLIED REFERENCES +Synchronous functions within the dispatch framework hold an implied reference +on the target queue. In other words, the synchronous function borrows the +reference of the calling function (this is valid because the calling function +is blocked waiting for the result of the synchronous function, and therefore +cannot modify the reference count of the target queue until after the +synchronous function has returned). +.Pp +This is in contrast to asynchronous functions which must retain both the block +and target queue for the duration of the asynchronous operation (as the calling +function may immediately release its interest in these objects). .Sh FUNDAMENTALS Conceptually, .Fn dispatch_apply @@ -74,7 +85,19 @@ The .Fn dispatch_apply function is a wrapper around .Fn dispatch_apply_f . +.Sh CAVEATS +Unlike +.Fn dispatch_async , +a block submitted to +.Fn dispatch_apply +is expected to be either independent or dependent +.Em only +on work already performed in lower-indexed invocations of the block. If +the block's index dependency is non-linear, it is recommended to +use a for-loop around invocations of +.Fn dispatch_async . .Sh SEE ALSO +.Xr dispatch 3 , .Xr dispatch_async 3 , -.Xr dispatch_semaphore_create 3 , -.Xr dispatch_queue_create 3 +.Xr dispatch_queue_create 3 , +.Xr dispatch_semaphore_create 3 diff --git a/man/dispatch_async.3 b/man/dispatch_async.3 index 4b874fb..9c09bb2 100644 --- a/man/dispatch_async.3 +++ b/man/dispatch_async.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. .Dd May 1, 2009 .Dt dispatch_async 3 .Os Darwin @@ -115,14 +115,14 @@ async_read(object_t obj, // This is just an example of nested blocks. dispatch_retain(destination_queue); - + dispatch_async(obj->queue, ^{ ssize_t r = read(obj->fd, where, bytes); int err = errno; dispatch_async(destination_queue, ^{ reply_block(r, err); - }); + }); dispatch_release(destination_queue); }); } @@ -131,7 +131,7 @@ async_read(object_t obj, While .Fn dispatch_sync can replace a lock, it cannot replace a recursive lock. Unlike locks, queues -support both asynchronous and synchrnous operations, and those operations are +support both asynchronous and synchronous operations, and those operations are ordered by definition. A recursive call to .Fn dispatch_sync causes a simple deadlock as the currently executing block waits for the next @@ -171,7 +171,7 @@ against queue B which runs on thread Y which recursively calls against queue A, which deadlocks both examples. This is bug-for-bug compatible with nontrivial pthread usage. In fact, nontrivial reentrancy is impossible to support in recursive locks once the ultimate level of reentrancy is deployed -(IPC or RPC). +(IPC or RPC). .Sh IMPLIED REFERENCES Synchronous functions within the dispatch framework hold an implied reference on the target queue. In other words, the synchronous function borrows the @@ -228,7 +228,8 @@ when it is invoked on the target .Fa queue . .Pp .Sh SEE ALSO +.Xr dispatch 3 , +.Xr dispatch_apply 3 , .Xr dispatch_once 3 , .Xr dispatch_queue_create 3 , -.Xr dispatch_semaphore_create 3 , -.Xr dispatch_apply 3 +.Xr dispatch_semaphore_create 3 diff --git a/man/dispatch_benchmark.3 b/man/dispatch_benchmark.3 index 0890aff..f3e1132 100644 --- a/man/dispatch_benchmark.3 +++ b/man/dispatch_benchmark.3 @@ -11,6 +11,10 @@ .Fo dispatch_benchmark .Fa "size_t count" "void (^block)(void)" .Fc +.Ft uint64_t +.Fo dispatch_benchmark_f +.Fa "size_t count" "void *context" "void (*function)(void *)" +.Fc .Sh DESCRIPTION The .Fn dispatch_benchmark diff --git a/man/dispatch_data_create.3 b/man/dispatch_data_create.3 new file mode 100644 index 0000000..96965f2 --- /dev/null +++ b/man/dispatch_data_create.3 @@ -0,0 +1,206 @@ +.\" Copyright (c) 2010 Apple Inc. All rights reserved. +.Dd December 1, 2010 +.Dt dispatch_data_create 3 +.Os Darwin +.Sh NAME +.Nm dispatch_data_create , +.Nm dispatch_data_create_concat , +.Nm dispatch_data_create_subrange , +.Nm dispatch_data_create_map , +.Nm dispatch_data_apply , +.Nm dispatch_data_copy_region , +.Nm dispatch_data_get_size +.Nd create and manipulate dispatch data objects +.Sh SYNOPSIS +.Fd #include +.Ft dispatch_data_t +.Fo dispatch_data_create +.Fa "const void* buffer" +.Fa "size_t size" +.Fa "dispatch_queue_t queue" +.Fa "dispatch_block_t destructor" +.Fc +.Ft dispatch_data_t +.Fo dispatch_data_create_concat +.Fa "dispatch_data_t data1" +.Fa "dispatch_data_t data2" +.Fc +.Ft dispatch_data_t +.Fo dispatch_data_create_subrange +.Fa "dispatch_data_t data" +.Fa "size_t offset" +.Fa "size_t length" +.Fc +.Ft dispatch_data_t +.Fo dispatch_data_create_map +.Fa "dispatch_data_t data" +.Fa "const void **buffer_ptr" +.Fa "size_t *size_ptr" +.Fc +.Ft bool +.Fo dispatch_data_apply +.Fa "dispatch_data_t data" +.Fa "bool (^applier)(dispatch_data_t, size_t, const void *, size_t)" +.Fc +.Ft dispatch_data_t +.Fo dispatch_data_copy_region +.Fa "dispatch_data_t data" +.Fa "size_t location" +.Fa "size_t *offset_ptr" +.Fc +.Ft size_t +.Fo dispatch_data_get_size +.Fa "dispatch_data_t data" +.Fc +.Vt dispatch_data_t dispatch_data_empty ; +.Sh DESCRIPTION +Dispatch data objects are opaque containers of bytes that represent one or more +regions of memory. They are created either from memory buffers managed by the +application or the system or from other dispatch data objects. Dispatch data +objects are immutable and the memory regions they represent are required to +remain unchanged for the lifetime of all data objects that reference them. +Dispatch data objects avoid copying the represented memory as much as possible. +Multiple data objects can represent the same memory regions or subsections +thereof. +.Sh CREATION +The +.Fn dispatch_data_create +function creates a new dispatch data object of given +.Fa size +from a +.Fa buffer . +The provided +.Fa destructor +block will be submitted to the specified +.Fa queue +when the object reaches the end of its lifecycle, indicating that the system no +longer references the +.Fa buffer . +This allows the application to deallocate +the associated storage. The +.Fa queue +argument is ignored if one of the following predefined destructors is passed: +.Bl -tag -width DISPATCH_DATA_DESTRUCTOR_DEFAULT -compact -offset indent +.It DISPATCH_DATA_DESTRUCTOR_FREE +indicates that the provided buffer can be deallocated with +.Xr free 3 +directly. +.It DISPATCH_DATA_DESTRUCTOR_DEFAULT +indicates that the provided buffer is not managed by the application and should +be copied into memory managed and automatically deallocated by the system. +.El +.Pp +The +.Fn dispatch_data_create_concat +function creates a new data object representing the concatenation of the memory +regions represented by the provided data objects. +.Pp +The +.Fn dispatch_data_create_subrange +function creates a new data object representing the sub-region of the provided +.Fa data +object specified by the +.Fa offset +and +.Fa length +parameters. +.Pp +The +.Fn dispatch_data_create_map +function creates a new data object by mapping the memory represented by the +provided +.Fa data +object as a single contiguous memory region (moving or copying memory as +necessary). If the +.Fa buffer_ptr +and +.Fa size_ptr +references are not +.Dv NULL , +they are filled with the location and extent of the contiguous region, allowing +direct read access to the mapped memory. These values are valid only as long as +the newly created object has not been released. +.Sh ACCESS +The +.Fn dispatch_data_apply +function provides read access to represented memory without requiring it to be +mapped as a single contiguous region. It traverses the memory regions +represented by the +.Fa data +argument in logical order, invokes the specified +.Fa applier +block for each region and returns a boolean indicating whether traversal +completed successfully. The +.Fa applier +block is passed the following arguments for each memory region and returns a +boolean indicating whether traversal should continue: +.Bl -tag -width "dispatch_data_t rgn" -compact -offset indent +.It Fa "dispatch_data_t rgn" +data object representing the region +.It Fa "size_t offset" +logical position of the region in +.Fa data +.It Vt "const void *loc" +memory location of the region +.It Vt "size_t size" +extent of the region +.El +The +.Fa rgn +data object is released by the system when the +.Fa applier +block returns. +The associated memory location +.Fa loc +is valid only as long as +.Fa rgn +has not been deallocated; if +.Fa loc +is needed outside of the +.Fa applier +block, the +.Fa rgn +object must be retained in the block. +.Pp +The +.Fn dispatch_data_copy_region +function finds the contiguous memory region containing the logical position +specified by the +.Fa location +argument among the regions represented by the provided +.Fa data +object and returns a newly created copy of the data object representing that +region. The variable specified by the +.Fa offset_ptr +argument is filled with the logical position where the returned object starts +in the +.Fa data +object. +.Pp +The +.Fn dispatch_data_get_size +function returns the logical size of the memory region or regions represented +by the provided +.Fa data +object. +.Sh EMPTY DATA OBJECT +The +.Vt dispatch_data_empty +object is the global singleton object representing a zero-length memory region. +It is a valid input to any dispatch_data functions that take data object +parameters. +.Sh MEMORY MODEL +Dispatch data objects are retained and released via calls to +.Fn dispatch_retain +and +.Fn dispatch_release . +Data objects passed as arguments to a dispatch data +.Sy create +or +.Sy copy +function can be released when the function returns. The newly created object +holds implicit references to their constituent memory regions as necessary. +.Sh SEE ALSO +.Xr dispatch 3 , +.Xr dispatch_object 3 , +.Xr dispatch_io_read 3 diff --git a/man/dispatch_group_create.3 b/man/dispatch_group_create.3 index 5cca4ca..1dae0ef 100644 --- a/man/dispatch_group_create.3 +++ b/man/dispatch_group_create.3 @@ -84,7 +84,7 @@ associated with the by submitting the .Fa block to the specified -.Fa queue +.Fa queue once all blocks associated with the .Fa group have completed. @@ -142,8 +142,9 @@ and .Fn dispatch_group_notify_f respectively. .Sh SEE ALSO -.Xr dispatch_object 3 , +.Xr dispatch 3 , .Xr dispatch_async 3 , -.Xr dispatch_time 3 , +.Xr dispatch_object 3 , .Xr dispatch_queue_create 3 , -.Xr dispatch_semaphore_create 3 +.Xr dispatch_semaphore_create 3 , +.Xr dispatch_time 3 diff --git a/man/dispatch_io_create.3 b/man/dispatch_io_create.3 new file mode 100644 index 0000000..9087442 --- /dev/null +++ b/man/dispatch_io_create.3 @@ -0,0 +1,238 @@ +.\" Copyright (c) 2010 Apple Inc. All rights reserved. +.Dd December 1, 2010 +.Dt dispatch_io_create 3 +.Os Darwin +.Sh NAME +.Nm dispatch_io_create , +.Nm dispatch_io_create_with_path , +.Nm dispatch_io_close , +.Nm dispatch_io_set_high_water , +.Nm dispatch_io_set_low_water , +.Nm dispatch_io_set_interval +.Nd open, close and configure dispatch I/O channels +.Sh SYNOPSIS +.Fd #include +.Ft dispatch_io_t +.Fo dispatch_io_create +.Fa "dispatch_io_type_t type" +.Fa "int fd" +.Fa "dispatch_queue_t queue" +.Fa "void (^cleanup_handler)(int error)" +.Fc +.Ft dispatch_io_t +.Fo dispatch_io_create_with_path +.Fa "dispatch_io_type_t type" +.Fa "const char *path" +.Fa "int oflag" +.Fa "mode_t mode" +.Fa "dispatch_queue_t queue" +.Fa "void (^cleanup_handler)(int error)" +.Fc +.Ft void +.Fo dispatch_io_close +.Fa "dispatch_io_t channel" +.Fa "dispatch_io_close_flags_t flags" +.Fc +.Ft void +.Fo dispatch_io_set_high_water +.Fa "dispatch_io_t channel" +.Fa "size_t high_water" +.Fc +.Ft void +.Fo dispatch_io_set_low_water +.Fa "dispatch_io_t channel" +.Fa "size_t low_water" +.Fc +.Ft void +.Fo dispatch_io_set_interval +.Fa "dispatch_io_t channel" +.Fa "uint64_t interval" +.Fa "dispatch_io_interval_flags_t flags" +.Fc +.Sh DESCRIPTION +The dispatch I/O framework is an API for asynchronous read and write I/O +operations. It is an application of the ideas and idioms present in the +.Xr dispatch 3 +framework to device I/O. Dispatch I/O enables an application to more easily +avoid blocking I/O operations and allows it to more directly express its I/O +requirements than by using the raw POSIX file API. Dispatch I/O will make a +best effort to optimize how and when asynchronous I/O operations are performed +based on the capabilities of the targeted device. +.Pp +This page provides details on how to create and configure dispatch I/O +channels. Reading from and writing to these channels is covered in the +.Xr dispatch_io_read 3 +page. The dispatch I/O framework also provides the convenience functions +.Xr dispatch_read 3 +and +.Xr dispatch_write 3 +for uses that do not require the full functionality provided by I/O channels. +.Sh FUNDAMENTALS +A dispatch I/O channel represents the asynchronous I/O policy applied to a file +descriptor and encapsulates it for the purposes of ownership tracking while +I/O operations are ongoing. +.Sh CHANNEL TYPES +Dispatch I/O channels can have one of the following types: +.Bl -tag -width DISPATCH_IO_STREAM -compact -offset indent +.It DISPATCH_IO_STREAM +channels that represent a stream of bytes and do not support reads and writes +at arbitrary offsets, such as pipes or sockets. Channels of this type perform +read and write operations sequentially at the current file pointer position and +ignore any offset specified. Depending on the underlying file descriptor, read +operations may be performed simultaneously with write operations. +.It DISPATCH_IO_RANDOM +channels that represent random access files on disk. Only supported for +seekable file descriptors and paths. Channels of this type may perform +submitted read and write operations concurrently at the specified offset +(interpreted relative to the position of the file pointer when the channel was +created). +.El +.Sh CHANNEL OPENING AND CLOSING +The +.Fn dispatch_io_create +and +.Fn dispatch_io_create_with_path +functions create a dispatch I/O channel of provided +.Fa type +from a file descriptor +.Fa fd +or a pathname, respectively. They can be thought of as +analogous to the +.Xr fdopen 3 +POSIX function and the +.Xr fopen 3 +function in the standard C library. For a channel created from a +pathname, the provided +.Fa path , +.Fa oflag +and +.Fa mode +parameters will be passed to +.Xr open 2 +when the first I/O operation on the channel is ready to execute. The provided +.Fa cleanup_handler +block will be submitted to the specified +.Fa queue +when all I/O operations on the channel have completed and is is closed or +reaches the end of its lifecycle. If an error occurs during channel creation, +the +.Fa cleanup_handler +block will be submitted immediately and passed an +.Fa error +parameter with the POSIX error encountered. After creating a dispatch I/O +channel from a file descriptor, the application must take care not to modify +that file descriptor until the associated +.Fa cleanup_handler +is invoked, see +.Sx "FILEDESCRIPTOR OWNERSHIP" +for details. +.Pp +The +.Fn dispatch_io_close +function closes a dispatch I/O channel to new submissions of I/O operations. If +.Dv DISPATCH_IO_STOP +is passed in the +.Fa flags +parameter, the system will in addition not perform the I/O operations already +submitted to the channel that are still pending and will make a best effort to +interrupt any ongoing operations. Handlers for operations so affected will be +passed the +.Er ECANCELED +error code, along with any partial results. +.Sh CHANNEL CONFIGURATION +Dispatch I/O channels have high-water mark, low-water mark and interval +configuration settings that determine if and when partial results from I/O +operations are delivered via their associated I/O handlers. +.Pp +The +.Fn dispatch_io_set_high_water +and +.Fn dispatch_io_set_low_water +functions configure the water mark settings of a +.Fa channel . +The system will read +or write at least the number of bytes specified by +.Fa low_water +before submitting an I/O handler with partial results, and will make a best +effort to submit an I/O handler as soon as the number of bytes read or written +reaches +.Fa high_water . +.Pp +The +.Fn dispatch_io_set_interval +function configures the time +.Fa interval +at which I/O handlers are submitted (measured in nanoseconds). If +.Dv DISPATCH_IO_STRICT_INTERVAL +is passed in the +.Fa flags +parameter, the interval will be strictly observed even if there is an +insufficient amount of data to deliver; otherwise delivery will be skipped for +intervals where the amount of available data is inferior to the channel's +low-water mark. Note that the system may defer enqueueing interval I/O handlers +by a small unspecified amount of leeway in order to align with other system +activity for improved system performance or power consumption. +.Pp +.Sh DATA DELIVERY +The size of data objects passed to I/O handlers for a channel will never be +larger than the high-water mark set on the channel; it will also never be +smaller than the low-water mark, except in the following cases: +.Bl -dash -offset indent -compact +.It +the final handler invocation for an I/O operation +.It +EOF was encountered +.It +the channel has an interval with the +.Dv DISPATCH_IO_STRICT_INTERVAL +flag set +.El +Bear in mind that dispatch I/O channels will typically deliver amounts of data +significantly higher than the low-water mark. The default value for the +low-water mark is unspecified, but must be assumed to allow intermediate +handler invocations. The default value for the high-water mark is +unlimited (i.e.\& +.Dv SIZE_MAX ) . +Channels that require intermediate results of fixed size should have both the +low-water and the high-water mark set to that size. Channels that do not wish +to receive any intermediate results should have the low-water mark set to +.Dv SIZE_MAX . +.Pp +.Sh FILEDESCRIPTOR OWNERSHIP +When an application creates a dispatch I/O channel from a file descriptor with +the +.Fn dispatch_io_create +function, the system takes control of that file descriptor until the channel is +closed, an error occurs on the file descriptor or all references to the channel +are released. At that time the channel's cleanup handler will be enqueued and +control over the file descriptor relinquished, making it safe for the +application to +.Xr close 2 +the file descriptor. While a file descriptor is under the control of a dispatch +I/O channel, file descriptor flags such as +.Dv O_NONBLOCK +will be modified by the system on behalf of the application. It is an error for +the application to modify a file descriptor directly while it is under the +control of a dispatch I/O channel, but it may create further I/O channels +from that file descriptor or use the +.Xr dispatch_read 3 +and +.Xr dispatch_write 3 +convenience functions with that file descriptor. If multiple I/O channels have +been created from the same file descriptor, all the associated cleanup handlers +will be submitted together once the last channel has been closed resp.\& all +references to those channels have been released. If convenience functions have +also been used on that file descriptor, submission of their handlers will be +tied to the submission of the channel cleanup handlers as well. +.Sh MEMORY MODEL +Dispatch I/O channel objects are retained and released via calls to +.Fn dispatch_retain +and +.Fn dispatch_release . +.Sh SEE ALSO +.Xr dispatch 3 , +.Xr dispatch_io_read 3 , +.Xr dispatch_object 3 , +.Xr dispatch_read 3 , +.Xr fopen 3 , +.Xr open 2 diff --git a/man/dispatch_io_read.3 b/man/dispatch_io_read.3 new file mode 100644 index 0000000..51c3b1c --- /dev/null +++ b/man/dispatch_io_read.3 @@ -0,0 +1,151 @@ +.\" Copyright (c) 2010 Apple Inc. All rights reserved. +.Dd December 1, 2010 +.Dt dispatch_io_read 3 +.Os Darwin +.Sh NAME +.Nm dispatch_io_read , +.Nm dispatch_io_write +.Nd submit read and write operations to dispatch I/O channels +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fo dispatch_io_read +.Fa "dispatch_io_t channel" +.Fa "off_t offset" +.Fa "size_t length" +.Fa "dispatch_queue_t queue" +.Fa "void (^handler)(bool done, dispatch_data_t data, int error)" +.Fc +.Ft void +.Fo dispatch_io_write +.Fa "dispatch_io_t channel" +.Fa "off_t offset" +.Fa "dispatch_data_t dispatch" +.Fa "dispatch_queue_t queue" +.Fa "void (^handler)(bool done, dispatch_data_t data, int error)" +.Fc +.Sh DESCRIPTION +The dispatch I/O framework is an API for asynchronous read and write I/O +operations. It is an application of the ideas and idioms present in the +.Xr dispatch 3 +framework to device I/O. Dispatch I/O enables an application to more easily +avoid blocking I/O operations and allows it to more directly express its I/O +requirements than by using the raw POSIX file API. Dispatch I/O will make a +best effort to optimize how and when asynchronous I/O operations are performed +based on the capabilities of the targeted device. +.Pp +This page provides details on how to read from and write to dispatch I/O +channels. Creation and configuration of these channels is covered in the +.Xr dispatch_io_create 3 +page. The dispatch I/O framework also provides the convenience functions +.Xr dispatch_read 3 +and +.Xr dispatch_write 3 +for uses that do not require the full functionality provided by I/O channels. +.Pp +.Sh FUNDAMENTALS +The +.Fn dispatch_io_read +and +.Fn dispatch_io_write +functions are used to perform asynchronous read and write operations on +dispatch I/O channels. They can be thought of as asynchronous versions of the +.Xr fread 3 +and +.Xr fwrite 3 +functions in the standard C library. +.Sh READ OPERATIONS +The +.Fn dispatch_io_read +function schedules an I/O read operation on the specified dispatch I/O +.Va channel . +As results from the read operation become available, the provided +.Va handler +block will be submitted to the specified +.Va queue . +The block will be passed a dispatch data object representing the data that has +been read since the handler's previous invocation. +.Pp +The +.Va offset +parameter indicates where the read operation should begin. For a channel of +.Dv DISPATCH_IO_RANDOM +type it is interpreted relative to the position of the file pointer when the +channel was created, for a channel of +.Dv DISPATCH_IO_STREAM +type it is ignored and the read operation will begin at the current file +pointer position. +.Pp +The +.Va length +parameter indicates the number of bytes that should be read from the I/O +channel. Pass +.Dv SIZE_MAX +to keep reading until EOF is encountered (for a channel created from a +disk-based file this happens when reading past the end of the physical file). +.Sh WRITE OPERATIONS +The +.Fn dispatch_io_write +function schedules an I/O write operation on the specified dispatch I/O +.Va channel . +As the write operation progresses, the provided +.Va handler +block will be submitted to the specified +.Va queue . +The block will be passed a dispatch data object representing the data that +remains to be written as part of this I/O operation. +.Pp +The +.Va offset +parameter indicates where the write operation should begin. It is interpreted +as for read operations above. +.Pp +The +.Va data +parameter specifies the location and amount of data to be written, encapsulated +as a dispatch data object. The object is retained by the system until the write +operation is complete. +.Sh I/O HANDLER BLOCKS +Dispatch I/O handler blocks submitted to a channel via the +.Fn dispatch_io_read +or +.Fn dispatch_io_write +functions will be executed one or more times depending on system load and the +channel's configuration settings (see +.Xr dispatch_io_create 3 +for details). The handler block need not be reentrant safe, +no new I/O handler instance is submitted until the previously enqueued handler +block has returned. +.Pp +The dispatch +.Va data +object passed to an I/O handler block will be released by the system when the +block returns, if access to the memory buffer it represents is needed outside +of the handler, the handler block must retain the data object or create a new +(e.g.\& concatenated) data object from it (see +.Xr dispatch_data_create 3 +for details). +.Pp +Once an I/O handler block is invoked with the +.Va done +flag set, the associated I/O operation is complete and that handler block will +not be run again. If an unrecoverable error occurs while performing the I/O +operation, the handler block will be submitted with the +.Va done +flag set and the appriate POSIX error code in the +.Va error +parameter. An invocation of a handler block with the +.Va done +flag set, zero +.Va error +and +.Va data +set to +.Vt dispatch_data_empty +indicates that the I/O operation has encountered EOF. +.Sh SEE ALSO +.Xr dispatch 3 , +.Xr dispatch_data_create 3 , +.Xr dispatch_io_create 3 , +.Xr dispatch_read 3 , +.Xr fread 3 diff --git a/man/dispatch_object.3 b/man/dispatch_object.3 index b60831a..29c1621 100644 --- a/man/dispatch_object.3 +++ b/man/dispatch_object.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. .Dd May 1, 2009 .Dt dispatch_object 3 .Os Darwin @@ -58,7 +58,7 @@ The invocation of blocks on dispatch queues or dispatch sources may be suspended or resumed with the functions .Fn dispatch_suspend and -.Fn dispatch_resume +.Fn dispatch_resume respectively. The dispatch framework always checks the suspension status before executing a block, but such changes never affect a block during execution (non-preemptive). @@ -69,8 +69,9 @@ a dispatch source is undefined. .Pp .Em Important : suspension applies to all aspects of the dispatch object life cycle, including -the finalizer function and cancellation handler. Therefore it is important to -balance calls to +the finalizer function and cancellation handler. Suspending an object causes it +to be retained and resuming an object causes it to be released. Therefore it is +important to balance calls to .Fn dispatch_suspend and .Fn dispatch_resume @@ -79,21 +80,26 @@ released. The result of releasing all references to a dispatch object while in a suspended state is undefined. .Sh CONTEXT POINTERS Dispatch queues and sources support supplemental context pointers. The value of -the context point may be retrieved and updated with +the context pointer may be retrieved and updated with .Fn dispatch_get_context and .Fn dispatch_set_context respectively. The .Fn dispatch_set_finalizer_f -specifies an optional per-object finalizer function to that is invoked -asynchronously when the last reference to the object is released. This gives the +specifies an optional per-object finalizer function that is invoked +asynchronously if the context pointer is not NULL when the last +reference to the object is released. +This gives the application an opportunity to free the context data associated with the object. +The finalizer will be run on the object's target queue. .Pp The result of getting or setting the context of an object that is not a dispatch queue or a dispatch source is undefined. .Sh SEE ALSO +.Xr dispatch 3 , .Xr dispatch_group_create 3 , .Xr dispatch_queue_create 3 , .Xr dispatch_semaphore_create 3 , -.Xr dispatch_source_create 3 +.Xr dispatch_source_create 3 , +.Xr dispatch_set_target_queue 3 diff --git a/man/dispatch_once.3 b/man/dispatch_once.3 index da21896..2118a23 100644 --- a/man/dispatch_once.3 +++ b/man/dispatch_once.3 @@ -42,3 +42,5 @@ The .Fn dispatch_once function is a wrapper around .Fn dispatch_once_f . +.Sh SEE ALSO +.Xr dispatch 3 diff --git a/man/dispatch_queue_create.3 b/man/dispatch_queue_create.3 index d11c1c1..9b3e6a9 100644 --- a/man/dispatch_queue_create.3 +++ b/man/dispatch_queue_create.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. .Dd May 1, 2008 .Dt dispatch_queue_create 3 .Os Darwin @@ -53,11 +53,13 @@ All blocks submitted to dispatch queues are dequeued in FIFO order. By default, queues created with .Fn dispatch_queue_create wait for the previously dequeued block to complete before dequeuing the next -block. This FIFO completion behavior is sometimes simply described as a "serial queue." +block. This FIFO completion behavior is sometimes simply described as a "serial +queue." All memory writes performed by a block dispatched to a serial queue are +guaranteed to be visible to subsequent blocks dispatched to the same queue. Queues are not bound to any specific thread of execution and blocks submitted -to independent queues may execute concurrently. -Queues, like all dispatch objects, are reference counted and newly created -queues have a reference count of one. +to independent queues may execute concurrently. Queues, like all dispatch +objects, are reference counted and newly created queues have a reference count +of one. .Pp The optional .Fa label @@ -80,13 +82,13 @@ argument is reserved for future use and must be NULL. Queues may be temporarily suspended and resumed with the functions .Fn dispatch_suspend and -.Fn dispatch_resume +.Fn dispatch_resume respectively. Suspension is checked prior to block execution and is .Em not preemptive. .Sh MAIN QUEUE -The dispatch framework provides a default serial queue for the application to use. -This queue is accessed via +The dispatch framework provides a default serial queue for the application to +use. This queue is accessed via .Fn dispatch_get_main_queue . Programs must call .Fn dispatch_main @@ -98,8 +100,8 @@ section for exceptions.) Unlike the main queue or queues allocated with .Fn dispatch_queue_create , the global concurrent queues schedule blocks as soon as threads become -available (non-FIFO completion order). The global concurrent queues represent -three priority bands: +available (non-FIFO completion order). Four global concurrent queues are +provided, representing the following priority bands: .Bl -bullet -compact -offset indent .It DISPATCH_QUEUE_PRIORITY_HIGH @@ -107,12 +109,26 @@ DISPATCH_QUEUE_PRIORITY_HIGH DISPATCH_QUEUE_PRIORITY_DEFAULT .It DISPATCH_QUEUE_PRIORITY_LOW +.It +DISPATCH_QUEUE_PRIORITY_BACKGROUND .El .Pp -Blocks submitted to the high priority global queue will be invoked before those -submitted to the default or low priority global queues. Blocks submitted to the -low priority global queue will only be invoked if no blocks are pending on the -default or high priority queues. +The priority of a global concurrent queue controls the scheduling priority of +the threads created by the system to invoke the blocks submitted to that queue. +Global queues with lower priority will be scheduled for execution after all +global queues with higher priority have been scheduled. Additionally, items on +the background priority global queue will execute on threads with background +state as described in +.Xr setpriority 2 +(i.e.\& disk I/O is throttled and the thread's scheduling priority is set to +lowest value). +.Pp +Use the +.Fn dispatch_get_global_queue +function to obtain the global queue of given priority. The +.Fa flags +argument is reserved for future use and must be zero. Passing any value other +than zero may result in a NULL return value. .Pp .Sh RETURN VALUES The @@ -131,13 +147,13 @@ function returns the default main queue. .Pp The .Fn dispatch_get_current_queue -function always returns a valid queue. When called from within a block submitted -to a dispatch queue, that queue will be returned. If this function is called from -the main thread before +function always returns a valid queue. When called from within a block +submitted to a dispatch queue, that queue will be returned. If this function is +called from the main thread before .Fn dispatch_main is called, then the result of .Fn dispatch_get_main_queue -is returned. Otherwise, the result of +is returned. The result of .Fo dispatch_get_global_queue .Fa DISPATCH_QUEUE_PRIORITY_DEFAULT .Fa 0 @@ -151,55 +167,73 @@ function never returns. The .Fn dispatch_set_target_queue function updates the target queue of the given dispatch object. The target -queue of an object is responsible for processing the object. Currently only -dispatch queues and dispatch sources are supported by this function. The result -of using -.Fn dispatch_set_target_queue -with any other dispatch object type is undefined. +queue of an object is responsible for processing the object. .Pp The new target queue is retained by the given object before the previous target -queue is released. The new target queue will take effect between block -executions, but not in the middle of any existing block executions +queue is released. The new target queue setting will take effect between block +executions on the object, but not in the middle of any existing block executions (non-preemptive). .Pp -The priority of a dispatch queue is inherited by its target queue. +The default target queue of all dispatch objects created by the application is +the default priority global concurrent queue. To reset an object's target queue +to the default, pass the +.Dv DISPATCH_TARGET_QUEUE_DEFAULT +constant to +.Fn dispatch_set_target_queue . +.Pp +The priority of a dispatch queue is inherited from its target queue. In order to change the priority of a queue created with .Fn dispatch_queue_create , use the .Fn dispatch_get_global_queue -function to obtain a target queue of the desired priority. The -.Fa flags -argument is reserved for future use and must be zero. Passing any value other -than zero may result in a -.Vt NULL -return value. +function to obtain a target queue of the desired priority. +.Pp +Blocks submitted to a serial queue whose target queue is another serial queue +will not be invoked concurrently with blocks submitted to the target queue or +to any other queue with that same target queue. .Pp The target queue of a dispatch source specifies where its event handler and cancellation handler blocks will be submitted. See .Xr dispatch_source_create 3 for more information about dispatch sources. .Pp -The result of passing the main queue or a global concurrent queue to the first +The target queue of a dispatch I/O channel specifies the priority of the global +queue where its I/O operations are executed. See +.Xr dispatch_io_create 3 +for more information about dispatch I/O channels. +.Pp +For all other dispatch object types, the only function of the target queue is +to determine where an object's finalizer function is invoked. +.Pp +The result of passing the main queue or a global concurrent queue as the first argument of .Fn dispatch_set_target_queue is undefined. .Pp -Directly or indirectly setting the target queue of a dispatch queue to itself is undefined. +Directly or indirectly setting the target queue of a dispatch queue to itself is +undefined. .Sh CAVEATS -Code cannot make any assumptions about the queue returned by -.Fn dispatch_get_current_queue . -The returned queue may have arbitrary policies that may surprise code that tries -to schedule work with the queue. The list of policies includes, but is not -limited to, queue width (i.e. serial vs. concurrent), scheduling priority, -security credential or filesystem configuration. Therefore, +The .Fn dispatch_get_current_queue -.Em MUST -only be used for identity tests or debugging. +function is only recommended for debugging and logging purposes. Code must not +make any assumptions about the queue returned, unless it is one of the global +queues or a queue the code has itself created. The returned queue may have +arbitrary policies that may surprise code that tries to schedule work with the +queue. The list of policies includes, but is not limited to, queue width (i.e. +serial vs. concurrent), scheduling priority, security credential or filesystem +configuration. +.Pp +It is equally unsafe for code to assume that synchronous execution onto a queue +is safe from deadlock if that queue is not the one returned by +.Fn dispatch_get_current_queue . .Sh COMPATIBILITY Cocoa applications need not call .Fn dispatch_main . -Blocks submitted to the main queue will be executed as part of the "common modes" -of the application's main NSRunLoop or CFRunLoop. +Blocks submitted to the main queue will be executed as part of the "common +modes" of the application's main NSRunLoop or CFRunLoop. +However, blocks submitted to the main queue in applications using +.Fn dispatch_main +are not guaranteed to execute on the main thread. .Pp The dispatch framework is a pure C level API. As a result, it does not catch exceptions generated by higher level languages such as Objective-C or C++. @@ -299,7 +333,7 @@ Note that in the above example .Va errno is a per-thread variable and must be copied out explicitly as the block may be invoked on different thread of execution than the caller. Another example of -per-thread data that would need to be copied is the use of +per-thread data that would need to be copied is the use of .Fn getpwnam instead of .Fn getpwnam_r . @@ -313,6 +347,7 @@ may persist from the block until back to the caller. Great care should be taken not to accidentally rely on this side-effect. .Pp .Sh SEE ALSO -.Xr dispatch_object 3 , +.Xr dispatch 3 , .Xr dispatch_async 3 , +.Xr dispatch_object 3 , .Xr dispatch_source_create 3 diff --git a/man/dispatch_read.3 b/man/dispatch_read.3 new file mode 100644 index 0000000..38e88de --- /dev/null +++ b/man/dispatch_read.3 @@ -0,0 +1,123 @@ +.\" Copyright (c) 2010 Apple Inc. All rights reserved. +.Dd December 1, 2010 +.Dt dispatch_read 3 +.Os Darwin +.Sh NAME +.Nm dispatch_read , +.Nm dispatch_write +.Nd asynchronously read from and write to file descriptors +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fo dispatch_read +.Fa "int fd" +.Fa "size_t length" +.Fa "dispatch_queue_t queue" +.Fa "void (^handler)(dispatch_data_t data, int error)" +.Fc +.Ft void +.Fo dispatch_write +.Fa "int fd" +.Fa "dispatch_data_t data" +.Fa "dispatch_queue_t queue" +.Fa "void (^handler)(dispatch_data_t data, int error))" +.Fc +.Sh DESCRIPTION +The +.Fn dispatch_read +and +.Fn dispatch_write +functions asynchronously read from and write to POSIX file descriptors. They +can be thought of as asynchronous, callback-based versions of the +.Fn fread +and +.Fn fwrite +functions provided by the standard C library. They are convenience functions +based on the +.Xr dispatch_io_read 3 +and +.Xr dispatch_io_write 3 +functions, intended for simple one-shot read or write requests. Multiple +request on the same file desciptor are better handled with the full underlying +dispatch I/O channel functions. +.Sh BEHAVIOR +The +.Fn dispatch_read +function schedules an asynchronous read operation on the file descriptor +.Va fd . +Once the file descriptor is readable, the system will read as much data as is +currently available, up to the specified +.Va length , +starting at the current file pointer position. The given +.Va handler +block will be submitted to +.Va queue +when the operation completes or an error occurs. The block will be passed a +dispatch +.Va data +object with the result of the read operation. If an error occurred while +reading from the file descriptor, the +.Va error +parameter to the block will be set to the appropriate POSIX error code and +.Va data +will contain any data that could be read successfully. If the file pointer +position is at end-of-file, emtpy +.Va data +and zero +.Va error +will be passed to the handler block. +.Pp +The +.Fn dispatch_write +function schedules an asynchronous write operation on the file descriptor +.Va fd . +The system will attempt to write the entire contents of the provided +.Va data +object to +.Va fd +at the current file pointer position. The given +.Va handler +block will be submitted to +.Va queue +when the operation completes or an error occurs. If the write operation +completed successfully, the +.Va error +parameter to the block will be set to zero, otherwise it will be set to the +appropriate POSIX error code and the +.Va data +parameter will contain any data that could not be written. +.Sh CAVEATS +The +.Va data +object passed to a +.Va handler +block is released by the system when the block returns. If +.Va data +is needed outside of the handler block, it must concatenate, copy, or retain +it. +.Pp +Once an asynchronous read or write operation has been submitted on a file +descriptor +.Va fd , +the system takes control of that file descriptor until the +.Va handler +block is executed. During this time the application must not manipulate +.Va fd +directly, in particular it is only safe to close +.Va fd +from the handler block (or after it has returned). +.Pp +If multiple asynchronous read or write operations are submitted to the same +file descriptor, they will be performed in order, but their handlers will only +be submitted once all operations have completed and control over the file +descriptor has been relinquished. For details on this and on the interaction +with dispatch I/O channels created from the same file descriptor, see +.Sx FILEDESCRIPTOR OWNERSHIP +in +.Xr dispatch_io_create 3 . +.Sh SEE ALSO +.Xr dispatch 3 , +.Xr dispatch_data_create 3 , +.Xr dispatch_io_create 3 , +.Xr dispatch_io_read 3 , +.Xr fread 3 diff --git a/man/dispatch_semaphore_create.3 b/man/dispatch_semaphore_create.3 index 1250642..096e0e3 100644 --- a/man/dispatch_semaphore_create.3 +++ b/man/dispatch_semaphore_create.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. .Dd May 1, 2009 .Dt dispatch_semaphore_create 3 .Os Darwin @@ -33,7 +33,8 @@ functions. .Sh COMPLETION SYNCHRONIZATION If the .Fa count -parameter is equal to zero, then the semaphore is useful for synchronizing completion of work. +parameter is equal to zero, then the semaphore is useful for synchronizing +completion of work. For example: .Bd -literal -offset indent sema = dispatch_semaphore_create(0); @@ -50,7 +51,8 @@ dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); .Sh FINITE RESOURCE POOL If the .Fa count -parameter is greater than zero, then the semaphore is useful for managing a finite pool of resources. +parameter is greater than zero, then the semaphore is useful for managing a +finite pool of resources. For example, a library that wants to limit Unix descriptor usage: .Bd -literal -offset indent sema = dispatch_semaphore_create(getdtablesize() / 4); @@ -81,7 +83,8 @@ Otherwise, zero is returned. .Pp The .Fn dispatch_semaphore_wait -function returns zero upon success and non-zero after the timeout expires. If the timeout is DISPATCH_TIME_FOREVER, then +function returns zero upon success and non-zero after the timeout expires. If +the timeout is DISPATCH_TIME_FOREVER, then .Fn dispatch_semaphore_wait waits forever and always returns zero. .Sh MEMORY MODEL @@ -90,6 +93,15 @@ Dispatch semaphores are retained and released via calls to and .Fn dispatch_release . .Sh CAVEATS +Unbalanced dispatch semaphores cannot be released. +For a given semaphore, calls to +.Fn dispatch_semaphore_signal +and +.Fn dispatch_semaphore_wait +must be balanced before +.Fn dispatch_release +is called on it. +.Pp Dispatch semaphores are strict counting semaphores. In other words, dispatch semaphores do not saturate at any particular value. Saturation can be achieved through atomic compare-and-swap logic. @@ -111,4 +123,5 @@ saturating_semaphore_wait(dispatch_semaphore_t dsema, int *sent) } .Ed .Sh SEE ALSO +.Xr dispatch 3 , .Xr dispatch_object 3 diff --git a/man/dispatch_source_create.3 b/man/dispatch_source_create.3 index 0a38cd2..1d774a9 100644 --- a/man/dispatch_source_create.3 +++ b/man/dispatch_source_create.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. .Dd May 1, 2009 .Dt dispatch_source_create 3 .Os Darwin @@ -38,7 +38,7 @@ .Fo dispatch_source_cancel .Fa "dispatch_source_t source" .Fc -.Ft void +.Ft long .Fo dispatch_source_testcancel .Fa "dispatch_source_t source" .Fc @@ -67,7 +67,7 @@ .Fa "uint64_t leeway" .Fc .Sh DESCRIPTION -Dispatch event sources may be used to monitor a variety of system objects and +Dispatch event sources may be used to monitor a variety of system objects and events including file descriptors, mach ports, processes, virtual filesystem nodes, signal delivery and timers. .Pp @@ -81,9 +81,17 @@ with calls to .Fn dispatch_retain and .Fn dispatch_release -respectively. Newly created sources are created in a suspended state. After the -source has been configured by setting an event handler, cancellation handler, -context, etc., the source must be activated by a call to +respectively. The +.Fa queue +parameter specifies the target queue of the new source object, it will +be retained by the source object. Pass the +.Dv DISPATCH_TARGET_QUEUE_DEFAULT +constant to use the default target queue (the default priority global +concurrent queue). +.Pp +Newly created sources are created in a suspended state. After the source has +been configured by setting an event handler, cancellation handler, context, +etc., the source must be activated by a call to .Fn dispatch_resume before any events will be delivered. .Pp @@ -117,21 +125,21 @@ and .Fa mask arguments to .Fn dispatch_source_create -and the return values of the +and the return values of the .Fn dispatch_source_get_handle , .Fn dispatch_source_get_mask , and -.Fn dispatch_source_get_data +.Fn dispatch_source_get_data functions should be interpreted according to the type of the dispatch source. .Pp -The +The .Fn dispatch_source_get_handle function returns the underlying handle to the dispatch source (i.e. file descriptor, mach port, process identifer, etc.). The result of this function may be cast directly to the underlying type. .Pp -The +The .Fn dispatch_source_get_mask function returns the set of flags that were specified at source creation time via the @@ -175,7 +183,7 @@ block need not be reentrant safe, as it is not resubmitted to the target .Fa queue until any prior invocation for that dispatch source has completed. -When the hander is set, the dispatch source will perform a +When the handler is set, the dispatch source will perform a .Fn Block_copy on the .Fa handler @@ -205,7 +213,7 @@ a cancellation handler is required for file descriptor and mach port based sources in order to safely close the descriptor or destroy the port. Closing the descriptor or port before the cancellation handler has run may result in a race condition: if a new descriptor is allocated with the same value as the recently -cosed descriptor while the source's event handler is still running, the event +closed descriptor while the source's event handler is still running, the event handler may read/write data to the wrong descriptor. .Pp .Sh DISPATCH SOURCE TYPES @@ -216,16 +224,16 @@ the interpretation of their parameters and returned data. .Vt DISPATCH_SOURCE_TYPE_DATA_OR .Pp Sources of this type allow applications to manually trigger the source's event -handler via a call to +handler via a call to .Fn dispatch_source_merge_data . The data will be merged with the source's pending data via an atomic add or logic OR (based on the source's type), and the event handler block will be submitted to the source's target queue. The -.Fa mask -and .Fa data -are application defined. These sources have no +is application defined. These sources have no .Fa handle +or +.Fa mask and zero should be used. .Pp .Vt DISPATCH_SOURCE_TYPE_MACH_SEND @@ -268,7 +276,7 @@ is the process identifier (pid_t) of the process to monitor and the may be one or more of the following: .Bl -tag -width "XXDISPATCH_PROC_SIGNAL" -compact -offset indent .It \(bu DISPATCH_PROC_EXIT -The process has exited and is available to +The process has exited and is available to .Xr wait 2 . .It \(bu DISPATCH_PROC_FORK The process has created one or more child processes. @@ -277,7 +285,7 @@ The process has become another executable image via a call to .Xr execve 2 or .Xr posix_spawn 2 . -.It \(bu DISPATCH_PROC_REAP +.It \(bu DISPATCH_PROC_REAP The process status has been collected by its parent process via .Xr wait 2 . .It \(bu DISPATCH_PROC_SIGNAL @@ -309,7 +317,7 @@ will be performed. .Pp Users of this source type are strongly encouraged to perform non-blocking I/O and handle any truncated reads or error conditions that may occur. See -.Xr fnctl 2 +.Xr fcntl 2 for additional information about setting the .Vt O_NONBLOCK flag on a file descriptor. @@ -378,7 +386,7 @@ for more information.) The in nanoseconds, specifies the period at which the timer should repeat. All timers will repeat indefinitely until .Fn dispatch_source_cancel -is called. The +is called. The .Fa leeway , in nanoseconds, is a hint to the system that it may defer the timer in order to align with other system activity for improved system performance or reduced @@ -387,7 +395,7 @@ every 5 minutes with a leeway of up to 30 seconds.) Note that some latency is to be expected for all timers even when a value of zero is used. .Pp .Em Note : -Under the C language, untyped numbers default to the +Under the C language, untyped numbers default to the .Vt int type. This can lead to truncation bugs when arithmetic operations with other numbers are expected to generate a @@ -404,7 +412,7 @@ as a suffix. For example: Sources of this type monitor the virtual filesystem nodes for state changes. The .Fa handle -is a file descriptor (int) referencing the node to monitor, and +is a file descriptor (int) referencing the node to monitor, and the .Fa mask may be one or more of the following: @@ -423,7 +431,7 @@ The link count on the referenced node has changed .It \(bu DISPATCH_VNODE_RENAME The referenced node was renamed .It \(bu DISPATCH_VNODE_REVOKE -Access to the referenced node was revoked via +Access to the referenced node was revoked via .Xr revoke 2 or the underlying fileystem was unmounted. .El @@ -445,7 +453,7 @@ is unused and should be zero. .Pp Users of this source type are strongly encouraged to perform non-blocking I/O and handle any truncated reads or error conditions that may occur. See -.Xr fnctl 2 +.Xr fcntl 2 for additional information about setting the .Vt O_NONBLOCK flag on a file descriptor. diff --git a/man/dispatch_time.3 b/man/dispatch_time.3 index 06d78e8..6d18873 100644 --- a/man/dispatch_time.3 +++ b/man/dispatch_time.3 @@ -105,6 +105,7 @@ These functions return an abstract value for use with or .Fn dispatch_semaphore_wait . .Sh SEE ALSO +.Xr dispatch 3 , .Xr dispatch_after 3 , .Xr dispatch_group_create 3 , .Xr dispatch_semaphore_create 3 diff --git a/private/Makefile.am b/private/Makefile.am new file mode 100644 index 0000000..488ef52 --- /dev/null +++ b/private/Makefile.am @@ -0,0 +1,10 @@ +# +# +# + +noinst_HEADERS= \ + benchmark.h \ + private.h \ + queue_private.h \ + source_private.h + diff --git a/src/benchmark.h b/private/benchmark.h similarity index 91% rename from src/benchmark.h rename to private/benchmark.h index b77af45..df42a8a 100644 --- a/src/benchmark.h +++ b/private/benchmark.h @@ -2,19 +2,19 @@ * 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@ */ @@ -64,17 +64,18 @@ __BEGIN_DECLS * 3) Code bound by critical sections may be inferred by retrograde changes in * performance as concurrency is increased. * 3a) Intentional: locks, mutexes, and condition variables. - * 3b) Accidental: unrelated and frequently modified data on the same cache-line. + * 3b) Accidental: unrelated and frequently modified data on the same + * cache-line. */ #ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL2 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_NOTHROW uint64_t dispatch_benchmark(size_t count, void (^block)(void)); #endif -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL3 DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL3 DISPATCH_NOTHROW uint64_t dispatch_benchmark_f(size_t count, void *ctxt, void (*func)(void *)); diff --git a/src/private.h b/private/private.h similarity index 51% rename from src/private.h rename to private/private.h index 8d817fe..9bb0e01 100644 --- a/src/private.h +++ b/private/private.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -27,14 +27,28 @@ #ifndef __DISPATCH_PRIVATE__ #define __DISPATCH_PRIVATE__ +#ifdef __APPLE__ +#include +#endif + +#if TARGET_OS_MAC #include #include #include +#endif +#if HAVE_UNISTD_H #include +#endif +#if HAVE_SYS_CDEFS_H #include -#include +#endif #include +#define DISPATCH_NO_LEGACY 1 +#ifdef DISPATCH_LEGACY // +#error "Dispatch legacy API unavailable." +#endif + #ifndef __DISPATCH_BUILDING_DISPATCH__ #include_next @@ -51,10 +65,6 @@ #include #include -#ifndef DISPATCH_NO_LEGACY -#include -#endif - #undef __DISPATCH_INDIRECT__ #endif /* !__DISPATCH_BUILDING_DISPATCH__ */ @@ -64,50 +74,72 @@ __BEGIN_DECLS -DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_NOTHROW void libdispatch_init(void); +#if TARGET_OS_MAC #define DISPATCH_COCOA_COMPAT 1 #if DISPATCH_COCOA_COMPAT -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_CONST DISPATCH_WARN_RESULT DISPATCH_NOTHROW mach_port_t _dispatch_get_main_queue_port_4CF(void); -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NOTHROW void _dispatch_main_queue_callback_4CF(mach_msg_header_t *msg); -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern void (*dispatch_begin_thread_4GC)(void); +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +void (*dispatch_begin_thread_4GC)(void); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +void (*dispatch_end_thread_4GC)(void); -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern void (*dispatch_end_thread_4GC)(void); +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT +void (*dispatch_no_worker_threads_4GC)(void); -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern void *(*_dispatch_begin_NSAutoReleasePool)(void); +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +void *(*_dispatch_begin_NSAutoReleasePool)(void); -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern void (*_dispatch_end_NSAutoReleasePool)(void *); +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT +void (*_dispatch_end_NSAutoReleasePool)(void *); + +#define _dispatch_time_after_nsec(t) \ + dispatch_time(DISPATCH_TIME_NOW, (t)) +#define _dispatch_time_after_usec(t) \ + dispatch_time(DISPATCH_TIME_NOW, (t) * NSEC_PER_USEC) +#define _dispatch_time_after_msec(t) \ + dispatch_time(DISPATCH_TIME_NOW, (t) * NSEC_PER_MSEC) +#define _dispatch_time_after_sec(t) \ + dispatch_time(DISPATCH_TIME_NOW, (t) * NSEC_PER_SEC) #endif +#endif /* TARGET_OS_MAC */ /* pthreads magic */ -DISPATCH_NOTHROW void dispatch_atfork_prepare(void); -DISPATCH_NOTHROW void dispatch_atfork_parent(void); -DISPATCH_NOTHROW void dispatch_atfork_child(void); -DISPATCH_NOTHROW void dispatch_init_pthread(pthread_t); +DISPATCH_EXPORT DISPATCH_NOTHROW void dispatch_atfork_prepare(void); +DISPATCH_EXPORT DISPATCH_NOTHROW void dispatch_atfork_parent(void); +DISPATCH_EXPORT DISPATCH_NOTHROW void dispatch_atfork_child(void); +#if TARGET_OS_MAC /* * Extract the context pointer from a mach message trailer. */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NONNULL_ALL +DISPATCH_NOTHROW void * dispatch_mach_msg_get_context(mach_msg_header_t *msg); +#endif /* TARGET_OS_MAC */ __END_DECLS diff --git a/src/queue_private.h b/private/queue_private.h similarity index 52% rename from src/queue_private.h rename to private/queue_private.h index 85f87c0..5ec36d0 100644 --- a/src/queue_private.h +++ b/private/queue_private.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 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@ */ @@ -46,42 +46,27 @@ enum { DISPATCH_QUEUE_OVERCOMMIT = 0x2ull, }; -#define DISPATCH_QUEUE_FLAGS_MASK (DISPATCH_QUEUE_OVERCOMMIT) - -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW -void -dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); -#endif - -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW -void -dispatch_barrier_sync_f(dispatch_queue_t dq, void *context, dispatch_function_t work); - -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW -void -dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); -#endif - -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW -void -dispatch_barrier_async_f(dispatch_queue_t dq, void *context, dispatch_function_t work); +#define DISPATCH_QUEUE_FLAGS_MASK (DISPATCH_QUEUE_OVERCOMMIT) /*! * @function dispatch_queue_set_width * * @abstract - * Set the width of concurrency for a given queue. The default width of a - * privately allocated queue is one. + * Set the width of concurrency for a given queue. The width of a serial queue + * is one. + * + * @discussion + * This SPI is DEPRECATED and will be removed in a future release. + * Uses of this SPI to make a queue concurrent by setting its width to LONG_MAX + * should be replaced by passing DISPATCH_QUEUE_CONCURRENT to + * dispatch_queue_create(). + * Uses of this SPI to limit queue concurrency are not recommended and should + * be replaced by alternative mechanisms such as a dispatch semaphore created + * with the desired concurrency width. * * @param queue - * The queue to adjust. Passing the main queue, a default concurrent queue or - * any other default queue will be ignored. + * The queue to adjust. Passing the main queue or a global concurrent queue + * will be ignored. * * @param width * The new maximum width of concurrency depending on available resources. @@ -89,19 +74,43 @@ dispatch_barrier_async_f(dispatch_queue_t dq, void *context, dispatch_function_t * Negative values are magic values that map to automatic width values. * Unknown negative values default to DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS. */ -#define DISPATCH_QUEUE_WIDTH_ACTIVE_CPUS -1 +#define DISPATCH_QUEUE_WIDTH_ACTIVE_CPUS -1 #define DISPATCH_QUEUE_WIDTH_MAX_PHYSICAL_CPUS -2 #define DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS -3 -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void -dispatch_queue_set_width(dispatch_queue_t dq, long width); +dispatch_queue_set_width(dispatch_queue_t dq, long width); // DEPRECATED +/*! + * @function dispatch_set_current_target_queue + * + * @abstract + * Synchronously sets the target queue of the current serial queue. + * + * @discussion + * This SPI is provided for a limited purpose case when calling + * dispatch_set_target_queue() is not sufficient. It works similarly to + * dispatch_set_target_queue() except the target queue of the current queue + * is immediately changed so that pending blocks on the queue will run on the + * new target queue. Calling this from outside of a block executing on a serial + * queue is undefined. + * + * @param queue + * The new target queue for the object. The queue is retained, and the + * previous target queue, if any, is released. + * If queue is DISPATCH_TARGET_QUEUE_DEFAULT, set the object's target queue + * to the default target queue for the given object type. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NOTHROW +void +dispatch_set_current_target_queue(dispatch_queue_t queue); -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_queue_offsets_s { +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT const struct dispatch_queue_offsets_s { // always add new fields at the end const uint16_t dqo_version; const uint16_t dqo_label; diff --git a/src/source_private.h b/private/source_private.h similarity index 62% rename from src/source_private.h rename to private/source_private.h index 9e45cc1..576f64a 100644 --- a/src/source_private.h +++ b/private/source_private.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -39,8 +39,17 @@ * The handle is a process identifier (pid_t). */ #define DISPATCH_SOURCE_TYPE_VFS (&_dispatch_source_type_vfs) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -extern const struct dispatch_source_type_s _dispatch_source_type_vfs; +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT const struct dispatch_source_type_s _dispatch_source_type_vfs; + +/*! + * @const DISPATCH_SOURCE_TYPE_VM + * @discussion A dispatch source that monitors virtual memory + * The mask is a mask of desired events from dispatch_source_vm_flags_t. + */ +#define DISPATCH_SOURCE_TYPE_VM (&_dispatch_source_type_vm) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) +DISPATCH_EXPORT const struct dispatch_source_type_s _dispatch_source_type_vm; /*! * @enum dispatch_source_vfs_flags_t @@ -91,11 +100,17 @@ enum { /*! * @enum dispatch_source_mach_send_flags_t * - * @constant DISPATCH_MACH_SEND_DELETED - * The receive right corresponding to the given send right was destroyed. + * @constant DISPATCH_MACH_SEND_POSSIBLE + * The mach port corresponding to the given send right has space available + * for messages. Delivered only once a mach_msg() to that send right with + * options MACH_SEND_MSG|MACH_SEND_TIMEOUT|MACH_SEND_NOTIFY has returned + * MACH_SEND_TIMED_OUT (and not again until the next such mach_msg() timeout). + * NOTE: The source must have registered the send right for monitoring with the + * system for such a mach_msg() to arm the send-possible notifcation, so + * the initial send attempt must occur from a source registration handler. */ enum { - DISPATCH_MACH_SEND_DELETED = 0x2, + DISPATCH_MACH_SEND_POSSIBLE = 0x8, }; /*! @@ -109,20 +124,39 @@ enum { DISPATCH_PROC_REAP = 0x10000000, }; +/*! + * @enum dispatch_source_vm_flags_t + * + * @constant DISPATCH_VM_PRESSURE + * The VM has experienced memory pressure. + */ + +enum { + DISPATCH_VM_PRESSURE = 0x80000000, +}; + +#if TARGET_IPHONE_SIMULATOR // rdar://problem/9219483 +#define DISPATCH_VM_PRESSURE DISPATCH_VNODE_ATTRIB +#endif + __BEGIN_DECLS +#if TARGET_OS_MAC /*! * @typedef dispatch_mig_callback_t * * @abstract * The signature of a function that handles Mach message delivery and response. */ -typedef boolean_t (*dispatch_mig_callback_t)(mach_msg_header_t *message, mach_msg_header_t *reply); +typedef boolean_t (*dispatch_mig_callback_t)(mach_msg_header_t *message, + mach_msg_header_t *reply); -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW mach_msg_return_t -dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback_t callback); +dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, + dispatch_mig_callback_t callback); +#endif __END_DECLS diff --git a/resolver/resolved.h b/resolver/resolved.h new file mode 100644 index 0000000..bb9a82d --- /dev/null +++ b/resolver/resolved.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + diff --git a/resolver/resolver.c b/resolver/resolver.c new file mode 100644 index 0000000..8b390b4 --- /dev/null +++ b/resolver/resolver.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010 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@ + */ + diff --git a/resolver/resolver.h b/resolver/resolver.h new file mode 100644 index 0000000..5b1cd04 --- /dev/null +++ b/resolver/resolver.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_RESOLVERS__ +#define __DISPATCH_RESOLVERS__ + + +#endif diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..20b2baa --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,73 @@ +# +# +# + +lib_LTLIBRARIES=libdispatch.la + +libdispatch_la_SOURCES= \ + apply.c \ + benchmark.c \ + data.c \ + init.c \ + io.c \ + object.c \ + once.c \ + queue.c \ + semaphore.c \ + source.c \ + time.c \ + protocol.defs \ + provider.d \ + data_internal.h \ + internal.h \ + io_internal.h \ + object_internal.h \ + queue_internal.h \ + semaphore_internal.h \ + shims.h \ + source_internal.h \ + trace.h \ + shims/atomic.h \ + shims/getprogname.h \ + shims/hw_config.h \ + shims/malloc_zone.h \ + shims/perfmon.h \ + shims/time.h \ + shims/tsd.h + +INCLUDES=-I$(top_builddir) -I$(top_srcdir) -I$(top_srcdir)/private \ + @APPLE_LIBC_SOURCE_PATH@ @APPLE_LIBCLOSURE_SOURCE_PATH@ @APPLE_XNU_SOURCE_PATH@ + +libdispatch_la_CFLAGS=-Wall $(VISIBILITY_FLAGS) $(OMIT_LEAF_FP_FLAGS) +libdispatch_la_CFLAGS+=$(MARCH_FLAGS) $(CBLOCKS_FLAGS) $(KQUEUE_CFLAGS) + +libdispatch_la_LDFLAGS=-avoid-version + +if HAVE_DARWIN_LD +libdispatch_la_LDFLAGS+=-Wl,-compatibility_version,1 -Wl,-current_version,$(VERSION) +endif + +CLEANFILES= + +if USE_MIG +BUILT_SOURCES= \ + protocolUser.c \ + protocol.h \ + protocolServer.c \ + protocolServer.h + +nodist_libdispatch_la_SOURCES=$(BUILT_SOURCES) +CLEANFILES+=$(BUILT_SOURCES) + +%User.c %.h %Server.c %Server.h: $(abs_srcdir)/%.defs + $(MIG) -user $*User.c -header $*.h \ + -server $*Server.c -sheader $*Server.h $< +endif + +if USE_XNU_SOURCE +# hack for pthread_machdep.h's #include +$(libdispatch_la_OBJECTS): $(abs_srcdir)/System +$(abs_srcdir)/System: + $(LN_S) -fh "@APPLE_XNU_SOURCE_SYSTEM_PATH@" System +CLEANFILES+=System +endif diff --git a/src/apply.c b/src/apply.c index 88d8066..9a63439 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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 "internal.h" @@ -24,98 +24,155 @@ // local thread to be sufficiently away to avoid cache-line contention with the // busy 'da_index' variable. // -// NOTE: 'char' arrays cause GCC to insert buffer overflow detection logic +// NOTE: 'char' arrays cause GCC to insert buffer overflow detection logic struct dispatch_apply_s { - long _da_pad0[DISPATCH_CACHELINE_SIZE / sizeof(long)]; - void (*da_func)(void *, size_t); - void *da_ctxt; - size_t da_iterations; - size_t da_index; - uint32_t da_thr_cnt; - dispatch_semaphore_t da_sema; - long _da_pad1[DISPATCH_CACHELINE_SIZE / sizeof(long)]; + long _da_pad0[DISPATCH_CACHELINE_SIZE / sizeof(long)]; + void (*da_func)(void *, size_t); + void *da_ctxt; + size_t da_iterations; + size_t da_index; + uint32_t da_thr_cnt; + _dispatch_thread_semaphore_t da_sema; + dispatch_queue_t da_queue; + long _da_pad1[DISPATCH_CACHELINE_SIZE / sizeof(long)]; }; -static void -_dispatch_apply2(void *_ctxt) +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_apply_invoke(void *ctxt) { - struct dispatch_apply_s *da = _ctxt; + struct dispatch_apply_s *da = ctxt; size_t const iter = da->da_iterations; typeof(da->da_func) const func = da->da_func; - void *const ctxt = da->da_ctxt; + void *const da_ctxt = da->da_ctxt; size_t idx; _dispatch_workitem_dec(); // this unit executes many items + // Make nested dispatch_apply fall into serial case rdar://problem/9294578 + _dispatch_thread_setspecific(dispatch_apply_key, (void*)~0ul); // Striding is the responsibility of the caller. - while (fastpath((idx = dispatch_atomic_inc(&da->da_index) - 1) < iter)) { - func(ctxt, idx); + while (fastpath((idx = dispatch_atomic_inc2o(da, da_index) - 1) < iter)) { + _dispatch_client_callout2(da_ctxt, idx, func); _dispatch_workitem_inc(); } + _dispatch_thread_setspecific(dispatch_apply_key, NULL); - if (dispatch_atomic_dec(&da->da_thr_cnt) == 0) { - dispatch_semaphore_signal(da->da_sema); + dispatch_atomic_release_barrier(); + if (dispatch_atomic_dec2o(da, da_thr_cnt) == 0) { + _dispatch_thread_semaphore_signal(da->da_sema); } } +DISPATCH_NOINLINE +static void +_dispatch_apply2(void *ctxt) +{ + _dispatch_apply_invoke(ctxt); +} + static void -_dispatch_apply_serial(void *context) +_dispatch_apply3(void *ctxt) { - struct dispatch_apply_s *da = context; + struct dispatch_apply_s *da = ctxt; + dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + + _dispatch_thread_setspecific(dispatch_queue_key, da->da_queue); + _dispatch_apply_invoke(ctxt); + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); +} + +static void +_dispatch_apply_serial(void *ctxt) +{ + struct dispatch_apply_s *da = ctxt; size_t idx = 0; _dispatch_workitem_dec(); // this unit executes many items do { - da->da_func(da->da_ctxt, idx); + _dispatch_client_callout2(da->da_ctxt, idx, da->da_func); _dispatch_workitem_inc(); } while (++idx < da->da_iterations); } -#ifdef __BLOCKS__ -#if DISPATCH_COCOA_COMPAT -DISPATCH_NOINLINE -static void -_dispatch_apply_slow(size_t iterations, dispatch_queue_t dq, void (^work)(size_t)) -{ - struct Block_basic *bb = (void *)_dispatch_Block_copy((void *)work); - dispatch_apply_f(iterations, dq, bb, (void *)bb->Block_invoke); - Block_release(bb); -} -#endif +// 256 threads should be good enough for the short to mid term +#define DISPATCH_APPLY_MAX_CPUS 256 -void -dispatch_apply(size_t iterations, dispatch_queue_t dq, void (^work)(size_t)) +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_apply_f2(dispatch_queue_t dq, struct dispatch_apply_s *da, + dispatch_function_t func) { -#if DISPATCH_COCOA_COMPAT - // Under GC, blocks transferred to other threads must be Block_copy()ed - // rdar://problem/7455071 - if (dispatch_begin_thread_4GC) { - return _dispatch_apply_slow(iterations, dq, work); + struct dispatch_apply_dc_s { + DISPATCH_CONTINUATION_HEADER(dispatch_apply_dc_s); + } da_dc[DISPATCH_APPLY_MAX_CPUS]; + size_t i; + + for (i = 0; i < da->da_thr_cnt - 1; i++) { + da_dc[i].do_vtable = NULL; + da_dc[i].do_next = &da_dc[i + 1]; + da_dc[i].dc_func = func; + da_dc[i].dc_ctxt = da; } -#endif - struct Block_basic *bb = (void *)work; - dispatch_apply_f(iterations, dq, bb, (void *)bb->Block_invoke); + + da->da_sema = _dispatch_get_thread_semaphore(); + + _dispatch_queue_push_list(dq, (void *)&da_dc[0], + (void *)&da_dc[da->da_thr_cnt - 2]); + // Call the first element directly + _dispatch_apply2(da); + _dispatch_workitem_inc(); + + _dispatch_thread_semaphore_wait(da->da_sema); + _dispatch_put_thread_semaphore(da->da_sema); } -#endif -// 256 threads should be good enough for the short to mid term -#define DISPATCH_APPLY_MAX_CPUS 256 +static void +_dispatch_apply_redirect(void *ctxt) +{ + struct dispatch_apply_s *da = ctxt; + uint32_t da_width = 2 * (da->da_thr_cnt - 1); + dispatch_queue_t dq = da->da_queue, rq = dq, tq; + + do { + uint32_t running = dispatch_atomic_add2o(rq, dq_running, da_width); + uint32_t width = rq->dq_width; + if (slowpath(running > width)) { + uint32_t excess = width > 1 ? running - width : da_width; + for (tq = dq; 1; tq = tq->do_targetq) { + (void)dispatch_atomic_sub2o(tq, dq_running, excess); + if (tq == rq) { + break; + } + } + da_width -= excess; + if (slowpath(!da_width)) { + return _dispatch_apply_serial(da); + } + da->da_thr_cnt -= excess / 2; + } + rq = rq->do_targetq; + } while (slowpath(rq->do_targetq)); + _dispatch_apply_f2(rq, da, _dispatch_apply3); + do { + (void)dispatch_atomic_sub2o(dq, dq_running, da_width); + dq = dq->do_targetq; + } while (slowpath(dq->do_targetq)); +} DISPATCH_NOINLINE void -dispatch_apply_f(size_t iterations, dispatch_queue_t dq, void *ctxt, void (*func)(void *, size_t)) +dispatch_apply_f(size_t iterations, dispatch_queue_t dq, void *ctxt, + void (*func)(void *, size_t)) { - struct dispatch_apply_dc_s { - DISPATCH_CONTINUATION_HEADER(dispatch_apply_dc_s); - } da_dc[DISPATCH_APPLY_MAX_CPUS]; struct dispatch_apply_s da; - size_t i; da.da_func = func; da.da_ctxt = ctxt; da.da_iterations = iterations; da.da_index = 0; da.da_thr_cnt = _dispatch_hw_config.cc_max_active; + da.da_queue = NULL; if (da.da_thr_cnt > DISPATCH_APPLY_MAX_CPUS) { da.da_thr_cnt = DISPATCH_APPLY_MAX_CPUS; @@ -126,46 +183,62 @@ dispatch_apply_f(size_t iterations, dispatch_queue_t dq, void *ctxt, void (*func if (iterations < da.da_thr_cnt) { da.da_thr_cnt = (uint32_t)iterations; } - if (slowpath(dq->dq_width <= 2 || da.da_thr_cnt <= 1)) { + if (slowpath(dq->dq_width <= 2) || slowpath(da.da_thr_cnt <= 1) || + slowpath(_dispatch_thread_getspecific(dispatch_apply_key))) { return dispatch_sync_f(dq, &da, _dispatch_apply_serial); } - - for (i = 0; i < da.da_thr_cnt; i++) { - da_dc[i].do_vtable = NULL; - da_dc[i].do_next = &da_dc[i + 1]; - da_dc[i].dc_func = _dispatch_apply2; - da_dc[i].dc_ctxt = &da; + dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + if (slowpath(dq->do_targetq)) { + if (slowpath(dq == old_dq)) { + return dispatch_sync_f(dq, &da, _dispatch_apply_serial); + } else { + da.da_queue = dq; + return dispatch_sync_f(dq, &da, _dispatch_apply_redirect); + } } + dispatch_atomic_acquire_barrier(); + _dispatch_thread_setspecific(dispatch_queue_key, dq); + _dispatch_apply_f2(dq, &da, _dispatch_apply2); + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); +} - da.da_sema = _dispatch_get_thread_semaphore(); +#ifdef __BLOCKS__ +#if DISPATCH_COCOA_COMPAT +DISPATCH_NOINLINE +static void +_dispatch_apply_slow(size_t iterations, dispatch_queue_t dq, + void (^work)(size_t)) +{ + struct Block_basic *bb = (void *)_dispatch_Block_copy((void *)work); + dispatch_apply_f(iterations, dq, bb, (void *)bb->Block_invoke); + Block_release(bb); +} +#endif - // some queues are easy to borrow and some are not - if (slowpath(dq->do_targetq)) { - _dispatch_queue_push_list(dq, (void *)&da_dc[0], (void *)&da_dc[da.da_thr_cnt - 1]); - } else { - dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); - // root queues are always concurrent and safe to borrow - _dispatch_queue_push_list(dq, (void *)&da_dc[1], (void *)&da_dc[da.da_thr_cnt - 1]); - _dispatch_thread_setspecific(dispatch_queue_key, dq); - // The first da_dc[] element was explicitly not pushed on to the queue. - // We need to either call it like so: - // da_dc[0].dc_func(da_dc[0].dc_ctxt); - // Or, given that we know the 'func' and 'ctxt', we can call it directly: - _dispatch_apply2(&da); - _dispatch_workitem_inc(); - _dispatch_thread_setspecific(dispatch_queue_key, old_dq); +void +dispatch_apply(size_t iterations, dispatch_queue_t dq, void (^work)(size_t)) +{ +#if DISPATCH_COCOA_COMPAT + // Under GC, blocks transferred to other threads must be Block_copy()ed + // rdar://problem/7455071 + if (dispatch_begin_thread_4GC) { + return _dispatch_apply_slow(iterations, dq, work); } - dispatch_semaphore_wait(da.da_sema, DISPATCH_TIME_FOREVER); - _dispatch_put_thread_semaphore(da.da_sema); +#endif + struct Block_basic *bb = (void *)work; + dispatch_apply_f(iterations, dq, bb, (void *)bb->Block_invoke); } +#endif #if 0 #ifdef __BLOCKS__ void -dispatch_stride(size_t offset, size_t stride, size_t iterations, dispatch_queue_t dq, void (^work)(size_t)) +dispatch_stride(size_t offset, size_t stride, size_t iterations, + dispatch_queue_t dq, void (^work)(size_t)) { struct Block_basic *bb = (void *)work; - dispatch_stride_f(offset, stride, iterations, dq, bb, (void *)bb->Block_invoke); + dispatch_stride_f(offset, stride, iterations, dq, bb, + (void *)bb->Block_invoke); } #endif diff --git a/src/benchmark.c b/src/benchmark.c index fafe909..246affa 100644 --- a/src/benchmark.c +++ b/src/benchmark.c @@ -2,19 +2,19 @@ * 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@ */ @@ -22,7 +22,9 @@ struct __dispatch_benchmark_data_s { +#if HAVE_MACH_ABSOLUTE_TIME mach_timebase_info_data_t tbi; +#endif uint64_t loop_cost; void (*func)(void *); void *ctxt; @@ -38,28 +40,32 @@ _dispatch_benchmark_init(void *context) register void (*f)(void *) = bdata->func; register void *c = bdata->ctxt; register size_t cnt = bdata->count; + size_t i = 0; uint64_t start, delta; -#ifdef __LP64__ +#if defined(__LP64__) __uint128_t lcost; #else long double lcost; #endif +#if HAVE_MACH_ABSOLUTE_TIME kern_return_t kr; - size_t i = 0; kr = mach_timebase_info(&bdata->tbi); dispatch_assert_zero(kr); +#endif - start = mach_absolute_time(); + start = _dispatch_absolute_time(); do { i++; f(c); } while (i < cnt); - delta = mach_absolute_time() - start; + delta = _dispatch_absolute_time() - start; lcost = delta; +#if HAVE_MACH_ABSOLUTE_TIME lcost *= bdata->tbi.numer; lcost /= bdata->tbi.denom; +#endif lcost /= cnt; bdata->loop_cost = lcost; @@ -75,7 +81,8 @@ dispatch_benchmark(size_t count, void (^block)(void)) #endif uint64_t -dispatch_benchmark_f(size_t count, register void *ctxt, register void (*func)(void *)) +dispatch_benchmark_f(size_t count, register void *ctxt, + register void (*func)(void *)) { static struct __dispatch_benchmark_data_s bdata = { .func = (void *)dummy_function, @@ -83,7 +90,7 @@ dispatch_benchmark_f(size_t count, register void *ctxt, register void (*func)(vo }; static dispatch_once_t pred; uint64_t ns, start, delta; -#ifdef __LP64__ +#if defined(__LP64__) __uint128_t conversion, big_denom; #else long double conversion, big_denom; @@ -96,16 +103,20 @@ dispatch_benchmark_f(size_t count, register void *ctxt, register void (*func)(vo return 0; } - start = mach_absolute_time(); + start = _dispatch_absolute_time(); do { i++; func(ctxt); } while (i < count); - delta = mach_absolute_time() - start; + delta = _dispatch_absolute_time() - start; conversion = delta; +#if HAVE_MACH_ABSOLUTE_TIME conversion *= bdata.tbi.numer; big_denom = bdata.tbi.denom; +#else + big_denom = delta; +#endif big_denom *= count; conversion /= big_denom; ns = conversion; diff --git a/src/data.c b/src/data.c new file mode 100644 index 0000000..e125656 --- /dev/null +++ b/src/data.c @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2009-2011 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 "internal.h" + +// Dispatch data objects are dispatch objects with standard retain/release +// memory management. A dispatch data object either points to a number of other +// dispatch data objects or is a leaf data object. A leaf data object contains +// a pointer to represented memory. A composite data object specifies the total +// size of data it represents and list of constituent records. +// +// A leaf data object has a single entry in records[], the object size is the +// same as records[0].length and records[0].from is always 0. In other words, a +// leaf data object always points to a full represented buffer, so a composite +// dispatch data object is needed to represent a subrange of a memory region. + +#define _dispatch_data_retain(x) dispatch_retain(x) +#define _dispatch_data_release(x) dispatch_release(x) + +static void _dispatch_data_dispose(dispatch_data_t data); +static size_t _dispatch_data_debug(dispatch_data_t data, char* buf, + size_t bufsiz); + +#if DISPATCH_DATA_MOVABLE +static const dispatch_block_t _dispatch_data_destructor_unlock = ^{ + DISPATCH_CRASH("unlock destructor called"); +}; +#define DISPATCH_DATA_DESTRUCTOR_UNLOCK (_dispatch_data_destructor_unlock) +#endif + +const struct dispatch_data_vtable_s _dispatch_data_vtable = { + .do_type = DISPATCH_DATA_TYPE, + .do_kind = "data", + .do_dispose = _dispatch_data_dispose, + .do_invoke = NULL, + .do_probe = (void *)dummy_function_r0, + .do_debug = _dispatch_data_debug, +}; + +static dispatch_data_t +_dispatch_data_init(size_t n) +{ + dispatch_data_t data = calloc(1ul, sizeof(struct dispatch_data_s) + + n * sizeof(range_record)); + data->num_records = n; + data->do_vtable = &_dispatch_data_vtable; + data->do_xref_cnt = 1; + data->do_ref_cnt = 1; + data->do_targetq = dispatch_get_global_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + data->do_next = DISPATCH_OBJECT_LISTLESS; + return data; +} + +dispatch_data_t +dispatch_data_create(const void* buffer, size_t size, dispatch_queue_t queue, + dispatch_block_t destructor) +{ + dispatch_data_t data; + if (!buffer || !size) { + // Empty data requested so return the singleton empty object. Call + // destructor immediately in this case to ensure any unused associated + // storage is released. + if (destructor == DISPATCH_DATA_DESTRUCTOR_FREE) { + free((void*)buffer); + } else if (destructor != DISPATCH_DATA_DESTRUCTOR_DEFAULT) { + dispatch_async(queue ? queue : dispatch_get_global_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), destructor); + } + return dispatch_data_empty; + } + data = _dispatch_data_init(1); + // Leaf objects always point to the entirety of the memory region + data->leaf = true; + data->size = size; + data->records[0].from = 0; + data->records[0].length = size; + data->destructor = DISPATCH_DATA_DESTRUCTOR_FREE; + if (destructor == DISPATCH_DATA_DESTRUCTOR_DEFAULT) { + // The default destructor was provided, indicating the data should be + // copied. + void *data_buf = malloc(size); + if (slowpath(!data_buf)) { + free(data); + return NULL; + } + buffer = memcpy(data_buf, buffer, size); + } else { + if (destructor != DISPATCH_DATA_DESTRUCTOR_FREE) { + data->destructor = Block_copy(destructor); + } +#if DISPATCH_DATA_MOVABLE + // A non-default destructor was provided, indicating the system does not + // own the buffer. Mark the object as locked since the application has + // direct access to the buffer and it cannot be reallocated/moved. + data->locked = 1; +#endif + } + data->records[0].data_object = (void*)buffer; + if (queue) { + _dispatch_retain(queue); + data->do_targetq = queue; + } + return data; +} + +static void +_dispatch_data_dispose(dispatch_data_t dd) +{ + dispatch_block_t destructor = dd->destructor; + if (destructor == DISPATCH_DATA_DESTRUCTOR_DEFAULT) { + size_t i; + for (i = 0; i < dd->num_records; ++i) { + _dispatch_data_release(dd->records[i].data_object); + } +#if DISPATCH_DATA_MOVABLE + } else if (destructor == DISPATCH_DATA_DESTRUCTOR_UNLOCK) { + dispatch_data_t data = (dispatch_data_t)dd->records[0].data_object; + (void)dispatch_atomic_dec2o(data, locked); + _dispatch_data_release(data); +#endif + } else if (destructor == DISPATCH_DATA_DESTRUCTOR_FREE) { + free(dd->records[0].data_object); + } else { + dispatch_async_f(dd->do_targetq, destructor, + _dispatch_call_block_and_release); + } + _dispatch_dispose(dd); +} + +static size_t +_dispatch_data_debug(dispatch_data_t dd, char* buf, size_t bufsiz) +{ + size_t offset = 0; + if (dd->leaf) { + offset += snprintf(&buf[offset], bufsiz - offset, + "leaf: %d, size: %zd, data: %p", dd->leaf, dd->size, + dd->records[0].data_object); + } else { + offset += snprintf(&buf[offset], bufsiz - offset, + "leaf: %d, size: %zd, num_records: %zd", dd->leaf, + dd->size, dd->num_records); + size_t i; + for (i = 0; i < dd->num_records; ++i) { + range_record r = dd->records[i]; + offset += snprintf(&buf[offset], bufsiz - offset, + "records[%zd] from: %zd, length %zd, data_object: %p", i, + r.from, r.length, r.data_object); + } + } + return offset; +} + +size_t +dispatch_data_get_size(dispatch_data_t dd) +{ + return dd->size; +} + +dispatch_data_t +dispatch_data_create_concat(dispatch_data_t dd1, dispatch_data_t dd2) +{ + dispatch_data_t data; + if (!dd1->size) { + _dispatch_data_retain(dd2); + return dd2; + } + if (!dd2->size) { + _dispatch_data_retain(dd1); + return dd1; + } + data = _dispatch_data_init(dd1->num_records + dd2->num_records); + data->size = dd1->size + dd2->size; + // Copy the constituent records into the newly created data object + memcpy(data->records, dd1->records, dd1->num_records * + sizeof(range_record)); + memcpy(data->records + dd1->num_records, dd2->records, dd2->num_records * + sizeof(range_record)); + // Reference leaf objects as sub-objects + if (dd1->leaf) { + data->records[0].data_object = dd1; + } + if (dd2->leaf) { + data->records[dd1->num_records].data_object = dd2; + } + size_t i; + for (i = 0; i < data->num_records; ++i) { + _dispatch_data_retain(data->records[i].data_object); + } + return data; +} + +dispatch_data_t +dispatch_data_create_subrange(dispatch_data_t dd, size_t offset, + size_t length) +{ + dispatch_data_t data; + if (offset >= dd->size || !length) { + return dispatch_data_empty; + } else if ((offset + length) > dd->size) { + length = dd->size - offset; + } else if (length == dd->size) { + _dispatch_data_retain(dd); + return dd; + } + if (dd->leaf) { + data = _dispatch_data_init(1); + data->size = length; + data->records[0].from = offset; + data->records[0].length = length; + data->records[0].data_object = dd; + _dispatch_data_retain(dd); + return data; + } + // Subrange of a composite dispatch data object: find the record containing + // the specified offset + data = dispatch_data_empty; + size_t i = 0, bytes_left = length; + while (i < dd->num_records && offset >= dd->records[i].length) { + offset -= dd->records[i++].length; + } + while (i < dd->num_records) { + size_t record_len = dd->records[i].length - offset; + if (record_len > bytes_left) { + record_len = bytes_left; + } + dispatch_data_t subrange = dispatch_data_create_subrange( + dd->records[i].data_object, dd->records[i].from + offset, + record_len); + dispatch_data_t concat = dispatch_data_create_concat(data, subrange); + _dispatch_data_release(data); + _dispatch_data_release(subrange); + data = concat; + bytes_left -= record_len; + if (!bytes_left) { + return data; + } + offset = 0; + i++; + } + // Crashing here indicates memory corruption of passed in data object + DISPATCH_CRASH("dispatch_data_create_subrange out of bounds"); + return NULL; +} + +// When mapping a leaf object or a subrange of a leaf object, return a direct +// pointer to the represented buffer. For all other data objects, copy the +// represented buffers into a contiguous area. In the future it might +// be possible to relocate the buffers instead (if not marked as locked). +dispatch_data_t +dispatch_data_create_map(dispatch_data_t dd, const void **buffer_ptr, + size_t *size_ptr) +{ + dispatch_data_t data = dd; + void *buffer = NULL; + size_t size = dd->size, offset = 0; + if (!size) { + data = dispatch_data_empty; + goto out; + } + if (!dd->leaf && dd->num_records == 1 && + ((dispatch_data_t)dd->records[0].data_object)->leaf) { + offset = dd->records[0].from; + dd = (dispatch_data_t)(dd->records[0].data_object); + } + if (dd->leaf) { +#if DISPATCH_DATA_MOVABLE + data = _dispatch_data_init(1); + // Make sure the underlying leaf object does not move the backing buffer + (void)dispatch_atomic_inc2o(dd, locked); + data->size = size; + data->destructor = DISPATCH_DATA_DESTRUCTOR_UNLOCK; + data->records[0].data_object = dd; + data->records[0].from = offset; + data->records[0].length = size; + _dispatch_data_retain(dd); +#else + _dispatch_data_retain(data); +#endif + buffer = dd->records[0].data_object + offset; + goto out; + } + // Composite data object, copy the represented buffers + buffer = malloc(size); + if (!buffer) { + data = NULL; + size = 0; + goto out; + } + dispatch_data_apply(dd, ^(dispatch_data_t region DISPATCH_UNUSED, + size_t off, const void* buf, size_t len) { + memcpy(buffer + off, buf, len); + return (bool)true; + }); + data = dispatch_data_create(buffer, size, NULL, + DISPATCH_DATA_DESTRUCTOR_FREE); +out: + if (buffer_ptr) { + *buffer_ptr = buffer; + } + if (size_ptr) { + *size_ptr = size; + } + return data; +} + +static bool +_dispatch_data_apply(dispatch_data_t dd, size_t offset, size_t from, + size_t size, dispatch_data_applier_t applier) +{ + bool result = true; + dispatch_data_t data = dd; + const void *buffer; + dispatch_assert(dd->size); +#if DISPATCH_DATA_MOVABLE + if (dd->leaf) { + data = _dispatch_data_init(1); + // Make sure the underlying leaf object does not move the backing buffer + (void)dispatch_atomic_inc2o(dd, locked); + data->size = size; + data->destructor = DISPATCH_DATA_DESTRUCTOR_UNLOCK; + data->records[0].data_object = dd; + data->records[0].from = from; + data->records[0].length = size; + _dispatch_data_retain(dd); + buffer = dd->records[0].data_object + from; + result = applier(data, offset, buffer, size); + _dispatch_data_release(data); + return result; + } +#else + if (!dd->leaf && dd->num_records == 1 && + ((dispatch_data_t)dd->records[0].data_object)->leaf) { + from = dd->records[0].from; + dd = (dispatch_data_t)(dd->records[0].data_object); + } + if (dd->leaf) { + buffer = dd->records[0].data_object + from; + return applier(data, offset, buffer, size); + } +#endif + size_t i; + for (i = 0; i < dd->num_records && result; ++i) { + result = _dispatch_data_apply(dd->records[i].data_object, + offset, dd->records[i].from, dd->records[i].length, + applier); + offset += dd->records[i].length; + } + return result; +} + +bool +dispatch_data_apply(dispatch_data_t dd, dispatch_data_applier_t applier) +{ + if (!dd->size) { + return true; + } + return _dispatch_data_apply(dd, 0, 0, dd->size, applier); +} + +// Returs either a leaf object or an object composed of a single leaf object +dispatch_data_t +dispatch_data_copy_region(dispatch_data_t dd, size_t location, + size_t *offset_ptr) +{ + if (location >= dd->size) { + *offset_ptr = 0; + return dispatch_data_empty; + } + dispatch_data_t data; + size_t size = dd->size, offset = 0, from = 0; + while (true) { + if (dd->leaf) { + _dispatch_data_retain(dd); + *offset_ptr = offset; + if (size == dd->size) { + return dd; + } else { + // Create a new object for the requested subrange of the leaf + data = _dispatch_data_init(1); + data->size = size; + data->records[0].from = from; + data->records[0].length = size; + data->records[0].data_object = dd; + return data; + } + } else { + // Find record at the specified location + size_t i, pos; + for (i = 0; i < dd->num_records; ++i) { + pos = offset + dd->records[i].length; + if (location < pos) { + size = dd->records[i].length; + from = dd->records[i].from; + data = (dispatch_data_t)(dd->records[i].data_object); + if (dd->num_records == 1 && data->leaf) { + // Return objects composed of a single leaf node + *offset_ptr = offset; + _dispatch_data_retain(dd); + return dd; + } else { + // Drill down into other objects + dd = data; + break; + } + } else { + offset = pos; + } + } + } + } +} diff --git a/src/data_internal.h b/src/data_internal.h new file mode 100644 index 0000000..314efa7 --- /dev/null +++ b/src/data_internal.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2009-2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_DATA_INTERNAL__ +#define __DISPATCH_DATA_INTERNAL__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +struct dispatch_data_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_data_s); +}; + +extern const struct dispatch_data_vtable_s _dispatch_data_vtable; + +typedef struct range_record_s { + void* data_object; + size_t from; + size_t length; +} range_record; + +struct dispatch_data_s { + DISPATCH_STRUCT_HEADER(dispatch_data_s, dispatch_data_vtable_s); +#if DISPATCH_DATA_MOVABLE + unsigned int locked; +#endif + bool leaf; + dispatch_block_t destructor; + size_t size, num_records; + range_record records[]; +}; + +#endif // __DISPATCH_DATA_INTERNAL__ diff --git a/src/hw_shims.h b/src/hw_shims.h deleted file mode 100644 index b99bf17..0000000 --- a/src/hw_shims.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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@ - */ - -/* - * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch - * which are subject to change in future releases of Mac OS X. Any applications - * relying on these interfaces WILL break. - */ - -#ifndef __DISPATCH_HW_SHIMS__ -#define __DISPATCH_HW_SHIMS__ - -/* x86 has a 64 byte cacheline */ -#define DISPATCH_CACHELINE_SIZE 64 -#define ROUND_UP_TO_CACHELINE_SIZE(x) (((x) + (DISPATCH_CACHELINE_SIZE - 1)) & ~(DISPATCH_CACHELINE_SIZE - 1)) -#define ROUND_UP_TO_VECTOR_SIZE(x) (((x) + 15) & ~15) - -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) -// GCC generates suboptimal register pressure -// LLVM does better, but doesn't support tail calls -// 6248590 __sync_*() intrinsics force a gratuitous "lea" instruction, with resulting register pressure -#if 0 && defined(__i386__) || defined(__x86_64__) -#define dispatch_atomic_xchg(p, n) ({ typeof(*(p)) _r; asm("xchg %0, %1" : "=r" (_r) : "m" (*(p)), "0" (n)); _r; }) -#else -#define dispatch_atomic_xchg(p, n) __sync_lock_test_and_set((p), (n)) -#endif -#define dispatch_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n)) -#define dispatch_atomic_inc(p) __sync_add_and_fetch((p), 1) -#define dispatch_atomic_dec(p) __sync_sub_and_fetch((p), 1) -#define dispatch_atomic_add(p, v) __sync_add_and_fetch((p), (v)) -#define dispatch_atomic_sub(p, v) __sync_sub_and_fetch((p), (v)) -#define dispatch_atomic_or(p, v) __sync_fetch_and_or((p), (v)) -#define dispatch_atomic_and(p, v) __sync_fetch_and_and((p), (v)) -#if defined(__i386__) || defined(__x86_64__) -/* GCC emits nothing for __sync_synchronize() on i386/x86_64. */ -#define dispatch_atomic_barrier() __asm__ __volatile__("mfence") -#else -#define dispatch_atomic_barrier() __sync_synchronize() -#endif -#else -#error "Please upgrade to GCC 4.2 or newer." -#endif - -#if defined(__i386__) || defined(__x86_64__) -#define _dispatch_hardware_pause() asm("pause") -#define _dispatch_debugger() asm("int3") -#else -#define _dispatch_hardware_pause() asm("") -#define _dispatch_debugger() asm("trap") -#endif -// really just a low level abort() -#define _dispatch_hardware_crash() __builtin_trap() - - -#endif diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..d72219c --- /dev/null +++ b/src/init.c @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2008-2011 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@ + */ + +// Contains exported global data and initialization & other routines that must +// only exist once in the shared library even when resolvers are used. + +#include "internal.h" + +#if HAVE_MACH +#include "protocolServer.h" +#endif + +#pragma mark - +#pragma mark dispatch_init + +#if USE_LIBDISPATCH_INIT_CONSTRUCTOR +DISPATCH_NOTHROW __attribute__((constructor)) +void +_libdispatch_init(void); + +DISPATCH_EXPORT DISPATCH_NOTHROW +void +_libdispatch_init(void) +{ + libdispatch_init(); +} +#endif + +DISPATCH_EXPORT DISPATCH_NOTHROW +void +dispatch_atfork_prepare(void) +{ +} + +DISPATCH_EXPORT DISPATCH_NOTHROW +void +dispatch_atfork_parent(void) +{ +} + +void +dummy_function(void) +{ +} + +long +dummy_function_r0(void) +{ + return 0; +} + +#pragma mark - +#pragma mark dispatch_globals + +#if DISPATCH_COCOA_COMPAT +// dispatch_begin_thread_4GC having non-default value triggers GC-only slow +// paths and is checked frequently, testing against NULL is faster than +// comparing for equality with "dummy_function" +void (*dispatch_begin_thread_4GC)(void) = NULL; +void (*dispatch_end_thread_4GC)(void) = dummy_function; +void (*dispatch_no_worker_threads_4GC)(void) = NULL; +void *(*_dispatch_begin_NSAutoReleasePool)(void) = (void *)dummy_function; +void (*_dispatch_end_NSAutoReleasePool)(void *) = (void *)dummy_function; +#endif + +struct _dispatch_hw_config_s _dispatch_hw_config; +bool _dispatch_safe_fork = true; + +const struct dispatch_queue_offsets_s dispatch_queue_offsets = { + .dqo_version = 3, + .dqo_label = offsetof(struct dispatch_queue_s, dq_label), + .dqo_label_size = sizeof(((dispatch_queue_t)NULL)->dq_label), + .dqo_flags = 0, + .dqo_flags_size = 0, + .dqo_width = offsetof(struct dispatch_queue_s, dq_width), + .dqo_width_size = sizeof(((dispatch_queue_t)NULL)->dq_width), + .dqo_serialnum = offsetof(struct dispatch_queue_s, dq_serialnum), + .dqo_serialnum_size = sizeof(((dispatch_queue_t)NULL)->dq_serialnum), + .dqo_running = offsetof(struct dispatch_queue_s, dq_running), + .dqo_running_size = sizeof(((dispatch_queue_t)NULL)->dq_running), +}; + +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +DISPATCH_CACHELINE_ALIGN +struct dispatch_queue_s _dispatch_main_q = { +#if !DISPATCH_USE_RESOLVERS + .do_vtable = &_dispatch_queue_vtable, + .do_targetq = &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY], +#endif + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .dq_label = "com.apple.main-thread", + .dq_running = 1, + .dq_width = 1, + .dq_serialnum = 1, +}; + +const struct dispatch_queue_attr_vtable_s dispatch_queue_attr_vtable = { + .do_type = DISPATCH_QUEUE_ATTR_TYPE, + .do_kind = "queue-attr", +}; + +struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent = { + .do_vtable = &dispatch_queue_attr_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_next = DISPATCH_OBJECT_LISTLESS, +}; + +struct dispatch_data_s _dispatch_data_empty = { +#if !DISPATCH_USE_RESOLVERS + .do_vtable = &_dispatch_data_vtable, +#endif + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_next = DISPATCH_OBJECT_LISTLESS, +}; + +const dispatch_block_t _dispatch_data_destructor_free = ^{ + DISPATCH_CRASH("free destructor called"); +}; + +#pragma mark - +#pragma mark dispatch_log + +static char _dispatch_build[16]; + +static void +_dispatch_bug_init(void *context DISPATCH_UNUSED) +{ +#ifdef __APPLE__ + int mib[] = { CTL_KERN, KERN_OSVERSION }; + size_t bufsz = sizeof(_dispatch_build); + + sysctl(mib, 2, _dispatch_build, &bufsz, NULL, 0); +#else + /* + * XXXRW: What to do here for !Mac OS X? + */ + memset(_dispatch_build, 0, sizeof(_dispatch_build)); +#endif +} + +void +_dispatch_bug(size_t line, long val) +{ + static dispatch_once_t pred; + static void *last_seen; + void *ra = __builtin_return_address(0); + + dispatch_once_f(&pred, NULL, _dispatch_bug_init); + if (last_seen != ra) { + last_seen = ra; + _dispatch_log("BUG in libdispatch: %s - %lu - 0x%lx", + _dispatch_build, (unsigned long)line, val); + } +} + +void +_dispatch_bug_mach_client(const char* msg, mach_msg_return_t kr) +{ + static void *last_seen; + void *ra = __builtin_return_address(0); + if (last_seen != ra) { + last_seen = ra; + _dispatch_log("BUG in libdispatch client: %s %s - 0x%x", msg, + mach_error_string(kr), kr); + } +} + +void +_dispatch_abort(size_t line, long val) +{ + _dispatch_bug(line, val); + abort(); +} + +void +_dispatch_log(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + _dispatch_logv(msg, ap); + va_end(ap); +} + +static FILE *dispatch_logfile; +static bool dispatch_log_disabled; + +static void +_dispatch_logv_init(void *context DISPATCH_UNUSED) +{ +#if DISPATCH_DEBUG + bool log_to_file = true; +#else + bool log_to_file = false; +#endif + char *e = getenv("LIBDISPATCH_LOG"); + if (e) { + if (strcmp(e, "YES") == 0) { + // default + } else if (strcmp(e, "NO") == 0) { + dispatch_log_disabled = true; + } else if (strcmp(e, "syslog") == 0) { + log_to_file = false; + } else if (strcmp(e, "file") == 0) { + log_to_file = true; + } else if (strcmp(e, "stderr") == 0) { + log_to_file = true; + dispatch_logfile = stderr; + } + } + if (!dispatch_log_disabled) { + if (log_to_file && !dispatch_logfile) { + char path[PATH_MAX]; + snprintf(path, sizeof(path), "/var/tmp/libdispatch.%d.log", + getpid()); + dispatch_logfile = fopen(path, "a"); + } + if (dispatch_logfile) { + struct timeval tv; + gettimeofday(&tv, NULL); + fprintf(dispatch_logfile, "=== log file opened for %s[%u] at " + "%ld.%06u ===\n", getprogname() ?: "", getpid(), + tv.tv_sec, tv.tv_usec); + fflush(dispatch_logfile); + } + } +} + +void +_dispatch_logv(const char *msg, va_list ap) +{ + static dispatch_once_t pred; + dispatch_once_f(&pred, NULL, _dispatch_logv_init); + + if (slowpath(dispatch_log_disabled)) { + return; + } + if (slowpath(dispatch_logfile)) { + vfprintf(dispatch_logfile, msg, ap); + // TODO: May cause interleaving with another thread's log + fputc('\n', dispatch_logfile); + fflush(dispatch_logfile); + return; + } + vsyslog(LOG_NOTICE, msg, ap); +} + +#pragma mark - +#pragma mark dispatch_debug + +void +dispatch_debug(dispatch_object_t dou, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + dispatch_debugv(dou._do, msg, ap); + va_end(ap); +} + +void +dispatch_debugv(dispatch_object_t dou, const char *msg, va_list ap) +{ + char buf[4096]; + size_t offs; + + if (dou._do && dou._do->do_vtable->do_debug) { + offs = dx_debug(dou._do, buf, sizeof(buf)); + } else { + offs = snprintf(buf, sizeof(buf), "NULL vtable slot"); + } + + snprintf(buf + offs, sizeof(buf) - offs, ": %s", msg); + _dispatch_logv(buf, ap); +} + +#pragma mark - +#pragma mark dispatch_block_t + +#ifdef __BLOCKS__ + +#undef _dispatch_Block_copy +dispatch_block_t +_dispatch_Block_copy(dispatch_block_t db) +{ + dispatch_block_t rval; + + while (!(rval = Block_copy(db))) { + sleep(1); + } + return rval; +} + +void +_dispatch_call_block_and_release(void *block) +{ + void (^b)(void) = block; + b(); + Block_release(b); +} + +#endif // __BLOCKS__ + +#pragma mark - +#pragma mark dispatch_client_callout + +#if DISPATCH_USE_CLIENT_CALLOUT + +#undef _dispatch_client_callout +#undef _dispatch_client_callout2 + +DISPATCH_NOINLINE +void +_dispatch_client_callout(void *ctxt, dispatch_function_t f) +{ + return f(ctxt); +} + +DISPATCH_NOINLINE +void +_dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) +{ + return f(ctxt, i); +} + +#endif + +#pragma mark - +#pragma mark dispatch_source_types + +static void +dispatch_source_type_timer_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, + unsigned long mask, + dispatch_queue_t q DISPATCH_UNUSED) +{ + ds->ds_refs = calloc(1ul, sizeof(struct dispatch_timer_source_refs_s)); + if (slowpath(!ds->ds_refs)) return; + ds->ds_needs_rearm = true; + ds->ds_is_timer = true; + ds_timer(ds->ds_refs).flags = mask; +} + +const struct dispatch_source_type_s _dispatch_source_type_timer = { + .ke = { + .filter = DISPATCH_EVFILT_TIMER, + }, + .mask = DISPATCH_TIMER_WALL_CLOCK, + .init = dispatch_source_type_timer_init, +}; + +const struct dispatch_source_type_s _dispatch_source_type_read = { + .ke = { + .filter = EVFILT_READ, + .flags = EV_DISPATCH, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_write = { + .ke = { + .filter = EVFILT_WRITE, + .flags = EV_DISPATCH, + }, +}; + +#if DISPATCH_USE_VM_PRESSURE +#if TARGET_IPHONE_SIMULATOR // rdar://problem/9219483 +static int _dispatch_ios_simulator_memory_warnings_fd = -1; +static void +_dispatch_ios_simulator_vm_source_init(void *context DISPATCH_UNUSED) +{ + char *e = getenv("IPHONE_SIMULATOR_MEMORY_WARNINGS"); + if (!e) return; + _dispatch_ios_simulator_memory_warnings_fd = open(e, O_EVTONLY); + if (_dispatch_ios_simulator_memory_warnings_fd == -1) { + (void)dispatch_assume_zero(errno); + } +} +static void +dispatch_source_type_vm_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, + unsigned long mask, + dispatch_queue_t q DISPATCH_UNUSED) +{ + static dispatch_once_t pred; + dispatch_once_f(&pred, NULL, _dispatch_ios_simulator_vm_source_init); + ds->ds_dkev->dk_kevent.ident = (mask & DISPATCH_VM_PRESSURE ? + _dispatch_ios_simulator_memory_warnings_fd : -1); +} + +const struct dispatch_source_type_s _dispatch_source_type_vm = { + .ke = { + .filter = EVFILT_VNODE, + .flags = EV_CLEAR, + }, + .mask = NOTE_ATTRIB, + .init = dispatch_source_type_vm_init, +}; +#else +static void +dispatch_source_type_vm_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, + unsigned long mask DISPATCH_UNUSED, + dispatch_queue_t q DISPATCH_UNUSED) +{ + ds->ds_is_level = false; +} + +const struct dispatch_source_type_s _dispatch_source_type_vm = { + .ke = { + .filter = EVFILT_VM, + .flags = EV_DISPATCH, + }, + .mask = NOTE_VM_PRESSURE, + .init = dispatch_source_type_vm_init, +}; +#endif +#endif + +const struct dispatch_source_type_s _dispatch_source_type_proc = { + .ke = { + .filter = EVFILT_PROC, + .flags = EV_CLEAR, + }, + .mask = NOTE_EXIT|NOTE_FORK|NOTE_EXEC +#if HAVE_DECL_NOTE_SIGNAL + |NOTE_SIGNAL +#endif +#if HAVE_DECL_NOTE_REAP + |NOTE_REAP +#endif + , +}; + +const struct dispatch_source_type_s _dispatch_source_type_signal = { + .ke = { + .filter = EVFILT_SIGNAL, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_vnode = { + .ke = { + .filter = EVFILT_VNODE, + .flags = EV_CLEAR, + }, + .mask = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK| + NOTE_RENAME|NOTE_REVOKE +#if HAVE_DECL_NOTE_NONE + |NOTE_NONE +#endif + , +}; + +const struct dispatch_source_type_s _dispatch_source_type_vfs = { + .ke = { + .filter = EVFILT_FS, + .flags = EV_CLEAR, + }, + .mask = VQ_NOTRESP|VQ_NEEDAUTH|VQ_LOWDISK|VQ_MOUNT|VQ_UNMOUNT|VQ_DEAD| + VQ_ASSIST|VQ_NOTRESPLOCK +#if HAVE_DECL_VQ_UPDATE + |VQ_UPDATE +#endif +#if HAVE_DECL_VQ_VERYLOWDISK + |VQ_VERYLOWDISK +#endif + , +}; + +const struct dispatch_source_type_s _dispatch_source_type_data_add = { + .ke = { + .filter = DISPATCH_EVFILT_CUSTOM_ADD, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_data_or = { + .ke = { + .filter = DISPATCH_EVFILT_CUSTOM_OR, + .flags = EV_CLEAR, + .fflags = ~0, + }, +}; + +#if HAVE_MACH + +static void +dispatch_source_type_mach_send_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, unsigned long mask, + dispatch_queue_t q DISPATCH_UNUSED) +{ + static dispatch_once_t pred; + dispatch_once_f(&pred, NULL, _dispatch_mach_notify_source_init); + if (!mask) { + // Preserve legacy behavior that (mask == 0) => DISPATCH_MACH_SEND_DEAD + ds->ds_dkev->dk_kevent.fflags = DISPATCH_MACH_SEND_DEAD; + ds->ds_pending_data_mask = DISPATCH_MACH_SEND_DEAD; + } +} + +const struct dispatch_source_type_s _dispatch_source_type_mach_send = { + .ke = { + .filter = EVFILT_MACHPORT, + .flags = EV_CLEAR, + }, + .mask = DISPATCH_MACH_SEND_DEAD|DISPATCH_MACH_SEND_POSSIBLE, + .init = dispatch_source_type_mach_send_init, +}; + +static void +dispatch_source_type_mach_recv_init(dispatch_source_t ds, + dispatch_source_type_t type DISPATCH_UNUSED, + uintptr_t handle DISPATCH_UNUSED, + unsigned long mask DISPATCH_UNUSED, + dispatch_queue_t q DISPATCH_UNUSED) +{ + ds->ds_is_level = false; +} + +const struct dispatch_source_type_s _dispatch_source_type_mach_recv = { + .ke = { + .filter = EVFILT_MACHPORT, + .flags = EV_DISPATCH, + .fflags = DISPATCH_MACH_RECV_MESSAGE, + }, + .init = dispatch_source_type_mach_recv_init, +}; + +#pragma mark - +#pragma mark dispatch_mig + +void * +dispatch_mach_msg_get_context(mach_msg_header_t *msg) +{ + mach_msg_context_trailer_t *tp; + void *context = NULL; + + tp = (mach_msg_context_trailer_t *)((uint8_t *)msg + + round_msg(msg->msgh_size)); + if (tp->msgh_trailer_size >= + (mach_msg_size_t)sizeof(mach_msg_context_trailer_t)) { + context = (void *)(uintptr_t)tp->msgh_context; + } + return context; +} + +kern_return_t +_dispatch_wakeup_main_thread(mach_port_t mp DISPATCH_UNUSED) +{ + // dummy function just to pop out the main thread out of mach_msg() + return 0; +} + +kern_return_t +_dispatch_consume_send_once_right(mach_port_t mp DISPATCH_UNUSED) +{ + // dummy function to consume a send-once right + return 0; +} + +kern_return_t +_dispatch_mach_notify_port_destroyed(mach_port_t notify DISPATCH_UNUSED, + mach_port_t name) +{ + kern_return_t kr; + // this function should never be called + (void)dispatch_assume_zero(name); + kr = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE,-1); + DISPATCH_VERIFY_MIG(kr); + (void)dispatch_assume_zero(kr); + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_mach_notify_no_senders(mach_port_t notify, + mach_port_mscount_t mscnt DISPATCH_UNUSED) +{ + // this function should never be called + (void)dispatch_assume_zero(notify); + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_mach_notify_send_once(mach_port_t notify DISPATCH_UNUSED) +{ + // we only register for dead-name notifications + // some code deallocated our send-once right without consuming it +#if DISPATCH_DEBUG + _dispatch_log("Corruption: An app/library deleted a libdispatch " + "dead-name notification"); +#endif + return KERN_SUCCESS; +} + + +#endif // HAVE_MACH diff --git a/src/internal.h b/src/internal.h index d55540b..24d3a04 100644 --- a/src/internal.h +++ b/src/internal.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -27,25 +27,33 @@ #ifndef __DISPATCH_INTERNAL__ #define __DISPATCH_INTERNAL__ +#include + #define __DISPATCH_BUILDING_DISPATCH__ #define __DISPATCH_INDIRECT__ -#include "dispatch.h" -#include "base.h" -#include "time.h" -#include "queue.h" -#include "object.h" -#include "source.h" -#include "group.h" -#include "semaphore.h" -#include "once.h" -#include "benchmark.h" + + +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* private.h uses #include_next and must be included last to avoid picking * up installed headers. */ #include "queue_private.h" #include "source_private.h" +#include "benchmark.h" #include "private.h" -#include "legacy.h" + /* More #includes at EOF (dependent on the contents of internal.h) ... */ /* The "_debug" library build */ @@ -53,9 +61,25 @@ #define DISPATCH_DEBUG 0 #endif +#ifndef DISPATCH_PROFILE +#define DISPATCH_PROFILE 0 +#endif + +#if DISPATCH_DEBUG && !defined(DISPATCH_USE_CLIENT_CALLOUT) +#define DISPATCH_USE_CLIENT_CALLOUT 1 +#endif + +#if (DISPATCH_DEBUG || DISPATCH_PROFILE) && !defined(DISPATCH_USE_DTRACE) +#define DISPATCH_USE_DTRACE 1 +#endif +#if HAVE_LIBKERN_OSCROSSENDIAN_H #include +#endif +#if HAVE_LIBKERN_OSATOMIC_H #include +#endif +#if HAVE_MACH #include #include #include @@ -70,14 +94,17 @@ #include #include #include +#endif /* HAVE_MACH */ +#if HAVE_MALLOC_MALLOC_H #include +#endif #include #include #include #include #include -#include #include +#include #include #ifdef __BLOCKS__ @@ -88,7 +115,11 @@ #include #include #include +#include #include +#if USE_POSIX_SEM +#include +#endif #include #include #include @@ -97,9 +128,32 @@ #include #include #include +#if HAVE_UNISTD_H #include +#endif -#define DISPATCH_NOINLINE __attribute__((noinline)) +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif +#ifndef __has_include +#define __has_include(x) 0 +#endif +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#define DISPATCH_NOINLINE __attribute__((__noinline__)) +#define DISPATCH_USED __attribute__((__used__)) +#define DISPATCH_UNUSED __attribute__((__unused__)) +#define DISPATCH_WEAK __attribute__((__weak__)) +#if DISPATCH_DEBUG +#define DISPATCH_ALWAYS_INLINE_NDEBUG +#else +#define DISPATCH_ALWAYS_INLINE_NDEBUG __attribute__((__always_inline__)) +#endif // workaround 6368156 #ifdef NSEC_PER_SEC @@ -116,184 +170,256 @@ #define NSEC_PER_USEC 1000ull /* I wish we had __builtin_expect_range() */ -#define fastpath(x) ((typeof(x))__builtin_expect((long)(x), ~0l)) -#define slowpath(x) ((typeof(x))__builtin_expect((long)(x), 0l)) - -void _dispatch_bug(size_t line, long val) __attribute__((__noinline__)); -void _dispatch_abort(size_t line, long val) __attribute__((__noinline__,__noreturn__)); -void _dispatch_log(const char *msg, ...) __attribute__((__noinline__,__format__(printf,1,2))); -void _dispatch_logv(const char *msg, va_list) __attribute__((__noinline__,__format__(printf,1,0))); +#define fastpath(x) ((typeof(x))__builtin_expect((long)(x), ~0l)) +#define slowpath(x) ((typeof(x))__builtin_expect((long)(x), 0l)) + +DISPATCH_NOINLINE +void _dispatch_bug(size_t line, long val); +DISPATCH_NOINLINE +void _dispatch_bug_mach_client(const char *msg, mach_msg_return_t kr); +DISPATCH_NOINLINE DISPATCH_NORETURN +void _dispatch_abort(size_t line, long val); +DISPATCH_NOINLINE __attribute__((__format__(printf,1,2))) +void _dispatch_log(const char *msg, ...); +DISPATCH_NOINLINE __attribute__((__format__(printf,1,0))) +void _dispatch_logv(const char *msg, va_list); /* - * For reporting bugs within libdispatch when using the "_debug" version of the library. + * For reporting bugs within libdispatch when using the "_debug" version of the + * library. */ -#define dispatch_assert(e) do { \ - if (__builtin_constant_p(e)) { \ - char __compile_time_assert__[(bool)(e) ? 1 : -1] __attribute__((unused)); \ - } else { \ - typeof(e) _e = fastpath(e); /* always eval 'e' */ \ - if (DISPATCH_DEBUG && !_e) { \ - _dispatch_abort(__LINE__, (long)_e); \ - } \ - } \ +#define dispatch_assert(e) do { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(bool)(e) ? 1 : -1] DISPATCH_UNUSED; \ + } else { \ + typeof(e) _e = fastpath(e); /* always eval 'e' */ \ + if (DISPATCH_DEBUG && !_e) { \ + _dispatch_abort(__LINE__, (long)_e); \ + } \ + } \ } while (0) -/* A lot of API return zero upon success and not-zero on fail. Let's capture and log the non-zero value */ -#define dispatch_assert_zero(e) do { \ - if (__builtin_constant_p(e)) { \ - char __compile_time_assert__[(bool)(!(e)) ? 1 : -1] __attribute__((unused)); \ - } else { \ - typeof(e) _e = slowpath(e); /* always eval 'e' */ \ - if (DISPATCH_DEBUG && _e) { \ - _dispatch_abort(__LINE__, (long)_e); \ - } \ - } \ +/* + * A lot of API return zero upon success and not-zero on fail. Let's capture + * and log the non-zero value + */ +#define dispatch_assert_zero(e) do { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(bool)(e) ? -1 : 1] DISPATCH_UNUSED; \ + } else { \ + typeof(e) _e = slowpath(e); /* always eval 'e' */ \ + if (DISPATCH_DEBUG && _e) { \ + _dispatch_abort(__LINE__, (long)_e); \ + } \ + } \ } while (0) /* - * For reporting bugs or impedance mismatches between libdispatch and external subsystems. - * These do NOT abort(), and are always compiled into the product. + * For reporting bugs or impedance mismatches between libdispatch and external + * subsystems. These do NOT abort(), and are always compiled into the product. * * In particular, we wrap all system-calls with assume() macros. */ -#define dispatch_assume(e) ({ \ - typeof(e) _e = fastpath(e); /* always eval 'e' */ \ - if (!_e) { \ - if (__builtin_constant_p(e)) { \ - char __compile_time_assert__[(e) ? 1 : -1]; \ - (void)__compile_time_assert__; \ - } \ - _dispatch_bug(__LINE__, (long)_e); \ - } \ - _e; \ +#define dispatch_assume(e) ({ \ + typeof(e) _e = fastpath(e); /* always eval 'e' */ \ + if (!_e) { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(bool)(e) ? 1 : -1]; \ + (void)__compile_time_assert__; \ + } \ + _dispatch_bug(__LINE__, (long)_e); \ + } \ + _e; \ }) -/* A lot of API return zero upon success and not-zero on fail. Let's capture and log the non-zero value */ -#define dispatch_assume_zero(e) ({ \ - typeof(e) _e = slowpath(e); /* always eval 'e' */ \ - if (_e) { \ - if (__builtin_constant_p(e)) { \ - char __compile_time_assert__[(e) ? -1 : 1]; \ - (void)__compile_time_assert__; \ - } \ - _dispatch_bug(__LINE__, (long)_e); \ - } \ - _e; \ +/* + * A lot of API return zero upon success and not-zero on fail. Let's capture + * and log the non-zero value + */ +#define dispatch_assume_zero(e) ({ \ + typeof(e) _e = slowpath(e); /* always eval 'e' */ \ + if (_e) { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(bool)(e) ? -1 : 1]; \ + (void)__compile_time_assert__; \ + } \ + _dispatch_bug(__LINE__, (long)_e); \ + } \ + _e; \ }) /* * For reporting bugs in clients when using the "_debug" version of the library. */ -#define dispatch_debug_assert(e, msg, args...) do { \ - if (__builtin_constant_p(e)) { \ - char __compile_time_assert__[(bool)(e) ? 1 : -1] __attribute__((unused)); \ - } else { \ - typeof(e) _e = fastpath(e); /* always eval 'e' */ \ - if (DISPATCH_DEBUG && !_e) { \ - _dispatch_log("%s() 0x%lx: " msg, __func__, (long)_e, ##args); \ - abort(); \ - } \ - } \ +#define dispatch_debug_assert(e, msg, args...) do { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(bool)(e) ? 1 : -1] DISPATCH_UNUSED; \ + } else { \ + typeof(e) _e = fastpath(e); /* always eval 'e' */ \ + if (DISPATCH_DEBUG && !_e) { \ + _dispatch_log("%s() 0x%lx: " msg, __func__, (long)_e, ##args); \ + abort(); \ + } \ + } \ } while (0) +/* Make sure the debug statments don't get too stale */ +#define _dispatch_debug(x, args...) \ +({ \ + if (DISPATCH_DEBUG) { \ + _dispatch_log("libdispatch: %u\t%p\t" x, __LINE__, \ + (void *)_dispatch_thread_self(), ##args); \ + } \ +}) +#if DISPATCH_DEBUG +#if HAVE_MACH +DISPATCH_NOINLINE DISPATCH_USED +void dispatch_debug_machport(mach_port_t name, const char* str); +#endif +DISPATCH_NOINLINE DISPATCH_USED +void dispatch_debug_kevents(struct kevent* kev, size_t count, const char* str); +#else +static inline void +dispatch_debug_kevents(struct kevent* kev DISPATCH_UNUSED, + size_t count DISPATCH_UNUSED, + const char* str DISPATCH_UNUSED) {} +#endif -#ifdef __BLOCKS__ -dispatch_block_t _dispatch_Block_copy(dispatch_block_t block); -void _dispatch_call_block_and_release(void *block); -void _dispatch_call_block_and_release2(void *block, void *ctxt); -#endif /* __BLOCKS__ */ +#if DISPATCH_USE_CLIENT_CALLOUT -void dummy_function(void); -long dummy_function_r0(void); +DISPATCH_NOTHROW void +_dispatch_client_callout(void *ctxt, dispatch_function_t f); +DISPATCH_NOTHROW void +_dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)); +#else -/* Make sure the debug statments don't get too stale */ -#define _dispatch_debug(x, args...) \ -({ \ - if (DISPATCH_DEBUG) { \ - _dispatch_log("libdispatch: %u\t%p\t" x, __LINE__, _dispatch_thread_self(), ##args); \ - } \ -}) +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_client_callout(void *ctxt, dispatch_function_t f) +{ + return f(ctxt); +} +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) +{ + return f(ctxt, i); +} -#if DISPATCH_DEBUG -void dispatch_debug_kevents(struct kevent* kev, size_t count, const char* str); -#else -#define dispatch_debug_kevents(x, y, z) #endif -uint64_t _dispatch_get_nanoseconds(void); +#ifdef __BLOCKS__ +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_client_callout_block(dispatch_block_t b) +{ + struct Block_basic *bb = (void*)b; + return _dispatch_client_callout(b, (dispatch_function_t)bb->Block_invoke); +} -void _dispatch_source_drain_kevent(struct kevent *); +dispatch_block_t _dispatch_Block_copy(dispatch_block_t block); +#define _dispatch_Block_copy(x) ((typeof(x))_dispatch_Block_copy(x)) +void _dispatch_call_block_and_release(void *block); +#endif /* __BLOCKS__ */ -dispatch_source_t -_dispatch_source_create2(dispatch_source_t ds, - dispatch_source_attr_t attr, - void *context, - dispatch_source_handler_function_t handler); +void dummy_function(void); +long dummy_function_r0(void); + +void _dispatch_source_drain_kevent(struct kevent *); -void _dispatch_update_kq(const struct kevent *); +long _dispatch_update_kq(const struct kevent *); void _dispatch_run_timers(void); // Returns howsoon with updated time value, or NULL if no timers active. struct timespec *_dispatch_get_next_timer_fire(struct timespec *howsoon); -dispatch_semaphore_t _dispatch_get_thread_semaphore(void); -void _dispatch_put_thread_semaphore(dispatch_semaphore_t); - bool _dispatch_source_testcancel(dispatch_source_t); uint64_t _dispatch_timeout(dispatch_time_t when); -__private_extern__ bool _dispatch_safe_fork; +extern bool _dispatch_safe_fork; -__private_extern__ struct _dispatch_hw_config_s { +extern struct _dispatch_hw_config_s { uint32_t cc_max_active; uint32_t cc_max_logical; uint32_t cc_max_physical; } _dispatch_hw_config; /* #includes dependent on internal.h */ +#include "shims.h" #include "object_internal.h" -#include "hw_shims.h" -#include "os_shims.h" #include "queue_internal.h" #include "semaphore_internal.h" #include "source_internal.h" +#include "data_internal.h" +#include "io_internal.h" +#include "trace.h" -// MIG_REPLY_MISMATCH means either: -// 1) A signal handler is NOT using async-safe API. See the sigaction(2) man page for more info. -// 2) A hand crafted call to mach_msg*() screwed up. Use MIG. -#define DISPATCH_VERIFY_MIG(x) do { \ - if ((x) == MIG_REPLY_MISMATCH) { \ - __crashreporter_info__ = "MIG_REPLY_MISMATCH"; \ - _dispatch_hardware_crash(); \ - } \ - } while (0) +// SnowLeopard and iOS Simulator fallbacks -#if defined(__x86_64__) || defined(__i386__) -// total hack to ensure that return register of a function is not trashed -#define DISPATCH_CRASH(x) do { \ - asm("mov %1, %0" : "=m" (__crashreporter_info__) : "c" ("BUG IN LIBDISPATCH: " x)); \ - _dispatch_hardware_crash(); \ - } while (0) +#if HAVE_PTHREAD_WORKQUEUES +#if !defined(WORKQ_BG_PRIOQUEUE) || \ + (TARGET_IPHONE_SIMULATOR && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) +#undef WORKQ_BG_PRIOQUEUE +#define WORKQ_BG_PRIOQUEUE WORKQ_LOW_PRIOQUEUE +#endif +#endif // HAVE_PTHREAD_WORKQUEUES -#define DISPATCH_CLIENT_CRASH(x) do { \ - asm("mov %1, %0" : "=m" (__crashreporter_info__) : "c" ("BUG IN CLIENT OF LIBDISPATCH: " x)); \ - _dispatch_hardware_crash(); \ - } while (0) +#if HAVE_MACH +#if !defined(MACH_NOTIFY_SEND_POSSIBLE) || \ + (TARGET_IPHONE_SIMULATOR && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) +#undef MACH_NOTIFY_SEND_POSSIBLE +#define MACH_NOTIFY_SEND_POSSIBLE MACH_NOTIFY_DEAD_NAME +#endif +#endif // HAVE_MACH -#else +#ifdef EVFILT_VM +#if TARGET_IPHONE_SIMULATOR && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 +#undef DISPATCH_USE_MALLOC_VM_PRESSURE_SOURCE +#define DISPATCH_USE_MALLOC_VM_PRESSURE_SOURCE 0 +#endif +#ifndef DISPATCH_USE_VM_PRESSURE +#define DISPATCH_USE_VM_PRESSURE 1 +#endif +#ifndef DISPATCH_USE_MALLOC_VM_PRESSURE_SOURCE +#define DISPATCH_USE_MALLOC_VM_PRESSURE_SOURCE 1 +#endif +#endif // EVFILT_VM -#define DISPATCH_CRASH(x) do { \ - __crashreporter_info__ = "BUG IN LIBDISPATCH: " x; \ - _dispatch_hardware_crash(); \ - } while (0) +#if defined(F_SETNOSIGPIPE) && defined(F_GETNOSIGPIPE) +#if TARGET_IPHONE_SIMULATOR && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 +#undef DISPATCH_USE_SETNOSIGPIPE +#define DISPATCH_USE_SETNOSIGPIPE 0 +#endif +#ifndef DISPATCH_USE_SETNOSIGPIPE +#define DISPATCH_USE_SETNOSIGPIPE 1 +#endif +#endif // F_SETNOSIGPIPE -#define DISPATCH_CLIENT_CRASH(x) do { \ - __crashreporter_info__ = "BUG IN CLIENT OF LIBDISPATCH: " x; \ - _dispatch_hardware_crash(); \ - } while (0) +#define _dispatch_set_crash_log_message(x) + +#if HAVE_MACH +// MIG_REPLY_MISMATCH means either: +// 1) A signal handler is NOT using async-safe API. See the sigaction(2) man +// page for more info. +// 2) A hand crafted call to mach_msg*() screwed up. Use MIG. +#define DISPATCH_VERIFY_MIG(x) do { \ + if ((x) == MIG_REPLY_MISMATCH) { \ + _dispatch_set_crash_log_message("MIG_REPLY_MISMATCH"); \ + _dispatch_hardware_crash(); \ + } \ + } while (0) #endif +#define DISPATCH_CRASH(x) do { \ + _dispatch_set_crash_log_message("BUG IN LIBDISPATCH: " x); \ + _dispatch_hardware_crash(); \ + } while (0) + +#define DISPATCH_CLIENT_CRASH(x) do { \ + _dispatch_set_crash_log_message("BUG IN CLIENT OF LIBDISPATCH: " x); \ + _dispatch_hardware_crash(); \ + } while (0) #endif /* __DISPATCH_INTERNAL__ */ diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..b306054 --- /dev/null +++ b/src/io.c @@ -0,0 +1,2155 @@ +/* + * Copyright (c) 2009-2011 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 "internal.h" + +typedef void (^dispatch_fd_entry_init_callback_t)(dispatch_fd_entry_t fd_entry); + +DISPATCH_EXPORT DISPATCH_NOTHROW +void _dispatch_iocntl(uint32_t param, uint64_t value); + +static void _dispatch_io_dispose(dispatch_io_t channel); +static dispatch_operation_t _dispatch_operation_create( + dispatch_op_direction_t direction, dispatch_io_t channel, off_t offset, + size_t length, dispatch_data_t data, dispatch_queue_t queue, + dispatch_io_handler_t handler); +static void _dispatch_operation_dispose(dispatch_operation_t operation); +static void _dispatch_operation_enqueue(dispatch_operation_t op, + dispatch_op_direction_t direction, dispatch_data_t data); +static dispatch_source_t _dispatch_operation_timer(dispatch_queue_t tq, + dispatch_operation_t op); +static inline void _dispatch_fd_entry_retain(dispatch_fd_entry_t fd_entry); +static inline void _dispatch_fd_entry_release(dispatch_fd_entry_t fd_entry); +static void _dispatch_fd_entry_init_async(dispatch_fd_t fd, + dispatch_fd_entry_init_callback_t completion_callback); +static dispatch_fd_entry_t _dispatch_fd_entry_create_with_fd(dispatch_fd_t fd, + uintptr_t hash); +static dispatch_fd_entry_t _dispatch_fd_entry_create_with_path( + dispatch_io_path_data_t path_data, dev_t dev, mode_t mode); +static int _dispatch_fd_entry_open(dispatch_fd_entry_t fd_entry, + dispatch_io_t channel); +static void _dispatch_fd_entry_cleanup_operations(dispatch_fd_entry_t fd_entry, + dispatch_io_t channel); +static void _dispatch_stream_init(dispatch_fd_entry_t fd_entry, + dispatch_queue_t tq); +static void _dispatch_stream_dispose(dispatch_fd_entry_t fd_entry, + dispatch_op_direction_t direction); +static void _dispatch_disk_init(dispatch_fd_entry_t fd_entry, dev_t dev); +static void _dispatch_disk_dispose(dispatch_disk_t disk); +static void _dispatch_stream_enqueue_operation(dispatch_stream_t stream, + dispatch_operation_t operation, dispatch_data_t data); +static void _dispatch_disk_enqueue_operation(dispatch_disk_t dsk, + dispatch_operation_t operation, dispatch_data_t data); +static void _dispatch_stream_cleanup_operations(dispatch_stream_t stream, + dispatch_io_t channel); +static void _dispatch_disk_cleanup_operations(dispatch_disk_t disk, + dispatch_io_t channel); +static void _dispatch_stream_source_handler(void *ctx); +static void _dispatch_stream_handler(void *ctx); +static void _dispatch_disk_handler(void *ctx); +static void _dispatch_disk_perform(void *ctxt); +static void _dispatch_operation_advise(dispatch_operation_t op, + size_t chunk_size); +static int _dispatch_operation_perform(dispatch_operation_t op); +static void _dispatch_operation_deliver_data(dispatch_operation_t op, + dispatch_op_flags_t flags); + +// Macros to wrap syscalls which return -1 on error, and retry on EINTR +#define _dispatch_io_syscall_switch_noerr(_err, _syscall, ...) do { \ + switch (((_err) = (((_syscall) == -1) ? errno : 0))) { \ + case EINTR: continue; \ + __VA_ARGS__ \ + } \ + } while (0) +#define _dispatch_io_syscall_switch(__err, __syscall, ...) do { \ + _dispatch_io_syscall_switch_noerr(__err, __syscall, \ + case 0: break; \ + __VA_ARGS__ \ + ); \ + } while (0) +#define _dispatch_io_syscall(__syscall) do { int __err; \ + _dispatch_io_syscall_switch(__err, __syscall); \ + } while (0) + +enum { + DISPATCH_OP_COMPLETE = 1, + DISPATCH_OP_DELIVER, + DISPATCH_OP_DELIVER_AND_COMPLETE, + DISPATCH_OP_COMPLETE_RESUME, + DISPATCH_OP_RESUME, + DISPATCH_OP_ERR, + DISPATCH_OP_FD_ERR, +}; + +#pragma mark - +#pragma mark dispatch_io_vtable + +static const struct dispatch_io_vtable_s _dispatch_io_vtable = { + .do_type = DISPATCH_IO_TYPE, + .do_kind = "channel", + .do_dispose = _dispatch_io_dispose, + .do_invoke = NULL, + .do_probe = (void *)dummy_function_r0, + .do_debug = (void *)dummy_function_r0, +}; + +static const struct dispatch_operation_vtable_s _dispatch_operation_vtable = { + .do_type = DISPATCH_OPERATION_TYPE, + .do_kind = "operation", + .do_dispose = _dispatch_operation_dispose, + .do_invoke = NULL, + .do_probe = (void *)dummy_function_r0, + .do_debug = (void *)dummy_function_r0, +}; + +static const struct dispatch_disk_vtable_s _dispatch_disk_vtable = { + .do_type = DISPATCH_DISK_TYPE, + .do_kind = "disk", + .do_dispose = _dispatch_disk_dispose, + .do_invoke = NULL, + .do_probe = (void *)dummy_function_r0, + .do_debug = (void *)dummy_function_r0, +}; + +#pragma mark - +#pragma mark dispatch_io_hashtables + +#if TARGET_OS_EMBEDDED +#define DIO_HASH_SIZE 64u // must be a power of two +#else +#define DIO_HASH_SIZE 256u // must be a power of two +#endif +#define DIO_HASH(x) ((uintptr_t)((x) & (DIO_HASH_SIZE - 1))) + +// Global hashtable of dev_t -> disk_s mappings +DISPATCH_CACHELINE_ALIGN +static TAILQ_HEAD(, dispatch_disk_s) _dispatch_io_devs[DIO_HASH_SIZE]; +// Global hashtable of fd -> fd_entry_s mappings +DISPATCH_CACHELINE_ALIGN +static TAILQ_HEAD(, dispatch_fd_entry_s) _dispatch_io_fds[DIO_HASH_SIZE]; + +static dispatch_once_t _dispatch_io_devs_lockq_pred; +static dispatch_queue_t _dispatch_io_devs_lockq; +static dispatch_queue_t _dispatch_io_fds_lockq; + +static void +_dispatch_io_fds_lockq_init(void *context DISPATCH_UNUSED) +{ + _dispatch_io_fds_lockq = dispatch_queue_create( + "com.apple.libdispatch-io.fd_lockq", NULL); + unsigned int i; + for (i = 0; i < DIO_HASH_SIZE; i++) { + TAILQ_INIT(&_dispatch_io_fds[i]); + } +} + +static void +_dispatch_io_devs_lockq_init(void *context DISPATCH_UNUSED) +{ + _dispatch_io_devs_lockq = dispatch_queue_create( + "com.apple.libdispatch-io.dev_lockq", NULL); + unsigned int i; + for (i = 0; i < DIO_HASH_SIZE; i++) { + TAILQ_INIT(&_dispatch_io_devs[i]); + } +} + +#pragma mark - +#pragma mark dispatch_io_defaults + +enum { + DISPATCH_IOCNTL_CHUNK_PAGES = 1, + DISPATCH_IOCNTL_LOW_WATER_CHUNKS, + DISPATCH_IOCNTL_INITIAL_DELIVERY, + DISPATCH_IOCNTL_MAX_PENDING_IO_REQS, +}; + +static struct dispatch_io_defaults_s { + size_t chunk_pages, low_water_chunks, max_pending_io_reqs; + bool initial_delivery; +} dispatch_io_defaults = { + .chunk_pages = DIO_MAX_CHUNK_PAGES, + .low_water_chunks = DIO_DEFAULT_LOW_WATER_CHUNKS, + .max_pending_io_reqs = DIO_MAX_PENDING_IO_REQS, +}; + +#define _dispatch_iocntl_set_default(p, v) do { \ + dispatch_io_defaults.p = (typeof(dispatch_io_defaults.p))(v); \ + } while (0) + +void +_dispatch_iocntl(uint32_t param, uint64_t value) +{ + switch (param) { + case DISPATCH_IOCNTL_CHUNK_PAGES: + _dispatch_iocntl_set_default(chunk_pages, value); + break; + case DISPATCH_IOCNTL_LOW_WATER_CHUNKS: + _dispatch_iocntl_set_default(low_water_chunks, value); + break; + case DISPATCH_IOCNTL_INITIAL_DELIVERY: + _dispatch_iocntl_set_default(initial_delivery, value); + case DISPATCH_IOCNTL_MAX_PENDING_IO_REQS: + _dispatch_iocntl_set_default(max_pending_io_reqs, value); + break; + } +} + +#pragma mark - +#pragma mark dispatch_io_t + +static dispatch_io_t +_dispatch_io_create(dispatch_io_type_t type) +{ + dispatch_io_t channel = calloc(1ul, sizeof(struct dispatch_io_s)); + channel->do_vtable = &_dispatch_io_vtable; + channel->do_next = DISPATCH_OBJECT_LISTLESS; + channel->do_ref_cnt = 1; + channel->do_xref_cnt = 1; + channel->do_targetq = _dispatch_get_root_queue(0, true); + channel->params.type = type; + channel->params.high = SIZE_MAX; + channel->params.low = dispatch_io_defaults.low_water_chunks * + dispatch_io_defaults.chunk_pages * PAGE_SIZE; + channel->queue = dispatch_queue_create("com.apple.libdispatch-io.channelq", + NULL); + return channel; +} + +static void +_dispatch_io_init(dispatch_io_t channel, dispatch_fd_entry_t fd_entry, + dispatch_queue_t queue, int err, void (^cleanup_handler)(int)) +{ + // Enqueue the cleanup handler on the suspended close queue + if (cleanup_handler) { + _dispatch_retain(queue); + dispatch_async(!err ? fd_entry->close_queue : channel->queue, ^{ + dispatch_async(queue, ^{ + _dispatch_io_debug("cleanup handler invoke", -1); + cleanup_handler(err); + }); + _dispatch_release(queue); + }); + } + if (fd_entry) { + channel->fd_entry = fd_entry; + dispatch_retain(fd_entry->barrier_queue); + dispatch_retain(fd_entry->barrier_group); + channel->barrier_queue = fd_entry->barrier_queue; + channel->barrier_group = fd_entry->barrier_group; + } else { + // Still need to create a barrier queue, since all operations go + // through it + channel->barrier_queue = dispatch_queue_create( + "com.apple.libdispatch-io.barrierq", NULL); + channel->barrier_group = dispatch_group_create(); + } +} + +static void +_dispatch_io_dispose(dispatch_io_t channel) +{ + if (channel->fd_entry) { + if (channel->fd_entry->path_data) { + // This modification is safe since path_data->channel is checked + // only on close_queue (which is still suspended at this point) + channel->fd_entry->path_data->channel = NULL; + } + // Cleanup handlers will only run when all channels related to this + // fd are complete + _dispatch_fd_entry_release(channel->fd_entry); + } + if (channel->queue) { + dispatch_release(channel->queue); + } + if (channel->barrier_queue) { + dispatch_release(channel->barrier_queue); + } + if (channel->barrier_group) { + dispatch_release(channel->barrier_group); + } + _dispatch_dispose(channel); +} + +static int +_dispatch_io_validate_type(dispatch_io_t channel, mode_t mode) +{ + int err = 0; + if (S_ISDIR(mode)) { + err = EISDIR; + } else if (channel->params.type == DISPATCH_IO_RANDOM && + (S_ISFIFO(mode) || S_ISSOCK(mode))) { + err = ESPIPE; + } + return err; +} + +static int +_dispatch_io_get_error(dispatch_operation_t op, dispatch_io_t channel, + bool ignore_closed) +{ + // On _any_ queue + int err; + if (op) { + channel = op->channel; + } + if (channel->atomic_flags & (DIO_CLOSED|DIO_STOPPED)) { + if (!ignore_closed || channel->atomic_flags & DIO_STOPPED) { + err = ECANCELED; + } else { + err = 0; + } + } else { + err = op ? op->fd_entry->err : channel->err; + } + return err; +} + +#pragma mark - +#pragma mark dispatch_io_channels + +dispatch_io_t +dispatch_io_create(dispatch_io_type_t type, dispatch_fd_t fd, + dispatch_queue_t queue, void (^cleanup_handler)(int)) +{ + if (type != DISPATCH_IO_STREAM && type != DISPATCH_IO_RANDOM) { + return NULL; + } + _dispatch_io_debug("io create", fd); + dispatch_io_t channel = _dispatch_io_create(type); + channel->fd = fd; + channel->fd_actual = fd; + dispatch_suspend(channel->queue); + _dispatch_retain(queue); + _dispatch_retain(channel); + _dispatch_fd_entry_init_async(fd, ^(dispatch_fd_entry_t fd_entry) { + // On barrier queue + int err = fd_entry->err; + if (!err) { + err = _dispatch_io_validate_type(channel, fd_entry->stat.mode); + } + if (!err && type == DISPATCH_IO_RANDOM) { + off_t f_ptr; + _dispatch_io_syscall_switch_noerr(err, + f_ptr = lseek(fd_entry->fd, 0, SEEK_CUR), + case 0: channel->f_ptr = f_ptr; break; + default: (void)dispatch_assume_zero(err); break; + ); + } + channel->err = err; + _dispatch_fd_entry_retain(fd_entry); + _dispatch_io_init(channel, fd_entry, queue, err, cleanup_handler); + dispatch_resume(channel->queue); + _dispatch_release(channel); + _dispatch_release(queue); + }); + return channel; +} + +dispatch_io_t +dispatch_io_create_with_path(dispatch_io_type_t type, const char *path, + int oflag, mode_t mode, dispatch_queue_t queue, + void (^cleanup_handler)(int error)) +{ + if ((type != DISPATCH_IO_STREAM && type != DISPATCH_IO_RANDOM) || + !(path && *path == '/')) { + return NULL; + } + size_t pathlen = strlen(path); + dispatch_io_path_data_t path_data = malloc(sizeof(*path_data) + pathlen+1); + if (!path_data) { + return NULL; + } + _dispatch_io_debug("io create with path %s", -1, path); + dispatch_io_t channel = _dispatch_io_create(type); + channel->fd = -1; + channel->fd_actual = -1; + path_data->channel = channel; + path_data->oflag = oflag; + path_data->mode = mode; + path_data->pathlen = pathlen; + memcpy(path_data->path, path, pathlen + 1); + _dispatch_retain(queue); + _dispatch_retain(channel); + dispatch_async(channel->queue, ^{ + int err = 0; + struct stat st; + _dispatch_io_syscall_switch_noerr(err, + (path_data->oflag & O_NOFOLLOW) == O_NOFOLLOW || + (path_data->oflag & O_SYMLINK) == O_SYMLINK ? + lstat(path_data->path, &st) : stat(path_data->path, &st), + case 0: + err = _dispatch_io_validate_type(channel, st.st_mode); + break; + default: + if ((path_data->oflag & O_CREAT) && + (*(path_data->path + path_data->pathlen - 1) != '/')) { + // Check parent directory + char *c = strrchr(path_data->path, '/'); + dispatch_assert(c); + *c = 0; + int perr; + _dispatch_io_syscall_switch_noerr(perr, + stat(path_data->path, &st), + case 0: + // Since the parent directory exists, open() will + // create a regular file after the fd_entry has + // been filled in + st.st_mode = S_IFREG; + err = 0; + break; + ); + *c = '/'; + } + break; + ); + channel->err = err; + if (err) { + free(path_data); + _dispatch_io_init(channel, NULL, queue, err, cleanup_handler); + _dispatch_release(channel); + _dispatch_release(queue); + return; + } + dispatch_suspend(channel->queue); + dispatch_once_f(&_dispatch_io_devs_lockq_pred, NULL, + _dispatch_io_devs_lockq_init); + dispatch_async(_dispatch_io_devs_lockq, ^{ + dispatch_fd_entry_t fd_entry = _dispatch_fd_entry_create_with_path( + path_data, st.st_dev, st.st_mode); + _dispatch_io_init(channel, fd_entry, queue, 0, cleanup_handler); + dispatch_resume(channel->queue); + _dispatch_release(channel); + _dispatch_release(queue); + }); + }); + return channel; +} + +dispatch_io_t +dispatch_io_create_with_io(dispatch_io_type_t type, dispatch_io_t in_channel, + dispatch_queue_t queue, void (^cleanup_handler)(int error)) +{ + if (type != DISPATCH_IO_STREAM && type != DISPATCH_IO_RANDOM) { + return NULL; + } + _dispatch_io_debug("io create with io %p", -1, in_channel); + dispatch_io_t channel = _dispatch_io_create(type); + dispatch_suspend(channel->queue); + _dispatch_retain(queue); + _dispatch_retain(channel); + _dispatch_retain(in_channel); + dispatch_async(in_channel->queue, ^{ + int err0 = _dispatch_io_get_error(NULL, in_channel, false); + if (err0) { + channel->err = err0; + _dispatch_io_init(channel, NULL, queue, err0, cleanup_handler); + dispatch_resume(channel->queue); + _dispatch_release(channel); + _dispatch_release(in_channel); + _dispatch_release(queue); + return; + } + dispatch_async(in_channel->barrier_queue, ^{ + int err = _dispatch_io_get_error(NULL, in_channel, false); + // If there is no error, the fd_entry for the in_channel is valid. + // Since we are running on in_channel's queue, the fd_entry has been + // fully resolved and will stay valid for the duration of this block + if (!err) { + err = in_channel->err; + if (!err) { + err = in_channel->fd_entry->err; + } + } + if (!err) { + err = _dispatch_io_validate_type(channel, + in_channel->fd_entry->stat.mode); + } + if (!err && type == DISPATCH_IO_RANDOM && in_channel->fd != -1) { + off_t f_ptr; + _dispatch_io_syscall_switch_noerr(err, + f_ptr = lseek(in_channel->fd_entry->fd, 0, SEEK_CUR), + case 0: channel->f_ptr = f_ptr; break; + default: (void)dispatch_assume_zero(err); break; + ); + } + channel->err = err; + if (err) { + _dispatch_io_init(channel, NULL, queue, err, cleanup_handler); + dispatch_resume(channel->queue); + _dispatch_release(channel); + _dispatch_release(in_channel); + _dispatch_release(queue); + return; + } + if (in_channel->fd == -1) { + // in_channel was created from path + channel->fd = -1; + channel->fd_actual = -1; + mode_t mode = in_channel->fd_entry->stat.mode; + dev_t dev = in_channel->fd_entry->stat.dev; + size_t path_data_len = sizeof(struct dispatch_io_path_data_s) + + in_channel->fd_entry->path_data->pathlen + 1; + dispatch_io_path_data_t path_data = malloc(path_data_len); + memcpy(path_data, in_channel->fd_entry->path_data, + path_data_len); + path_data->channel = channel; + // lockq_io_devs is known to already exist + dispatch_async(_dispatch_io_devs_lockq, ^{ + dispatch_fd_entry_t fd_entry; + fd_entry = _dispatch_fd_entry_create_with_path(path_data, + dev, mode); + _dispatch_io_init(channel, fd_entry, queue, 0, + cleanup_handler); + dispatch_resume(channel->queue); + _dispatch_release(channel); + _dispatch_release(queue); + }); + } else { + dispatch_fd_entry_t fd_entry = in_channel->fd_entry; + channel->fd = in_channel->fd; + channel->fd_actual = in_channel->fd_actual; + _dispatch_fd_entry_retain(fd_entry); + _dispatch_io_init(channel, fd_entry, queue, 0, cleanup_handler); + dispatch_resume(channel->queue); + _dispatch_release(channel); + _dispatch_release(queue); + } + _dispatch_release(in_channel); + }); + }); + return channel; +} + +#pragma mark - +#pragma mark dispatch_io_accessors + +void +dispatch_io_set_high_water(dispatch_io_t channel, size_t high_water) +{ + _dispatch_retain(channel); + dispatch_async(channel->queue, ^{ + _dispatch_io_debug("io set high water", channel->fd); + if (channel->params.low > high_water) { + channel->params.low = high_water; + } + channel->params.high = high_water ? high_water : 1; + _dispatch_release(channel); + }); +} + +void +dispatch_io_set_low_water(dispatch_io_t channel, size_t low_water) +{ + _dispatch_retain(channel); + dispatch_async(channel->queue, ^{ + _dispatch_io_debug("io set low water", channel->fd); + if (channel->params.high < low_water) { + channel->params.high = low_water ? low_water : 1; + } + channel->params.low = low_water; + _dispatch_release(channel); + }); +} + +void +dispatch_io_set_interval(dispatch_io_t channel, uint64_t interval, + unsigned long flags) +{ + _dispatch_retain(channel); + dispatch_async(channel->queue, ^{ + _dispatch_io_debug("io set interval", channel->fd); + channel->params.interval = interval; + channel->params.interval_flags = flags; + _dispatch_release(channel); + }); +} + +void +_dispatch_io_set_target_queue(dispatch_io_t channel, dispatch_queue_t dq) +{ + _dispatch_retain(dq); + _dispatch_retain(channel); + dispatch_async(channel->queue, ^{ + dispatch_queue_t prev_dq = channel->do_targetq; + channel->do_targetq = dq; + _dispatch_release(prev_dq); + _dispatch_release(channel); + }); +} + +dispatch_fd_t +dispatch_io_get_descriptor(dispatch_io_t channel) +{ + if (channel->atomic_flags & (DIO_CLOSED|DIO_STOPPED)) { + return -1; + } + dispatch_fd_t fd = channel->fd_actual; + if (fd == -1 && + _dispatch_thread_getspecific(dispatch_io_key) == channel) { + dispatch_fd_entry_t fd_entry = channel->fd_entry; + (void)_dispatch_fd_entry_open(fd_entry, channel); + } + return channel->fd_actual; +} + +#pragma mark - +#pragma mark dispatch_io_operations + +static void +_dispatch_io_stop(dispatch_io_t channel) +{ + _dispatch_io_debug("io stop", channel->fd); + (void)dispatch_atomic_or2o(channel, atomic_flags, DIO_STOPPED); + _dispatch_retain(channel); + dispatch_async(channel->queue, ^{ + dispatch_async(channel->barrier_queue, ^{ + dispatch_fd_entry_t fd_entry = channel->fd_entry; + if (fd_entry) { + _dispatch_io_debug("io stop cleanup", channel->fd); + _dispatch_fd_entry_cleanup_operations(fd_entry, channel); + channel->fd_entry = NULL; + _dispatch_fd_entry_release(fd_entry); + } else if (channel->fd != -1) { + // Stop after close, need to check if fd_entry still exists + _dispatch_retain(channel); + dispatch_async(_dispatch_io_fds_lockq, ^{ + _dispatch_io_debug("io stop after close cleanup", + channel->fd); + dispatch_fd_entry_t fdi; + uintptr_t hash = DIO_HASH(channel->fd); + TAILQ_FOREACH(fdi, &_dispatch_io_fds[hash], fd_list) { + if (fdi->fd == channel->fd) { + _dispatch_fd_entry_cleanup_operations(fdi, channel); + break; + } + } + _dispatch_release(channel); + }); + } + _dispatch_release(channel); + }); + }); +} + +void +dispatch_io_close(dispatch_io_t channel, unsigned long flags) +{ + if (flags & DISPATCH_IO_STOP) { + // Don't stop an already stopped channel + if (channel->atomic_flags & DIO_STOPPED) { + return; + } + return _dispatch_io_stop(channel); + } + // Don't close an already closed or stopped channel + if (channel->atomic_flags & (DIO_CLOSED|DIO_STOPPED)) { + return; + } + _dispatch_retain(channel); + dispatch_async(channel->queue, ^{ + dispatch_async(channel->barrier_queue, ^{ + _dispatch_io_debug("io close", channel->fd); + (void)dispatch_atomic_or2o(channel, atomic_flags, DIO_CLOSED); + dispatch_fd_entry_t fd_entry = channel->fd_entry; + if (fd_entry) { + if (!fd_entry->path_data) { + channel->fd_entry = NULL; + } + _dispatch_fd_entry_release(fd_entry); + } + _dispatch_release(channel); + }); + }); +} + +void +dispatch_io_barrier(dispatch_io_t channel, dispatch_block_t barrier) +{ + _dispatch_retain(channel); + dispatch_async(channel->queue, ^{ + dispatch_queue_t io_q = channel->do_targetq; + dispatch_queue_t barrier_queue = channel->barrier_queue; + dispatch_group_t barrier_group = channel->barrier_group; + dispatch_async(barrier_queue, ^{ + dispatch_suspend(barrier_queue); + dispatch_group_notify(barrier_group, io_q, ^{ + _dispatch_thread_setspecific(dispatch_io_key, channel); + barrier(); + _dispatch_thread_setspecific(dispatch_io_key, NULL); + dispatch_resume(barrier_queue); + _dispatch_release(channel); + }); + }); + }); +} + +void +dispatch_io_read(dispatch_io_t channel, off_t offset, size_t length, + dispatch_queue_t queue, dispatch_io_handler_t handler) +{ + _dispatch_retain(channel); + _dispatch_retain(queue); + dispatch_async(channel->queue, ^{ + dispatch_operation_t op; + op = _dispatch_operation_create(DOP_DIR_READ, channel, offset, + length, dispatch_data_empty, queue, handler); + if (op) { + dispatch_queue_t barrier_q = channel->barrier_queue; + dispatch_async(barrier_q, ^{ + _dispatch_operation_enqueue(op, DOP_DIR_READ, + dispatch_data_empty); + }); + } + _dispatch_release(channel); + _dispatch_release(queue); + }); +} + +void +dispatch_io_write(dispatch_io_t channel, off_t offset, dispatch_data_t data, + dispatch_queue_t queue, dispatch_io_handler_t handler) +{ + _dispatch_io_data_retain(data); + _dispatch_retain(channel); + _dispatch_retain(queue); + dispatch_async(channel->queue, ^{ + dispatch_operation_t op; + op = _dispatch_operation_create(DOP_DIR_WRITE, channel, offset, + dispatch_data_get_size(data), data, queue, handler); + if (op) { + dispatch_queue_t barrier_q = channel->barrier_queue; + dispatch_async(barrier_q, ^{ + _dispatch_operation_enqueue(op, DOP_DIR_WRITE, data); + _dispatch_io_data_release(data); + }); + } else { + _dispatch_io_data_release(data); + } + _dispatch_release(channel); + _dispatch_release(queue); + }); +} + +void +dispatch_read(dispatch_fd_t fd, size_t length, dispatch_queue_t queue, + void (^handler)(dispatch_data_t, int)) +{ + _dispatch_retain(queue); + _dispatch_fd_entry_init_async(fd, ^(dispatch_fd_entry_t fd_entry) { + // On barrier queue + if (fd_entry->err) { + int err = fd_entry->err; + dispatch_async(queue, ^{ + _dispatch_io_debug("convenience handler invoke", fd); + handler(dispatch_data_empty, err); + }); + _dispatch_release(queue); + return; + } + // Safe to access fd_entry on barrier queue + dispatch_io_t channel = fd_entry->convenience_channel; + if (!channel) { + channel = _dispatch_io_create(DISPATCH_IO_STREAM); + channel->fd = fd; + channel->fd_actual = fd; + channel->fd_entry = fd_entry; + dispatch_retain(fd_entry->barrier_queue); + dispatch_retain(fd_entry->barrier_group); + channel->barrier_queue = fd_entry->barrier_queue; + channel->barrier_group = fd_entry->barrier_group; + fd_entry->convenience_channel = channel; + } + __block dispatch_data_t deliver_data = dispatch_data_empty; + __block int err = 0; + dispatch_async(fd_entry->close_queue, ^{ + dispatch_async(queue, ^{ + _dispatch_io_debug("convenience handler invoke", fd); + handler(deliver_data, err); + _dispatch_io_data_release(deliver_data); + }); + _dispatch_release(queue); + }); + dispatch_operation_t op = + _dispatch_operation_create(DOP_DIR_READ, channel, 0, + length, dispatch_data_empty, + _dispatch_get_root_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, + false), ^(bool done, dispatch_data_t data, int error) { + if (data) { + data = dispatch_data_create_concat(deliver_data, data); + _dispatch_io_data_release(deliver_data); + deliver_data = data; + } + if (done) { + err = error; + } + }); + if (op) { + _dispatch_operation_enqueue(op, DOP_DIR_READ, dispatch_data_empty); + } + }); +} + +void +dispatch_write(dispatch_fd_t fd, dispatch_data_t data, dispatch_queue_t queue, + void (^handler)(dispatch_data_t, int)) +{ + _dispatch_io_data_retain(data); + _dispatch_retain(queue); + _dispatch_fd_entry_init_async(fd, ^(dispatch_fd_entry_t fd_entry) { + // On barrier queue + if (fd_entry->err) { + int err = fd_entry->err; + dispatch_async(queue, ^{ + _dispatch_io_debug("convenience handler invoke", fd); + handler(NULL, err); + }); + _dispatch_release(queue); + return; + } + // Safe to access fd_entry on barrier queue + dispatch_io_t channel = fd_entry->convenience_channel; + if (!channel) { + channel = _dispatch_io_create(DISPATCH_IO_STREAM); + channel->fd = fd; + channel->fd_actual = fd; + channel->fd_entry = fd_entry; + dispatch_retain(fd_entry->barrier_queue); + dispatch_retain(fd_entry->barrier_group); + channel->barrier_queue = fd_entry->barrier_queue; + channel->barrier_group = fd_entry->barrier_group; + fd_entry->convenience_channel = channel; + } + __block dispatch_data_t deliver_data = NULL; + __block int err = 0; + dispatch_async(fd_entry->close_queue, ^{ + dispatch_async(queue, ^{ + _dispatch_io_debug("convenience handler invoke", fd); + handler(deliver_data, err); + if (deliver_data) { + _dispatch_io_data_release(deliver_data); + } + }); + _dispatch_release(queue); + }); + dispatch_operation_t op = + _dispatch_operation_create(DOP_DIR_WRITE, channel, 0, + dispatch_data_get_size(data), data, + _dispatch_get_root_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, + false), ^(bool done, dispatch_data_t d, int error) { + if (done) { + if (d) { + _dispatch_io_data_retain(d); + deliver_data = d; + } + err = error; + } + }); + if (op) { + _dispatch_operation_enqueue(op, DOP_DIR_WRITE, data); + } + _dispatch_io_data_release(data); + }); +} + +#pragma mark - +#pragma mark dispatch_operation_t + +static dispatch_operation_t +_dispatch_operation_create(dispatch_op_direction_t direction, + dispatch_io_t channel, off_t offset, size_t length, + dispatch_data_t data, dispatch_queue_t queue, + dispatch_io_handler_t handler) +{ + // On channel queue + dispatch_assert(direction < DOP_DIR_MAX); + _dispatch_io_debug("operation create", channel->fd); +#if DISPATCH_IO_DEBUG + int fd = channel->fd; +#endif + // Safe to call _dispatch_io_get_error() with channel->fd_entry since + // that can only be NULL if atomic_flags are set rdar://problem/8362514 + int err = _dispatch_io_get_error(NULL, channel, false); + if (err || !length) { + _dispatch_io_data_retain(data); + _dispatch_retain(queue); + dispatch_async(channel->barrier_queue, ^{ + dispatch_async(queue, ^{ + dispatch_data_t d = data; + if (direction == DOP_DIR_READ && err) { + d = NULL; + } else if (direction == DOP_DIR_WRITE && !err) { + d = NULL; + } + _dispatch_io_debug("IO handler invoke", fd); + handler(true, d, err); + _dispatch_io_data_release(data); + }); + _dispatch_release(queue); + }); + return NULL; + } + dispatch_operation_t op; + op = calloc(1ul, sizeof(struct dispatch_operation_s)); + op->do_vtable = &_dispatch_operation_vtable; + op->do_next = DISPATCH_OBJECT_LISTLESS; + op->do_ref_cnt = 1; + op->do_xref_cnt = 0; // operation object is not exposed externally + op->op_q = dispatch_queue_create("com.apple.libdispatch-io.opq", NULL); + op->op_q->do_targetq = queue; + _dispatch_retain(queue); + op->active = false; + op->direction = direction; + op->offset = offset + channel->f_ptr; + op->length = length; + op->handler = Block_copy(handler); + _dispatch_retain(channel); + op->channel = channel; + op->params = channel->params; + // Take a snapshot of the priority of the channel queue. The actual I/O + // for this operation will be performed at this priority + dispatch_queue_t targetq = op->channel->do_targetq; + while (fastpath(targetq->do_targetq)) { + targetq = targetq->do_targetq; + } + op->do_targetq = targetq; + return op; +} + +static void +_dispatch_operation_dispose(dispatch_operation_t op) +{ + // Deliver the data if there's any + if (op->fd_entry) { + _dispatch_operation_deliver_data(op, DOP_DONE); + dispatch_group_leave(op->fd_entry->barrier_group); + _dispatch_fd_entry_release(op->fd_entry); + } + if (op->channel) { + _dispatch_release(op->channel); + } + if (op->timer) { + dispatch_release(op->timer); + } + // For write operations, op->buf is owned by op->buf_data + if (op->buf && op->direction == DOP_DIR_READ) { + free(op->buf); + } + if (op->buf_data) { + _dispatch_io_data_release(op->buf_data); + } + if (op->data) { + _dispatch_io_data_release(op->data); + } + if (op->op_q) { + dispatch_release(op->op_q); + } + Block_release(op->handler); + _dispatch_dispose(op); +} + +static void +_dispatch_operation_enqueue(dispatch_operation_t op, + dispatch_op_direction_t direction, dispatch_data_t data) +{ + // Called from the barrier queue + _dispatch_io_data_retain(data); + // If channel is closed or stopped, then call the handler immediately + int err = _dispatch_io_get_error(NULL, op->channel, false); + if (err) { + dispatch_io_handler_t handler = op->handler; + dispatch_async(op->op_q, ^{ + dispatch_data_t d = data; + if (direction == DOP_DIR_READ && err) { + d = NULL; + } else if (direction == DOP_DIR_WRITE && !err) { + d = NULL; + } + handler(true, d, err); + _dispatch_io_data_release(data); + }); + _dispatch_release(op); + return; + } + // Finish operation init + op->fd_entry = op->channel->fd_entry; + _dispatch_fd_entry_retain(op->fd_entry); + dispatch_group_enter(op->fd_entry->barrier_group); + dispatch_disk_t disk = op->fd_entry->disk; + if (!disk) { + dispatch_stream_t stream = op->fd_entry->streams[direction]; + dispatch_async(stream->dq, ^{ + _dispatch_stream_enqueue_operation(stream, op, data); + _dispatch_io_data_release(data); + }); + } else { + dispatch_async(disk->pick_queue, ^{ + _dispatch_disk_enqueue_operation(disk, op, data); + _dispatch_io_data_release(data); + }); + } +} + +static bool +_dispatch_operation_should_enqueue(dispatch_operation_t op, + dispatch_queue_t tq, dispatch_data_t data) +{ + // On stream queue or disk queue + _dispatch_io_debug("enqueue operation", op->fd_entry->fd); + _dispatch_io_data_retain(data); + op->data = data; + int err = _dispatch_io_get_error(op, NULL, true); + if (err) { + op->err = err; + // Final release + _dispatch_release(op); + return false; + } + if (op->params.interval) { + dispatch_resume(_dispatch_operation_timer(tq, op)); + } + return true; +} + +static dispatch_source_t +_dispatch_operation_timer(dispatch_queue_t tq, dispatch_operation_t op) +{ + // On stream queue or pick queue + if (op->timer) { + return op->timer; + } + dispatch_source_t timer = dispatch_source_create( + DISPATCH_SOURCE_TYPE_TIMER, 0, 0, tq); + dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, + op->params.interval), op->params.interval, 0); + dispatch_source_set_event_handler(timer, ^{ + // On stream queue or pick queue + if (dispatch_source_testcancel(timer)) { + // Do nothing. The operation has already completed + return; + } + dispatch_op_flags_t flags = DOP_DEFAULT; + if (op->params.interval_flags & DISPATCH_IO_STRICT_INTERVAL) { + // Deliver even if there is less data than the low-water mark + flags |= DOP_DELIVER; + } + // If the operation is active, dont deliver data + if ((op->active) && (flags & DOP_DELIVER)) { + op->flags = flags; + } else { + _dispatch_operation_deliver_data(op, flags); + } + }); + op->timer = timer; + return op->timer; +} + +#pragma mark - +#pragma mark dispatch_fd_entry_t + +static inline void +_dispatch_fd_entry_retain(dispatch_fd_entry_t fd_entry) { + dispatch_suspend(fd_entry->close_queue); +} + +static inline void +_dispatch_fd_entry_release(dispatch_fd_entry_t fd_entry) { + dispatch_resume(fd_entry->close_queue); +} + +static void +_dispatch_fd_entry_init_async(dispatch_fd_t fd, + dispatch_fd_entry_init_callback_t completion_callback) +{ + static dispatch_once_t _dispatch_io_fds_lockq_pred; + dispatch_once_f(&_dispatch_io_fds_lockq_pred, NULL, + _dispatch_io_fds_lockq_init); + dispatch_async(_dispatch_io_fds_lockq, ^{ + _dispatch_io_debug("fd entry init", fd); + dispatch_fd_entry_t fd_entry = NULL; + // Check to see if there is an existing entry for the given fd + uintptr_t hash = DIO_HASH(fd); + TAILQ_FOREACH(fd_entry, &_dispatch_io_fds[hash], fd_list) { + if (fd_entry->fd == fd) { + // Retain the fd_entry to ensure it cannot go away until the + // stat() has completed + _dispatch_fd_entry_retain(fd_entry); + break; + } + } + if (!fd_entry) { + // If we did not find an existing entry, create one + fd_entry = _dispatch_fd_entry_create_with_fd(fd, hash); + } + dispatch_async(fd_entry->barrier_queue, ^{ + _dispatch_io_debug("fd entry init completion", fd); + completion_callback(fd_entry); + // stat() is complete, release reference to fd_entry + _dispatch_fd_entry_release(fd_entry); + }); + }); +} + +static dispatch_fd_entry_t +_dispatch_fd_entry_create(dispatch_queue_t q) +{ + dispatch_fd_entry_t fd_entry; + fd_entry = calloc(1ul, sizeof(struct dispatch_fd_entry_s)); + fd_entry->close_queue = dispatch_queue_create( + "com.apple.libdispatch-io.closeq", NULL); + // Use target queue to ensure that no concurrent lookups are going on when + // the close queue is running + fd_entry->close_queue->do_targetq = q; + _dispatch_retain(q); + // Suspend the cleanup queue until closing + _dispatch_fd_entry_retain(fd_entry); + return fd_entry; +} + +static dispatch_fd_entry_t +_dispatch_fd_entry_create_with_fd(dispatch_fd_t fd, uintptr_t hash) +{ + // On fds lock queue + _dispatch_io_debug("fd entry create", fd); + dispatch_fd_entry_t fd_entry = _dispatch_fd_entry_create( + _dispatch_io_fds_lockq); + fd_entry->fd = fd; + TAILQ_INSERT_TAIL(&_dispatch_io_fds[hash], fd_entry, fd_list); + fd_entry->barrier_queue = dispatch_queue_create( + "com.apple.libdispatch-io.barrierq", NULL); + fd_entry->barrier_group = dispatch_group_create(); + dispatch_async(fd_entry->barrier_queue, ^{ + _dispatch_io_debug("fd entry stat", fd); + int err, orig_flags, orig_nosigpipe = -1; + struct stat st; + _dispatch_io_syscall_switch(err, + fstat(fd, &st), + default: fd_entry->err = err; return; + ); + fd_entry->stat.dev = st.st_dev; + fd_entry->stat.mode = st.st_mode; + _dispatch_io_syscall_switch(err, + orig_flags = fcntl(fd, F_GETFL), + default: (void)dispatch_assume_zero(err); break; + ); +#if DISPATCH_USE_SETNOSIGPIPE // rdar://problem/4121123 + if (S_ISFIFO(st.st_mode)) { + _dispatch_io_syscall_switch(err, + orig_nosigpipe = fcntl(fd, F_GETNOSIGPIPE), + default: (void)dispatch_assume_zero(err); break; + ); + if (orig_nosigpipe != -1) { + _dispatch_io_syscall_switch(err, + orig_nosigpipe = fcntl(fd, F_SETNOSIGPIPE, 1), + default: + orig_nosigpipe = -1; + (void)dispatch_assume_zero(err); + break; + ); + } + } +#endif + if (S_ISREG(st.st_mode)) { + if (orig_flags != -1) { + _dispatch_io_syscall_switch(err, + fcntl(fd, F_SETFL, orig_flags & ~O_NONBLOCK), + default: + orig_flags = -1; + (void)dispatch_assume_zero(err); + break; + ); + } + int32_t dev = major(st.st_dev); + // We have to get the disk on the global dev queue. The + // barrier queue cannot continue until that is complete + dispatch_suspend(fd_entry->barrier_queue); + dispatch_once_f(&_dispatch_io_devs_lockq_pred, NULL, + _dispatch_io_devs_lockq_init); + dispatch_async(_dispatch_io_devs_lockq, ^{ + _dispatch_disk_init(fd_entry, dev); + dispatch_resume(fd_entry->barrier_queue); + }); + } else { + if (orig_flags != -1) { + _dispatch_io_syscall_switch(err, + fcntl(fd, F_SETFL, orig_flags | O_NONBLOCK), + default: + orig_flags = -1; + (void)dispatch_assume_zero(err); + break; + ); + } + _dispatch_stream_init(fd_entry, _dispatch_get_root_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, false)); + } + fd_entry->orig_flags = orig_flags; + fd_entry->orig_nosigpipe = orig_nosigpipe; + }); + // This is the first item run when the close queue is resumed, indicating + // that all channels associated with this entry have been closed and that + // all operations associated with this entry have been freed + dispatch_async(fd_entry->close_queue, ^{ + if (!fd_entry->disk) { + _dispatch_io_debug("close queue fd_entry cleanup", fd); + dispatch_op_direction_t dir; + for (dir = 0; dir < DOP_DIR_MAX; dir++) { + _dispatch_stream_dispose(fd_entry, dir); + } + } else { + dispatch_disk_t disk = fd_entry->disk; + dispatch_async(_dispatch_io_devs_lockq, ^{ + _dispatch_release(disk); + }); + } + // Remove this entry from the global fd list + TAILQ_REMOVE(&_dispatch_io_fds[hash], fd_entry, fd_list); + }); + // If there was a source associated with this stream, disposing of the + // source cancels it and suspends the close queue. Freeing the fd_entry + // structure must happen after the source cancel handler has finished + dispatch_async(fd_entry->close_queue, ^{ + _dispatch_io_debug("close queue release", fd); + dispatch_release(fd_entry->close_queue); + _dispatch_io_debug("barrier queue release", fd); + dispatch_release(fd_entry->barrier_queue); + _dispatch_io_debug("barrier group release", fd); + dispatch_release(fd_entry->barrier_group); + if (fd_entry->orig_flags != -1) { + _dispatch_io_syscall( + fcntl(fd, F_SETFL, fd_entry->orig_flags) + ); + } +#if DISPATCH_USE_SETNOSIGPIPE // rdar://problem/4121123 + if (fd_entry->orig_nosigpipe != -1) { + _dispatch_io_syscall( + fcntl(fd, F_SETNOSIGPIPE, fd_entry->orig_nosigpipe) + ); + } +#endif + if (fd_entry->convenience_channel) { + fd_entry->convenience_channel->fd_entry = NULL; + dispatch_release(fd_entry->convenience_channel); + } + free(fd_entry); + }); + return fd_entry; +} + +static dispatch_fd_entry_t +_dispatch_fd_entry_create_with_path(dispatch_io_path_data_t path_data, + dev_t dev, mode_t mode) +{ + // On devs lock queue + _dispatch_io_debug("fd entry create with path %s", -1, path_data->path); + dispatch_fd_entry_t fd_entry = _dispatch_fd_entry_create( + path_data->channel->queue); + if (S_ISREG(mode)) { + _dispatch_disk_init(fd_entry, major(dev)); + } else { + _dispatch_stream_init(fd_entry, _dispatch_get_root_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, false)); + } + fd_entry->fd = -1; + fd_entry->orig_flags = -1; + fd_entry->path_data = path_data; + fd_entry->stat.dev = dev; + fd_entry->stat.mode = mode; + fd_entry->barrier_queue = dispatch_queue_create( + "com.apple.libdispatch-io.barrierq", NULL); + fd_entry->barrier_group = dispatch_group_create(); + // This is the first item run when the close queue is resumed, indicating + // that the channel associated with this entry has been closed and that + // all operations associated with this entry have been freed + dispatch_async(fd_entry->close_queue, ^{ + _dispatch_io_debug("close queue fd_entry cleanup", -1); + if (!fd_entry->disk) { + dispatch_op_direction_t dir; + for (dir = 0; dir < DOP_DIR_MAX; dir++) { + _dispatch_stream_dispose(fd_entry, dir); + } + } + if (fd_entry->fd != -1) { + close(fd_entry->fd); + } + if (fd_entry->path_data->channel) { + // If associated channel has not been released yet, mark it as + // no longer having an fd_entry (for stop after close). + // It is safe to modify channel since we are on close_queue with + // target queue the channel queue + fd_entry->path_data->channel->fd_entry = NULL; + } + }); + dispatch_async(fd_entry->close_queue, ^{ + _dispatch_io_debug("close queue release", -1); + dispatch_release(fd_entry->close_queue); + dispatch_release(fd_entry->barrier_queue); + dispatch_release(fd_entry->barrier_group); + free(fd_entry->path_data); + free(fd_entry); + }); + return fd_entry; +} + +static int +_dispatch_fd_entry_open(dispatch_fd_entry_t fd_entry, dispatch_io_t channel) +{ + if (!(fd_entry->fd == -1 && fd_entry->path_data)) { + return 0; + } + if (fd_entry->err) { + return fd_entry->err; + } + int fd = -1; + int oflag = fd_entry->disk ? fd_entry->path_data->oflag & ~O_NONBLOCK : + fd_entry->path_data->oflag | O_NONBLOCK; +open: + fd = open(fd_entry->path_data->path, oflag, fd_entry->path_data->mode); + if (fd == -1) { + int err = errno; + if (err == EINTR) { + goto open; + } + (void)dispatch_atomic_cmpxchg2o(fd_entry, err, 0, err); + return err; + } + if (!dispatch_atomic_cmpxchg2o(fd_entry, fd, -1, fd)) { + // Lost the race with another open + close(fd); + } else { + channel->fd_actual = fd; + } + return 0; +} + +static void +_dispatch_fd_entry_cleanup_operations(dispatch_fd_entry_t fd_entry, + dispatch_io_t channel) +{ + if (fd_entry->disk) { + if (channel) { + _dispatch_retain(channel); + } + _dispatch_fd_entry_retain(fd_entry); + dispatch_async(fd_entry->disk->pick_queue, ^{ + _dispatch_disk_cleanup_operations(fd_entry->disk, channel); + _dispatch_fd_entry_release(fd_entry); + if (channel) { + _dispatch_release(channel); + } + }); + } else { + dispatch_op_direction_t direction; + for (direction = 0; direction < DOP_DIR_MAX; direction++) { + dispatch_stream_t stream = fd_entry->streams[direction]; + if (!stream) { + continue; + } + if (channel) { + _dispatch_retain(channel); + } + _dispatch_fd_entry_retain(fd_entry); + dispatch_async(stream->dq, ^{ + _dispatch_stream_cleanup_operations(stream, channel); + _dispatch_fd_entry_release(fd_entry); + if (channel) { + _dispatch_release(channel); + } + }); + } + } +} + +#pragma mark - +#pragma mark dispatch_stream_t/dispatch_disk_t + +static void +_dispatch_stream_init(dispatch_fd_entry_t fd_entry, dispatch_queue_t tq) +{ + dispatch_op_direction_t direction; + for (direction = 0; direction < DOP_DIR_MAX; direction++) { + dispatch_stream_t stream; + stream = calloc(1ul, sizeof(struct dispatch_stream_s)); + stream->dq = dispatch_queue_create("com.apple.libdispatch-io.streamq", + NULL); + _dispatch_retain(tq); + stream->dq->do_targetq = tq; + TAILQ_INIT(&stream->operations[DISPATCH_IO_RANDOM]); + TAILQ_INIT(&stream->operations[DISPATCH_IO_STREAM]); + fd_entry->streams[direction] = stream; + } +} + +static void +_dispatch_stream_dispose(dispatch_fd_entry_t fd_entry, + dispatch_op_direction_t direction) +{ + // On close queue + dispatch_stream_t stream = fd_entry->streams[direction]; + if (!stream) { + return; + } + dispatch_assert(TAILQ_EMPTY(&stream->operations[DISPATCH_IO_STREAM])); + dispatch_assert(TAILQ_EMPTY(&stream->operations[DISPATCH_IO_RANDOM])); + if (stream->source) { + // Balanced by source cancel handler: + _dispatch_fd_entry_retain(fd_entry); + dispatch_source_cancel(stream->source); + dispatch_resume(stream->source); + dispatch_release(stream->source); + } + dispatch_release(stream->dq); + free(stream); +} + +static void +_dispatch_disk_init(dispatch_fd_entry_t fd_entry, dev_t dev) +{ + // On devs lock queue + dispatch_disk_t disk; + char label_name[256]; + // Check to see if there is an existing entry for the given device + uintptr_t hash = DIO_HASH(dev); + TAILQ_FOREACH(disk, &_dispatch_io_devs[hash], disk_list) { + if (disk->dev == dev) { + _dispatch_retain(disk); + goto out; + } + } + // Otherwise create a new entry + size_t pending_reqs_depth = dispatch_io_defaults.max_pending_io_reqs; + disk = calloc(1ul, sizeof(struct dispatch_disk_s) + (pending_reqs_depth * + sizeof(dispatch_operation_t))); + disk->do_vtable = &_dispatch_disk_vtable; + disk->do_next = DISPATCH_OBJECT_LISTLESS; + disk->do_ref_cnt = 1; + disk->do_xref_cnt = 0; + disk->advise_list_depth = pending_reqs_depth; + disk->do_targetq = _dispatch_get_root_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, + false); + disk->dev = dev; + TAILQ_INIT(&disk->operations); + disk->cur_rq = TAILQ_FIRST(&disk->operations); + sprintf(label_name, "com.apple.libdispatch-io.deviceq.%d", dev); + disk->pick_queue = dispatch_queue_create(label_name, NULL); + TAILQ_INSERT_TAIL(&_dispatch_io_devs[hash], disk, disk_list); +out: + fd_entry->disk = disk; + TAILQ_INIT(&fd_entry->stream_ops); +} + +static void +_dispatch_disk_dispose(dispatch_disk_t disk) +{ + uintptr_t hash = DIO_HASH(disk->dev); + TAILQ_REMOVE(&_dispatch_io_devs[hash], disk, disk_list); + dispatch_assert(TAILQ_EMPTY(&disk->operations)); + size_t i; + for (i=0; iadvise_list_depth; ++i) { + dispatch_assert(!disk->advise_list[i]); + } + dispatch_release(disk->pick_queue); + free(disk); +} + +#pragma mark - +#pragma mark dispatch_stream_operations/dispatch_disk_operations + +static inline bool +_dispatch_stream_operation_avail(dispatch_stream_t stream) +{ + return !(TAILQ_EMPTY(&stream->operations[DISPATCH_IO_RANDOM])) || + !(TAILQ_EMPTY(&stream->operations[DISPATCH_IO_STREAM])); +} + +static void +_dispatch_stream_enqueue_operation(dispatch_stream_t stream, + dispatch_operation_t op, dispatch_data_t data) +{ + if (!_dispatch_operation_should_enqueue(op, stream->dq, data)) { + return; + } + bool no_ops = !_dispatch_stream_operation_avail(stream); + TAILQ_INSERT_TAIL(&stream->operations[op->params.type], op, operation_list); + if (no_ops) { + dispatch_async_f(stream->dq, stream, _dispatch_stream_handler); + } +} + +static void +_dispatch_disk_enqueue_operation(dispatch_disk_t disk, dispatch_operation_t op, + dispatch_data_t data) +{ + if (!_dispatch_operation_should_enqueue(op, disk->pick_queue, data)) { + return; + } + if (op->params.type == DISPATCH_IO_STREAM) { + if (TAILQ_EMPTY(&op->fd_entry->stream_ops)) { + TAILQ_INSERT_TAIL(&disk->operations, op, operation_list); + } + TAILQ_INSERT_TAIL(&op->fd_entry->stream_ops, op, stream_list); + } else { + TAILQ_INSERT_TAIL(&disk->operations, op, operation_list); + } + _dispatch_disk_handler(disk); +} + +static void +_dispatch_stream_complete_operation(dispatch_stream_t stream, + dispatch_operation_t op) +{ + // On stream queue + _dispatch_io_debug("complete operation", op->fd_entry->fd); + TAILQ_REMOVE(&stream->operations[op->params.type], op, operation_list); + if (op == stream->op) { + stream->op = NULL; + } + if (op->timer) { + dispatch_source_cancel(op->timer); + } + // Final release will deliver any pending data + _dispatch_release(op); +} + +static void +_dispatch_disk_complete_operation(dispatch_disk_t disk, dispatch_operation_t op) +{ + // On pick queue + _dispatch_io_debug("complete operation", op->fd_entry->fd); + // Current request is always the last op returned + if (disk->cur_rq == op) { + disk->cur_rq = TAILQ_PREV(op, dispatch_disk_operations_s, + operation_list); + } + if (op->params.type == DISPATCH_IO_STREAM) { + // Check if there are other pending stream operations behind it + dispatch_operation_t op_next = TAILQ_NEXT(op, stream_list); + TAILQ_REMOVE(&op->fd_entry->stream_ops, op, stream_list); + if (op_next) { + TAILQ_INSERT_TAIL(&disk->operations, op_next, operation_list); + } + } + TAILQ_REMOVE(&disk->operations, op, operation_list); + if (op->timer) { + dispatch_source_cancel(op->timer); + } + // Final release will deliver any pending data + _dispatch_release(op); +} + +static dispatch_operation_t +_dispatch_stream_pick_next_operation(dispatch_stream_t stream, + dispatch_operation_t op) +{ + // On stream queue + if (!op) { + // On the first run through, pick the first operation + if (!_dispatch_stream_operation_avail(stream)) { + return op; + } + if (!TAILQ_EMPTY(&stream->operations[DISPATCH_IO_STREAM])) { + op = TAILQ_FIRST(&stream->operations[DISPATCH_IO_STREAM]); + } else if (!TAILQ_EMPTY(&stream->operations[DISPATCH_IO_RANDOM])) { + op = TAILQ_FIRST(&stream->operations[DISPATCH_IO_RANDOM]); + } + return op; + } + if (op->params.type == DISPATCH_IO_STREAM) { + // Stream operations need to be serialized so continue the current + // operation until it is finished + return op; + } + // Get the next random operation (round-robin) + if (op->params.type == DISPATCH_IO_RANDOM) { + op = TAILQ_NEXT(op, operation_list); + if (!op) { + op = TAILQ_FIRST(&stream->operations[DISPATCH_IO_RANDOM]); + } + return op; + } + return NULL; +} + +static dispatch_operation_t +_dispatch_disk_pick_next_operation(dispatch_disk_t disk) +{ + // On pick queue + dispatch_operation_t op; + if (!TAILQ_EMPTY(&disk->operations)) { + if (disk->cur_rq == NULL) { + op = TAILQ_FIRST(&disk->operations); + } else { + op = disk->cur_rq; + do { + op = TAILQ_NEXT(op, operation_list); + if (!op) { + op = TAILQ_FIRST(&disk->operations); + } + // TODO: more involved picking algorithm rdar://problem/8780312 + } while (op->active && op != disk->cur_rq); + } + if (!op->active) { + disk->cur_rq = op; + return op; + } + } + return NULL; +} + +static void +_dispatch_stream_cleanup_operations(dispatch_stream_t stream, + dispatch_io_t channel) +{ + // On stream queue + dispatch_operation_t op, tmp; + typeof(*stream->operations) *operations; + operations = &stream->operations[DISPATCH_IO_RANDOM]; + TAILQ_FOREACH_SAFE(op, operations, operation_list, tmp) { + if (!channel || op->channel == channel) { + _dispatch_stream_complete_operation(stream, op); + } + } + operations = &stream->operations[DISPATCH_IO_STREAM]; + TAILQ_FOREACH_SAFE(op, operations, operation_list, tmp) { + if (!channel || op->channel == channel) { + _dispatch_stream_complete_operation(stream, op); + } + } + if (stream->source_running && !_dispatch_stream_operation_avail(stream)) { + dispatch_suspend(stream->source); + stream->source_running = false; + } +} + +static void +_dispatch_disk_cleanup_operations(dispatch_disk_t disk, dispatch_io_t channel) +{ + // On pick queue + dispatch_operation_t op, tmp; + TAILQ_FOREACH_SAFE(op, &disk->operations, operation_list, tmp) { + if (!channel || op->channel == channel) { + _dispatch_disk_complete_operation(disk, op); + } + } +} + +#pragma mark - +#pragma mark dispatch_stream_handler/dispatch_disk_handler + +static dispatch_source_t +_dispatch_stream_source(dispatch_stream_t stream, dispatch_operation_t op) +{ + // On stream queue + if (stream->source) { + return stream->source; + } + dispatch_fd_t fd = op->fd_entry->fd; + _dispatch_io_debug("stream source create", fd); + dispatch_source_t source = NULL; + if (op->direction == DOP_DIR_READ) { + source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, + stream->dq); + } else if (op->direction == DOP_DIR_WRITE) { + source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, + stream->dq); + } else { + dispatch_assert(op->direction < DOP_DIR_MAX); + return NULL; + } + dispatch_set_context(source, stream); + dispatch_source_set_event_handler_f(source, + _dispatch_stream_source_handler); + // Close queue must not run user cleanup handlers until sources are fully + // unregistered + dispatch_queue_t close_queue = op->fd_entry->close_queue; + dispatch_source_set_cancel_handler(source, ^{ + _dispatch_io_debug("stream source cancel", fd); + dispatch_resume(close_queue); + }); + stream->source = source; + return stream->source; +} + +static void +_dispatch_stream_source_handler(void *ctx) +{ + // On stream queue + dispatch_stream_t stream = (dispatch_stream_t)ctx; + dispatch_suspend(stream->source); + stream->source_running = false; + return _dispatch_stream_handler(stream); +} + +static void +_dispatch_stream_handler(void *ctx) +{ + // On stream queue + dispatch_stream_t stream = (dispatch_stream_t)ctx; + dispatch_operation_t op; +pick: + op = _dispatch_stream_pick_next_operation(stream, stream->op); + if (!op) { + _dispatch_debug("no operation found: stream %p", stream); + return; + } + int err = _dispatch_io_get_error(op, NULL, true); + if (err) { + op->err = err; + _dispatch_stream_complete_operation(stream, op); + goto pick; + } + stream->op = op; + _dispatch_io_debug("stream handler", op->fd_entry->fd); + dispatch_fd_entry_t fd_entry = op->fd_entry; + _dispatch_fd_entry_retain(fd_entry); + // For performance analysis + if (!op->total && dispatch_io_defaults.initial_delivery) { + // Empty delivery to signal the start of the operation + _dispatch_io_debug("initial delivery", op->fd_entry->fd); + _dispatch_operation_deliver_data(op, DOP_DELIVER); + } + // TODO: perform on the operation target queue to get correct priority + int result = _dispatch_operation_perform(op), flags = -1; + switch (result) { + case DISPATCH_OP_DELIVER: + flags = DOP_DEFAULT; + // Fall through + case DISPATCH_OP_DELIVER_AND_COMPLETE: + flags = (flags != DOP_DEFAULT) ? DOP_DELIVER | DOP_NO_EMPTY : + DOP_DEFAULT; + _dispatch_operation_deliver_data(op, flags); + // Fall through + case DISPATCH_OP_COMPLETE: + if (flags != DOP_DEFAULT) { + _dispatch_stream_complete_operation(stream, op); + } + if (_dispatch_stream_operation_avail(stream)) { + dispatch_async_f(stream->dq, stream, _dispatch_stream_handler); + } + break; + case DISPATCH_OP_COMPLETE_RESUME: + _dispatch_stream_complete_operation(stream, op); + // Fall through + case DISPATCH_OP_RESUME: + if (_dispatch_stream_operation_avail(stream)) { + stream->source_running = true; + dispatch_resume(_dispatch_stream_source(stream, op)); + } + break; + case DISPATCH_OP_ERR: + _dispatch_stream_cleanup_operations(stream, op->channel); + break; + case DISPATCH_OP_FD_ERR: + _dispatch_fd_entry_retain(fd_entry); + dispatch_async(fd_entry->barrier_queue, ^{ + _dispatch_fd_entry_cleanup_operations(fd_entry, NULL); + _dispatch_fd_entry_release(fd_entry); + }); + break; + default: + break; + } + _dispatch_fd_entry_release(fd_entry); + return; +} + +static void +_dispatch_disk_handler(void *ctx) +{ + // On pick queue + dispatch_disk_t disk = (dispatch_disk_t)ctx; + if (disk->io_active) { + return; + } + _dispatch_io_debug("disk handler", -1); + dispatch_operation_t op; + size_t i = disk->free_idx, j = disk->req_idx; + if (j <= i) { + j += disk->advise_list_depth; + } + while (i <= j) { + if ((!disk->advise_list[i%disk->advise_list_depth]) && + (op = _dispatch_disk_pick_next_operation(disk))) { + int err = _dispatch_io_get_error(op, NULL, true); + if (err) { + op->err = err; + _dispatch_disk_complete_operation(disk, op); + continue; + } + _dispatch_retain(op); + disk->advise_list[i%disk->advise_list_depth] = op; + op->active = true; + } else { + // No more operations to get + break; + } + i++; + } + disk->free_idx = (i%disk->advise_list_depth); + op = disk->advise_list[disk->req_idx]; + if (op) { + disk->io_active = true; + dispatch_async_f(op->do_targetq, disk, _dispatch_disk_perform); + } +} + +static void +_dispatch_disk_perform(void *ctxt) +{ + dispatch_disk_t disk = ctxt; + size_t chunk_size = dispatch_io_defaults.chunk_pages * PAGE_SIZE; + _dispatch_io_debug("disk perform", -1); + dispatch_operation_t op; + size_t i = disk->advise_idx, j = disk->free_idx; + if (j <= i) { + j += disk->advise_list_depth; + } + do { + op = disk->advise_list[i%disk->advise_list_depth]; + if (!op) { + // Nothing more to advise, must be at free_idx + dispatch_assert(i%disk->advise_list_depth == disk->free_idx); + break; + } + if (op->direction == DOP_DIR_WRITE) { + // TODO: preallocate writes ? rdar://problem/9032172 + continue; + } + if (op->fd_entry->fd == -1 && _dispatch_fd_entry_open(op->fd_entry, + op->channel)) { + continue; + } + // For performance analysis + if (!op->total && dispatch_io_defaults.initial_delivery) { + // Empty delivery to signal the start of the operation + _dispatch_io_debug("initial delivery", op->fd_entry->fd); + _dispatch_operation_deliver_data(op, DOP_DELIVER); + } + // Advise two chunks if the list only has one element and this is the + // first advise on the operation + if ((j-i) == 1 && !disk->advise_list[disk->free_idx] && + !op->advise_offset) { + chunk_size *= 2; + } + _dispatch_operation_advise(op, chunk_size); + } while (++i < j); + disk->advise_idx = i%disk->advise_list_depth; + op = disk->advise_list[disk->req_idx]; + int result = _dispatch_operation_perform(op); + disk->advise_list[disk->req_idx] = NULL; + disk->req_idx = (++disk->req_idx)%disk->advise_list_depth; + dispatch_async(disk->pick_queue, ^{ + switch (result) { + case DISPATCH_OP_DELIVER: + _dispatch_operation_deliver_data(op, DOP_DELIVER); + break; + case DISPATCH_OP_COMPLETE: + _dispatch_disk_complete_operation(disk, op); + break; + case DISPATCH_OP_DELIVER_AND_COMPLETE: + _dispatch_operation_deliver_data(op, DOP_DELIVER); + _dispatch_disk_complete_operation(disk, op); + break; + case DISPATCH_OP_ERR: + _dispatch_disk_cleanup_operations(disk, op->channel); + break; + case DISPATCH_OP_FD_ERR: + _dispatch_disk_cleanup_operations(disk, NULL); + break; + default: + dispatch_assert(result); + break; + } + op->active = false; + disk->io_active = false; + _dispatch_disk_handler(disk); + // Balancing the retain in _dispatch_disk_handler. Note that op must be + // released at the very end, since it might hold the last reference to + // the disk + _dispatch_release(op); + }); +} + +#pragma mark - +#pragma mark dispatch_operation_perform + +static void +_dispatch_operation_advise(dispatch_operation_t op, size_t chunk_size) +{ + int err; + struct radvisory advise; + // No point in issuing a read advise for the next chunk if we are already + // a chunk ahead from reading the bytes + if (op->advise_offset > (off_t)((op->offset+op->total) + chunk_size + + PAGE_SIZE)) { + return; + } + advise.ra_count = (int)chunk_size; + if (!op->advise_offset) { + op->advise_offset = op->offset; + // If this is the first time through, align the advised range to a + // page boundary + size_t pg_fraction = (size_t)((op->offset + chunk_size) % PAGE_SIZE); + advise.ra_count += (int)(pg_fraction ? PAGE_SIZE - pg_fraction : 0); + } + advise.ra_offset = op->advise_offset; + op->advise_offset += advise.ra_count; + _dispatch_io_syscall_switch(err, + fcntl(op->fd_entry->fd, F_RDADVISE, &advise), + // TODO: set disk status on error + default: (void)dispatch_assume_zero(err); break; + ); +} + +static int +_dispatch_operation_perform(dispatch_operation_t op) +{ + int err = _dispatch_io_get_error(op, NULL, true); + if (err) { + goto error; + } + if (!op->buf) { + size_t max_buf_siz = op->params.high; + size_t chunk_siz = dispatch_io_defaults.chunk_pages * PAGE_SIZE; + if (op->direction == DOP_DIR_READ) { + // If necessary, create a buffer for the ongoing operation, large + // enough to fit chunk_pages but at most high-water + size_t data_siz = dispatch_data_get_size(op->data); + if (data_siz) { + dispatch_assert(data_siz < max_buf_siz); + max_buf_siz -= data_siz; + } + if (max_buf_siz > chunk_siz) { + max_buf_siz = chunk_siz; + } + if (op->length < SIZE_MAX) { + op->buf_siz = op->length - op->total; + if (op->buf_siz > max_buf_siz) { + op->buf_siz = max_buf_siz; + } + } else { + op->buf_siz = max_buf_siz; + } + op->buf = valloc(op->buf_siz); + _dispatch_io_debug("buffer allocated", op->fd_entry->fd); + } else if (op->direction == DOP_DIR_WRITE) { + // Always write the first data piece, if that is smaller than a + // chunk, accumulate further data pieces until chunk size is reached + if (chunk_siz > max_buf_siz) { + chunk_siz = max_buf_siz; + } + op->buf_siz = 0; + dispatch_data_apply(op->data, + ^(dispatch_data_t region DISPATCH_UNUSED, + size_t offset DISPATCH_UNUSED, + const void* buf DISPATCH_UNUSED, size_t len) { + size_t siz = op->buf_siz + len; + if (!op->buf_siz || siz <= chunk_siz) { + op->buf_siz = siz; + } + return (bool)(siz < chunk_siz); + }); + if (op->buf_siz > max_buf_siz) { + op->buf_siz = max_buf_siz; + } + dispatch_data_t d; + d = dispatch_data_create_subrange(op->data, 0, op->buf_siz); + op->buf_data = dispatch_data_create_map(d, (const void**)&op->buf, + NULL); + _dispatch_io_data_release(d); + _dispatch_io_debug("buffer mapped", op->fd_entry->fd); + } + } + if (op->fd_entry->fd == -1) { + err = _dispatch_fd_entry_open(op->fd_entry, op->channel); + if (err) { + goto error; + } + } + void *buf = op->buf + op->buf_len; + size_t len = op->buf_siz - op->buf_len; + off_t off = op->offset + op->total; + ssize_t processed = -1; +syscall: + if (op->direction == DOP_DIR_READ) { + if (op->params.type == DISPATCH_IO_STREAM) { + processed = read(op->fd_entry->fd, buf, len); + } else if (op->params.type == DISPATCH_IO_RANDOM) { + processed = pread(op->fd_entry->fd, buf, len, off); + } + } else if (op->direction == DOP_DIR_WRITE) { + if (op->params.type == DISPATCH_IO_STREAM) { + processed = write(op->fd_entry->fd, buf, len); + } else if (op->params.type == DISPATCH_IO_RANDOM) { + processed = pwrite(op->fd_entry->fd, buf, len, off); + } + } + // Encountered an error on the file descriptor + if (processed == -1) { + err = errno; + if (err == EINTR) { + goto syscall; + } + goto error; + } + // EOF is indicated by two handler invocations + if (processed == 0) { + _dispatch_io_debug("EOF", op->fd_entry->fd); + return DISPATCH_OP_DELIVER_AND_COMPLETE; + } + op->buf_len += processed; + op->total += processed; + if (op->total == op->length) { + // Finished processing all the bytes requested by the operation + return DISPATCH_OP_COMPLETE; + } else { + // Deliver data only if we satisfy the filters + return DISPATCH_OP_DELIVER; + } +error: + if (err == EAGAIN) { + // For disk based files with blocking I/O we should never get EAGAIN + dispatch_assert(!op->fd_entry->disk); + _dispatch_io_debug("EAGAIN %d", op->fd_entry->fd, err); + if (op->direction == DOP_DIR_READ && op->total && + op->channel == op->fd_entry->convenience_channel) { + // Convenience read with available data completes on EAGAIN + return DISPATCH_OP_COMPLETE_RESUME; + } + return DISPATCH_OP_RESUME; + } + op->err = err; + switch (err) { + case ECANCELED: + return DISPATCH_OP_ERR; + case EBADF: + (void)dispatch_atomic_cmpxchg2o(op->fd_entry, err, 0, err); + return DISPATCH_OP_FD_ERR; + default: + return DISPATCH_OP_COMPLETE; + } +} + +static void +_dispatch_operation_deliver_data(dispatch_operation_t op, + dispatch_op_flags_t flags) +{ + // Either called from stream resp. pick queue or when op is finalized + dispatch_data_t data = NULL; + int err = 0; + size_t undelivered = op->undelivered + op->buf_len; + bool deliver = (flags & (DOP_DELIVER|DOP_DONE)) || + (op->flags & DOP_DELIVER); + op->flags = DOP_DEFAULT; + if (!deliver) { + // Don't deliver data until low water mark has been reached + if (undelivered >= op->params.low) { + deliver = true; + } else if (op->buf_len < op->buf_siz) { + // Request buffer is not yet used up + _dispatch_io_debug("buffer data", op->fd_entry->fd); + return; + } + } else { + err = op->err; + if (!err && (op->channel->atomic_flags & DIO_STOPPED)) { + err = ECANCELED; + op->err = err; + } + } + // Deliver data or buffer used up + if (op->direction == DOP_DIR_READ) { + if (op->buf_len) { + void *buf = op->buf; + data = dispatch_data_create(buf, op->buf_len, NULL, + DISPATCH_DATA_DESTRUCTOR_FREE); + op->buf = NULL; + op->buf_len = 0; + dispatch_data_t d = dispatch_data_create_concat(op->data, data); + _dispatch_io_data_release(op->data); + _dispatch_io_data_release(data); + data = d; + } else { + data = op->data; + } + op->data = deliver ? dispatch_data_empty : data; + } else if (op->direction == DOP_DIR_WRITE) { + if (deliver) { + data = dispatch_data_create_subrange(op->data, op->buf_len, + op->length); + } + if (op->buf_len == op->buf_siz) { + _dispatch_io_data_release(op->buf_data); + op->buf_data = NULL; + op->buf = NULL; + op->buf_len = 0; + // Trim newly written buffer from head of unwritten data + dispatch_data_t d; + if (deliver) { + _dispatch_io_data_retain(data); + d = data; + } else { + d = dispatch_data_create_subrange(op->data, op->buf_len, + op->length); + } + _dispatch_io_data_release(op->data); + op->data = d; + } + } else { + dispatch_assert(op->direction < DOP_DIR_MAX); + return; + } + if (!deliver || ((flags & DOP_NO_EMPTY) && !dispatch_data_get_size(data))) { + op->undelivered = undelivered; + _dispatch_io_debug("buffer data", op->fd_entry->fd); + return; + } + op->undelivered = 0; + _dispatch_io_debug("deliver data", op->fd_entry->fd); + dispatch_op_direction_t direction = op->direction; + __block dispatch_data_t d = data; + dispatch_io_handler_t handler = op->handler; +#if DISPATCH_IO_DEBUG + int fd = op->fd_entry->fd; +#endif + dispatch_fd_entry_t fd_entry = op->fd_entry; + _dispatch_fd_entry_retain(fd_entry); + dispatch_io_t channel = op->channel; + _dispatch_retain(channel); + // Note that data delivery may occur after the operation is freed + dispatch_async(op->op_q, ^{ + bool done = (flags & DOP_DONE); + if (done) { + if (direction == DOP_DIR_READ && err) { + if (dispatch_data_get_size(d)) { + _dispatch_io_debug("IO handler invoke", fd); + handler(false, d, 0); + } + d = NULL; + } else if (direction == DOP_DIR_WRITE && !err) { + d = NULL; + } + } + _dispatch_io_debug("IO handler invoke", fd); + handler(done, d, err); + _dispatch_release(channel); + _dispatch_fd_entry_release(fd_entry); + _dispatch_io_data_release(data); + }); +} diff --git a/src/io_internal.h b/src/io_internal.h new file mode 100644 index 0000000..c43bd75 --- /dev/null +++ b/src/io_internal.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2009-2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_IO_INTERNAL__ +#define __DISPATCH_IO_INTERNAL__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +#define _DISPATCH_IO_LABEL_SIZE 16 + +#ifndef DISPATCH_IO_DEBUG +#define DISPATCH_IO_DEBUG 0 +#endif + +#if TARGET_OS_EMBEDDED // rdar://problem/9032036 +#define DIO_MAX_CHUNK_PAGES 128u // 512kB chunk size +#else +#define DIO_MAX_CHUNK_PAGES 256u // 1024kB chunk size +#endif + +#define DIO_DEFAULT_LOW_WATER_CHUNKS 1u // default low-water mark +#define DIO_MAX_PENDING_IO_REQS 6u // Pending I/O read advises + +typedef unsigned int dispatch_op_direction_t; +enum { + DOP_DIR_READ = 0, + DOP_DIR_WRITE, + DOP_DIR_MAX, + DOP_DIR_IGNORE = UINT_MAX, +}; + +typedef unsigned int dispatch_op_flags_t; +#define DOP_DEFAULT 0u // check conditions to determine delivery +#define DOP_DELIVER 1u // always deliver operation +#define DOP_DONE 2u // operation is done (implies deliver) +#define DOP_STOP 4u // operation interrupted by chan stop (implies done) +#define DOP_NO_EMPTY 8u // don't deliver empty data + +// dispatch_io_t atomic_flags +#define DIO_CLOSED 1u // channel has been closed +#define DIO_STOPPED 2u // channel has been stopped (implies closed) + +#define _dispatch_io_data_retain(x) dispatch_retain(x) +#define _dispatch_io_data_release(x) dispatch_release(x) + +#if DISPATCH_IO_DEBUG +#define _dispatch_io_debug(msg, fd, args...) \ + _dispatch_debug("fd %d: " msg, (fd), ##args) +#else +#define _dispatch_io_debug(msg, fd, args...) +#endif + +DISPATCH_DECL(dispatch_operation); +DISPATCH_DECL(dispatch_disk); + +struct dispatch_stream_s { + dispatch_queue_t dq; + dispatch_source_t source; + dispatch_operation_t op; + bool source_running; + TAILQ_HEAD(, dispatch_operation_s) operations[2]; +}; + +typedef struct dispatch_stream_s *dispatch_stream_t; + +struct dispatch_io_path_data_s { + dispatch_io_t channel; + int oflag; + mode_t mode; + size_t pathlen; + char path[]; +}; + +typedef struct dispatch_io_path_data_s *dispatch_io_path_data_t; + +struct dispatch_stat_s { + dev_t dev; + mode_t mode; +}; + +struct dispatch_disk_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_disk_s); +}; + +struct dispatch_disk_s { + DISPATCH_STRUCT_HEADER(dispatch_disk_s, dispatch_disk_vtable_s); + dev_t dev; + TAILQ_HEAD(dispatch_disk_operations_s, dispatch_operation_s) operations; + dispatch_operation_t cur_rq; + dispatch_queue_t pick_queue; + + size_t free_idx; + size_t req_idx; + size_t advise_idx; + bool io_active; + int err; + TAILQ_ENTRY(dispatch_disk_s) disk_list; + size_t advise_list_depth; + dispatch_operation_t advise_list[]; +}; + +struct dispatch_fd_entry_s { + dispatch_fd_t fd; + dispatch_io_path_data_t path_data; + int orig_flags, orig_nosigpipe, err; + struct dispatch_stat_s stat; + dispatch_stream_t streams[2]; + dispatch_disk_t disk; + dispatch_queue_t close_queue, barrier_queue; + dispatch_group_t barrier_group; + dispatch_io_t convenience_channel; + TAILQ_HEAD(, dispatch_operation_s) stream_ops; + TAILQ_ENTRY(dispatch_fd_entry_s) fd_list; +}; + +typedef struct dispatch_fd_entry_s *dispatch_fd_entry_t; + +typedef struct dispatch_io_param_s { + dispatch_io_type_t type; // STREAM OR RANDOM + size_t low; + size_t high; + uint64_t interval; + unsigned long interval_flags; +} dispatch_io_param_s; + +struct dispatch_operation_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_operation_s); +}; + +struct dispatch_operation_s { + DISPATCH_STRUCT_HEADER(dispatch_operation_s, dispatch_operation_vtable_s); + dispatch_queue_t op_q; + dispatch_op_direction_t direction; // READ OR WRITE + dispatch_io_param_s params; + off_t offset; + size_t length; + int err; + dispatch_io_handler_t handler; + dispatch_io_t channel; + dispatch_fd_entry_t fd_entry; + dispatch_source_t timer; + bool active; + int count; + off_t advise_offset; + void* buf; + dispatch_op_flags_t flags; + size_t buf_siz, buf_len, undelivered, total; + dispatch_data_t buf_data, data; + TAILQ_ENTRY(dispatch_operation_s) operation_list; + // the request list in the fd_entry stream_ops + TAILQ_ENTRY(dispatch_operation_s) stream_list; +}; + +struct dispatch_io_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_io_s); +}; + +struct dispatch_io_s { + DISPATCH_STRUCT_HEADER(dispatch_io_s, dispatch_io_vtable_s); + dispatch_queue_t queue, barrier_queue; + dispatch_group_t barrier_group; + dispatch_io_param_s params; + dispatch_fd_entry_t fd_entry; + unsigned int atomic_flags; + dispatch_fd_t fd, fd_actual; + off_t f_ptr; + int err; // contains creation errors only +}; + +void _dispatch_io_set_target_queue(dispatch_io_t channel, dispatch_queue_t dq); + +#endif // __DISPATCH_IO_INTERNAL__ diff --git a/src/legacy.c b/src/legacy.c deleted file mode 100644 index 6232990..0000000 --- a/src/legacy.c +++ /dev/null @@ -1,444 +0,0 @@ -/* - * 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 "internal.h" -#include "legacy.h" - -/* - * LEGACY: This header file describles LEGACY interfaces to libdispatch from an - * earlier revision of the API. These interfaces WILL be removed in the future. - */ - -DISPATCH_PUBLIC_API DISPATCH_NONNULL1 DISPATCH_NONNULL2 -dispatch_item_t -LEGACY_dispatch_call(dispatch_queue_t, dispatch_legacy_block_t work, dispatch_legacy_block_t completion) -__asm__("_dispatch_call2"); - -DISPATCH_PUBLIC_API DISPATCH_PURE DISPATCH_WARN_RESULT -dispatch_queue_t -LEGACY_dispatch_queue_get_current(void) -__asm__("_dispatch_queue_get_current"); - -///////////////////////////////////////////////////////////////////////////// - -dispatch_queue_t -LEGACY_dispatch_queue_get_current(void) -{ - return _dispatch_queue_get_current(); -} - -dispatch_item_t -LEGACY_dispatch_call(dispatch_queue_t dq, - dispatch_legacy_block_t dispatch_block, - dispatch_legacy_block_t callback_block) -{ - dispatch_queue_t lq = _dispatch_queue_get_current() ?: dispatch_get_main_queue(); - dispatch_item_t di; - - di = dispatch_block ? calloc(1, ROUND_UP_TO_CACHELINE_SIZE(sizeof(*di))) : NULL; - - if (!di) { - return di; - } - - if (callback_block) { - dispatch_retain(lq); - } - - dispatch_async(dq, ^{ - dispatch_block(di); - - if (callback_block) { - dispatch_async(lq, ^{ - callback_block(di); - free(di); - dispatch_release(lq); - }); - } else { - free(di); - } - }); - - return di; -} - -sigset_t -dispatch_event_get_signals(dispatch_event_t de) -{ - sigset_t ret; - sigemptyset(&ret); - sigaddset(&ret, (int)dispatch_event_get_signal(de)); - return ret; -} - -void dispatch_cancel(dispatch_source_t ds) { dispatch_source_cancel(ds); } -long dispatch_testcancel(dispatch_source_t ds) { return dispatch_source_testcancel(ds); } - -void dispatch_queue_resume(dispatch_queue_t dq) { dispatch_resume(dq); } -void dispatch_queue_retain(dispatch_queue_t dq) { dispatch_retain(dq); } -void dispatch_queue_release(dispatch_queue_t dq) { dispatch_release(dq); } - -void dispatch_source_suspend(dispatch_source_t ds) { dispatch_suspend(ds); } -void dispatch_source_resume(dispatch_source_t ds) { dispatch_resume(ds); } -void dispatch_source_release(dispatch_source_t ds) { dispatch_release(ds); } - -void dispatch_source_attr_release(dispatch_source_attr_t attr) { dispatch_release(attr); } -void dispatch_queue_attr_release(dispatch_queue_attr_t attr) { dispatch_release(attr); } - -void *dispatch_queue_get_context(dispatch_queue_t dq) { return dispatch_get_context(dq); } -void dispatch_queue_set_context(dispatch_queue_t dq, void *context) { dispatch_set_context(dq, context); } - -void *dispatch_source_get_context(dispatch_source_t ds) { return dispatch_get_context(ds); } -void dispatch_source_set_context(dispatch_source_t ds, void *context) { dispatch_set_context(ds, context); } - -void dispatch_source_custom_trigger(dispatch_source_t ds) { dispatch_source_merge_data(ds, 1); } - -void -dispatch_source_trigger(dispatch_source_t ds, unsigned long val) -{ - dispatch_source_merge_data(ds, val); -} - -int dispatch_source_get_descriptor(dispatch_source_t ds) { return (int)dispatch_source_get_handle(ds); } - -pid_t dispatch_source_get_pid(dispatch_source_t ds) { return (pid_t)dispatch_source_get_handle(ds); } - -mach_port_t dispatch_source_get_machport(dispatch_source_t ds) { return (mach_port_t)dispatch_source_get_handle(ds); } - -uint64_t dispatch_source_get_flags(dispatch_source_t ds) { return dispatch_source_get_mask(ds); } - -dispatch_source_t dispatch_event_get_source(dispatch_event_t event) { return event; } - -long dispatch_event_get_error(dispatch_event_t event, long* error) { return dispatch_source_get_error(event, error); } - -uint64_t dispatch_event_get_flags(dispatch_event_t event) { return dispatch_source_get_data(event); } - -size_t dispatch_event_get_bytes_available(dispatch_event_t event) { return (size_t)dispatch_source_get_data(event); } - -unsigned long dispatch_event_get_count(dispatch_event_t event) { return (unsigned long)dispatch_source_get_data(event); } - -long dispatch_event_get_signal(dispatch_event_t event) { return (long)dispatch_source_get_handle(event); } - -dispatch_source_t -dispatch_source_custom_create( - unsigned long behavior, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_event_handler_t handler) { - return dispatch_source_data_create(behavior, attr, queue, handler); -} - -dispatch_source_t -dispatch_source_custom_create_f( - unsigned long behavior, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_event_handler_function_t handler) { - return dispatch_source_data_create_f(behavior, attr, queue, h_context, handler); -} - -#define _dispatch_source_call_block ((void *)-1) - - - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_timer_create(uint64_t flags, - uint64_t nanoseconds, - uint64_t leeway, - dispatch_source_attr_t attr, - dispatch_queue_t q, - dispatch_source_handler_t callback) -{ - return dispatch_source_timer_create_f(flags, nanoseconds, leeway, - attr, q, callback, _dispatch_source_call_block); -} -#endif - -dispatch_source_t -dispatch_source_timer_create_f(uint64_t timer_flags, - uint64_t nanoseconds, - uint64_t leeway, - dispatch_source_attr_t attr, - dispatch_queue_t q, - void *context, - dispatch_source_handler_function_t callback) -{ - dispatch_source_t ds; - dispatch_time_t start; - - // 6866347 - make sure nanoseconds won't overflow - if ((int64_t)nanoseconds < 0) { - nanoseconds = INT64_MAX; - } - - if (timer_flags & DISPATCH_TIMER_ONESHOT) { - timer_flags |= DISPATCH_TIMER_WALL_CLOCK; - } - if (timer_flags == (DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_WALL_CLOCK)) { - static const struct timespec t0; - start = dispatch_walltime(&t0, nanoseconds); - } else if (timer_flags & DISPATCH_TIMER_WALL_CLOCK) { - start = dispatch_walltime(DISPATCH_TIME_NOW, nanoseconds); - } else { - start = dispatch_time(DISPATCH_TIME_NOW, nanoseconds); - } - if (timer_flags & DISPATCH_TIMER_ONESHOT) { - // 6866347 - make sure nanoseconds won't overflow - nanoseconds = INT64_MAX; // non-repeating (~292 years) - } - - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, (unsigned long)timer_flags, q); - if (!ds) { - return NULL; - } - ds = _dispatch_source_create2(ds, attr, context, callback); - if (!ds) { - return NULL; - } - dispatch_source_set_timer(ds, start, nanoseconds, leeway); - - return ds; -} - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_read_create(int descriptor, - dispatch_source_attr_t attr, - dispatch_queue_t q, - dispatch_source_handler_t callback) -{ - return dispatch_source_read_create_f(descriptor, - attr, q, callback, _dispatch_source_call_block); -} -#endif - -dispatch_source_t -dispatch_source_read_create_f(int fd, - dispatch_source_attr_t attr, - dispatch_queue_t q, - void *context, - dispatch_source_handler_function_t callback) -{ - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, q); - return _dispatch_source_create2(ds, attr, context, callback); -} - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_write_create(int descriptor, - dispatch_source_attr_t attr, - dispatch_queue_t q, - dispatch_source_handler_t callback) -{ - return dispatch_source_write_create_f(descriptor, - attr, q, callback, _dispatch_source_call_block); -} -#endif - -dispatch_source_t -dispatch_source_write_create_f(int fd, - dispatch_source_attr_t attr, - dispatch_queue_t q, - void *context, - dispatch_source_handler_function_t callback) -{ - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, q); - return _dispatch_source_create2(ds, attr, context, callback); -} - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_vnode_create(int descriptor, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t q, - dispatch_source_handler_t callback) -{ - return dispatch_source_vnode_create_f(descriptor, - flags, attr, q, callback, _dispatch_source_call_block); -} -#endif - -dispatch_source_t -dispatch_source_vnode_create_f(int fd, - uint64_t event_mask, - dispatch_source_attr_t attr, - dispatch_queue_t q, - void *context, - dispatch_source_handler_function_t callback) -{ - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, (unsigned long)event_mask, q); - return _dispatch_source_create2(ds, attr, context, callback); -} - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_signal_create(unsigned long sig, - dispatch_source_attr_t attr, - dispatch_queue_t q, - dispatch_source_handler_t callback) -{ - return dispatch_source_signal_create_f(sig, - attr, q, callback, _dispatch_source_call_block); -} -#endif - -dispatch_source_t -dispatch_source_signal_create_f(unsigned long signo, - dispatch_source_attr_t attr, - dispatch_queue_t q, - void *context, - dispatch_source_handler_function_t callback) -{ - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, signo, 0, q); - return _dispatch_source_create2(ds, attr, context, callback); -} - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_proc_create(pid_t pid, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t q, - dispatch_source_handler_t callback) -{ - return dispatch_source_proc_create_f(pid, - flags, attr, q, callback, _dispatch_source_call_block); -} -#endif - -dispatch_source_t -dispatch_source_proc_create_f(pid_t pid, - uint64_t event_mask, - dispatch_source_attr_t attr, - dispatch_queue_t q, - void *context, - dispatch_source_handler_function_t callback) -{ - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, (unsigned long)event_mask, q); - return _dispatch_source_create2(ds, attr, context, callback); -} - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_vfs_create(uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t q, - dispatch_source_handler_t callback) -{ - return dispatch_source_vfs_create_f(flags, - attr, q, callback, _dispatch_source_call_block); -} -#endif - -dispatch_source_t -dispatch_source_vfs_create_f(uint64_t event_mask, - dispatch_source_attr_t attr, - dispatch_queue_t q, - void *context, - dispatch_source_handler_function_t callback) -{ - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_VFS, 0, (unsigned long)event_mask, q); - return _dispatch_source_create2(ds, attr, context, callback); -} - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_data_create(unsigned long behavior, - dispatch_source_attr_t attr, - dispatch_queue_t q, - dispatch_source_handler_t callback) -{ - return dispatch_source_data_create_f(behavior, - attr, q, callback, _dispatch_source_call_block); -} -#endif - -#ifdef __BLOCKS__ -dispatch_source_t -dispatch_source_machport_create(mach_port_t mport, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t dq, - dispatch_source_handler_t callback) -{ - return dispatch_source_machport_create_f(mport, flags, - attr, dq, callback, _dispatch_source_call_block); -} -#endif - -dispatch_source_t -dispatch_source_data_create_f(unsigned long behavior, - dispatch_source_attr_t attr, - dispatch_queue_t q, - void *context, - dispatch_source_handler_function_t callback) -{ - dispatch_source_t ds; - dispatch_source_type_t type; - switch (behavior) { - case DISPATCH_SOURCE_CUSTOM_ADD: - type = DISPATCH_SOURCE_TYPE_DATA_ADD; - break; - case DISPATCH_SOURCE_CUSTOM_OR: - type = DISPATCH_SOURCE_TYPE_DATA_OR; - break; - default: - return NULL; - } - ds = dispatch_source_create(type, 0, 0, q); - return _dispatch_source_create2(ds, attr, context, callback); -} - -dispatch_source_t -dispatch_source_machport_create_f(mach_port_t mport, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t dq, - void *ctxt, - dispatch_source_handler_function_t func) -{ - dispatch_source_t ds; - dispatch_source_type_t type; - unsigned long newflags = 0; - - if (flags & ~(DISPATCH_MACHPORT_DEAD|DISPATCH_MACHPORT_RECV)) { - return NULL; - } - // XXX DELETED - if (flags & DISPATCH_MACHPORT_DEAD) { - type = DISPATCH_SOURCE_TYPE_MACH_SEND; - newflags |= DISPATCH_MACH_SEND_DEAD; - } else { - type = DISPATCH_SOURCE_TYPE_MACH_RECV; - } - - ds = dispatch_source_create(type, mport, newflags, dq); - return _dispatch_source_create2(ds, attr, ctxt, func); -} - diff --git a/src/legacy.h b/src/legacy.h deleted file mode 100644 index fb154f8..0000000 --- a/src/legacy.h +++ /dev/null @@ -1,748 +0,0 @@ -/* - * 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@ - */ - -/* - * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch - * which are subject to change in future releases of Mac OS X. Any applications - * relying on these interfaces WILL break. - */ - -/* - * 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__ - -#ifndef __DISPATCH_INDIRECT__ -#error "Please #include instead of this file directly." -#include // for HeaderDoc -#endif - -#include - -#define DISPATCH_DEPRECATED __attribute__((deprecated)) -#define DISPATCH_PUBLIC_API __attribute__((visibility("default"))) - -typedef struct dispatch_item_s *dispatch_item_t; - -struct dispatch_item_s { - void * di_objc_isa; /* FIXME -- someday... */ - struct dispatch_item_s *volatile di_next; - dispatch_queue_t di_cback_q; - uint32_t di_flags; - semaphore_t di_semaphore; - void * di_work_func; - void * di_work_ctxt; - void * di_cback_func; - void * di_cback_ctxt; - void * di_ctxt; -}; - -// Use: dispatch_source_t -typedef struct dispatch_source_s *dispatch_event_t; - -// Obsolete -#ifdef __BLOCKS__ -typedef void (^dispatch_legacy_block_t)(dispatch_item_t); -typedef void (^dispatch_queue_deletion_block_t)(dispatch_queue_t queue); -typedef void (^dispatch_source_deletion_t)(dispatch_source_t source); -typedef void (^dispatch_event_callback_t)(dispatch_event_t event); -typedef void (^dispatch_source_handler_t)(dispatch_source_t source); -typedef dispatch_source_handler_t dispatch_event_handler_t; -typedef void (^dispatch_source_finalizer_t)(dispatch_source_t source); -#endif /* __BLOCKS__ */ - -// Obsolete -typedef void (*dispatch_source_handler_function_t)(void *, dispatch_source_t); -typedef void (*dispatch_source_finalizer_function_t)(void *, dispatch_source_t); -typedef dispatch_source_handler_function_t dispatch_event_handler_function_t; - -DISPATCH_DECL(dispatch_source_attr); - -#define DISPATCH_SOURCE_CREATE_SUSPENDED ((dispatch_source_attr_t)~0ul) - -#ifdef __BLOCKS__ -typedef void (^dispatch_queue_finalizer_t)(dispatch_queue_t queue); -#endif - -typedef void (*dispatch_queue_finalizer_function_t)(void *, dispatch_queue_t); - -__BEGIN_DECLS - -/*! - * @function dispatch_queue_attr_create - * - * @abstract - * Creates a new dispatch queue attribute structure. These attributes may be - * provided at creation time to modify the default behavior of the queue. - * - * @discussion - * The values present in this structure are copied to newly created queues. - * The same attribute structure may be provided to multiple calls to - * dispatch_queue_create() but only the values in the structure at the time the - * call is made will be used. - * - * @result - * The new dispatch queue attribute structure, initialized to default values. - */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW -dispatch_queue_attr_t -dispatch_queue_attr_create(void); - -/*! - * @function dispatch_queue_attr_set_priority - * - * @abstract - * Set the priority level for a dispatch queue. - * - * @discussion - * Priority levels may be: - * - DISPATCH_QUEUE_PRIORITY_HIGH - * - DISPATCH_QUEUE_PRIORITY_DEFAULT - * - DISPATCH_QUEUE_PRIORITY_LOW - * Queues set to high priority will be processed - * before queues set to default priority or low priority. - * Queues set to low priority will be processed only if all - * high priority and default priority queues are empty. - */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW -void -dispatch_queue_attr_set_priority(dispatch_queue_attr_t attr, int priority); - -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW -long -dispatch_queue_attr_set_finalizer( - dispatch_queue_attr_t attr, - dispatch_queue_finalizer_t finalizer); -#endif - -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW -void -dispatch_queue_attr_set_finalizer_f(dispatch_queue_attr_t attr, void *context, dispatch_queue_finalizer_function_t finalizer); - -/*! - * @function dispatch_get_concurrent_queue - * - * @abstract - * Returns a well-known global concurrent queue of a given priority level. - * - * @discussion - * Blocks submitted to the returned queue may be invoked concurrently with - * respect to each other. - * - * These queues are useful for performing one-shot asynchronous operations, - * e.g. dispatch_async() to an "anonymous" queue; or for performing parallel - * loops concurrently on multiple processors, e.g. dispatch_apply(). - * - * The dispatch queues returned by this function are managed by the system for - * the lifetime of the application, and need not be retained or released - * directly by the application. Furthermore, dispatch_suspend() and - * dispatch_queue_resume() are not supported on these global queues, and will - * be ignored. - * - * @param priority - * The requested priority level for the queue (default is zero): - * - DISPATCH_QUEUE_PRIORITY_HIGH - * - DISPATCH_QUEUE_PRIORITY_DEFAULT - * - DISPATCH_QUEUE_PRIORITY_LOW - * - * @result - * Returns a concurrent dispatch queue for use with dispatch_async(), - * dispatch_apply(), et al. - */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW -dispatch_queue_t -dispatch_get_concurrent_queue(long priority); - -DISPATCH_PUBLIC_API //DISPATCH_DEPRECATED -void -dispatch_queue_attr_set_flags(dispatch_queue_attr_t attr, uint64_t flags); - -#ifdef __BLOCKS__ -DISPATCH_PUBLIC_API DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_DEPRECATED -dispatch_item_t -dispatch_call(dispatch_queue_t, dispatch_legacy_block_t work, dispatch_legacy_block_t completion) -__asm__("_dispatch_call2"); -#endif /* __BLOCKS__ */ - -DISPATCH_PUBLIC_API DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_DEPRECATED -dispatch_queue_t -dispatch_queue_get_current(void); - -// Use: dispatch_retain -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_queue_retain(dispatch_queue_t); - -// Use: dispatch_release -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_queue_release(dispatch_queue_t); - -// Use: dispatch_resume -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_queue_resume(dispatch_queue_t); - -// Use: dispatch_release -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_source_release(dispatch_source_t); - -// Use: dispatch_suspend -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_source_suspend(dispatch_source_t); - -// Use: dispatch_resume -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_source_resume(dispatch_source_t); - -// Use: dispatch_release -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_queue_attr_release(dispatch_queue_attr_t); - -// Use: dispatch_release -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_source_attr_release(dispatch_source_attr_t); - -// Use: dispatch_source_get_handle -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_DEPRECATED -sigset_t -dispatch_event_get_signals(dispatch_event_t event); - -// Use: dispatch_get_context -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL //DISPATCH_DEPRECATED -void * -dispatch_queue_get_context(dispatch_queue_t queue); - -// Use: dispatch_set_context -DISPATCH_PUBLIC_API DISPATCH_NONNULL1 //DISPATCH_DEPRECATED -void -dispatch_queue_set_context(dispatch_queue_t queue, void *context); - -// Use: dispatch_get_context -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL //DISPATCH_DEPRECATED -void * -dispatch_source_get_context(dispatch_source_t source); - -// Use: dispatch_set_context -DISPATCH_PUBLIC_API DISPATCH_NONNULL1 //DISPATCH_DEPRECATED -void -dispatch_source_set_context(dispatch_source_t source, void * context); - -// Use: dispatch_source_merge_data -DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED -void -dispatch_source_custom_trigger(dispatch_source_t ds); - -// Use: dispatch_source_cancel -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW -void -dispatch_cancel(dispatch_source_t); - -// Use: dispatch_source_testcancel -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW -long -dispatch_testcancel(dispatch_source_t); - -// Use: dispatch_source_set_timer -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW -long -dispatch_source_timer_set_time(dispatch_source_t ds, - uint64_t nanoseconds, - uint64_t leeway); - -// Use: dispatch_source_merge_data -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW -void -dispatch_source_trigger(dispatch_source_t source, unsigned long value); - -enum { - DISPATCH_ERROR_DOMAIN_NO_ERROR = 0, - DISPATCH_ERROR_DOMAIN_POSIX = 1, - DISPATCH_ERROR_DOMAIN_MACH = 2, -}; - -// Obsolete -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_WARN_RESULT DISPATCH_NOTHROW -long -dispatch_source_get_error(dispatch_source_t source, long* error); - -// Use: dispatch_source_get_handle -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -mach_port_t -dispatch_source_get_machport(dispatch_source_t source); - -// Use: dispatch_source_get_handle -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -pid_t -dispatch_source_get_descriptor(dispatch_source_t source); - -// Use: dispatch_source_get_handle -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -pid_t -dispatch_source_get_pid(dispatch_source_t source); - -// Use: dispatch_source_get_mask -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -uint64_t -dispatch_source_get_flags(dispatch_source_t source); - -// LEGACY: dispatch_event_t == dispatch_source_t -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -dispatch_source_t -dispatch_event_get_source(dispatch_event_t event); - -// Use: dispatch_source_get_error -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_WARN_RESULT DISPATCH_NOTHROW -long -dispatch_event_get_error(dispatch_event_t event, long* error); - -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -uint64_t -dispatch_event_get_nanoseconds(dispatch_event_t event); - -// Use: dispatch_source_get_handle -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -long -dispatch_event_get_signal(dispatch_event_t event); - -// Use: dispatch_source_get_data -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -uint64_t -dispatch_event_get_flags(dispatch_event_t event); - -// Use: dispatch_source_get_data -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW -size_t -dispatch_event_get_bytes_available(dispatch_event_t event); - -// Use: dispatch_source_get_data -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL_ALL DISPATCH_NOTHROW -unsigned long -dispatch_event_get_count(dispatch_event_t event); - -// Obsolete -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW -dispatch_source_attr_t -dispatch_source_attr_create(void); - -// Obsolete -#if defined(__BLOCKS__) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NOTHROW -dispatch_source_finalizer_t -dispatch_source_attr_get_finalizer(dispatch_source_attr_t attr); -#endif /* __BLOCKS__ */ - -// Obsolete -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW -dispatch_source_attr_t -dispatch_source_attr_copy(dispatch_source_attr_t proto); - -// Obsolete -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW -long -dispatch_source_attr_set_finalizer( - dispatch_source_attr_t attr, - dispatch_source_finalizer_t finalizer); -#endif /* __BLOCKS__ */ - -// Obsolete -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW -void -dispatch_source_attr_set_finalizer_f( - dispatch_source_attr_t attr, - void *context, - dispatch_source_finalizer_function_t finalizer); - -// Obsolete -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_NONNULL1 DISPATCH_NOTHROW -void -dispatch_source_attr_set_context( - dispatch_source_attr_t attr, - void *context); - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL5 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_mig_create( - mach_port_t mport, - size_t max_size, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_mig_callback_t mig_callback); - -enum { - DISPATCH_TIMER_WALL_CLOCK = 0x4, -}; - -enum { - DISPATCH_TIMER_INTERVAL = 0x0, - DISPATCH_TIMER_ONESHOT = 0x1, - DISPATCH_TIMER_ABSOLUTE = 0x3, -}; - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL5 DISPATCH_NONNULL6 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_timer_create( - uint64_t flags, - uint64_t nanoseconds, - uint64_t leeway, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL5 DISPATCH_NONNULL7 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_timer_create_f( - uint64_t flags, - uint64_t nanoseconds, - uint64_t leeway, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_signal_create( - unsigned long signo, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_signal_create_f( - unsigned long sig, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL4 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_read_create( - int descriptor, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_read_create_f( - int descriptor, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL4 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_write_create( - int descriptor, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_write_create_f( - int descriptor, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL5 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_vnode_create( - int descriptor, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL6 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_vnode_create_f( - int descriptor, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL5 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_proc_create( - pid_t pid, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL6 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_proc_create_f( - pid_t pid, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -enum { - DISPATCH_MACHPORT_DEAD = 0x1, - DISPATCH_MACHPORT_RECV = 0x2, - DISPATCH_MACHPORT_DELETED = 0x4, -}; - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_machport_create( - mach_port_t mport, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_machport_create_f( - mach_port_t mport, - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -enum { - DISPATCH_SOURCE_DATA_ADD = 1, - DISPATCH_SOURCE_DATA_OR, -}; - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA..., ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL4 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_data_create( - unsigned long behavior, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA..., ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_data_create_f( - unsigned long behavior, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -enum { - DISPATCH_SOURCE_CUSTOM_ADD = DISPATCH_SOURCE_DATA_ADD, - DISPATCH_SOURCE_CUSTOM_OR = DISPATCH_SOURCE_DATA_OR, -}; - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA..., ...) -#ifdef __BLOCKS__ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL2 DISPATCH_NONNULL3 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_custom_create( - unsigned long behavior, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_event_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA..., ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW -dispatch_source_t -dispatch_source_custom_create_f( - unsigned long behavior, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_event_handler_function_t handler); - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_VFS, ...) -#if defined(__BLOCKS__) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL4 -dispatch_source_t -dispatch_source_vfs_create( - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - dispatch_source_handler_t handler); -#endif /* __BLOCKS__ */ - -// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_VFS, ...) -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 -dispatch_source_t -dispatch_source_vfs_create_f( - uint64_t flags, - dispatch_source_attr_t attr, - dispatch_queue_t queue, - void *h_context, - dispatch_source_handler_function_t handler); - -/* - * Raw Mach message support from MIG source. - * - * It is possible to use the following callback style with the MIG source to - * obtain the raw mach message (and send no reply) similar to CFMachPort. - * (For more specific CFMachPort compatibility, see below). - * - * void handle_mach_msg(mach_msg_header *msg) { ... } - * ... - * DISPATCH_MACHPORT_CALLBACK_DECL(mig_compat_callback, handle_mach_msg); - * ... - * mig = dispatch_source_mig_create(mp, MY_MAX_MSG_SIZE, NULL, - * queue, mig_compat_callback); - */ -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -boolean_t -_dispatch_machport_callback(mach_msg_header_t *msg, mach_msg_header_t *reply, void (*callback)(mach_msg_header_t *)); - -#define DISPATCH_MACHPORT_CALLBACK_DECL(new_callback, existing_callback) \ -__private_extern__ boolean_t \ -new_callback(mach_msg_header_t *msg, mach_msg_header_t *reply) \ -{ return _dispatch_machport_callback(msg, reply, existing_callback); } - -/* - * CFMachPort compatibility. - * - * It is possible to use existing CFMachPort callbacks with dispatch mig sources - * by delcaring the following shim and using the shim as the mig server callback - * to dispatch_source_mig_create(). - * The CFMachPortRef "port" parameter of the CFMachPortCallBack will be NULL. - * If mach_port_set_context() is used, that value will be passed into the "info" - * parameter of the CFMachPortCallBack. - * - * DISPATCH_CFMACHPORT_CALLBACK_DECL(mig_callback, MyCFMachPortCallBack); - * - * ... - * { - * kr = mach_port_set_context(mach_task_self(), mp, (mach_vm_address_t)context); - * mig = dispatch_source_mig_create(mp, MY_MAX_MSG_SIZE, NULL, - * queue, mig_callback); - */ -struct __CFMachPort; - -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -boolean_t -_dispatch_CFMachPortCallBack(mach_msg_header_t *msg, mach_msg_header_t *reply, void (*callback)(struct __CFMachPort *, void *msg, signed long size, void *)); - -#define DISPATCH_CFMACHPORT_CALLBACK_DECL(new_callback, existing_callback) \ -__private_extern__ boolean_t \ -new_callback(mach_msg_header_t *msg, mach_msg_header_t *reply) \ -{ return _dispatch_CFMachPortCallBack(msg, reply, existing_callback); } - -__END_DECLS - -#endif diff --git a/src/object.c b/src/object.c index 8746495..b84979b 100644 --- a/src/object.c +++ b/src/object.c @@ -1,62 +1,32 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 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 "internal.h" - -void -dispatch_debug(dispatch_object_t dou, const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - - dispatch_debugv(dou._do, msg, ap); - - va_end(ap); -} - -void -dispatch_debugv(dispatch_object_t dou, const char *msg, va_list ap) -{ - char buf[4096]; - size_t offs; - - if (dou._do && dou._do->do_vtable->do_debug) { - offs = dx_debug(dou._do, buf, sizeof(buf)); - } else { - offs = snprintf(buf, sizeof(buf), "NULL vtable slot"); - } - - snprintf(buf + offs, sizeof(buf) - offs, ": %s", msg); - - _dispatch_logv(buf, ap); -} - void dispatch_retain(dispatch_object_t dou) { - if (dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) { + if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; // global object } - if ((dispatch_atomic_inc(&dou._do->do_xref_cnt) - 1) == 0) { + if (slowpath((dispatch_atomic_inc2o(dou._do, do_xref_cnt) - 1) == 0)) { DISPATCH_CLIENT_CRASH("Resurrection of an object"); } } @@ -64,10 +34,10 @@ dispatch_retain(dispatch_object_t dou) void _dispatch_retain(dispatch_object_t dou) { - if (dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) { + if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; // global object } - if ((dispatch_atomic_inc(&dou._do->do_ref_cnt) - 1) == 0) { + if (slowpath((dispatch_atomic_inc2o(dou._do, do_ref_cnt) - 1) == 0)) { DISPATCH_CLIENT_CRASH("Resurrection of an object"); } } @@ -75,23 +45,18 @@ _dispatch_retain(dispatch_object_t dou) void dispatch_release(dispatch_object_t dou) { - typeof(dou._do->do_xref_cnt) oldval; - - if (dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) { + if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; } - oldval = dispatch_atomic_dec(&dou._do->do_xref_cnt) + 1; - - if (fastpath(oldval > 1)) { + unsigned int xref_cnt = dispatch_atomic_dec2o(dou._do, do_xref_cnt) + 1; + if (fastpath(xref_cnt > 1)) { return; } - if (oldval == 1) { -#ifndef DISPATCH_NO_LEGACY + if (fastpath(xref_cnt == 1)) { if (dou._do->do_vtable == (void*)&_dispatch_source_kevent_vtable) { - return _dispatch_source_legacy_xref_release(dou._ds); + return _dispatch_source_xref_release(dou._ds); } -#endif if (slowpath(DISPATCH_OBJECT_SUSPENDED(dou._do))) { // Arguments for and against this assert are within 6705399 DISPATCH_CLIENT_CRASH("Release of a suspended object"); @@ -121,25 +86,21 @@ _dispatch_dispose(dispatch_object_t dou) void _dispatch_release(dispatch_object_t dou) { - typeof(dou._do->do_ref_cnt) oldval; - - if (dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) { + if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; // global object } - oldval = dispatch_atomic_dec(&dou._do->do_ref_cnt) + 1; - - if (fastpath(oldval > 1)) { + unsigned int ref_cnt = dispatch_atomic_dec2o(dou._do, do_ref_cnt) + 1; + if (fastpath(ref_cnt > 1)) { return; } - if (oldval == 1) { - if (dou._do->do_next != DISPATCH_OBJECT_LISTLESS) { + if (fastpath(ref_cnt == 1)) { + if (slowpath(dou._do->do_next != DISPATCH_OBJECT_LISTLESS)) { DISPATCH_CRASH("release while enqueued"); } - if (dou._do->do_xref_cnt) { + if (slowpath(dou._do->do_xref_cnt)) { DISPATCH_CRASH("release while external references exist"); } - return dx_dispose(dou._do); } DISPATCH_CRASH("over-release"); @@ -171,30 +132,55 @@ dispatch_suspend(dispatch_object_t dou) if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; } - dispatch_atomic_add(&dou._do->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_INTERVAL); + // rdar://8181908 explains why we need to do an internal retain at every + // suspension. + (void)dispatch_atomic_add2o(dou._do, do_suspend_cnt, + DISPATCH_OBJECT_SUSPEND_INTERVAL); + _dispatch_retain(dou._do); +} + +DISPATCH_NOINLINE +static void +_dispatch_resume_slow(dispatch_object_t dou) +{ + _dispatch_wakeup(dou._do); + // Balancing the retain() done in suspend() for rdar://8181908 + _dispatch_release(dou._do); } void dispatch_resume(dispatch_object_t dou) { + // Global objects cannot be suspended or resumed. This also has the + // side effect of saturating the suspend count of an object and + // guarding against resuming due to overflow. if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; } - switch (dispatch_atomic_sub(&dou._do->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_INTERVAL) + DISPATCH_OBJECT_SUSPEND_INTERVAL) { - case DISPATCH_OBJECT_SUSPEND_INTERVAL: - _dispatch_wakeup(dou._do); - break; - case 0: - DISPATCH_CLIENT_CRASH("Over-resume of an object"); - break; - default: - break; + // Check the previous value of the suspend count. If the previous + // value was a single suspend interval, the object should be resumed. + // If the previous value was less than the suspend interval, the object + // has been over-resumed. + unsigned int suspend_cnt = dispatch_atomic_sub2o(dou._do, do_suspend_cnt, + DISPATCH_OBJECT_SUSPEND_INTERVAL) + + DISPATCH_OBJECT_SUSPEND_INTERVAL; + if (fastpath(suspend_cnt > DISPATCH_OBJECT_SUSPEND_INTERVAL)) { + // Balancing the retain() done in suspend() for rdar://8181908 + return _dispatch_release(dou._do); + } + if (fastpath(suspend_cnt == DISPATCH_OBJECT_SUSPEND_INTERVAL)) { + return _dispatch_resume_slow(dou); } + DISPATCH_CLIENT_CRASH("Over-resume of an object"); } size_t -dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz) +_dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz) { - return snprintf(buf, bufsiz, "refcnt = 0x%x, suspend_cnt = 0x%x, ", - dou._do->do_ref_cnt, dou._do->do_suspend_cnt); + return snprintf(buf, bufsiz, "xrefcnt = 0x%x, refcnt = 0x%x, " + "suspend_cnt = 0x%x, locked = %d, ", dou._do->do_xref_cnt, + dou._do->do_ref_cnt, + dou._do->do_suspend_cnt / DISPATCH_OBJECT_SUSPEND_INTERVAL, + dou._do->do_suspend_cnt & 1); } + diff --git a/src/object_internal.h b/src/object_internal.h index cc048be..0627cfd 100644 --- a/src/object_internal.h +++ b/src/object_internal.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 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@ */ @@ -28,32 +28,42 @@ #define __DISPATCH_OBJECT_INTERNAL__ enum { - _DISPATCH_CONTINUATION_TYPE = 0x00000, // meta-type for continuations + _DISPATCH_CONTINUATION_TYPE = 0x00000, // meta-type for continuations _DISPATCH_QUEUE_TYPE = 0x10000, // meta-type for queues _DISPATCH_SOURCE_TYPE = 0x20000, // meta-type for sources _DISPATCH_SEMAPHORE_TYPE = 0x30000, // meta-type for semaphores - _DISPATCH_ATTR_TYPE = 0x10000000, // meta-type for attribute structures - + _DISPATCH_NODE_TYPE = 0x40000, // meta-type for data node + _DISPATCH_IO_TYPE = 0x50000, // meta-type for io channels + _DISPATCH_OPERATION_TYPE = 0x60000, // meta-type for io operations + _DISPATCH_DISK_TYPE = 0x70000, // meta-type for io disks + _DISPATCH_META_TYPE_MASK = 0xfff0000, // mask for object meta-types + _DISPATCH_ATTR_TYPE = 0x10000000, // meta-type for attributes + DISPATCH_CONTINUATION_TYPE = _DISPATCH_CONTINUATION_TYPE, - - DISPATCH_QUEUE_ATTR_TYPE = _DISPATCH_QUEUE_TYPE | _DISPATCH_ATTR_TYPE, + + DISPATCH_DATA_TYPE = _DISPATCH_NODE_TYPE, + + DISPATCH_IO_TYPE = _DISPATCH_IO_TYPE, + DISPATCH_OPERATION_TYPE = _DISPATCH_OPERATION_TYPE, + DISPATCH_DISK_TYPE = _DISPATCH_DISK_TYPE, + + DISPATCH_QUEUE_ATTR_TYPE = _DISPATCH_QUEUE_TYPE |_DISPATCH_ATTR_TYPE, DISPATCH_QUEUE_TYPE = 1 | _DISPATCH_QUEUE_TYPE, DISPATCH_QUEUE_GLOBAL_TYPE = 2 | _DISPATCH_QUEUE_TYPE, DISPATCH_QUEUE_MGR_TYPE = 3 | _DISPATCH_QUEUE_TYPE, + DISPATCH_QUEUE_SPECIFIC_TYPE = 4 | _DISPATCH_QUEUE_TYPE, DISPATCH_SEMAPHORE_TYPE = _DISPATCH_SEMAPHORE_TYPE, - - DISPATCH_SOURCE_ATTR_TYPE = _DISPATCH_SOURCE_TYPE | _DISPATCH_ATTR_TYPE, - + DISPATCH_SOURCE_KEVENT_TYPE = 1 | _DISPATCH_SOURCE_TYPE, }; -#define DISPATCH_VTABLE_HEADER(x) \ - unsigned long const do_type; \ +#define DISPATCH_VTABLE_HEADER(x) \ + unsigned long const do_type; \ const char *const do_kind; \ - size_t (*const do_debug)(struct x *, char *, size_t); \ - struct dispatch_queue_s *(*const do_invoke)(struct x *); \ + size_t (*const do_debug)(struct x *, char *, size_t); \ + struct dispatch_queue_s *(*const do_invoke)(struct x *); \ bool (*const do_probe)(struct x *); \ void (*const do_dispose)(struct x *) @@ -64,34 +74,31 @@ enum { #define dx_invoke(x) (x)->do_vtable->do_invoke(x) #define dx_probe(x) (x)->do_vtable->do_probe(x) -#define DISPATCH_STRUCT_HEADER(x, y) \ - const struct y *do_vtable; \ - struct x *volatile do_next; \ - unsigned int do_ref_cnt; \ - unsigned int do_xref_cnt; \ - unsigned int do_suspend_cnt; \ - struct dispatch_queue_s *do_targetq; \ +#define DISPATCH_STRUCT_HEADER(x, y) \ + const struct y *do_vtable; \ + struct x *volatile do_next; \ + unsigned int do_ref_cnt; \ + unsigned int do_xref_cnt; \ + unsigned int do_suspend_cnt; \ + struct dispatch_queue_s *do_targetq; \ void *do_ctxt; \ - void *do_finalizer + void *do_finalizer; -#define DISPATCH_OBJECT_GLOBAL_REFCNT (~0u) -#define DISPATCH_OBJECT_SUSPEND_LOCK 1u // "word and bit" must be a power of two to be safely subtracted +#define DISPATCH_OBJECT_GLOBAL_REFCNT (~0u) +// "word and bit" must be a power of two to be safely subtracted +#define DISPATCH_OBJECT_SUSPEND_LOCK 1u #define DISPATCH_OBJECT_SUSPEND_INTERVAL 2u -#define DISPATCH_OBJECT_SUSPENDED(x) ((x)->do_suspend_cnt >= DISPATCH_OBJECT_SUSPEND_INTERVAL) +#define DISPATCH_OBJECT_SUSPENDED(x) \ + ((x)->do_suspend_cnt >= DISPATCH_OBJECT_SUSPEND_INTERVAL) #ifdef __LP64__ // the bottom nibble must not be zero, the rest of the bits should be random -// we sign extend the 64-bit version so that a better instruction encoding is generated on Intel -#define DISPATCH_OBJECT_LISTLESS ((void *)0xffffffff89abcdef) +// we sign extend the 64-bit version so that a better instruction encoding is +// generated on Intel +#define DISPATCH_OBJECT_LISTLESS ((void *)0xffffffff89abcdef) #else -#define DISPATCH_OBJECT_LISTLESS ((void *)0x89abcdef) +#define DISPATCH_OBJECT_LISTLESS ((void *)0x89abcdef) #endif -#define _dispatch_trysuspend(x) __sync_bool_compare_and_swap(&(x)->do_suspend_cnt, 0, DISPATCH_OBJECT_SUSPEND_INTERVAL) -// _dispatch_source_invoke() relies on this testing the whole suspend count -// word, not just the lock bit. In other words, no point taking the lock -// if the source is suspended or canceled. -#define _dispatch_trylock(x) dispatch_atomic_cmpxchg(&(x)->do_suspend_cnt, 0, DISPATCH_OBJECT_SUSPEND_LOCK) - struct dispatch_object_vtable_s { DISPATCH_VTABLE_HEADER(dispatch_object_s); }; @@ -100,7 +107,8 @@ struct dispatch_object_s { DISPATCH_STRUCT_HEADER(dispatch_object_s, dispatch_object_vtable_s); }; -size_t dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz); +size_t _dispatch_object_debug_attr(dispatch_object_t dou, char* buf, + size_t bufsiz); void _dispatch_retain(dispatch_object_t dou); void _dispatch_release(dispatch_object_t dou); diff --git a/src/once.c b/src/once.c index 9046c06..ab4a4e8 100644 --- a/src/once.c +++ b/src/once.c @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -23,9 +23,17 @@ #undef dispatch_once #undef dispatch_once_f + +struct _dispatch_once_waiter_s { + volatile struct _dispatch_once_waiter_s *volatile dow_next; + _dispatch_thread_semaphore_t dow_sema; +}; + +#define DISPATCH_ONCE_DONE ((struct _dispatch_once_waiter_s *)~0l) + #ifdef __BLOCKS__ void -dispatch_once(dispatch_once_t *val, void (^block)(void)) +dispatch_once(dispatch_once_t *val, dispatch_block_t block) { struct Block_basic *bb = (void *)block; @@ -35,12 +43,17 @@ dispatch_once(dispatch_once_t *val, void (^block)(void)) DISPATCH_NOINLINE void -dispatch_once_f(dispatch_once_t *val, void *ctxt, void (*func)(void *)) +dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func) { - volatile long *vval = val; + struct _dispatch_once_waiter_s * volatile *vval = + (struct _dispatch_once_waiter_s**)val; + struct _dispatch_once_waiter_s dow = { NULL, 0 }; + struct _dispatch_once_waiter_s *tail, *tmp; + _dispatch_thread_semaphore_t sema; - if (dispatch_atomic_cmpxchg(val, 0l, 1l)) { - func(ctxt); + if (dispatch_atomic_cmpxchg(vval, NULL, &dow)) { + dispatch_atomic_acquire_barrier(); + _dispatch_client_callout(ctxt, func); // The next barrier must be long and strong. // @@ -52,25 +65,25 @@ dispatch_once_f(dispatch_once_t *val, void *ctxt, void (*func)(void *)) // The dispatch_once*() wrapper macro causes the callee's // instruction stream to look like this (pseudo-RISC): // - // load r5, pred-addr - // cmpi r5, -1 - // beq 1f - // call dispatch_once*() - // 1f: - // load r6, data-addr + // load r5, pred-addr + // cmpi r5, -1 + // beq 1f + // call dispatch_once*() + // 1f: + // load r6, data-addr // // May be re-ordered like so: // - // load r6, data-addr - // load r5, pred-addr - // cmpi r5, -1 - // beq 1f - // call dispatch_once*() - // 1f: + // load r6, data-addr + // load r5, pred-addr + // cmpi r5, -1 + // beq 1f + // call dispatch_once*() + // 1f: // // Normally, a barrier on the read side is used to workaround // the weakly ordered memory model. But barriers are expensive - // and we only need to synchronize once! After func(ctxt) + // and we only need to synchronize once! After func(ctxt) // completes, the predicate will be marked as "done" and the // branch predictor will correctly skip the call to // dispatch_once*(). @@ -91,14 +104,32 @@ dispatch_once_f(dispatch_once_t *val, void *ctxt, void (*func)(void *)) // // On some CPUs, the most fully synchronizing instruction might // need to be issued. - - dispatch_atomic_barrier(); - *val = ~0l; - } else { - do { - _dispatch_hardware_pause(); - } while (*vval != ~0l); - dispatch_atomic_barrier(); + dispatch_atomic_maximally_synchronizing_barrier(); + //dispatch_atomic_release_barrier(); // assumed contained in above + tmp = dispatch_atomic_xchg(vval, DISPATCH_ONCE_DONE); + tail = &dow; + while (tail != tmp) { + while (!tmp->dow_next) { + _dispatch_hardware_pause(); + } + sema = tmp->dow_sema; + tmp = (struct _dispatch_once_waiter_s*)tmp->dow_next; + _dispatch_thread_semaphore_signal(sema); + } + } else { + dow.dow_sema = _dispatch_get_thread_semaphore(); + for (;;) { + tmp = *vval; + if (tmp == DISPATCH_ONCE_DONE) { + break; + } + dispatch_atomic_store_barrier(); + if (dispatch_atomic_cmpxchg(vval, tmp, &dow)) { + dow.dow_next = tmp; + _dispatch_thread_semaphore_wait(dow.dow_sema); + } + } + _dispatch_put_thread_semaphore(dow.dow_sema); } } diff --git a/src/os_shims.h b/src/os_shims.h deleted file mode 100644 index 7efd28e..0000000 --- a/src/os_shims.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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@ - */ - -/* - * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch - * which are subject to change in future releases of Mac OS X. Any applications - * relying on these interfaces WILL break. - */ - -#ifndef __DISPATCH_OS_SHIMS__ -#define __DISPATCH_OS_SHIMS__ - -#include -#include -#include - -__private_extern__ const char *__crashreporter_info__; - -static const unsigned long dispatch_queue_key = __PTK_LIBDISPATCH_KEY0; -static const unsigned long dispatch_sema4_key = __PTK_LIBDISPATCH_KEY1; -static const unsigned long dispatch_cache_key = __PTK_LIBDISPATCH_KEY2; -static const unsigned long dispatch_bcounter_key = __PTK_LIBDISPATCH_KEY3; -//__PTK_LIBDISPATCH_KEY4 -//__PTK_LIBDISPATCH_KEY5 - - -#define SIMULATE_5491082 1 -#ifndef _PTHREAD_TSD_OFFSET -#define _PTHREAD_TSD_OFFSET 0 -#endif - -static inline void -_dispatch_thread_setspecific(unsigned long k, void *v) -{ -#if defined(SIMULATE_5491082) && defined(__i386__) - asm("movl %1, %%gs:%0" : "=m" (*(void **)(k * sizeof(void *) + _PTHREAD_TSD_OFFSET)) : "ri" (v) : "memory"); -#elif defined(SIMULATE_5491082) && defined(__x86_64__) - asm("movq %1, %%gs:%0" : "=m" (*(void **)(k * sizeof(void *) + _PTHREAD_TSD_OFFSET)) : "rn" (v) : "memory"); -#else - int res; - if (_pthread_has_direct_tsd()) { - res = _pthread_setspecific_direct(k, v); - } else { - res = pthread_setspecific(k, v); - } - dispatch_assert_zero(res); -#endif -} - -static inline void * -_dispatch_thread_getspecific(unsigned long k) -{ -#if defined(SIMULATE_5491082) && (defined(__i386__) || defined(__x86_64__)) - void *rval; - asm("mov %%gs:%1, %0" : "=r" (rval) : "m" (*(void **)(k * sizeof(void *) + _PTHREAD_TSD_OFFSET))); - return rval; -#else - if (_pthread_has_direct_tsd()) { - return _pthread_getspecific_direct(k); - } else { - return pthread_getspecific(k); - } -#endif -} - -static inline void -_dispatch_thread_key_init_np(unsigned long k, void (*d)(void *)) -{ - dispatch_assert_zero(pthread_key_init_np((int)k, d)); -} - -#define _dispatch_thread_self pthread_self - - -#if DISPATCH_PERF_MON - -#if defined(SIMULATE_5491082) && (defined(__i386__) || defined(__x86_64__)) -#ifdef __LP64__ -#define _dispatch_workitem_inc() asm("incq %%gs:%0" : "+m" \ - (*(void **)(dispatch_bcounter_key * sizeof(void *) + _PTHREAD_TSD_OFFSET)) :: "cc") -#define _dispatch_workitem_dec() asm("decq %%gs:%0" : "+m" \ - (*(void **)(dispatch_bcounter_key * sizeof(void *) + _PTHREAD_TSD_OFFSET)) :: "cc") -#else -#define _dispatch_workitem_inc() asm("incl %%gs:%0" : "+m" \ - (*(void **)(dispatch_bcounter_key * sizeof(void *) + _PTHREAD_TSD_OFFSET)) :: "cc") -#define _dispatch_workitem_dec() asm("decl %%gs:%0" : "+m" \ - (*(void **)(dispatch_bcounter_key * sizeof(void *) + _PTHREAD_TSD_OFFSET)) :: "cc") -#endif -#else -static inline void -_dispatch_workitem_inc(void) -{ - unsigned long cnt = (unsigned long)_dispatch_thread_getspecific(dispatch_bcounter_key); - _dispatch_thread_setspecific(dispatch_bcounter_key, (void *)++cnt); -} -static inline void -_dispatch_workitem_dec(void) -{ - unsigned long cnt = (unsigned long)_dispatch_thread_getspecific(dispatch_bcounter_key); - _dispatch_thread_setspecific(dispatch_bcounter_key, (void *)--cnt); -} -#endif - -// C99 doesn't define flsll() or ffsll() -#ifdef __LP64__ -#define flsll(x) flsl(x) -#else -static inline unsigned int -flsll(uint64_t val) -{ - union { - struct { -#ifdef __BIG_ENDIAN__ - unsigned int hi, low; -#else - unsigned int low, hi; -#endif - } words; - uint64_t word; - } _bucket = { - .word = val, - }; - if (_bucket.words.hi) { - return fls(_bucket.words.hi) + 32; - } - return fls(_bucket.words.low); -} -#endif - -#else -#define _dispatch_workitem_inc() -#define _dispatch_workitem_dec() -#endif // DISPATCH_PERF_MON - -#endif diff --git a/src/protocol.defs b/src/protocol.defs index e6bd400..bf5fe5b 100644 --- a/src/protocol.defs +++ b/src/protocol.defs @@ -1,45 +1,48 @@ /* - * Copyright (c) 2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ -/* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. - */ #include #include -// '64' is used to align with Mach notifications and so that we don't fight with the notify symbols in Libsystem +// '64' is used to align with Mach notifications and so that we don't fight +// with the notify symbols in Libsystem subsystem libdispatch_internal_protocol 64; serverprefix _dispatch_; userprefix _dispatch_send_; -skip; /* was MACH_NOTIFY_FIRST: 64 */ +skip; /* was MACH_NOTIFY_FIRST: 64 */ /* MACH_NOTIFY_PORT_DELETED: 65 */ simpleroutine mach_notify_port_deleted( - _notify : mach_port_move_send_once_t; - _name : mach_port_name_t + _notify : mach_port_move_send_once_t; + _name : mach_port_name_t ); -skip; /* was MACH_NOTIFY_MSG_ACCEPTED: 66 */ +/* MACH_NOTIFY_SEND_POSSIBLE: 66 */ +simpleroutine +mach_notify_send_possible( + _notify : mach_port_move_send_once_t; + _name : mach_port_name_t +); skip; /* was NOTIFY_OWNERSHIP_RIGHTS: 67 */ @@ -48,28 +51,28 @@ skip; /* was NOTIFY_RECEIVE_RIGHTS: 68 */ /* MACH_NOTIFY_PORT_DESTROYED: 69 */ simpleroutine mach_notify_port_destroyed( - _notify : mach_port_move_send_once_t; - _rights : mach_port_move_receive_t + _notify : mach_port_move_send_once_t; + _rights : mach_port_move_receive_t ); /* MACH_NOTIFY_NO_SENDERS: 70 */ simpleroutine mach_notify_no_senders( - _notify : mach_port_move_send_once_t; - _mscnt : mach_port_mscount_t + _notify : mach_port_move_send_once_t; + _mscnt : mach_port_mscount_t ); /* MACH_NOTIFY_SEND_ONCE: 71 */ simpleroutine mach_notify_send_once( - _notify : mach_port_move_send_once_t + _notify : mach_port_move_send_once_t ); /* MACH_NOTIFY_DEAD_NAME: 72 */ simpleroutine mach_notify_dead_name( - _notify : mach_port_move_send_once_t; - _name : mach_port_name_t + _notify : mach_port_move_send_once_t; + _name : mach_port_name_t ); /* highly unlikely additional Mach notifications */ @@ -81,11 +84,11 @@ skip; simpleroutine wakeup_main_thread( - _port : mach_port_t; - WaitTime _waitTimeout : natural_t + _port : mach_port_t; + WaitTime _waitTimeout : natural_t ); simpleroutine consume_send_once_right( - _port : mach_port_move_send_once_t + _port : mach_port_move_send_once_t ); diff --git a/src/provider.d b/src/provider.d new file mode 100644 index 0000000..59fe790 --- /dev/null +++ b/src/provider.d @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010 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@ + */ + +typedef struct dispatch_object_s *dispatch_object_t; +typedef struct dispatch_queue_s *dispatch_queue_t; +typedef void (*dispatch_function_t)(void *); + +provider dispatch { + probe queue__push(dispatch_queue_t queue, const char *label, + dispatch_object_t item, const char *kind, + dispatch_function_t function, void *context); + probe queue__pop(dispatch_queue_t queue, const char *label, + dispatch_object_t item, const char *kind, + dispatch_function_t function, void *context); + probe callout__entry(dispatch_queue_t queue, const char *label, + dispatch_function_t function, void *context); + probe callout__return(dispatch_queue_t queue, const char *label, + dispatch_function_t function, void *context); +}; + +#pragma D attributes Evolving/Evolving/Common provider dispatch provider +#pragma D attributes Private/Private/Common provider dispatch module +#pragma D attributes Private/Private/Common provider dispatch function +#pragma D attributes Evolving/Evolving/Common provider dispatch name +#pragma D attributes Evolving/Evolving/Common provider dispatch args diff --git a/src/queue.c b/src/queue.c index 231dc37..595bac5 100644 --- a/src/queue.c +++ b/src/queue.c @@ -1,530 +1,538 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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 "internal.h" +#if HAVE_MACH #include "protocol.h" +#endif -void -dummy_function(void) -{ -} +#if (!HAVE_PTHREAD_WORKQUEUES || DISPATCH_DEBUG) && \ + !defined(DISPATCH_ENABLE_THREAD_POOL) +#define DISPATCH_ENABLE_THREAD_POOL 1 +#endif -long -dummy_function_r0(void) -{ - return 0; -} +static void _dispatch_cache_cleanup(void *value); +static void _dispatch_async_f_redirect(dispatch_queue_t dq, + dispatch_continuation_t dc); +static void _dispatch_queue_cleanup(void *ctxt); +static bool _dispatch_queue_wakeup_global(dispatch_queue_t dq); +static void _dispatch_queue_drain(dispatch_queue_t dq); +static inline _dispatch_thread_semaphore_t + _dispatch_queue_drain_one_barrier_sync(dispatch_queue_t dq); +static void _dispatch_worker_thread2(void *context); +#if DISPATCH_ENABLE_THREAD_POOL +static void *_dispatch_worker_thread(void *context); +static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset); +#endif +static bool _dispatch_mgr_wakeup(dispatch_queue_t dq); +static dispatch_queue_t _dispatch_mgr_thread(dispatch_queue_t dq); -static bool _dispatch_select_workaround; -static fd_set _dispatch_rfds; -static fd_set _dispatch_wfds; -static void *_dispatch_rfd_ptrs[FD_SETSIZE]; -static void *_dispatch_wfd_ptrs[FD_SETSIZE]; +#if DISPATCH_COCOA_COMPAT +static unsigned int _dispatch_worker_threads; +static dispatch_once_t _dispatch_main_q_port_pred; +static mach_port_t main_q_port; + +static void _dispatch_main_q_port_init(void *ctxt); +static void _dispatch_queue_wakeup_main(void); +static void _dispatch_main_queue_drain(void); +#endif + +#pragma mark - +#pragma mark dispatch_queue_vtable + +const struct dispatch_queue_vtable_s _dispatch_queue_vtable = { + .do_type = DISPATCH_QUEUE_TYPE, + .do_kind = "queue", + .do_dispose = _dispatch_queue_dispose, + .do_invoke = NULL, + .do_probe = (void *)dummy_function_r0, + .do_debug = dispatch_queue_debug, +}; + +static const struct dispatch_queue_vtable_s _dispatch_queue_root_vtable = { + .do_type = DISPATCH_QUEUE_GLOBAL_TYPE, + .do_kind = "global-queue", + .do_debug = dispatch_queue_debug, + .do_probe = _dispatch_queue_wakeup_global, +}; +static const struct dispatch_queue_vtable_s _dispatch_queue_mgr_vtable = { + .do_type = DISPATCH_QUEUE_MGR_TYPE, + .do_kind = "mgr-queue", + .do_invoke = _dispatch_mgr_thread, + .do_debug = dispatch_queue_debug, + .do_probe = _dispatch_mgr_wakeup, +}; + +#pragma mark - +#pragma mark dispatch_root_queue + +#if HAVE_PTHREAD_WORKQUEUES +static const int _dispatch_root_queue_wq_priorities[] = { + [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = WORKQ_LOW_PRIOQUEUE, + [DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY] = WORKQ_LOW_PRIOQUEUE, + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY] = WORKQ_DEFAULT_PRIOQUEUE, + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = + WORKQ_DEFAULT_PRIOQUEUE, + [DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY] = WORKQ_HIGH_PRIOQUEUE, + [DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = WORKQ_HIGH_PRIOQUEUE, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY] = WORKQ_BG_PRIOQUEUE, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY] = + WORKQ_BG_PRIOQUEUE, +}; +#endif +#if DISPATCH_ENABLE_THREAD_POOL static struct dispatch_semaphore_s _dispatch_thread_mediator[] = { - { + [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = { .do_vtable = &_dispatch_semaphore_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, }, - { + [DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_semaphore_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, }, - { + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY] = { .do_vtable = &_dispatch_semaphore_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, }, - { + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_semaphore_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, }, - { + [DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY] = { .do_vtable = &_dispatch_semaphore_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, }, - { + [DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = { + .do_vtable = &_dispatch_semaphore_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + }, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY] = { + .do_vtable = &_dispatch_semaphore_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + }, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_semaphore_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, }, }; - -static struct dispatch_queue_s _dispatch_root_queues[]; - -static inline dispatch_queue_t -_dispatch_get_root_queue(long priority, bool overcommit) -{ - if (overcommit) switch (priority) { - case DISPATCH_QUEUE_PRIORITY_LOW: - return &_dispatch_root_queues[1]; - case DISPATCH_QUEUE_PRIORITY_DEFAULT: - return &_dispatch_root_queues[3]; - case DISPATCH_QUEUE_PRIORITY_HIGH: - return &_dispatch_root_queues[5]; - } - switch (priority) { - case DISPATCH_QUEUE_PRIORITY_LOW: - return &_dispatch_root_queues[0]; - case DISPATCH_QUEUE_PRIORITY_DEFAULT: - return &_dispatch_root_queues[2]; - case DISPATCH_QUEUE_PRIORITY_HIGH: - return &_dispatch_root_queues[4]; - default: - return NULL; - } -} - -#ifdef __BLOCKS__ -dispatch_block_t -_dispatch_Block_copy(dispatch_block_t db) -{ - dispatch_block_t rval; - - while (!(rval = Block_copy(db))) { - sleep(1); - } - - return rval; -} -#define _dispatch_Block_copy(x) ((typeof(x))_dispatch_Block_copy(x)) - -void -_dispatch_call_block_and_release(void *block) -{ - void (^b)(void) = block; - b(); - Block_release(b); -} - -void -_dispatch_call_block_and_release2(void *block, void *ctxt) -{ - void (^b)(void*) = block; - b(ctxt); - Block_release(b); -} - -#endif /* __BLOCKS__ */ - -struct dispatch_queue_attr_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_queue_attr_s); -}; - -struct dispatch_queue_attr_s { - DISPATCH_STRUCT_HEADER(dispatch_queue_attr_s, dispatch_queue_attr_vtable_s); - - // Public: - int qa_priority; - void* finalizer_ctxt; - dispatch_queue_finalizer_function_t finalizer_func; - - // Private: - unsigned long qa_flags; -}; - -static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset); - -#define _dispatch_queue_trylock(dq) dispatch_atomic_cmpxchg(&(dq)->dq_running, 0, 1) -static inline void _dispatch_queue_unlock(dispatch_queue_t dq); -static void _dispatch_queue_invoke(dispatch_queue_t dq); -static void _dispatch_queue_serial_drain_till_empty(dispatch_queue_t dq); -static bool _dispatch_queue_wakeup_global(dispatch_queue_t dq); -static struct dispatch_object_s *_dispatch_queue_concurrent_drain_one(dispatch_queue_t dq); - -static bool _dispatch_program_is_probably_callback_driven; - -#if DISPATCH_COCOA_COMPAT -// dispatch_begin_thread_4GC having non-default value triggers GC-only slow paths and -// is checked frequently, testing against NULL is faster than comparing for equality -// with "dummy_function" -void (*dispatch_begin_thread_4GC)(void) = NULL; -void (*dispatch_end_thread_4GC)(void) = dummy_function; -void *(*_dispatch_begin_NSAutoReleasePool)(void) = (void *)dummy_function; -void (*_dispatch_end_NSAutoReleasePool)(void *) = (void *)dummy_function; -static void _dispatch_queue_wakeup_main(void); - -static dispatch_once_t _dispatch_main_q_port_pred; -static bool main_q_is_draining; -static mach_port_t main_q_port; #endif -static void _dispatch_cache_cleanup2(void *value); -static void _dispatch_force_cache_cleanup(void); - -static const struct dispatch_queue_vtable_s _dispatch_queue_vtable = { - .do_type = DISPATCH_QUEUE_TYPE, - .do_kind = "queue", - .do_dispose = _dispatch_queue_dispose, - .do_invoke = (void *)dummy_function_r0, - .do_probe = (void *)dummy_function_r0, - .do_debug = dispatch_queue_debug, -}; - -static const struct dispatch_queue_vtable_s _dispatch_queue_root_vtable = { - .do_type = DISPATCH_QUEUE_GLOBAL_TYPE, - .do_kind = "global-queue", - .do_debug = dispatch_queue_debug, - .do_probe = _dispatch_queue_wakeup_global, -}; - #define MAX_THREAD_COUNT 255 struct dispatch_root_queue_context_s { +#if HAVE_PTHREAD_WORKQUEUES pthread_workqueue_t dgq_kworkqueue; +#endif uint32_t dgq_pending; +#if DISPATCH_ENABLE_THREAD_POOL uint32_t dgq_thread_pool_size; dispatch_semaphore_t dgq_thread_mediator; +#endif }; -#define DISPATCH_ROOT_QUEUE_COUNT (DISPATCH_QUEUE_PRIORITY_COUNT * 2) static struct dispatch_root_queue_context_s _dispatch_root_queue_contexts[] = { - { - .dgq_thread_mediator = &_dispatch_thread_mediator[0], + [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = { +#if DISPATCH_ENABLE_THREAD_POOL + .dgq_thread_mediator = &_dispatch_thread_mediator[ + DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY], .dgq_thread_pool_size = MAX_THREAD_COUNT, +#endif }, - { - .dgq_thread_mediator = &_dispatch_thread_mediator[1], + [DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY] = { +#if DISPATCH_ENABLE_THREAD_POOL + .dgq_thread_mediator = &_dispatch_thread_mediator[ + DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY], .dgq_thread_pool_size = MAX_THREAD_COUNT, +#endif }, - { - .dgq_thread_mediator = &_dispatch_thread_mediator[2], + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY] = { +#if DISPATCH_ENABLE_THREAD_POOL + .dgq_thread_mediator = &_dispatch_thread_mediator[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY], .dgq_thread_pool_size = MAX_THREAD_COUNT, +#endif }, - { - .dgq_thread_mediator = &_dispatch_thread_mediator[3], + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = { +#if DISPATCH_ENABLE_THREAD_POOL + .dgq_thread_mediator = &_dispatch_thread_mediator[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY], .dgq_thread_pool_size = MAX_THREAD_COUNT, +#endif }, - { - .dgq_thread_mediator = &_dispatch_thread_mediator[4], + [DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY] = { +#if DISPATCH_ENABLE_THREAD_POOL + .dgq_thread_mediator = &_dispatch_thread_mediator[ + DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY], .dgq_thread_pool_size = MAX_THREAD_COUNT, +#endif }, - { - .dgq_thread_mediator = &_dispatch_thread_mediator[5], + [DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = { +#if DISPATCH_ENABLE_THREAD_POOL + .dgq_thread_mediator = &_dispatch_thread_mediator[ + DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY], + .dgq_thread_pool_size = MAX_THREAD_COUNT, +#endif + }, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY] = { +#if DISPATCH_ENABLE_THREAD_POOL + .dgq_thread_mediator = &_dispatch_thread_mediator[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY], + .dgq_thread_pool_size = MAX_THREAD_COUNT, +#endif + }, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY] = { +#if DISPATCH_ENABLE_THREAD_POOL + .dgq_thread_mediator = &_dispatch_thread_mediator[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY], .dgq_thread_pool_size = MAX_THREAD_COUNT, +#endif }, }; -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol // dq_running is set to 2 so that barrier operations go through the slow path -static struct dispatch_queue_s _dispatch_root_queues[] = { - { +DISPATCH_CACHELINE_ALIGN +struct dispatch_queue_s _dispatch_root_queues[] = { + [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, - .do_ctxt = &_dispatch_root_queue_contexts[0], + .do_ctxt = &_dispatch_root_queue_contexts[ + DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY], .dq_label = "com.apple.root.low-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 4, }, - { + [DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, - .do_ctxt = &_dispatch_root_queue_contexts[1], + .do_ctxt = &_dispatch_root_queue_contexts[ + DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.low-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 5, }, - { + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, - .do_ctxt = &_dispatch_root_queue_contexts[2], + .do_ctxt = &_dispatch_root_queue_contexts[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY], .dq_label = "com.apple.root.default-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 6, }, - { + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, - .do_ctxt = &_dispatch_root_queue_contexts[3], + .do_ctxt = &_dispatch_root_queue_contexts[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.default-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 7, }, - { + [DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, - .do_ctxt = &_dispatch_root_queue_contexts[4], + .do_ctxt = &_dispatch_root_queue_contexts[ + DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY], .dq_label = "com.apple.root.high-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 8, }, - { + [DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, - .do_ctxt = &_dispatch_root_queue_contexts[5], + .do_ctxt = &_dispatch_root_queue_contexts[ + DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.high-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 9, }, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY] = { + .do_vtable = &_dispatch_queue_root_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_ctxt = &_dispatch_root_queue_contexts[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY], + + .dq_label = "com.apple.root.background-priority", + .dq_running = 2, + .dq_width = UINT32_MAX, + .dq_serialnum = 10, + }, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY] = { + .do_vtable = &_dispatch_queue_root_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_ctxt = &_dispatch_root_queue_contexts[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY], + + .dq_label = "com.apple.root.background-overcommit-priority", + .dq_running = 2, + .dq_width = UINT32_MAX, + .dq_serialnum = 11, + }, }; -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol -struct dispatch_queue_s _dispatch_main_q = { - .do_vtable = &_dispatch_queue_vtable, +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +DISPATCH_CACHELINE_ALIGN +struct dispatch_queue_s _dispatch_mgr_q = { + .do_vtable = &_dispatch_queue_mgr_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, - .do_targetq = &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_COUNT / 2], + .do_targetq = &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY], - .dq_label = "com.apple.main-thread", - .dq_running = 1, + .dq_label = "com.apple.libdispatch-manager", .dq_width = 1, - .dq_serialnum = 1, + .dq_serialnum = 2, }; -#if DISPATCH_PERF_MON -static OSSpinLock _dispatch_stats_lock; -static size_t _dispatch_bad_ratio; -static struct { - uint64_t time_total; - uint64_t count_total; - uint64_t thread_total; -} _dispatch_stats[65]; // ffs*/fls*() returns zero when no bits are set -static void _dispatch_queue_merge_stats(uint64_t start); -#endif +dispatch_queue_t +dispatch_get_global_queue(long priority, unsigned long flags) +{ + if (flags & ~DISPATCH_QUEUE_OVERCOMMIT) { + return NULL; + } + return _dispatch_get_root_queue(priority, + flags & DISPATCH_QUEUE_OVERCOMMIT); +} -static void *_dispatch_worker_thread(void *context); -static void _dispatch_worker_thread2(void *context); +dispatch_queue_t +dispatch_get_current_queue(void) +{ + return _dispatch_queue_get_current() ?: _dispatch_get_root_queue(0, true); +} -malloc_zone_t *_dispatch_ccache_zone; +#pragma mark - +#pragma mark dispatch_init -static inline void -_dispatch_continuation_free(dispatch_continuation_t dc) +static void +_dispatch_hw_config_init(void) { - dispatch_continuation_t prev_dc = _dispatch_thread_getspecific(dispatch_cache_key); - dc->do_next = prev_dc; - _dispatch_thread_setspecific(dispatch_cache_key, dc); + _dispatch_hw_config.cc_max_active = _dispatch_get_activecpu(); + _dispatch_hw_config.cc_max_logical = _dispatch_get_logicalcpu_max(); + _dispatch_hw_config.cc_max_physical = _dispatch_get_physicalcpu_max(); } -static inline void -_dispatch_continuation_pop(dispatch_object_t dou) +static inline bool +_dispatch_root_queues_init_workq(void) { - dispatch_continuation_t dc = dou._dc; - dispatch_group_t dg; + bool result = false; +#if HAVE_PTHREAD_WORKQUEUES +#if DISPATCH_ENABLE_THREAD_POOL + if (slowpath(getenv("LIBDISPATCH_DISABLE_KWQ"))) return result; +#endif + int i, r; + pthread_workqueue_attr_t pwq_attr; + r = pthread_workqueue_attr_init_np(&pwq_attr); + (void)dispatch_assume_zero(r); + for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { + pthread_workqueue_t pwq = NULL; + const int prio = _dispatch_root_queue_wq_priorities[i]; - if (DISPATCH_OBJ_IS_VTABLE(dou._do)) { - return _dispatch_queue_invoke(dou._dq); + r = pthread_workqueue_attr_setqueuepriority_np(&pwq_attr, prio); + (void)dispatch_assume_zero(r); + r = pthread_workqueue_attr_setovercommit_np(&pwq_attr, i & 1); + (void)dispatch_assume_zero(r); + r = pthread_workqueue_create_np(&pwq, &pwq_attr); + (void)dispatch_assume_zero(r); + result = result || dispatch_assume(pwq); + _dispatch_root_queue_contexts[i].dgq_kworkqueue = pwq; } + r = pthread_workqueue_attr_destroy_np(&pwq_attr); + (void)dispatch_assume_zero(r); +#endif // HAVE_PTHREAD_WORKQUEUES + return result; +} - // Add the item back to the cache before calling the function. This - // allows the 'hot' continuation to be used for a quick callback. - // - // The ccache version is per-thread. - // Therefore, the object has not been reused yet. - // This generates better assembly. - if ((long)dou._do->do_vtable & DISPATCH_OBJ_ASYNC_BIT) { - _dispatch_continuation_free(dc); - } - if ((long)dou._do->do_vtable & DISPATCH_OBJ_GROUP_BIT) { - dg = dc->dc_group; - } else { - dg = NULL; +static inline void +_dispatch_root_queues_init_thread_pool(void) +{ +#if DISPATCH_ENABLE_THREAD_POOL + int i; + for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { +#if TARGET_OS_EMBEDDED + // some software hangs if the non-overcommitting queues do not + // overcommit when threads block. Someday, this behavior should apply + // to all platforms + if (!(i & 1)) { + _dispatch_root_queue_contexts[i].dgq_thread_pool_size = + _dispatch_hw_config.cc_max_active; + } +#endif +#if USE_MACH_SEM + // override the default FIFO behavior for the pool semaphores + kern_return_t kr = semaphore_create(mach_task_self(), + &_dispatch_thread_mediator[i].dsema_port, SYNC_POLICY_LIFO, 0); + DISPATCH_VERIFY_MIG(kr); + (void)dispatch_assume_zero(kr); + (void)dispatch_assume(_dispatch_thread_mediator[i].dsema_port); +#elif USE_POSIX_SEM + /* XXXRW: POSIX semaphores don't support LIFO? */ + int ret = sem_init(&_dispatch_thread_mediator[i].dsema_sem, 0, 0); + (void)dispatch_assume_zero(ret); +#endif } - dc->dc_func(dc->dc_ctxt); - if (dg) { - dispatch_group_leave(dg); - _dispatch_release(dg); +#else + DISPATCH_CRASH("Thread pool creation failed"); +#endif // DISPATCH_ENABLE_THREAD_POOL +} + +static void +_dispatch_root_queues_init(void *context DISPATCH_UNUSED) +{ + if (!_dispatch_root_queues_init_workq()) { + _dispatch_root_queues_init_thread_pool(); } + } -struct dispatch_object_s * -_dispatch_queue_concurrent_drain_one(dispatch_queue_t dq) +#define countof(x) (sizeof(x) / sizeof(x[0])) + +DISPATCH_EXPORT DISPATCH_NOTHROW +void +libdispatch_init(void) { - struct dispatch_object_s *head, *next, *const mediator = (void *)~0ul; + dispatch_assert(DISPATCH_QUEUE_PRIORITY_COUNT == 4); + dispatch_assert(DISPATCH_ROOT_QUEUE_COUNT == 8); + + dispatch_assert(DISPATCH_QUEUE_PRIORITY_LOW == + -DISPATCH_QUEUE_PRIORITY_HIGH); + dispatch_assert(countof(_dispatch_root_queues) == + DISPATCH_ROOT_QUEUE_COUNT); + dispatch_assert(countof(_dispatch_root_queue_contexts) == + DISPATCH_ROOT_QUEUE_COUNT); +#if HAVE_PTHREAD_WORKQUEUES + dispatch_assert(countof(_dispatch_root_queue_wq_priorities) == + DISPATCH_ROOT_QUEUE_COUNT); +#endif +#if DISPATCH_ENABLE_THREAD_POOL + dispatch_assert(countof(_dispatch_thread_mediator) == + DISPATCH_ROOT_QUEUE_COUNT); +#endif + dispatch_assert(sizeof(struct dispatch_source_s) == + sizeof(struct dispatch_queue_s) - DISPATCH_QUEUE_CACHELINE_PAD); +#if DISPATCH_DEBUG + dispatch_assert(sizeof(struct dispatch_queue_s) % DISPATCH_CACHELINE_SIZE + == 0); +#endif - // The mediator value acts both as a "lock" and a signal - head = dispatch_atomic_xchg(&dq->dq_items_head, mediator); + _dispatch_thread_key_create(&dispatch_queue_key, _dispatch_queue_cleanup); + _dispatch_thread_key_create(&dispatch_sema4_key, + (void (*)(void *))_dispatch_thread_semaphore_dispose); + _dispatch_thread_key_create(&dispatch_cache_key, _dispatch_cache_cleanup); + _dispatch_thread_key_create(&dispatch_io_key, NULL); + _dispatch_thread_key_create(&dispatch_apply_key, NULL); +#if DISPATCH_PERF_MON + _dispatch_thread_key_create(&dispatch_bcounter_key, NULL); +#endif - if (slowpath(head == NULL)) { - // The first xchg on the tail will tell the enqueueing thread that it - // is safe to blindly write out to the head pointer. A cmpxchg honors - // the algorithm. - dispatch_atomic_cmpxchg(&dq->dq_items_head, mediator, NULL); - _dispatch_debug("no work on global work queue"); - return NULL; - } +#if DISPATCH_USE_RESOLVERS // rdar://problem/8541707 + _dispatch_main_q.do_vtable = &_dispatch_queue_vtable; + _dispatch_main_q.do_targetq = &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY]; + _dispatch_data_empty.do_vtable = &_dispatch_data_vtable; +#endif - if (slowpath(head == mediator)) { - // This thread lost the race for ownership of the queue. - // - // The ratio of work to libdispatch overhead must be bad. This - // scenario implies that there are too many threads in the pool. - // Create a new pending thread and then exit this thread. - // The kernel will grant a new thread when the load subsides. - _dispatch_debug("Contention on queue: %p", dq); - _dispatch_queue_wakeup_global(dq); -#if DISPATCH_PERF_MON - dispatch_atomic_inc(&_dispatch_bad_ratio); -#endif - return NULL; - } - - // Restore the head pointer to a sane value before returning. - // If 'next' is NULL, then this item _might_ be the last item. - next = fastpath(head->do_next); - - if (slowpath(!next)) { - dq->dq_items_head = NULL; - - if (dispatch_atomic_cmpxchg(&dq->dq_items_tail, head, NULL)) { - // both head and tail are NULL now - goto out; - } - - // There must be a next item now. This thread won't wait long. - while (!(next = head->do_next)) { - _dispatch_hardware_pause(); - } - } - - dq->dq_items_head = next; - _dispatch_queue_wakeup_global(dq); -out: - return head; -} - -dispatch_queue_t -dispatch_get_current_queue(void) -{ - return _dispatch_queue_get_current() ?: _dispatch_get_root_queue(0, true); -} - -#undef dispatch_get_main_queue -__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) -dispatch_queue_t dispatch_get_main_queue(void); - -dispatch_queue_t -dispatch_get_main_queue(void) -{ - return &_dispatch_main_q; -} -#define dispatch_get_main_queue() (&_dispatch_main_q) - -struct _dispatch_hw_config_s _dispatch_hw_config; - -static void -_dispatch_queue_set_width_init(void) -{ - size_t valsz = sizeof(uint32_t); - - errno = 0; - sysctlbyname("hw.activecpu", &_dispatch_hw_config.cc_max_active, &valsz, NULL, 0); - dispatch_assume_zero(errno); - dispatch_assume(valsz == sizeof(uint32_t)); + _dispatch_thread_setspecific(dispatch_queue_key, &_dispatch_main_q); - errno = 0; - sysctlbyname("hw.logicalcpu_max", &_dispatch_hw_config.cc_max_logical, &valsz, NULL, 0); - dispatch_assume_zero(errno); - dispatch_assume(valsz == sizeof(uint32_t)); +#if DISPATCH_USE_PTHREAD_ATFORK + (void)dispatch_assume_zero(pthread_atfork(dispatch_atfork_prepare, + dispatch_atfork_parent, dispatch_atfork_child)); +#endif - errno = 0; - sysctlbyname("hw.physicalcpu_max", &_dispatch_hw_config.cc_max_physical, &valsz, NULL, 0); - dispatch_assume_zero(errno); - dispatch_assume(valsz == sizeof(uint32_t)); + _dispatch_hw_config_init(); } +DISPATCH_EXPORT DISPATCH_NOTHROW void -dispatch_queue_set_width(dispatch_queue_t dq, long width) +dispatch_atfork_child(void) { - int w = (int)width; // intentional truncation - uint32_t tmp; + void *crash = (void *)0x100; + size_t i; - if (slowpath(dq->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { - return; - } - if (w == 1 || w == 0) { - dq->dq_width = 1; + if (_dispatch_safe_fork) { return; } - if (w > 0) { - tmp = w; - } else switch (w) { - case DISPATCH_QUEUE_WIDTH_MAX_PHYSICAL_CPUS: - tmp = _dispatch_hw_config.cc_max_physical; - break; - case DISPATCH_QUEUE_WIDTH_ACTIVE_CPUS: - tmp = _dispatch_hw_config.cc_max_active; - break; - default: - // fall through - case DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS: - tmp = _dispatch_hw_config.cc_max_logical; - break; - } - // multiply by two since the running count is inc/dec by two (the low bit == barrier) - dq->dq_width = tmp * 2; - // XXX if the queue has items and the width is increased, we should try to wake the queue + _dispatch_main_q.dq_items_head = crash; + _dispatch_main_q.dq_items_tail = crash; + + _dispatch_mgr_q.dq_items_head = crash; + _dispatch_mgr_q.dq_items_tail = crash; + + for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { + _dispatch_root_queues[i].dq_items_head = crash; + _dispatch_root_queues[i].dq_items_tail = crash; + } } +#pragma mark - +#pragma mark dispatch_queue_t + // skip zero // 1 - main_q // 2 - mgr_q // 3 - _unused_ -// 4,5,6,7,8,9 - global queues +// 4,5,6,7,8,9,10,11 - global queues // we use 'xadd' on Intel, so the initial value == next assigned -static unsigned long _dispatch_queue_serial_numbers = 10; - -// Note to later developers: ensure that any initialization changes are -// made for statically allocated queues (i.e. _dispatch_main_q). -inline void -_dispatch_queue_init(dispatch_queue_t dq) -{ - dq->do_vtable = &_dispatch_queue_vtable; - dq->do_next = DISPATCH_OBJECT_LISTLESS; - dq->do_ref_cnt = 1; - dq->do_xref_cnt = 1; - dq->do_targetq = _dispatch_get_root_queue(0, true); - dq->dq_running = 0; - dq->dq_width = 1; - dq->dq_serialnum = dispatch_atomic_inc(&_dispatch_queue_serial_numbers) - 1; -} +unsigned long _dispatch_queue_serial_numbers = 12; dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) @@ -542,7 +550,9 @@ dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) } // XXX switch to malloc() - dq = calloc(1ul, sizeof(struct dispatch_queue_s) - DISPATCH_QUEUE_MIN_LABEL_SIZE + label_len + 1); + dq = calloc(1ul, sizeof(struct dispatch_queue_s) - + DISPATCH_QUEUE_MIN_LABEL_SIZE - DISPATCH_QUEUE_CACHELINE_PAD + + label_len + 1); if (slowpath(!dq)) { return dq; } @@ -550,31 +560,20 @@ dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) _dispatch_queue_init(dq); strcpy(dq->dq_label, label); -#ifndef DISPATCH_NO_LEGACY - if (slowpath(attr)) { - dq->do_targetq = _dispatch_get_root_queue(attr->qa_priority, attr->qa_flags & DISPATCH_QUEUE_OVERCOMMIT); - dq->dq_finalizer_ctxt = attr->finalizer_ctxt; - dq->dq_finalizer_func = attr->finalizer_func; -#ifdef __BLOCKS__ - if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) { - // if finalizer_ctxt is a Block, retain it. - dq->dq_finalizer_ctxt = Block_copy(dq->dq_finalizer_ctxt); - if (!(dq->dq_finalizer_ctxt)) { - goto out_bad; - } - } -#endif + if (fastpath(!attr)) { + return dq; + } + if (fastpath(attr == DISPATCH_QUEUE_CONCURRENT)) { + dq->dq_width = UINT32_MAX; + dq->do_targetq = _dispatch_get_root_queue(0, false); + } else { + dispatch_debug_assert(!attr, "Invalid attribute"); } -#endif - return dq; - -out_bad: - free(dq); - return NULL; } -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol void _dispatch_queue_dispose(dispatch_queue_t dq) { @@ -585,276 +584,989 @@ _dispatch_queue_dispose(dispatch_queue_t dq) DISPATCH_CRASH("Release of a queue while items are enqueued"); } -#ifndef DISPATCH_NO_LEGACY - if (dq->dq_finalizer_func) { - dq->dq_finalizer_func(dq->dq_finalizer_ctxt, dq); - } -#endif - // trash the tail queue so that use after free will crash dq->dq_items_tail = (void *)0x200; + dispatch_queue_t dqsq = dispatch_atomic_xchg2o(dq, dq_specific_q, + (void *)0x200); + if (dqsq) { + _dispatch_release(dqsq); + } + _dispatch_dispose(dq); } -DISPATCH_NOINLINE -void -_dispatch_queue_push_list_slow(dispatch_queue_t dq, struct dispatch_object_s *obj) +const char * +dispatch_queue_get_label(dispatch_queue_t dq) { - // 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. - _dispatch_retain(dq); - dq->dq_items_head = obj; - _dispatch_wakeup(dq); - _dispatch_release(dq); + return dq->dq_label; } -DISPATCH_NOINLINE static void -_dispatch_barrier_async_f_slow(dispatch_queue_t dq, void *context, dispatch_function_t func) +_dispatch_queue_set_width2(void *ctxt) { - dispatch_continuation_t dc = fastpath(_dispatch_continuation_alloc_from_heap()); - - dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT); - dc->dc_func = func; - dc->dc_ctxt = context; - - _dispatch_queue_push(dq, dc); -} + int w = (int)(intptr_t)ctxt; // intentional truncation + uint32_t tmp; + dispatch_queue_t dq = _dispatch_queue_get_current(); -#ifdef __BLOCKS__ -void -dispatch_barrier_async(dispatch_queue_t dq, void (^work)(void)) -{ - dispatch_barrier_async_f(dq, _dispatch_Block_copy(work), _dispatch_call_block_and_release); + if (w == 1 || w == 0) { + dq->dq_width = 1; + return; + } + if (w > 0) { + tmp = w; + } else switch (w) { + case DISPATCH_QUEUE_WIDTH_MAX_PHYSICAL_CPUS: + tmp = _dispatch_hw_config.cc_max_physical; + break; + case DISPATCH_QUEUE_WIDTH_ACTIVE_CPUS: + tmp = _dispatch_hw_config.cc_max_active; + break; + default: + // fall through + case DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS: + tmp = _dispatch_hw_config.cc_max_logical; + break; + } + // multiply by two since the running count is inc/dec by two + // (the low bit == barrier) + dq->dq_width = tmp * 2; } -#endif -DISPATCH_NOINLINE void -dispatch_barrier_async_f(dispatch_queue_t dq, void *context, dispatch_function_t func) +dispatch_queue_set_width(dispatch_queue_t dq, long width) { - dispatch_continuation_t dc = fastpath(_dispatch_continuation_alloc_cacheonly()); - - if (!dc) { - return _dispatch_barrier_async_f_slow(dq, context, func); + if (slowpath(dq->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + return; } - - dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT); - dc->dc_func = func; - dc->dc_ctxt = context; - - _dispatch_queue_push(dq, dc); + dispatch_barrier_async_f(dq, (void*)(intptr_t)width, + _dispatch_queue_set_width2); } -DISPATCH_NOINLINE +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol static void -_dispatch_async_f_slow(dispatch_queue_t dq, void *context, dispatch_function_t func) +_dispatch_set_target_queue2(void *ctxt) { - dispatch_continuation_t dc = fastpath(_dispatch_continuation_alloc_from_heap()); - - dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; - dc->dc_func = func; - dc->dc_ctxt = context; + dispatch_queue_t prev_dq, dq = _dispatch_queue_get_current(); - _dispatch_queue_push(dq, dc); + prev_dq = dq->do_targetq; + dq->do_targetq = ctxt; + _dispatch_release(prev_dq); } -#ifdef __BLOCKS__ void -dispatch_async(dispatch_queue_t dq, void (^work)(void)) +dispatch_set_target_queue(dispatch_object_t dou, dispatch_queue_t dq) { - dispatch_async_f(dq, _dispatch_Block_copy(work), _dispatch_call_block_and_release); + dispatch_queue_t prev_dq; + unsigned long type; + + if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + return; + } + type = dx_type(dou._do) & _DISPATCH_META_TYPE_MASK; + if (slowpath(!dq)) { + bool is_concurrent_q = (type == _DISPATCH_QUEUE_TYPE && + slowpath(dou._dq->dq_width > 1)); + dq = _dispatch_get_root_queue(0, !is_concurrent_q); + } + // TODO: put into the vtable + switch(type) { + case _DISPATCH_QUEUE_TYPE: + case _DISPATCH_SOURCE_TYPE: + _dispatch_retain(dq); + return dispatch_barrier_async_f(dou._dq, dq, + _dispatch_set_target_queue2); + case _DISPATCH_IO_TYPE: + return _dispatch_io_set_target_queue(dou._dchannel, dq); + default: + _dispatch_retain(dq); + dispatch_atomic_store_barrier(); + prev_dq = dispatch_atomic_xchg2o(dou._do, do_targetq, dq); + if (prev_dq) _dispatch_release(prev_dq); + return; + } } -#endif -DISPATCH_NOINLINE void -dispatch_async_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +dispatch_set_current_target_queue(dispatch_queue_t dq) { - dispatch_continuation_t dc = fastpath(_dispatch_continuation_alloc_cacheonly()); + dispatch_queue_t queue = _dispatch_queue_get_current(); - // unlike dispatch_sync_f(), we do NOT need to check the queue width, - // the "drain" function will do this test - - if (!dc) { - return _dispatch_async_f_slow(dq, ctxt, func); + if (slowpath(!queue)) { + DISPATCH_CLIENT_CRASH("SPI not called from a queue"); + } + if (slowpath(queue->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + DISPATCH_CLIENT_CRASH("SPI not supported on this queue"); + } + if (slowpath(queue->dq_width != 1)) { + DISPATCH_CLIENT_CRASH("SPI not called from a serial queue"); + } + if (slowpath(!dq)) { + dq = _dispatch_get_root_queue(0, true); } + _dispatch_retain(dq); + _dispatch_set_target_queue2(dq); +} + +#pragma mark - +#pragma mark dispatch_queue_specific + +struct dispatch_queue_specific_queue_s { + DISPATCH_STRUCT_HEADER(dispatch_queue_specific_queue_s, + dispatch_queue_specific_queue_vtable_s); + DISPATCH_QUEUE_HEADER; + union { + char _dqsq_pad[DISPATCH_QUEUE_MIN_LABEL_SIZE]; + struct { + char dq_label[16]; + TAILQ_HEAD(dispatch_queue_specific_head_s, + dispatch_queue_specific_s) dqsq_contexts; + }; + }; +}; +DISPATCH_DECL(dispatch_queue_specific_queue); - dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; - dc->dc_func = func; - dc->dc_ctxt = ctxt; +static void +_dispatch_queue_specific_queue_dispose(dispatch_queue_specific_queue_t dqsq); - _dispatch_queue_push(dq, dc); -} +struct dispatch_queue_specific_queue_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_queue_specific_queue_s); +}; -struct dispatch_barrier_sync_slow2_s { - dispatch_queue_t dbss2_dq; -#if DISPATCH_COCOA_COMPAT - dispatch_function_t dbss2_func; - dispatch_function_t dbss2_ctxt; -#endif - dispatch_semaphore_t dbss2_sema; +static const struct dispatch_queue_specific_queue_vtable_s + _dispatch_queue_specific_queue_vtable = { + .do_type = DISPATCH_QUEUE_SPECIFIC_TYPE, + .do_kind = "queue-context", + .do_dispose = _dispatch_queue_specific_queue_dispose, + .do_invoke = NULL, + .do_probe = (void *)dummy_function_r0, + .do_debug = (void *)dispatch_queue_debug, +}; + +struct dispatch_queue_specific_s { + const void *dqs_key; + void *dqs_ctxt; + dispatch_function_t dqs_destructor; + TAILQ_ENTRY(dispatch_queue_specific_s) dqs_list; }; +DISPATCH_DECL(dispatch_queue_specific); static void -_dispatch_barrier_sync_f_slow_invoke(void *ctxt) +_dispatch_queue_specific_queue_dispose(dispatch_queue_specific_queue_t dqsq) { - struct dispatch_barrier_sync_slow2_s *dbss2 = ctxt; + dispatch_queue_specific_t dqs, tmp; - dispatch_assert(dbss2->dbss2_dq == dispatch_get_current_queue()); -#if DISPATCH_COCOA_COMPAT - // When the main queue is bound to the main thread - if (dbss2->dbss2_dq == &_dispatch_main_q && pthread_main_np()) { - dbss2->dbss2_func(dbss2->dbss2_ctxt); - dbss2->dbss2_func = NULL; - dispatch_semaphore_signal(dbss2->dbss2_sema); - return; + TAILQ_FOREACH_SAFE(dqs, &dqsq->dqsq_contexts, dqs_list, tmp) { + if (dqs->dqs_destructor) { + dispatch_async_f(_dispatch_get_root_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, false), dqs->dqs_ctxt, + dqs->dqs_destructor); + } + free(dqs); } -#endif - dispatch_suspend(dbss2->dbss2_dq); - dispatch_semaphore_signal(dbss2->dbss2_sema); + _dispatch_queue_dispose((dispatch_queue_t)dqsq); } -DISPATCH_NOINLINE static void -_dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +_dispatch_queue_init_specific(dispatch_queue_t dq) { - - // 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, -#if DISPATCH_COCOA_COMPAT - .dbss2_func = func, - .dbss2_ctxt = ctxt, -#endif - .dbss2_sema = _dispatch_get_thread_semaphore(), - }; - struct dispatch_barrier_sync_slow_s { - DISPATCH_CONTINUATION_HEADER(dispatch_barrier_sync_slow_s); - } dbss = { - .do_vtable = (void *)DISPATCH_OBJ_BARRIER_BIT, - .dc_func = _dispatch_barrier_sync_f_slow_invoke, - .dc_ctxt = &dbss2, - }; - - _dispatch_queue_push(dq, (void *)&dbss); - dispatch_semaphore_wait(dbss2.dbss2_sema, DISPATCH_TIME_FOREVER); - _dispatch_put_thread_semaphore(dbss2.dbss2_sema); + dispatch_queue_specific_queue_t dqsq; -#if DISPATCH_COCOA_COMPAT - // Main queue bound to main thread - if (dbss2.dbss2_func == NULL) { - return; + dqsq = calloc(1ul, sizeof(struct dispatch_queue_specific_queue_s)); + _dispatch_queue_init((dispatch_queue_t)dqsq); + dqsq->do_vtable = &_dispatch_queue_specific_queue_vtable; + dqsq->do_xref_cnt = 0; + dqsq->do_targetq = _dispatch_get_root_queue(DISPATCH_QUEUE_PRIORITY_HIGH, + true); + dqsq->dq_width = UINT32_MAX; + strlcpy(dqsq->dq_label, "queue-specific", sizeof(dqsq->dq_label)); + TAILQ_INIT(&dqsq->dqsq_contexts); + dispatch_atomic_store_barrier(); + if (slowpath(!dispatch_atomic_cmpxchg2o(dq, dq_specific_q, NULL, dqsq))) { + _dispatch_release((dispatch_queue_t)dqsq); } -#endif - dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); - _dispatch_thread_setspecific(dispatch_queue_key, dq); - func(ctxt); - _dispatch_workitem_inc(); - _dispatch_thread_setspecific(dispatch_queue_key, old_dq); - dispatch_resume(dq); } -#ifdef __BLOCKS__ -#if DISPATCH_COCOA_COMPAT +static void +_dispatch_queue_set_specific(void *ctxt) +{ + dispatch_queue_specific_t dqs, dqsn = ctxt; + dispatch_queue_specific_queue_t dqsq = + (dispatch_queue_specific_queue_t)_dispatch_queue_get_current(); + + TAILQ_FOREACH(dqs, &dqsq->dqsq_contexts, dqs_list) { + if (dqs->dqs_key == dqsn->dqs_key) { + // Destroy previous context for existing key + if (dqs->dqs_destructor) { + dispatch_async_f(_dispatch_get_root_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, false), dqs->dqs_ctxt, + dqs->dqs_destructor); + } + if (dqsn->dqs_ctxt) { + // Copy new context for existing key + dqs->dqs_ctxt = dqsn->dqs_ctxt; + dqs->dqs_destructor = dqsn->dqs_destructor; + } else { + // Remove context storage for existing key + TAILQ_REMOVE(&dqsq->dqsq_contexts, dqs, dqs_list); + free(dqs); + } + return free(dqsn); + } + } + // Insert context storage for new key + TAILQ_INSERT_TAIL(&dqsq->dqsq_contexts, dqsn, dqs_list); +} + DISPATCH_NOINLINE +void +dispatch_queue_set_specific(dispatch_queue_t dq, const void *key, + void *ctxt, dispatch_function_t destructor) +{ + if (slowpath(!key)) { + return; + } + dispatch_queue_specific_t dqs; + + dqs = calloc(1, sizeof(struct dispatch_queue_specific_s)); + dqs->dqs_key = key; + dqs->dqs_ctxt = ctxt; + dqs->dqs_destructor = destructor; + if (slowpath(!dq->dq_specific_q)) { + _dispatch_queue_init_specific(dq); + } + dispatch_barrier_async_f(dq->dq_specific_q, dqs, + _dispatch_queue_set_specific); +} + static void -_dispatch_barrier_sync_slow(dispatch_queue_t dq, void (^work)(void)) +_dispatch_queue_get_specific(void *ctxt) +{ + void **ctxtp = ctxt; + void *key = *ctxtp; + dispatch_queue_specific_queue_t dqsq = + (dispatch_queue_specific_queue_t)_dispatch_queue_get_current(); + dispatch_queue_specific_t dqs; + + TAILQ_FOREACH(dqs, &dqsq->dqsq_contexts, dqs_list) { + if (dqs->dqs_key == key) { + *ctxtp = dqs->dqs_ctxt; + return; + } + } + *ctxtp = NULL; +} + +DISPATCH_NOINLINE +void * +dispatch_queue_get_specific(dispatch_queue_t dq, const void *key) { - // Blocks submitted to the main queue MUST be run on the main thread, - // therefore under GC we must Block_copy in order to notify the thread-local - // garbage collector that the objects are transferring to the main thread - // rdar://problem/7176237&7181849&7458685 - if (dispatch_begin_thread_4GC) { - dispatch_block_t block = _dispatch_Block_copy(work); - return dispatch_barrier_sync_f(dq, block, _dispatch_call_block_and_release); + if (slowpath(!key)) { + return NULL; + } + void *ctxt = NULL; + + if (fastpath(dq->dq_specific_q)) { + ctxt = (void *)key; + dispatch_sync_f(dq->dq_specific_q, &ctxt, _dispatch_queue_get_specific); + } + return ctxt; +} + +DISPATCH_NOINLINE +void * +dispatch_get_specific(const void *key) +{ + if (slowpath(!key)) { + return NULL; + } + void *ctxt = NULL; + dispatch_queue_t dq = _dispatch_queue_get_current(); + + while (slowpath(dq)) { + if (slowpath(dq->dq_specific_q)) { + ctxt = (void *)key; + dispatch_sync_f(dq->dq_specific_q, &ctxt, + _dispatch_queue_get_specific); + if (ctxt) break; + } + dq = dq->do_targetq; + } + return ctxt; +} + +#pragma mark - +#pragma mark dispatch_queue_debug + +size_t +_dispatch_queue_debug_attr(dispatch_queue_t dq, char* buf, size_t bufsiz) +{ + dispatch_queue_t target = dq->do_targetq; + return snprintf(buf, bufsiz, "target = %s[%p], width = 0x%x, " + "running = 0x%x, barrier = %d ", target ? target->dq_label : "", + target, dq->dq_width / 2, dq->dq_running / 2, dq->dq_running & 1); +} + +size_t +dispatch_queue_debug(dispatch_queue_t dq, char* buf, size_t bufsiz) +{ + size_t offset = 0; + offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", + dq->dq_label, dq); + offset += _dispatch_object_debug_attr(dq, &buf[offset], bufsiz - offset); + offset += _dispatch_queue_debug_attr(dq, &buf[offset], bufsiz - offset); + offset += snprintf(&buf[offset], bufsiz - offset, "}"); + return offset; +} + +#if DISPATCH_DEBUG +void +dispatch_debug_queue(dispatch_queue_t dq, const char* str) { + if (fastpath(dq)) { + dispatch_debug(dq, "%s", str); + } else { + _dispatch_log("queue[NULL]: %s", str); + } +} +#endif + +#if DISPATCH_PERF_MON +static OSSpinLock _dispatch_stats_lock; +static size_t _dispatch_bad_ratio; +static struct { + uint64_t time_total; + uint64_t count_total; + uint64_t thread_total; +} _dispatch_stats[65]; // ffs*/fls*() returns zero when no bits are set + +static void +_dispatch_queue_merge_stats(uint64_t start) +{ + uint64_t avg, delta = _dispatch_absolute_time() - start; + unsigned long count, bucket; + + count = (size_t)_dispatch_thread_getspecific(dispatch_bcounter_key); + _dispatch_thread_setspecific(dispatch_bcounter_key, NULL); + + if (count) { + avg = delta / count; + bucket = flsll(avg); + } else { + bucket = 0; + } + + // 64-bit counters on 32-bit require a lock or a queue + OSSpinLockLock(&_dispatch_stats_lock); + + _dispatch_stats[bucket].time_total += delta; + _dispatch_stats[bucket].count_total += count; + _dispatch_stats[bucket].thread_total++; + + OSSpinLockUnlock(&_dispatch_stats_lock); +} +#endif + +#pragma mark - +#pragma mark dispatch_continuation_t + +static malloc_zone_t *_dispatch_ccache_zone; + +static void +_dispatch_ccache_init(void *context DISPATCH_UNUSED) +{ + _dispatch_ccache_zone = malloc_create_zone(0, 0); + dispatch_assert(_dispatch_ccache_zone); + malloc_set_zone_name(_dispatch_ccache_zone, "DispatchContinuations"); +} + +static dispatch_continuation_t +_dispatch_continuation_alloc_from_heap(void) +{ + static dispatch_once_t pred; + dispatch_continuation_t dc; + + dispatch_once_f(&pred, NULL, _dispatch_ccache_init); + + while (!(dc = fastpath(malloc_zone_calloc(_dispatch_ccache_zone, 1, + ROUND_UP_TO_CACHELINE_SIZE(sizeof(*dc)))))) { + sleep(1); + } + + return dc; +} + +DISPATCH_ALWAYS_INLINE +static inline dispatch_continuation_t +_dispatch_continuation_alloc_cacheonly(void) +{ + dispatch_continuation_t dc; + dc = fastpath(_dispatch_thread_getspecific(dispatch_cache_key)); + if (dc) { + _dispatch_thread_setspecific(dispatch_cache_key, dc->do_next); + } + return dc; +} + +static void +_dispatch_force_cache_cleanup(void) +{ + dispatch_continuation_t dc; + dc = _dispatch_thread_getspecific(dispatch_cache_key); + if (dc) { + _dispatch_thread_setspecific(dispatch_cache_key, NULL); + _dispatch_cache_cleanup(dc); + } +} + +DISPATCH_NOINLINE +static void +_dispatch_cache_cleanup(void *value) +{ + dispatch_continuation_t dc, next_dc = value; + + while ((dc = next_dc)) { + next_dc = dc->do_next; + malloc_zone_free(_dispatch_ccache_zone, dc); + } +} + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_continuation_free(dispatch_continuation_t dc) +{ + dispatch_continuation_t prev_dc; + prev_dc = _dispatch_thread_getspecific(dispatch_cache_key); + dc->do_next = prev_dc; + _dispatch_thread_setspecific(dispatch_cache_key, dc); +} + +DISPATCH_ALWAYS_INLINE_NDEBUG +static inline void +_dispatch_continuation_redirect(dispatch_queue_t dq, dispatch_object_t dou) +{ + dispatch_continuation_t dc = dou._dc; + + _dispatch_trace_continuation_pop(dq, dou); + (void)dispatch_atomic_add2o(dq, dq_running, 2); + if (!DISPATCH_OBJ_IS_VTABLE(dc) && + (long)dc->do_vtable & DISPATCH_OBJ_SYNC_SLOW_BIT) { + dispatch_atomic_barrier(); + _dispatch_thread_semaphore_signal( + (_dispatch_thread_semaphore_t)dc->dc_ctxt); + } else { + _dispatch_async_f_redirect(dq, dc); + } +} + +DISPATCH_ALWAYS_INLINE_NDEBUG +static inline void +_dispatch_continuation_pop(dispatch_object_t dou) +{ + dispatch_continuation_t dc = dou._dc; + dispatch_group_t dg; + + _dispatch_trace_continuation_pop(_dispatch_queue_get_current(), dou); + if (DISPATCH_OBJ_IS_VTABLE(dou._do)) { + return _dispatch_queue_invoke(dou._dq); + } + + // Add the item back to the cache before calling the function. This + // allows the 'hot' continuation to be used for a quick callback. + // + // The ccache version is per-thread. + // Therefore, the object has not been reused yet. + // This generates better assembly. + if ((long)dc->do_vtable & DISPATCH_OBJ_ASYNC_BIT) { + _dispatch_continuation_free(dc); + } + if ((long)dc->do_vtable & DISPATCH_OBJ_GROUP_BIT) { + dg = dc->dc_group; + } else { + dg = NULL; + } + _dispatch_client_callout(dc->dc_ctxt, dc->dc_func); + if (dg) { + dispatch_group_leave(dg); + _dispatch_release(dg); + } +} + +#pragma mark - +#pragma mark dispatch_barrier_async + +DISPATCH_NOINLINE +static void +_dispatch_barrier_async_f_slow(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + dispatch_continuation_t dc = _dispatch_continuation_alloc_from_heap(); + + dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT); + dc->dc_func = func; + dc->dc_ctxt = ctxt; + + _dispatch_queue_push(dq, dc); +} + +DISPATCH_NOINLINE +void +dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + dispatch_continuation_t dc; + + dc = fastpath(_dispatch_continuation_alloc_cacheonly()); + if (!dc) { + return _dispatch_barrier_async_f_slow(dq, ctxt, func); + } + + dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT); + dc->dc_func = func; + dc->dc_ctxt = ctxt; + + _dispatch_queue_push(dq, dc); +} + +#ifdef __BLOCKS__ +void +dispatch_barrier_async(dispatch_queue_t dq, void (^work)(void)) +{ + dispatch_barrier_async_f(dq, _dispatch_Block_copy(work), + _dispatch_call_block_and_release); +} +#endif + +#pragma mark - +#pragma mark dispatch_async + +static void +_dispatch_async_f_redirect_invoke(void *_ctxt) +{ + struct dispatch_continuation_s *dc = _ctxt; + struct dispatch_continuation_s *other_dc = dc->dc_data[1]; + dispatch_queue_t old_dq, dq = dc->dc_data[0], rq; + + old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + _dispatch_thread_setspecific(dispatch_queue_key, dq); + _dispatch_continuation_pop(other_dc); + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); + + rq = dq->do_targetq; + while (slowpath(rq->do_targetq) && rq != old_dq) { + if (dispatch_atomic_sub2o(rq, dq_running, 2) == 0) { + _dispatch_wakeup(rq); + } + rq = rq->do_targetq; + } + + if (dispatch_atomic_sub2o(dq, dq_running, 2) == 0) { + _dispatch_wakeup(dq); + } + _dispatch_release(dq); +} + +DISPATCH_NOINLINE +static void +_dispatch_async_f2_slow(dispatch_queue_t dq, dispatch_continuation_t dc) +{ + _dispatch_wakeup(dq); + _dispatch_queue_push(dq, dc); +} + +DISPATCH_NOINLINE +static void +_dispatch_async_f_redirect(dispatch_queue_t dq, + dispatch_continuation_t other_dc) +{ + dispatch_continuation_t dc; + dispatch_queue_t rq; + + _dispatch_retain(dq); + + dc = fastpath(_dispatch_continuation_alloc_cacheonly()); + if (!dc) { + dc = _dispatch_continuation_alloc_from_heap(); + } + + dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; + dc->dc_func = _dispatch_async_f_redirect_invoke; + dc->dc_ctxt = dc; + dc->dc_data[0] = dq; + dc->dc_data[1] = other_dc; + + // Find the queue to redirect to + rq = dq->do_targetq; + while (slowpath(rq->do_targetq)) { + uint32_t running; + + if (slowpath(rq->dq_items_tail) || + slowpath(DISPATCH_OBJECT_SUSPENDED(rq)) || + slowpath(rq->dq_width == 1)) { + break; + } + running = dispatch_atomic_add2o(rq, dq_running, 2) - 2; + if (slowpath(running & 1) || slowpath(running + 2 > rq->dq_width)) { + if (slowpath(dispatch_atomic_sub2o(rq, dq_running, 2) == 0)) { + return _dispatch_async_f2_slow(rq, dc); + } + break; + } + rq = rq->do_targetq; + } + _dispatch_queue_push(rq, dc); +} + +DISPATCH_NOINLINE +static void +_dispatch_async_f2(dispatch_queue_t dq, dispatch_continuation_t dc) +{ + uint32_t running; + bool locked; + + do { + if (slowpath(dq->dq_items_tail) + || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))) { + break; + } + running = dispatch_atomic_add2o(dq, dq_running, 2); + if (slowpath(running > dq->dq_width)) { + if (slowpath(dispatch_atomic_sub2o(dq, dq_running, 2) == 0)) { + return _dispatch_async_f2_slow(dq, dc); + } + break; + } + locked = running & 1; + if (fastpath(!locked)) { + return _dispatch_async_f_redirect(dq, dc); + } + locked = dispatch_atomic_sub2o(dq, dq_running, 2) & 1; + // We might get lucky and find that the barrier has ended by now + } while (!locked); + + _dispatch_queue_push(dq, dc); +} + +DISPATCH_NOINLINE +static void +_dispatch_async_f_slow(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + dispatch_continuation_t dc = _dispatch_continuation_alloc_from_heap(); + + dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; + dc->dc_func = func; + dc->dc_ctxt = ctxt; + + // No fastpath/slowpath hint because we simply don't know + if (dq->do_targetq) { + return _dispatch_async_f2(dq, dc); + } + + _dispatch_queue_push(dq, dc); +} + +DISPATCH_NOINLINE +void +dispatch_async_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +{ + dispatch_continuation_t dc; + + // No fastpath/slowpath hint because we simply don't know + if (dq->dq_width == 1) { + return dispatch_barrier_async_f(dq, ctxt, func); + } + + dc = fastpath(_dispatch_continuation_alloc_cacheonly()); + if (!dc) { + return _dispatch_async_f_slow(dq, ctxt, func); + } + + dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; + dc->dc_func = func; + dc->dc_ctxt = ctxt; + + // No fastpath/slowpath hint because we simply don't know + if (dq->do_targetq) { + return _dispatch_async_f2(dq, dc); + } + + _dispatch_queue_push(dq, dc); +} + +#ifdef __BLOCKS__ +void +dispatch_async(dispatch_queue_t dq, void (^work)(void)) +{ + dispatch_async_f(dq, _dispatch_Block_copy(work), + _dispatch_call_block_and_release); +} +#endif + +#pragma mark - +#pragma mark dispatch_group_async + +DISPATCH_NOINLINE +void +dispatch_group_async_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + dispatch_continuation_t dc; + + _dispatch_retain(dg); + dispatch_group_enter(dg); + + dc = fastpath(_dispatch_continuation_alloc_cacheonly()); + if (!dc) { + dc = _dispatch_continuation_alloc_from_heap(); + } + + dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_GROUP_BIT); + dc->dc_func = func; + dc->dc_ctxt = ctxt; + dc->dc_group = dg; + + // No fastpath/slowpath hint because we simply don't know + if (dq->dq_width != 1 && dq->do_targetq) { + return _dispatch_async_f2(dq, dc); } - struct Block_basic *bb = (void *)work; - dispatch_barrier_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); + + _dispatch_queue_push(dq, dc); } -#endif +#ifdef __BLOCKS__ void -dispatch_barrier_sync(dispatch_queue_t dq, void (^work)(void)) +dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, + dispatch_block_t db) { -#if DISPATCH_COCOA_COMPAT - if (slowpath(dq == &_dispatch_main_q)) { - return _dispatch_barrier_sync_slow(dq, work); - } -#endif - struct Block_basic *bb = (void *)work; - dispatch_barrier_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); + dispatch_group_async_f(dg, dq, _dispatch_Block_copy(db), + _dispatch_call_block_and_release); } #endif -DISPATCH_NOINLINE -void -dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +#pragma mark - +#pragma mark dispatch_function_invoke + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_function_invoke(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); - - // 1) ensure that this thread hasn't enqueued anything ahead of this call - // 2) the queue is not suspended - // 3) the queue is not weird - if (slowpath(dq->dq_items_tail) - || slowpath(DISPATCH_OBJECT_SUSPENDED(dq)) - || slowpath(!_dispatch_queue_trylock(dq))) { - return _dispatch_barrier_sync_f_slow(dq, ctxt, func); - } - _dispatch_thread_setspecific(dispatch_queue_key, dq); - func(ctxt); + _dispatch_client_callout(ctxt, func); _dispatch_workitem_inc(); _dispatch_thread_setspecific(dispatch_queue_key, old_dq); - _dispatch_queue_unlock(dq); } +struct dispatch_function_recurse_s { + dispatch_queue_t dfr_dq; + void* dfr_ctxt; + dispatch_function_t dfr_func; +}; + static void -_dispatch_sync_f_slow2(void *ctxt) +_dispatch_function_recurse_invoke(void *ctxt) { - dispatch_queue_t dq = _dispatch_queue_get_current(); - dispatch_atomic_add(&dq->dq_running, 2); - dispatch_semaphore_signal(ctxt); + struct dispatch_function_recurse_s *dfr = ctxt; + _dispatch_function_invoke(dfr->dfr_dq, dfr->dfr_ctxt, dfr->dfr_func); } -DISPATCH_NOINLINE +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_function_recurse(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + struct dispatch_function_recurse_s dfr = { + .dfr_dq = dq, + .dfr_func = func, + .dfr_ctxt = ctxt, + }; + dispatch_sync_f(dq->do_targetq, &dfr, _dispatch_function_recurse_invoke); +} + +#pragma mark - +#pragma mark dispatch_barrier_sync + +struct dispatch_barrier_sync_slow_s { + DISPATCH_CONTINUATION_HEADER(dispatch_barrier_sync_slow_s); +}; + +struct dispatch_barrier_sync_slow2_s { + dispatch_queue_t dbss2_dq; +#if DISPATCH_COCOA_COMPAT + dispatch_function_t dbss2_func; + void *dbss2_ctxt; +#endif + _dispatch_thread_semaphore_t dbss2_sema; +}; + static void -_dispatch_sync_f_slow(dispatch_queue_t dq) +_dispatch_barrier_sync_f_slow_invoke(void *ctxt) { - // the global root queues do not need strict ordering - if (dq->do_targetq == NULL) { - dispatch_atomic_add(&dq->dq_running, 2); + struct dispatch_barrier_sync_slow2_s *dbss2 = ctxt; + + dispatch_assert(dbss2->dbss2_dq == _dispatch_queue_get_current()); +#if DISPATCH_COCOA_COMPAT + // When the main queue is bound to the main thread + if (dbss2->dbss2_dq == &_dispatch_main_q && pthread_main_np()) { + dbss2->dbss2_func(dbss2->dbss2_ctxt); + dbss2->dbss2_func = NULL; + dispatch_atomic_barrier(); + _dispatch_thread_semaphore_signal(dbss2->dbss2_sema); return; } +#endif + (void)dispatch_atomic_add2o(dbss2->dbss2_dq, do_suspend_cnt, + DISPATCH_OBJECT_SUSPEND_INTERVAL); + // rdar://9032024 running lock must be held until sync_f_slow returns + (void)dispatch_atomic_add2o(dbss2->dbss2_dq, dq_running, 2); + dispatch_atomic_barrier(); + _dispatch_thread_semaphore_signal(dbss2->dbss2_sema); +} - struct dispatch_sync_slow_s { - DISPATCH_CONTINUATION_HEADER(dispatch_sync_slow_s); - } dss = { - .do_vtable = NULL, - .dc_func = _dispatch_sync_f_slow2, - .dc_ctxt = _dispatch_get_thread_semaphore(), +DISPATCH_NOINLINE +static void +_dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + // It's preferred to execute synchronous blocks on the current thread + // due to thread-local side effects, garbage collection, etc. However, + // blocks submitted to the main thread MUST be run on the main thread + + struct dispatch_barrier_sync_slow2_s dbss2 = { + .dbss2_dq = dq, +#if DISPATCH_COCOA_COMPAT + .dbss2_func = func, + .dbss2_ctxt = ctxt, +#endif + .dbss2_sema = _dispatch_get_thread_semaphore(), + }; + struct dispatch_barrier_sync_slow_s dbss = { + .do_vtable = (void *)(DISPATCH_OBJ_BARRIER_BIT | + DISPATCH_OBJ_SYNC_SLOW_BIT), + .dc_func = _dispatch_barrier_sync_f_slow_invoke, + .dc_ctxt = &dbss2, }; + _dispatch_queue_push(dq, (void *)&dbss); - // XXX FIXME -- concurrent queues can be come serial again - _dispatch_queue_push(dq, (void *)&dss); + _dispatch_thread_semaphore_wait(dbss2.dbss2_sema); + _dispatch_put_thread_semaphore(dbss2.dbss2_sema); + +#if DISPATCH_COCOA_COMPAT + // Main queue bound to main thread + if (dbss2.dbss2_func == NULL) { + return; + } +#endif + dispatch_atomic_acquire_barrier(); + if (slowpath(dq->do_targetq) && slowpath(dq->do_targetq->do_targetq)) { + _dispatch_function_recurse(dq, ctxt, func); + } else { + _dispatch_function_invoke(dq, ctxt, func); + } + dispatch_atomic_release_barrier(); + if (fastpath(dq->do_suspend_cnt < 2 * DISPATCH_OBJECT_SUSPEND_INTERVAL)) { + // rdar://problem/8290662 "lock transfer" + // ensure drain of current barrier sync has finished + while (slowpath(dq->dq_running > 2)) { + _dispatch_hardware_pause(); + } + _dispatch_thread_semaphore_t sema; + sema = _dispatch_queue_drain_one_barrier_sync(dq); + if (sema) { + _dispatch_thread_semaphore_signal(sema); + return; + } + } + (void)dispatch_atomic_sub2o(dq, do_suspend_cnt, + DISPATCH_OBJECT_SUSPEND_INTERVAL); + if (slowpath(dispatch_atomic_sub2o(dq, dq_running, 2) == 0)) { + _dispatch_wakeup(dq); + } +} + +DISPATCH_NOINLINE +static void +_dispatch_barrier_sync_f2(dispatch_queue_t dq) +{ + if (!slowpath(DISPATCH_OBJECT_SUSPENDED(dq))) { + // rdar://problem/8290662 "lock transfer" + _dispatch_thread_semaphore_t sema; + sema = _dispatch_queue_drain_one_barrier_sync(dq); + if (sema) { + (void)dispatch_atomic_add2o(dq, do_suspend_cnt, + DISPATCH_OBJECT_SUSPEND_INTERVAL); + // rdar://9032024 running lock must be held until sync_f_slow + // returns: increment by 2 and decrement by 1 + (void)dispatch_atomic_inc2o(dq, dq_running); + _dispatch_thread_semaphore_signal(sema); + return; + } + } + if (slowpath(dispatch_atomic_dec2o(dq, dq_running) == 0)) { + _dispatch_wakeup(dq); + } +} + +DISPATCH_NOINLINE +static void +_dispatch_barrier_sync_f_invoke(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + dispatch_atomic_acquire_barrier(); + _dispatch_function_invoke(dq, ctxt, func); + dispatch_atomic_release_barrier(); + if (slowpath(dq->dq_items_tail)) { + return _dispatch_barrier_sync_f2(dq); + } + if (slowpath(dispatch_atomic_dec2o(dq, dq_running) == 0)) { + _dispatch_wakeup(dq); + } +} + +DISPATCH_NOINLINE +static void +_dispatch_barrier_sync_f_recurse(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + dispatch_atomic_acquire_barrier(); + _dispatch_function_recurse(dq, ctxt, func); + dispatch_atomic_release_barrier(); + if (slowpath(dq->dq_items_tail)) { + return _dispatch_barrier_sync_f2(dq); + } + if (slowpath(dispatch_atomic_dec2o(dq, dq_running) == 0)) { + _dispatch_wakeup(dq); + } +} - dispatch_semaphore_wait(dss.dc_ctxt, DISPATCH_TIME_FOREVER); - _dispatch_put_thread_semaphore(dss.dc_ctxt); +DISPATCH_NOINLINE +void +dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) +{ + // 1) ensure that this thread hasn't enqueued anything ahead of this call + // 2) the queue is not suspended + if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))){ + return _dispatch_barrier_sync_f_slow(dq, ctxt, func); + } + if (slowpath(!dispatch_atomic_cmpxchg2o(dq, dq_running, 0, 1))) { + // global queues and main queue bound to main thread always falls into + // the slow case + return _dispatch_barrier_sync_f_slow(dq, ctxt, func); + } + if (slowpath(dq->do_targetq->do_targetq)) { + return _dispatch_barrier_sync_f_recurse(dq, ctxt, func); + } + _dispatch_barrier_sync_f_invoke(dq, ctxt, func); } #ifdef __BLOCKS__ #if DISPATCH_COCOA_COMPAT DISPATCH_NOINLINE static void -_dispatch_sync_slow(dispatch_queue_t dq, void (^work)(void)) +_dispatch_barrier_sync_slow(dispatch_queue_t dq, void (^work)(void)) { // Blocks submitted to the main queue MUST be run on the main thread, // therefore under GC we must Block_copy in order to notify the thread-local @@ -862,218 +1574,257 @@ _dispatch_sync_slow(dispatch_queue_t dq, void (^work)(void)) // rdar://problem/7176237&7181849&7458685 if (dispatch_begin_thread_4GC) { dispatch_block_t block = _dispatch_Block_copy(work); - return dispatch_sync_f(dq, block, _dispatch_call_block_and_release); + return dispatch_barrier_sync_f(dq, block, + _dispatch_call_block_and_release); } struct Block_basic *bb = (void *)work; - dispatch_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); + dispatch_barrier_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); } #endif void -dispatch_sync(dispatch_queue_t dq, void (^work)(void)) +dispatch_barrier_sync(dispatch_queue_t dq, void (^work)(void)) { #if DISPATCH_COCOA_COMPAT if (slowpath(dq == &_dispatch_main_q)) { - return _dispatch_sync_slow(dq, work); + return _dispatch_barrier_sync_slow(dq, work); } #endif struct Block_basic *bb = (void *)work; - dispatch_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); + dispatch_barrier_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); } #endif +#pragma mark - +#pragma mark dispatch_sync + DISPATCH_NOINLINE -void -dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +static void +_dispatch_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { - typeof(dq->dq_running) prev_cnt; - dispatch_queue_t old_dq; + _dispatch_thread_semaphore_t sema = _dispatch_get_thread_semaphore(); + struct dispatch_sync_slow_s { + DISPATCH_CONTINUATION_HEADER(dispatch_sync_slow_s); + } dss = { + .do_vtable = (void*)DISPATCH_OBJ_SYNC_SLOW_BIT, + .dc_ctxt = (void*)sema, + }; + _dispatch_queue_push(dq, (void *)&dss); - if (dq->dq_width == 1) { - return dispatch_barrier_sync_f(dq, ctxt, func); - } + _dispatch_thread_semaphore_wait(sema); + _dispatch_put_thread_semaphore(sema); - // 1) ensure that this thread hasn't enqueued anything ahead of this call - // 2) the queue is not suspended - if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))) { - _dispatch_sync_f_slow(dq); + if (slowpath(dq->do_targetq->do_targetq)) { + _dispatch_function_recurse(dq, ctxt, func); } else { - prev_cnt = dispatch_atomic_add(&dq->dq_running, 2) - 2; - - if (slowpath(prev_cnt & 1)) { - if (dispatch_atomic_sub(&dq->dq_running, 2) == 0) { - _dispatch_wakeup(dq); - } - _dispatch_sync_f_slow(dq); - } + _dispatch_function_invoke(dq, ctxt, func); } - - old_dq = _dispatch_thread_getspecific(dispatch_queue_key); - _dispatch_thread_setspecific(dispatch_queue_key, dq); - func(ctxt); - _dispatch_workitem_inc(); - _dispatch_thread_setspecific(dispatch_queue_key, old_dq); - - if (slowpath(dispatch_atomic_sub(&dq->dq_running, 2) == 0)) { + if (slowpath(dispatch_atomic_sub2o(dq, dq_running, 2) == 0)) { _dispatch_wakeup(dq); } } -const char * -dispatch_queue_get_label(dispatch_queue_t dq) -{ - return dq->dq_label; -} - -#if DISPATCH_COCOA_COMPAT +DISPATCH_NOINLINE static void -_dispatch_main_q_port_init(void *ctxt __attribute__((unused))) +_dispatch_sync_f_slow2(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { - kern_return_t kr; - - kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &main_q_port); - DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); - kr = mach_port_insert_right(mach_task_self(), main_q_port, main_q_port, MACH_MSG_TYPE_MAKE_SEND); - DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); - - _dispatch_program_is_probably_callback_driven = true; - _dispatch_safe_fork = false; + if (slowpath(dispatch_atomic_sub2o(dq, dq_running, 2) == 0)) { + _dispatch_wakeup(dq); + } + _dispatch_sync_f_slow(dq, ctxt, func); } -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol DISPATCH_NOINLINE static void -_dispatch_queue_set_mainq_drain_state(bool arg) +_dispatch_sync_f_invoke(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { - main_q_is_draining = arg; + _dispatch_function_invoke(dq, ctxt, func); + if (slowpath(dispatch_atomic_sub2o(dq, dq_running, 2) == 0)) { + _dispatch_wakeup(dq); + } } -#endif -void -dispatch_main(void) +DISPATCH_NOINLINE +static void +_dispatch_sync_f_recurse(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { - if (pthread_main_np()) { - _dispatch_program_is_probably_callback_driven = true; - pthread_exit(NULL); - DISPATCH_CRASH("pthread_exit() returned"); + _dispatch_function_recurse(dq, ctxt, func); + if (slowpath(dispatch_atomic_sub2o(dq, dq_running, 2) == 0)) { + _dispatch_wakeup(dq); } - DISPATCH_CLIENT_CRASH("dispatch_main() must be called on the main thread"); } +DISPATCH_NOINLINE static void -_dispatch_sigsuspend(void *ctxt __attribute__((unused))) +_dispatch_sync_f2(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { - static const sigset_t mask; - - for (;;) { - sigsuspend(&mask); + // 1) ensure that this thread hasn't enqueued anything ahead of this call + // 2) the queue is not suspended + if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))){ + return _dispatch_sync_f_slow(dq, ctxt, func); + } + if (slowpath(dispatch_atomic_add2o(dq, dq_running, 2) & 1)) { + return _dispatch_sync_f_slow2(dq, ctxt, func); + } + if (slowpath(dq->do_targetq->do_targetq)) { + return _dispatch_sync_f_recurse(dq, ctxt, func); } + _dispatch_sync_f_invoke(dq, ctxt, func); } DISPATCH_NOINLINE -static void -_dispatch_queue_cleanup2(void) +void +dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { - dispatch_atomic_dec(&_dispatch_main_q.dq_running); - - if (dispatch_atomic_sub(&_dispatch_main_q.do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK) == 0) { - _dispatch_wakeup(&_dispatch_main_q); + if (fastpath(dq->dq_width == 1)) { + return dispatch_barrier_sync_f(dq, ctxt, func); } - - // overload the "probably" variable to mean that dispatch_main() or - // similar non-POSIX API was called - // this has to run before the DISPATCH_COCOA_COMPAT below - if (_dispatch_program_is_probably_callback_driven) { - dispatch_async_f(_dispatch_get_root_queue(0, 0), NULL, _dispatch_sigsuspend); - sleep(1); // workaround 6778970 + if (slowpath(!dq->do_targetq)) { + // the global root queues do not need strict ordering + (void)dispatch_atomic_add2o(dq, dq_running, 2); + return _dispatch_sync_f_invoke(dq, ctxt, func); } + _dispatch_sync_f2(dq, ctxt, func); +} +#ifdef __BLOCKS__ #if DISPATCH_COCOA_COMPAT - dispatch_once_f(&_dispatch_main_q_port_pred, NULL, _dispatch_main_q_port_init); - - mach_port_t mp = main_q_port; - kern_return_t kr; - - main_q_port = 0; - - if (mp) { - kr = mach_port_deallocate(mach_task_self(), mp); - DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); - kr = mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); - DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); +DISPATCH_NOINLINE +static void +_dispatch_sync_slow(dispatch_queue_t dq, void (^work)(void)) +{ + // Blocks submitted to the main queue MUST be run on the main thread, + // therefore under GC we must Block_copy in order to notify the thread-local + // garbage collector that the objects are transferring to the main thread + // rdar://problem/7176237&7181849&7458685 + if (dispatch_begin_thread_4GC) { + dispatch_block_t block = _dispatch_Block_copy(work); + return dispatch_sync_f(dq, block, _dispatch_call_block_and_release); } -#endif + struct Block_basic *bb = (void *)work; + dispatch_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); } +#endif -dispatch_queue_t -dispatch_get_concurrent_queue(long pri) +void +dispatch_sync(dispatch_queue_t dq, void (^work)(void)) { - if (pri > 0) { - pri = DISPATCH_QUEUE_PRIORITY_HIGH; - } else if (pri < 0) { - pri = DISPATCH_QUEUE_PRIORITY_LOW; +#if DISPATCH_COCOA_COMPAT + if (slowpath(dq == &_dispatch_main_q)) { + return _dispatch_sync_slow(dq, work); } - return _dispatch_get_root_queue(pri, false); +#endif + struct Block_basic *bb = (void *)work; + dispatch_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); } +#endif + +#pragma mark - +#pragma mark dispatch_after + +struct _dispatch_after_time_s { + void *datc_ctxt; + void (*datc_func)(void *); + dispatch_source_t ds; +}; static void -_dispatch_queue_cleanup(void *ctxt) +_dispatch_after_timer_callback(void *ctxt) { - if (ctxt == &_dispatch_main_q) { - return _dispatch_queue_cleanup2(); - } - // POSIX defines that destructors are only called if 'ctxt' is non-null - DISPATCH_CRASH("Premature thread exit while a dispatch queue is running"); -} + struct _dispatch_after_time_s *datc = ctxt; -dispatch_queue_t -dispatch_get_global_queue(long priority, unsigned long flags) -{ - if (flags & ~DISPATCH_QUEUE_OVERCOMMIT) { - return NULL; - } - return _dispatch_get_root_queue(priority, flags & DISPATCH_QUEUE_OVERCOMMIT); + dispatch_assert(datc->datc_func); + _dispatch_client_callout(datc->datc_ctxt, datc->datc_func); + + dispatch_source_t ds = datc->ds; + free(datc); + + dispatch_source_cancel(ds); // Needed until 7287561 gets integrated + dispatch_release(ds); } -#define countof(x) (sizeof(x) / sizeof(x[0])) +DISPATCH_NOINLINE void -libdispatch_init(void) +dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *ctxt, + dispatch_function_t func) { - dispatch_assert(DISPATCH_QUEUE_PRIORITY_COUNT == 3); - dispatch_assert(DISPATCH_ROOT_QUEUE_COUNT == 6); - - dispatch_assert(DISPATCH_QUEUE_PRIORITY_LOW == -DISPATCH_QUEUE_PRIORITY_HIGH); - dispatch_assert(countof(_dispatch_root_queues) == DISPATCH_ROOT_QUEUE_COUNT); - dispatch_assert(countof(_dispatch_thread_mediator) == DISPATCH_ROOT_QUEUE_COUNT); - dispatch_assert(countof(_dispatch_root_queue_contexts) == DISPATCH_ROOT_QUEUE_COUNT); + uint64_t delta; + struct _dispatch_after_time_s *datc = NULL; + dispatch_source_t ds; - _dispatch_thread_key_init_np(dispatch_queue_key, _dispatch_queue_cleanup); - _dispatch_thread_key_init_np(dispatch_sema4_key, (void (*)(void *))dispatch_release); // use the extern release - _dispatch_thread_key_init_np(dispatch_cache_key, _dispatch_cache_cleanup2); -#if DISPATCH_PERF_MON - _dispatch_thread_key_init_np(dispatch_bcounter_key, NULL); + if (when == DISPATCH_TIME_FOREVER) { +#if DISPATCH_DEBUG + DISPATCH_CLIENT_CRASH( + "dispatch_after_f() called with 'when' == infinity"); #endif + return; + } - _dispatch_thread_setspecific(dispatch_queue_key, &_dispatch_main_q); + // this function can and should be optimized to not use a dispatch source + delta = _dispatch_timeout(when); + if (delta == 0) { + return dispatch_async_f(queue, ctxt, func); + } + // on successful creation, source owns malloc-ed context (which it frees in + // the event handler) + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + dispatch_assert(ds); - _dispatch_queue_set_width_init(); + datc = malloc(sizeof(*datc)); + dispatch_assert(datc); + datc->datc_ctxt = ctxt; + datc->datc_func = func; + datc->ds = ds; + + dispatch_set_context(ds, datc); + dispatch_source_set_event_handler_f(ds, _dispatch_after_timer_callback); + dispatch_source_set_timer(ds, when, DISPATCH_TIME_FOREVER, 0); + dispatch_resume(ds); } +#ifdef __BLOCKS__ void -_dispatch_queue_unlock(dispatch_queue_t dq) +dispatch_after(dispatch_time_t when, dispatch_queue_t queue, + dispatch_block_t work) { - if (slowpath(dispatch_atomic_dec(&dq->dq_running))) { + // test before the copy of the block + if (when == DISPATCH_TIME_FOREVER) { +#if DISPATCH_DEBUG + DISPATCH_CLIENT_CRASH( + "dispatch_after() called with 'when' == infinity"); +#endif return; } + dispatch_after_f(when, queue, _dispatch_Block_copy(work), + _dispatch_call_block_and_release); +} +#endif +#pragma mark - +#pragma mark dispatch_wakeup + +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. + _dispatch_retain(dq); + dq->dq_items_head = obj; _dispatch_wakeup(dq); + _dispatch_release(dq); } -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol dispatch_queue_t _dispatch_wakeup(dispatch_object_t dou) { @@ -1086,7 +1837,11 @@ _dispatch_wakeup(dispatch_object_t dou) return NULL; } - if (!_dispatch_trylock(dou._do)) { + // _dispatch_source_invoke() relies on this testing the whole suspend count + // word, not just the lock bit. In other words, no point taking the lock + // if the source is suspended or canceled. + if (!dispatch_atomic_cmpxchg2o(dou._do, do_suspend_cnt, 0, + DISPATCH_OBJECT_SUSPEND_LOCK)) { #if DISPATCH_COCOA_COMPAT if (dou._dq == &_dispatch_main_q) { _dispatch_queue_wakeup_main(); @@ -1097,7 +1852,8 @@ _dispatch_wakeup(dispatch_object_t dou) _dispatch_retain(dou._do); tq = dou._do->do_targetq; _dispatch_queue_push(tq, dou._do); - return tq; // libdispatch doesn't need this, but the Instrument DTrace probe does + return tq; // libdispatch does not need this, but the Instrument DTrace + // probe does } #if DISPATCH_COCOA_COMPAT @@ -1107,7 +1863,8 @@ _dispatch_queue_wakeup_main(void) { kern_return_t kr; - dispatch_once_f(&_dispatch_main_q_port_pred, NULL, _dispatch_main_q_port_init); + dispatch_once_f(&_dispatch_main_q_port_pred, NULL, + _dispatch_main_q_port_init); kr = _dispatch_send_wakeup_main_thread(main_q_port, 0); @@ -1117,7 +1874,7 @@ _dispatch_queue_wakeup_main(void) case MACH_SEND_INVALID_DEST: break; default: - dispatch_assume_zero(kr); + (void)dispatch_assume_zero(kr); break; } @@ -1125,78 +1882,12 @@ _dispatch_queue_wakeup_main(void) } #endif -static inline int -_dispatch_rootq2wq_pri(long idx) -{ -#ifdef WORKQ_DEFAULT_PRIOQUEUE - switch (idx) { - case 0: - case 1: - return WORKQ_LOW_PRIOQUEUE; - case 2: - case 3: - default: - return WORKQ_DEFAULT_PRIOQUEUE; - case 4: - case 5: - return WORKQ_HIGH_PRIOQUEUE; - } -#else - return pri; -#endif -} - -static void -_dispatch_root_queues_init(void *context __attribute__((unused))) -{ - bool disable_wq = getenv("LIBDISPATCH_DISABLE_KWQ"); - pthread_workqueue_attr_t pwq_attr; - kern_return_t kr; - int i, r; - - r = pthread_workqueue_attr_init_np(&pwq_attr); - dispatch_assume_zero(r); - - for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { - r = pthread_workqueue_attr_setqueuepriority_np(&pwq_attr, _dispatch_rootq2wq_pri(i)); - dispatch_assume_zero(r); - r = pthread_workqueue_attr_setovercommit_np(&pwq_attr, i & 1); - dispatch_assume_zero(r); -// some software hangs if the non-overcommitting queues do not overcommit when threads block -#if 0 - if (!(i & 1)) { - dispatch_root_queue_contexts[i].dgq_thread_pool_size = _dispatch_hw_config.cc_max_active; - } -#endif - - r = 0; - if (disable_wq || (r = pthread_workqueue_create_np(&_dispatch_root_queue_contexts[i].dgq_kworkqueue, &pwq_attr))) { - if (r != ENOTSUP) { - dispatch_assume_zero(r); - } - // override the default FIFO behavior for the pool semaphores - kr = semaphore_create(mach_task_self(), &_dispatch_thread_mediator[i].dsema_port, SYNC_POLICY_LIFO, 0); - DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); - dispatch_assume(_dispatch_thread_mediator[i].dsema_port); - } else { - dispatch_assume(_dispatch_root_queue_contexts[i].dgq_kworkqueue); - } - } - - r = pthread_workqueue_attr_destroy_np(&pwq_attr); - dispatch_assume_zero(r); -} - -bool +static bool _dispatch_queue_wakeup_global(dispatch_queue_t dq) { static dispatch_once_t pred; struct dispatch_root_queue_context_s *qc = dq->do_ctxt; - pthread_workitem_handle_t wh; - unsigned int gen_cnt; - pthread_t pthr; - int r, t_count; + int r; if (!dq->dq_items_tail) { return false; @@ -1208,200 +1899,146 @@ _dispatch_queue_wakeup_global(dispatch_queue_t dq) dispatch_once_f(&pred, NULL, _dispatch_root_queues_init); - if (qc->dgq_kworkqueue) { - if (dispatch_atomic_cmpxchg(&qc->dgq_pending, 0, 1)) { +#if HAVE_PTHREAD_WORKQUEUES +#if DISPATCH_ENABLE_THREAD_POOL + if (qc->dgq_kworkqueue) +#endif + { + if (dispatch_atomic_cmpxchg2o(qc, dgq_pending, 0, 1)) { + pthread_workitem_handle_t wh; + unsigned int gen_cnt; _dispatch_debug("requesting new worker thread"); - r = pthread_workqueue_additem_np(qc->dgq_kworkqueue, _dispatch_worker_thread2, dq, &wh, &gen_cnt); - dispatch_assume_zero(r); + r = pthread_workqueue_additem_np(qc->dgq_kworkqueue, + _dispatch_worker_thread2, dq, &wh, &gen_cnt); + (void)dispatch_assume_zero(r); } else { - _dispatch_debug("work thread request still pending on global queue: %p", dq); + _dispatch_debug("work thread request still pending on global " + "queue: %p", dq); } goto out; } - +#endif // HAVE_PTHREAD_WORKQUEUES +#if DISPATCH_ENABLE_THREAD_POOL if (dispatch_semaphore_signal(qc->dgq_thread_mediator)) { goto out; } + pthread_t pthr; + int t_count; do { t_count = qc->dgq_thread_pool_size; if (!t_count) { _dispatch_debug("The thread pool is full: %p", dq); goto out; } - } while (!dispatch_atomic_cmpxchg(&qc->dgq_thread_pool_size, t_count, t_count - 1)); + } while (!dispatch_atomic_cmpxchg2o(qc, dgq_thread_pool_size, t_count, + t_count - 1)); while ((r = pthread_create(&pthr, NULL, _dispatch_worker_thread, dq))) { if (r != EAGAIN) { - dispatch_assume_zero(r); + (void)dispatch_assume_zero(r); } sleep(1); } r = pthread_detach(pthr); - dispatch_assume_zero(r); + (void)dispatch_assume_zero(r); +#endif // DISPATCH_ENABLE_THREAD_POOL out: return false; } -void -_dispatch_queue_serial_drain_till_empty(dispatch_queue_t dq) -{ -#if DISPATCH_PERF_MON - uint64_t start = mach_absolute_time(); -#endif - _dispatch_queue_drain(dq); -#if DISPATCH_PERF_MON - _dispatch_queue_merge_stats(start); -#endif - _dispatch_force_cache_cleanup(); -} +#pragma mark - +#pragma mark dispatch_queue_drain -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol DISPATCH_NOINLINE void _dispatch_queue_invoke(dispatch_queue_t dq) { - dispatch_queue_t tq = dq->do_targetq; - - if (!slowpath(DISPATCH_OBJECT_SUSPENDED(dq)) && fastpath(_dispatch_queue_trylock(dq))) { + if (!slowpath(DISPATCH_OBJECT_SUSPENDED(dq)) && + fastpath(dispatch_atomic_cmpxchg2o(dq, dq_running, 0, 1))) { + dispatch_atomic_acquire_barrier(); + dispatch_queue_t otq = dq->do_targetq, tq = NULL; _dispatch_queue_drain(dq); - if (tq == dq->do_targetq) { + if (dq->do_vtable->do_invoke) { + // Assume that object invoke checks it is executing on correct queue tq = dx_invoke(dq); - } else { + } else if (slowpath(otq != dq->do_targetq)) { + // An item on the queue changed the target queue tq = dq->do_targetq; } // We do not need to check the result. // When the suspend-count lock is dropped, then the check will happen. - dispatch_atomic_dec(&dq->dq_running); + dispatch_atomic_release_barrier(); + (void)dispatch_atomic_dec2o(dq, dq_running); if (tq) { return _dispatch_queue_push(tq, dq); } } dq->do_next = DISPATCH_OBJECT_LISTLESS; - if (dispatch_atomic_sub(&dq->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK) == 0) { + if (!dispatch_atomic_sub2o(dq, do_suspend_cnt, + DISPATCH_OBJECT_SUSPEND_LOCK)) { if (dq->dq_running == 0) { - _dispatch_wakeup(dq); // verify that the queue is idle + _dispatch_wakeup(dq); // verify that the queue is idle } } - _dispatch_release(dq); // added when the queue is put on the list -} - -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol -static void -_dispatch_set_target_queue2(void *ctxt) -{ - dispatch_queue_t prev_dq, dq = _dispatch_queue_get_current(); - - prev_dq = dq->do_targetq; - dq->do_targetq = ctxt; - _dispatch_release(prev_dq); -} - -void -dispatch_set_target_queue(dispatch_object_t dou, dispatch_queue_t dq) -{ - if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { - return; - } - // NOTE: we test for NULL target queues internally to detect root queues - // therefore, if the retain crashes due to a bad input, that is OK - _dispatch_retain(dq); - dispatch_barrier_async_f(dou._dq, dq, _dispatch_set_target_queue2); -} - -static void -_dispatch_async_f_redirect2(void *_ctxt) -{ - struct dispatch_continuation_s *dc = _ctxt; - struct dispatch_continuation_s *other_dc = dc->dc_data[1]; - dispatch_queue_t old_dq, dq = dc->dc_data[0]; - - old_dq = _dispatch_thread_getspecific(dispatch_queue_key); - _dispatch_thread_setspecific(dispatch_queue_key, dq); - _dispatch_continuation_pop(other_dc); - _dispatch_thread_setspecific(dispatch_queue_key, old_dq); - - if (dispatch_atomic_sub(&dq->dq_running, 2) == 0) { - _dispatch_wakeup(dq); - } - _dispatch_release(dq); + _dispatch_release(dq); // added when the queue is put on the list } static void -_dispatch_async_f_redirect(dispatch_queue_t dq, struct dispatch_object_s *other_dc) -{ - dispatch_continuation_t dc = (void *)other_dc; - dispatch_queue_t root_dq = dq; - - if (dc->dc_func == _dispatch_sync_f_slow2) { - return dc->dc_func(dc->dc_ctxt); - } - - dispatch_atomic_add(&dq->dq_running, 2); - _dispatch_retain(dq); - - dc = _dispatch_continuation_alloc_cacheonly() ?: _dispatch_continuation_alloc_from_heap(); - - dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; - dc->dc_func = _dispatch_async_f_redirect2; - dc->dc_ctxt = dc; - dc->dc_data[0] = dq; - dc->dc_data[1] = other_dc; - - do { - root_dq = root_dq->do_targetq; - } while (root_dq->do_targetq); - - _dispatch_queue_push(root_dq, dc); -} - - -void _dispatch_queue_drain(dispatch_queue_t dq) { - dispatch_queue_t orig_tq, old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + dispatch_queue_t orig_tq, old_dq; + old_dq = _dispatch_thread_getspecific(dispatch_queue_key); struct dispatch_object_s *dc = NULL, *next_dc = NULL; + // Continue draining sources after target queue change rdar://8928171 + bool check_tq = (dx_type(dq) != DISPATCH_SOURCE_KEVENT_TYPE); + orig_tq = dq->do_targetq; _dispatch_thread_setspecific(dispatch_queue_key, dq); + //dispatch_debug_queue(dq, __PRETTY_FUNCTION__); while (dq->dq_items_tail) { - while (!fastpath(dq->dq_items_head)) { + while (!(dc = fastpath(dq->dq_items_head))) { _dispatch_hardware_pause(); } - - dc = dq->dq_items_head; dq->dq_items_head = NULL; - do { - // Enqueue is TIGHTLY controlled, we won't wait long. - do { - next_dc = fastpath(dc->do_next); - } while (!next_dc && !dispatch_atomic_cmpxchg(&dq->dq_items_tail, dc, NULL)); + next_dc = fastpath(dc->do_next); + if (!next_dc && + !dispatch_atomic_cmpxchg2o(dq, dq_items_tail, dc, NULL)) { + // Enqueue is TIGHTLY controlled, we won't wait long. + while (!(next_dc = fastpath(dc->do_next))) { + _dispatch_hardware_pause(); + } + } if (DISPATCH_OBJECT_SUSPENDED(dq)) { goto out; } if (dq->dq_running > dq->dq_width) { goto out; } - if (orig_tq != dq->do_targetq) { + if (slowpath(orig_tq != dq->do_targetq) && check_tq) { goto out; } if (fastpath(dq->dq_width == 1)) { _dispatch_continuation_pop(dc); _dispatch_workitem_inc(); - } else if ((long)dc->do_vtable & DISPATCH_OBJ_BARRIER_BIT) { + } else if (!DISPATCH_OBJ_IS_VTABLE(dc) && + (long)dc->do_vtable & DISPATCH_OBJ_BARRIER_BIT) { if (dq->dq_running > 1) { goto out; } _dispatch_continuation_pop(dc); _dispatch_workitem_inc(); } else { - _dispatch_async_f_redirect(dq, dc); + _dispatch_continuation_redirect(dq, dc); } } while ((dc = next_dc)); } @@ -1411,56 +2048,193 @@ out: if (slowpath(dc)) { // 'dc' must NOT be "popped" // 'dc' might be the last item - if (next_dc || dispatch_atomic_cmpxchg(&dq->dq_items_tail, NULL, dc)) { - dq->dq_items_head = dc; - } else { - while (!(next_dc = dq->dq_items_head)) { + if (!next_dc && + !dispatch_atomic_cmpxchg2o(dq, dq_items_tail, NULL, dc)) { + // wait for enqueue slow path to finish + while (!(next_dc = fastpath(dq->dq_items_head))) { _dispatch_hardware_pause(); } - dq->dq_items_head = dc; dc->do_next = next_dc; } + dq->dq_items_head = dc; } _dispatch_thread_setspecific(dispatch_queue_key, old_dq); } -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol -void * -_dispatch_worker_thread(void *context) +static void +_dispatch_queue_serial_drain_till_empty(dispatch_queue_t dq) { - dispatch_queue_t dq = context; - struct dispatch_root_queue_context_s *qc = dq->do_ctxt; - sigset_t mask; - int r; +#if DISPATCH_PERF_MON + uint64_t start = _dispatch_absolute_time(); +#endif + _dispatch_queue_drain(dq); +#if DISPATCH_PERF_MON + _dispatch_queue_merge_stats(start); +#endif + _dispatch_force_cache_cleanup(); +} + +#if DISPATCH_COCOA_COMPAT +void +_dispatch_main_queue_drain(void) +{ + dispatch_queue_t dq = &_dispatch_main_q; + if (!dq->dq_items_tail) { + return; + } + struct dispatch_main_queue_drain_marker_s { + DISPATCH_CONTINUATION_HEADER(dispatch_main_queue_drain_marker_s); + } marker = { + .do_vtable = NULL, + }; + struct dispatch_object_s *dmarker = (void*)▮ + _dispatch_queue_push_notrace(dq, dmarker); + +#if DISPATCH_PERF_MON + uint64_t start = _dispatch_absolute_time(); +#endif + dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + _dispatch_thread_setspecific(dispatch_queue_key, dq); + + struct dispatch_object_s *dc = NULL, *next_dc = NULL; + while (dq->dq_items_tail) { + while (!(dc = fastpath(dq->dq_items_head))) { + _dispatch_hardware_pause(); + } + dq->dq_items_head = NULL; + do { + next_dc = fastpath(dc->do_next); + if (!next_dc && + !dispatch_atomic_cmpxchg2o(dq, dq_items_tail, dc, NULL)) { + // Enqueue is TIGHTLY controlled, we won't wait long. + while (!(next_dc = fastpath(dc->do_next))) { + _dispatch_hardware_pause(); + } + } + if (dc == dmarker) { + if (next_dc) { + dq->dq_items_head = next_dc; + _dispatch_queue_wakeup_main(); + } + goto out; + } + _dispatch_continuation_pop(dc); + _dispatch_workitem_inc(); + } while ((dc = next_dc)); + } + dispatch_assert(dc); // did not encounter marker + +out: + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); +#if DISPATCH_PERF_MON + _dispatch_queue_merge_stats(start); +#endif + _dispatch_force_cache_cleanup(); +} +#endif + +DISPATCH_ALWAYS_INLINE_NDEBUG +static inline _dispatch_thread_semaphore_t +_dispatch_queue_drain_one_barrier_sync(dispatch_queue_t dq) +{ + // rdar://problem/8290662 "lock transfer" + struct dispatch_object_s *dc, *next_dc; + + // queue is locked, or suspended and not being drained + dc = dq->dq_items_head; + if (slowpath(!dc) || DISPATCH_OBJ_IS_VTABLE(dc) || ((long)dc->do_vtable & + (DISPATCH_OBJ_BARRIER_BIT | DISPATCH_OBJ_SYNC_SLOW_BIT)) != + (DISPATCH_OBJ_BARRIER_BIT | DISPATCH_OBJ_SYNC_SLOW_BIT)) { + return 0; + } + // dequeue dc, it is a barrier sync + next_dc = fastpath(dc->do_next); + dq->dq_items_head = next_dc; + if (!next_dc && !dispatch_atomic_cmpxchg2o(dq, dq_items_tail, dc, NULL)) { + // Enqueue is TIGHTLY controlled, we won't wait long. + while (!(next_dc = fastpath(dc->do_next))) { + _dispatch_hardware_pause(); + } + dq->dq_items_head = next_dc; + } + _dispatch_trace_continuation_pop(dq, dc); + _dispatch_workitem_inc(); + + struct dispatch_barrier_sync_slow_s *dbssp = (void *)dc; + struct dispatch_barrier_sync_slow2_s *dbss2p = dbssp->dc_ctxt; + return dbss2p->dbss2_sema; +} + +static struct dispatch_object_s * +_dispatch_queue_concurrent_drain_one(dispatch_queue_t dq) +{ + struct dispatch_object_s *head, *next, *const mediator = (void *)~0ul; + + // The mediator value acts both as a "lock" and a signal + head = dispatch_atomic_xchg2o(dq, dq_items_head, mediator); + + if (slowpath(head == NULL)) { + // The first xchg on the tail will tell the enqueueing thread that it + // is safe to blindly write out to the head pointer. A cmpxchg honors + // the algorithm. + (void)dispatch_atomic_cmpxchg2o(dq, dq_items_head, mediator, NULL); + _dispatch_debug("no work on global work queue"); + return NULL; + } + + if (slowpath(head == mediator)) { + // This thread lost the race for ownership of the queue. + // + // The ratio of work to libdispatch overhead must be bad. This + // scenario implies that there are too many threads in the pool. + // Create a new pending thread and then exit this thread. + // The kernel will grant a new thread when the load subsides. + _dispatch_debug("Contention on queue: %p", dq); + _dispatch_queue_wakeup_global(dq); +#if DISPATCH_PERF_MON + dispatch_atomic_inc(&_dispatch_bad_ratio); +#endif + return NULL; + } + + // Restore the head pointer to a sane value before returning. + // If 'next' is NULL, then this item _might_ be the last item. + next = fastpath(head->do_next); - // workaround tweaks the kernel workqueue does for us - r = sigfillset(&mask); - dispatch_assume_zero(r); - r = _dispatch_pthread_sigmask(SIG_BLOCK, &mask, NULL); - dispatch_assume_zero(r); + if (slowpath(!next)) { + dq->dq_items_head = NULL; - do { - _dispatch_worker_thread2(context); - // we use 65 seconds in case there are any timers that run once a minute - } while (dispatch_semaphore_wait(qc->dgq_thread_mediator, dispatch_time(0, 65ull * NSEC_PER_SEC)) == 0); + if (dispatch_atomic_cmpxchg2o(dq, dq_items_tail, head, NULL)) { + // both head and tail are NULL now + goto out; + } - dispatch_atomic_inc(&qc->dgq_thread_pool_size); - if (dq->dq_items_tail) { - _dispatch_queue_wakeup_global(dq); + // There must be a next item now. This thread won't wait long. + while (!(next = head->do_next)) { + _dispatch_hardware_pause(); + } } - return NULL; + dq->dq_items_head = next; + _dispatch_queue_wakeup_global(dq); +out: + return head; } -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol -void +#pragma mark - +#pragma mark dispatch_worker_thread + +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +static void _dispatch_worker_thread2(void *context) { struct dispatch_object_s *item; dispatch_queue_t dq = context; struct dispatch_root_queue_context_s *qc = dq->do_ctxt; + if (_dispatch_thread_getspecific(dispatch_queue_key)) { DISPATCH_CRASH("Premature thread recycling"); } @@ -1469,6 +2243,7 @@ _dispatch_worker_thread2(void *context) qc->dgq_pending = 0; #if DISPATCH_COCOA_COMPAT + (void)dispatch_atomic_inc(&_dispatch_worker_threads); // ensure that high-level memory management techniques do not leak/crash if (dispatch_begin_thread_4GC) { dispatch_begin_thread_4GC(); @@ -1477,7 +2252,7 @@ _dispatch_worker_thread2(void *context) #endif #if DISPATCH_PERF_MON - uint64_t start = mach_absolute_time(); + uint64_t start = _dispatch_absolute_time(); #endif while ((item = fastpath(_dispatch_queue_concurrent_drain_one(dq)))) { _dispatch_continuation_pop(item); @@ -1489,366 +2264,238 @@ _dispatch_worker_thread2(void *context) #if DISPATCH_COCOA_COMPAT _dispatch_end_NSAutoReleasePool(pool); dispatch_end_thread_4GC(); + if (!dispatch_atomic_dec(&_dispatch_worker_threads) && + dispatch_no_worker_threads_4GC) { + dispatch_no_worker_threads_4GC(); + } #endif _dispatch_thread_setspecific(dispatch_queue_key, NULL); _dispatch_force_cache_cleanup(); -} - -#if DISPATCH_PERF_MON -void -_dispatch_queue_merge_stats(uint64_t start) -{ - uint64_t avg, delta = mach_absolute_time() - start; - unsigned long count, bucket; - - count = (size_t)_dispatch_thread_getspecific(dispatch_bcounter_key); - _dispatch_thread_setspecific(dispatch_bcounter_key, NULL); - - if (count) { - avg = delta / count; - bucket = flsll(avg); - } else { - bucket = 0; - } - - // 64-bit counters on 32-bit require a lock or a queue - OSSpinLockLock(&_dispatch_stats_lock); - - _dispatch_stats[bucket].time_total += delta; - _dispatch_stats[bucket].count_total += count; - _dispatch_stats[bucket].thread_total++; - OSSpinLockUnlock(&_dispatch_stats_lock); } -#endif -size_t -dispatch_queue_debug_attr(dispatch_queue_t dq, char* buf, size_t bufsiz) +#if DISPATCH_ENABLE_THREAD_POOL +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +static void * +_dispatch_worker_thread(void *context) { - return snprintf(buf, bufsiz, "parent = %p ", dq->do_targetq); -} + dispatch_queue_t dq = context; + struct dispatch_root_queue_context_s *qc = dq->do_ctxt; + sigset_t mask; + int r; -size_t -dispatch_queue_debug(dispatch_queue_t dq, char* buf, size_t bufsiz) -{ - size_t offset = 0; - offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dq->dq_label, dq); - offset += dispatch_object_debug_attr(dq, &buf[offset], bufsiz - offset); - offset += dispatch_queue_debug_attr(dq, &buf[offset], bufsiz - offset); - offset += snprintf(&buf[offset], bufsiz - offset, "}"); - return offset; -} + // workaround tweaks the kernel workqueue does for us + r = sigfillset(&mask); + (void)dispatch_assume_zero(r); + r = _dispatch_pthread_sigmask(SIG_BLOCK, &mask, NULL); + (void)dispatch_assume_zero(r); -#if DISPATCH_DEBUG -void -dispatch_debug_queue(dispatch_queue_t dq, const char* str) { - if (fastpath(dq)) { - dispatch_debug(dq, "%s", str); - } else { - _dispatch_log("queue[NULL]: %s", str); - } -} -#endif + do { + _dispatch_worker_thread2(context); + // we use 65 seconds in case there are any timers that run once a minute + } while (dispatch_semaphore_wait(qc->dgq_thread_mediator, + dispatch_time(0, 65ull * NSEC_PER_SEC)) == 0); -#if DISPATCH_COCOA_COMPAT -void -_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg __attribute__((unused))) -{ - if (main_q_is_draining) { - return; + (void)dispatch_atomic_inc2o(qc, dgq_thread_pool_size); + if (dq->dq_items_tail) { + _dispatch_queue_wakeup_global(dq); } - _dispatch_queue_set_mainq_drain_state(true); - _dispatch_queue_serial_drain_till_empty(&_dispatch_main_q); - _dispatch_queue_set_mainq_drain_state(false); -} - -mach_port_t -_dispatch_get_main_queue_port_4CF(void) -{ - dispatch_once_f(&_dispatch_main_q_port_pred, NULL, _dispatch_main_q_port_init); - return main_q_port; -} -#endif -static void -dispatch_queue_attr_dispose(dispatch_queue_attr_t attr) -{ - dispatch_queue_attr_set_finalizer(attr, NULL); - _dispatch_dispose(attr); + return NULL; } -static const struct dispatch_queue_attr_vtable_s dispatch_queue_attr_vtable = { - .do_type = DISPATCH_QUEUE_ATTR_TYPE, - .do_kind = "queue-attr", - .do_dispose = dispatch_queue_attr_dispose, -}; - -dispatch_queue_attr_t -dispatch_queue_attr_create(void) +int +_dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset) { - dispatch_queue_attr_t a = calloc(1, sizeof(struct dispatch_queue_attr_s)); - - if (a) { - a->do_vtable = &dispatch_queue_attr_vtable; - a->do_next = DISPATCH_OBJECT_LISTLESS; - a->do_ref_cnt = 1; - a->do_xref_cnt = 1; - a->do_targetq = _dispatch_get_root_queue(0, 0); - a->qa_flags = DISPATCH_QUEUE_OVERCOMMIT; - } - return a; -} + int r; -void -dispatch_queue_attr_set_flags(dispatch_queue_attr_t attr, uint64_t flags) -{ - dispatch_assert_zero(flags & ~DISPATCH_QUEUE_FLAGS_MASK); - attr->qa_flags = (unsigned long)flags & DISPATCH_QUEUE_FLAGS_MASK; -} - -void -dispatch_queue_attr_set_priority(dispatch_queue_attr_t attr, int priority) -{ - dispatch_debug_assert(attr, "NULL pointer"); - dispatch_debug_assert(priority <= DISPATCH_QUEUE_PRIORITY_HIGH && priority >= DISPATCH_QUEUE_PRIORITY_LOW, "Invalid priority"); + /* Workaround: 6269619 Not all signals can be delivered on any thread */ - if (priority > 0) { - priority = DISPATCH_QUEUE_PRIORITY_HIGH; - } else if (priority < 0) { - priority = DISPATCH_QUEUE_PRIORITY_LOW; - } + r = sigdelset(set, SIGILL); + (void)dispatch_assume_zero(r); + r = sigdelset(set, SIGTRAP); + (void)dispatch_assume_zero(r); +#if HAVE_DECL_SIGEMT + r = sigdelset(set, SIGEMT); + (void)dispatch_assume_zero(r); +#endif + r = sigdelset(set, SIGFPE); + (void)dispatch_assume_zero(r); + r = sigdelset(set, SIGBUS); + (void)dispatch_assume_zero(r); + r = sigdelset(set, SIGSEGV); + (void)dispatch_assume_zero(r); + r = sigdelset(set, SIGSYS); + (void)dispatch_assume_zero(r); + r = sigdelset(set, SIGPIPE); + (void)dispatch_assume_zero(r); - attr->qa_priority = priority; + return pthread_sigmask(how, set, oset); } - -void -dispatch_queue_attr_set_finalizer_f(dispatch_queue_attr_t attr, - void *context, dispatch_queue_finalizer_function_t finalizer) -{ -#ifdef __BLOCKS__ - if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) { - Block_release(attr->finalizer_ctxt); - } #endif - attr->finalizer_ctxt = context; - attr->finalizer_func = finalizer; -} - -#ifdef __BLOCKS__ -long -dispatch_queue_attr_set_finalizer(dispatch_queue_attr_t attr, - dispatch_queue_finalizer_t finalizer) -{ - void *ctxt; - dispatch_queue_finalizer_function_t func; - - if (finalizer) { - if (!(ctxt = Block_copy(finalizer))) { - return 1; - } - func = (void *)_dispatch_call_block_and_release2; - } else { - ctxt = NULL; - func = NULL; - } - dispatch_queue_attr_set_finalizer_f(attr, ctxt, func); +#pragma mark - +#pragma mark dispatch_main_queue - return 0; -} -#endif +static bool _dispatch_program_is_probably_callback_driven; +#if DISPATCH_COCOA_COMPAT static void -_dispatch_ccache_init(void *context __attribute__((unused))) -{ - _dispatch_ccache_zone = malloc_create_zone(0, 0); - dispatch_assert(_dispatch_ccache_zone); - malloc_set_zone_name(_dispatch_ccache_zone, "DispatchContinuations"); -} - -dispatch_continuation_t -_dispatch_continuation_alloc_from_heap(void) +_dispatch_main_q_port_init(void *ctxt DISPATCH_UNUSED) { - static dispatch_once_t pred; - dispatch_continuation_t dc; - - dispatch_once_f(&pred, NULL, _dispatch_ccache_init); - - while (!(dc = fastpath(malloc_zone_calloc(_dispatch_ccache_zone, 1, ROUND_UP_TO_CACHELINE_SIZE(sizeof(*dc)))))) { - sleep(1); - } + kern_return_t kr; - return dc; -} + kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, + &main_q_port); + DISPATCH_VERIFY_MIG(kr); + (void)dispatch_assume_zero(kr); + kr = mach_port_insert_right(mach_task_self(), main_q_port, main_q_port, + MACH_MSG_TYPE_MAKE_SEND); + DISPATCH_VERIFY_MIG(kr); + (void)dispatch_assume_zero(kr); -void -_dispatch_force_cache_cleanup(void) -{ - dispatch_continuation_t dc = _dispatch_thread_getspecific(dispatch_cache_key); - if (dc) { - _dispatch_thread_setspecific(dispatch_cache_key, NULL); - _dispatch_cache_cleanup2(dc); - } + _dispatch_program_is_probably_callback_driven = true; + _dispatch_safe_fork = false; } -DISPATCH_NOINLINE -void -_dispatch_cache_cleanup2(void *value) +mach_port_t +_dispatch_get_main_queue_port_4CF(void) { - dispatch_continuation_t dc, next_dc = value; - - while ((dc = next_dc)) { - next_dc = dc->do_next; - malloc_zone_free(_dispatch_ccache_zone, dc); - } + dispatch_once_f(&_dispatch_main_q_port_pred, NULL, + _dispatch_main_q_port_init); + return main_q_port; } -static char _dispatch_build[16]; +static bool main_q_is_draining; +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +DISPATCH_NOINLINE static void -_dispatch_bug_init(void *context __attribute__((unused))) +_dispatch_queue_set_mainq_drain_state(bool arg) { - int mib[] = { CTL_KERN, KERN_OSVERSION }; - size_t bufsz = sizeof(_dispatch_build); - - sysctl(mib, 2, _dispatch_build, &bufsz, NULL, 0); + main_q_is_draining = arg; } void -_dispatch_bug(size_t line, long val) +_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED) { - static dispatch_once_t pred; - static void *last_seen; - void *ra = __builtin_return_address(0); - - dispatch_once_f(&pred, NULL, _dispatch_bug_init); - if (last_seen != ra) { - last_seen = ra; - _dispatch_log("BUG in libdispatch: %s - %lu - 0x%lx", _dispatch_build, line, val); + if (main_q_is_draining) { + return; } + _dispatch_queue_set_mainq_drain_state(true); + _dispatch_main_queue_drain(); + _dispatch_queue_set_mainq_drain_state(false); } -void -_dispatch_abort(size_t line, long val) -{ - _dispatch_bug(line, val); - abort(); -} - -void -_dispatch_log(const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - - _dispatch_logv(msg, ap); - - va_end(ap); -} +#endif void -_dispatch_logv(const char *msg, va_list ap) +dispatch_main(void) { -#if DISPATCH_DEBUG - static FILE *logfile, *tmp; - char newbuf[strlen(msg) + 2]; - char path[PATH_MAX]; - - sprintf(newbuf, "%s\n", msg); - - if (!logfile) { - snprintf(path, sizeof(path), "/var/tmp/libdispatch.%d.log", getpid()); - tmp = fopen(path, "a"); - assert(tmp); - if (!dispatch_atomic_cmpxchg(&logfile, NULL, tmp)) { - fclose(tmp); - } else { - struct timeval tv; - gettimeofday(&tv, NULL); - fprintf(logfile, "=== log file opened for %s[%u] at %ld.%06u ===\n", - getprogname() ?: "", getpid(), tv.tv_sec, tv.tv_usec); - } +#if HAVE_PTHREAD_MAIN_NP + if (pthread_main_np()) { +#endif + _dispatch_program_is_probably_callback_driven = true; + pthread_exit(NULL); + DISPATCH_CRASH("pthread_exit() returned"); +#if HAVE_PTHREAD_MAIN_NP } - vfprintf(logfile, newbuf, ap); - fflush(logfile); -#else - vsyslog(LOG_NOTICE, msg, ap); + DISPATCH_CLIENT_CRASH("dispatch_main() must be called on the main thread"); #endif } -int -_dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset) +DISPATCH_NOINLINE DISPATCH_NORETURN +static void +_dispatch_sigsuspend(void) { - int r; - - /* Workaround: 6269619 Not all signals can be delivered on any thread */ - - r = sigdelset(set, SIGILL); - dispatch_assume_zero(r); - r = sigdelset(set, SIGTRAP); - dispatch_assume_zero(r); - r = sigdelset(set, SIGEMT); - dispatch_assume_zero(r); - r = sigdelset(set, SIGFPE); - dispatch_assume_zero(r); - r = sigdelset(set, SIGBUS); - dispatch_assume_zero(r); - r = sigdelset(set, SIGSEGV); - dispatch_assume_zero(r); - r = sigdelset(set, SIGSYS); - dispatch_assume_zero(r); - r = sigdelset(set, SIGPIPE); - dispatch_assume_zero(r); + static const sigset_t mask; - return pthread_sigmask(how, set, oset); +#if DISPATCH_COCOA_COMPAT + // Do not count the signal handling thread as a worker thread + (void)dispatch_atomic_dec(&_dispatch_worker_threads); +#endif + for (;;) { + sigsuspend(&mask); + } } -bool _dispatch_safe_fork = true; - -void -dispatch_atfork_prepare(void) +DISPATCH_NORETURN +static void +_dispatch_sig_thread(void *ctxt DISPATCH_UNUSED) { + // never returns, so burn bridges behind us + _dispatch_clear_stack(0); + _dispatch_sigsuspend(); } -void -dispatch_atfork_parent(void) +DISPATCH_NOINLINE +static void +_dispatch_queue_cleanup2(void) { -} + (void)dispatch_atomic_dec(&_dispatch_main_q.dq_running); -void -dispatch_atfork_child(void) -{ - void *crash = (void *)0x100; - size_t i; + if (dispatch_atomic_sub(&_dispatch_main_q.do_suspend_cnt, + DISPATCH_OBJECT_SUSPEND_LOCK) == 0) { + _dispatch_wakeup(&_dispatch_main_q); + } - if (_dispatch_safe_fork) { - return; + // overload the "probably" variable to mean that dispatch_main() or + // similar non-POSIX API was called + // this has to run before the DISPATCH_COCOA_COMPAT below + if (_dispatch_program_is_probably_callback_driven) { + dispatch_async_f(_dispatch_get_root_queue(0, false), NULL, + _dispatch_sig_thread); + sleep(1); // workaround 6778970 } - _dispatch_main_q.dq_items_head = crash; - _dispatch_main_q.dq_items_tail = crash; +#if DISPATCH_COCOA_COMPAT + dispatch_once_f(&_dispatch_main_q_port_pred, NULL, + _dispatch_main_q_port_init); - _dispatch_mgr_q.dq_items_head = crash; - _dispatch_mgr_q.dq_items_tail = crash; + mach_port_t mp = main_q_port; + kern_return_t kr; - for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { - _dispatch_root_queues[i].dq_items_head = crash; - _dispatch_root_queues[i].dq_items_tail = crash; + main_q_port = 0; + + if (mp) { + kr = mach_port_deallocate(mach_task_self(), mp); + DISPATCH_VERIFY_MIG(kr); + (void)dispatch_assume_zero(kr); + kr = mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, + -1); + DISPATCH_VERIFY_MIG(kr); + (void)dispatch_assume_zero(kr); } +#endif } -void -dispatch_init_pthread(pthread_t pthr __attribute__((unused))) +static void +_dispatch_queue_cleanup(void *ctxt) { + if (ctxt == &_dispatch_main_q) { + return _dispatch_queue_cleanup2(); + } + // POSIX defines that destructors are only called if 'ctxt' is non-null + DISPATCH_CRASH("Premature thread exit while a dispatch queue is running"); } +#pragma mark - +#pragma mark dispatch_manager_queue + +static unsigned int _dispatch_select_workaround; +static fd_set _dispatch_rfds; +static fd_set _dispatch_wfds; +static void **_dispatch_rfd_ptrs; +static void **_dispatch_wfd_ptrs; + static int _dispatch_kq; static void -_dispatch_get_kq_init(void *context __attribute__((unused))) +_dispatch_get_kq_init(void *context DISPATCH_UNUSED) { static const struct kevent kev = { .ident = 1, @@ -1857,15 +2504,18 @@ _dispatch_get_kq_init(void *context __attribute__((unused))) }; _dispatch_kq = kqueue(); + _dispatch_safe_fork = false; - // in case we fall back to select() - FD_SET(_dispatch_kq, &_dispatch_rfds); if (_dispatch_kq == -1) { - dispatch_assert_zero(errno); + DISPATCH_CLIENT_CRASH("kqueue() create failed: " + "probably out of file descriptors"); + } else if (dispatch_assume(_dispatch_kq < FD_SETSIZE)) { + // in case we fall back to select() + FD_SET(_dispatch_kq, &_dispatch_rfds); } - dispatch_assume_zero(kevent(_dispatch_kq, &kev, 1, NULL, 0, NULL)); + (void)dispatch_assume_zero(kevent(_dispatch_kq, &kev, 1, NULL, 0, NULL)); _dispatch_queue_push(_dispatch_mgr_q.do_targetq, &_dispatch_mgr_q); } @@ -1880,6 +2530,120 @@ _dispatch_get_kq(void) return _dispatch_kq; } +long +_dispatch_update_kq(const struct kevent *kev) +{ + struct kevent kev_copy = *kev; + // This ensures we don't get a pending kevent back while registering + // a new kevent + kev_copy.flags |= EV_RECEIPT; + + if (_dispatch_select_workaround && (kev_copy.flags & EV_DELETE)) { + // Only executed on manager queue + switch (kev_copy.filter) { + case EVFILT_READ: + if (kev_copy.ident < FD_SETSIZE && + FD_ISSET((int)kev_copy.ident, &_dispatch_rfds)) { + FD_CLR((int)kev_copy.ident, &_dispatch_rfds); + _dispatch_rfd_ptrs[kev_copy.ident] = 0; + (void)dispatch_atomic_dec(&_dispatch_select_workaround); + return 0; + } + break; + case EVFILT_WRITE: + if (kev_copy.ident < FD_SETSIZE && + FD_ISSET((int)kev_copy.ident, &_dispatch_wfds)) { + FD_CLR((int)kev_copy.ident, &_dispatch_wfds); + _dispatch_wfd_ptrs[kev_copy.ident] = 0; + (void)dispatch_atomic_dec(&_dispatch_select_workaround); + return 0; + } + break; + default: + break; + } + } + + int rval = kevent(_dispatch_get_kq(), &kev_copy, 1, &kev_copy, 1, NULL); + if (rval == -1) { + // If we fail to register with kevents, for other reasons aside from + // changelist elements. + (void)dispatch_assume_zero(errno); + //kev_copy.flags |= EV_ERROR; + //kev_copy.data = error; + return errno; + } + + // The following select workaround only applies to adding kevents + if ((kev->flags & (EV_DISABLE|EV_DELETE)) || + !(kev->flags & (EV_ADD|EV_ENABLE))) { + return 0; + } + + // Only executed on manager queue + switch (kev_copy.data) { + case 0: + return 0; + case EBADF: + break; + default: + // If an error occurred while registering with kevent, and it was + // because of a kevent changelist processing && the kevent involved + // either doing a read or write, it would indicate we were trying + // to register a /dev/* port; fall back to select + switch (kev_copy.filter) { + case EVFILT_READ: + if (dispatch_assume(kev_copy.ident < FD_SETSIZE)) { + if (!_dispatch_rfd_ptrs) { + _dispatch_rfd_ptrs = calloc(FD_SETSIZE, sizeof(void*)); + } + _dispatch_rfd_ptrs[kev_copy.ident] = kev_copy.udata; + FD_SET((int)kev_copy.ident, &_dispatch_rfds); + (void)dispatch_atomic_inc(&_dispatch_select_workaround); + _dispatch_debug("select workaround used to read fd %d: 0x%lx", + (int)kev_copy.ident, (long)kev_copy.data); + return 0; + } + break; + case EVFILT_WRITE: + if (dispatch_assume(kev_copy.ident < FD_SETSIZE)) { + if (!_dispatch_wfd_ptrs) { + _dispatch_wfd_ptrs = calloc(FD_SETSIZE, sizeof(void*)); + } + _dispatch_wfd_ptrs[kev_copy.ident] = kev_copy.udata; + FD_SET((int)kev_copy.ident, &_dispatch_wfds); + (void)dispatch_atomic_inc(&_dispatch_select_workaround); + _dispatch_debug("select workaround used to write fd %d: 0x%lx", + (int)kev_copy.ident, (long)kev_copy.data); + return 0; + } + break; + default: + // kevent error, _dispatch_source_merge_kevent() will handle it + _dispatch_source_drain_kevent(&kev_copy); + break; + } + break; + } + return kev_copy.data; +} + +static bool +_dispatch_mgr_wakeup(dispatch_queue_t dq) +{ + static const struct kevent kev = { + .ident = 1, + .filter = EVFILT_USER, + .fflags = NOTE_TRIGGER, + }; + + _dispatch_debug("waking up the _dispatch_mgr_q: %p", dq); + + _dispatch_update_kq(&kev); + + return false; +} + static void _dispatch_mgr_thread2(struct kevent *kev, size_t cnt) { @@ -1888,17 +2652,42 @@ _dispatch_mgr_thread2(struct kevent *kev, size_t cnt) for (i = 0; i < cnt; i++) { // EVFILT_USER isn't used by sources if (kev[i].filter == EVFILT_USER) { - // If _dispatch_mgr_thread2() ever is changed to return to the - // caller, then this should become _dispatch_queue_drain() - _dispatch_queue_serial_drain_till_empty(&_dispatch_mgr_q); + // If _dispatch_mgr_thread2() ever is changed to return to the + // caller, then this should become _dispatch_queue_drain() + _dispatch_queue_serial_drain_till_empty(&_dispatch_mgr_q); } else { _dispatch_source_drain_kevent(&kev[i]); } - } + } } -static dispatch_queue_t -_dispatch_mgr_invoke(dispatch_queue_t dq) +#if DISPATCH_USE_VM_PRESSURE && DISPATCH_USE_MALLOC_VM_PRESSURE_SOURCE +// VM Pressure source for malloc +static dispatch_source_t _dispatch_malloc_vm_pressure_source; + +static void +_dispatch_malloc_vm_pressure_handler(void *context DISPATCH_UNUSED) +{ + malloc_zone_pressure_relief(0,0); +} + +static void +_dispatch_malloc_vm_pressure_setup(void) +{ + _dispatch_malloc_vm_pressure_source = dispatch_source_create( + DISPATCH_SOURCE_TYPE_VM, 0, DISPATCH_VM_PRESSURE, + _dispatch_get_root_queue(0, true)); + dispatch_source_set_event_handler_f(_dispatch_malloc_vm_pressure_source, + _dispatch_malloc_vm_pressure_handler); + dispatch_resume(_dispatch_malloc_vm_pressure_source); +} +#else +#define _dispatch_malloc_vm_pressure_setup() +#endif + +DISPATCH_NOINLINE DISPATCH_NORETURN +static void +_dispatch_mgr_invoke(void) { static const struct timespec timeout_immediately = { 0, 0 }; struct timespec timeout; @@ -1906,84 +2695,111 @@ _dispatch_mgr_invoke(dispatch_queue_t dq) struct timeval sel_timeout, *sel_timeoutp; fd_set tmp_rfds, tmp_wfds; struct kevent kev[1]; - int k_cnt, k_err, i, r; + int k_cnt, err, i, r; - _dispatch_thread_setspecific(dispatch_queue_key, dq); + _dispatch_thread_setspecific(dispatch_queue_key, &_dispatch_mgr_q); +#if DISPATCH_COCOA_COMPAT + // Do not count the manager thread as a worker thread + (void)dispatch_atomic_dec(&_dispatch_worker_threads); +#endif + _dispatch_malloc_vm_pressure_setup(); for (;;) { _dispatch_run_timers(); timeoutp = _dispatch_get_next_timer_fire(&timeout); - + if (_dispatch_select_workaround) { FD_COPY(&_dispatch_rfds, &tmp_rfds); FD_COPY(&_dispatch_wfds, &tmp_wfds); if (timeoutp) { sel_timeout.tv_sec = timeoutp->tv_sec; - sel_timeout.tv_usec = (typeof(sel_timeout.tv_usec))(timeoutp->tv_nsec / 1000u); + sel_timeout.tv_usec = (typeof(sel_timeout.tv_usec)) + (timeoutp->tv_nsec / 1000u); sel_timeoutp = &sel_timeout; } else { sel_timeoutp = NULL; } - + r = select(FD_SETSIZE, &tmp_rfds, &tmp_wfds, NULL, sel_timeoutp); if (r == -1) { - if (errno != EBADF) { - dispatch_assume_zero(errno); + err = errno; + if (err != EBADF) { + if (err != EINTR) { + (void)dispatch_assume_zero(err); + } continue; } for (i = 0; i < FD_SETSIZE; i++) { if (i == _dispatch_kq) { continue; } - if (!FD_ISSET(i, &_dispatch_rfds) && !FD_ISSET(i, &_dispatch_wfds)) { + if (!FD_ISSET(i, &_dispatch_rfds) && !FD_ISSET(i, + &_dispatch_wfds)) { continue; } r = dup(i); if (r != -1) { close(r); } else { - FD_CLR(i, &_dispatch_rfds); - FD_CLR(i, &_dispatch_wfds); - _dispatch_rfd_ptrs[i] = 0; - _dispatch_wfd_ptrs[i] = 0; + if (FD_ISSET(i, &_dispatch_rfds)) { + FD_CLR(i, &_dispatch_rfds); + _dispatch_rfd_ptrs[i] = 0; + (void)dispatch_atomic_dec( + &_dispatch_select_workaround); + } + if (FD_ISSET(i, &_dispatch_wfds)) { + FD_CLR(i, &_dispatch_wfds); + _dispatch_wfd_ptrs[i] = 0; + (void)dispatch_atomic_dec( + &_dispatch_select_workaround); + } } } continue; } - + if (r > 0) { for (i = 0; i < FD_SETSIZE; i++) { if (i == _dispatch_kq) { continue; } if (FD_ISSET(i, &tmp_rfds)) { - FD_CLR(i, &_dispatch_rfds); // emulate EV_DISABLE - EV_SET(&kev[0], i, EVFILT_READ, EV_ADD|EV_ENABLE|EV_DISPATCH, 0, 1, _dispatch_rfd_ptrs[i]); + FD_CLR(i, &_dispatch_rfds); // emulate EV_DISABLE + EV_SET(&kev[0], i, EVFILT_READ, + EV_ADD|EV_ENABLE|EV_DISPATCH, 0, 1, + _dispatch_rfd_ptrs[i]); _dispatch_rfd_ptrs[i] = 0; + (void)dispatch_atomic_dec(&_dispatch_select_workaround); _dispatch_mgr_thread2(kev, 1); } if (FD_ISSET(i, &tmp_wfds)) { - FD_CLR(i, &_dispatch_wfds); // emulate EV_DISABLE - EV_SET(&kev[0], i, EVFILT_WRITE, EV_ADD|EV_ENABLE|EV_DISPATCH, 0, 1, _dispatch_wfd_ptrs[i]); + FD_CLR(i, &_dispatch_wfds); // emulate EV_DISABLE + EV_SET(&kev[0], i, EVFILT_WRITE, + EV_ADD|EV_ENABLE|EV_DISPATCH, 0, 1, + _dispatch_wfd_ptrs[i]); _dispatch_wfd_ptrs[i] = 0; + (void)dispatch_atomic_dec(&_dispatch_select_workaround); _dispatch_mgr_thread2(kev, 1); } } } - + timeoutp = &timeout_immediately; } - - k_cnt = kevent(_dispatch_kq, NULL, 0, kev, sizeof(kev) / sizeof(kev[0]), timeoutp); - k_err = errno; + + k_cnt = kevent(_dispatch_kq, NULL, 0, kev, sizeof(kev) / sizeof(kev[0]), + timeoutp); + err = errno; switch (k_cnt) { case -1: - if (k_err == EBADF) { + if (err == EBADF) { DISPATCH_CLIENT_CRASH("Do not close random Unix descriptors"); } - dispatch_assume_zero(k_err); + if (err != EINTR) { + (void)dispatch_assume_zero(err); + } continue; default: _dispatch_mgr_thread2(kev, (size_t)k_cnt); @@ -1993,179 +2809,13 @@ _dispatch_mgr_invoke(dispatch_queue_t dq) continue; } } - - return NULL; -} - -static bool -_dispatch_mgr_wakeup(dispatch_queue_t dq) -{ - static const struct kevent kev = { - .ident = 1, - .filter = EVFILT_USER, -#ifdef EV_TRIGGER - .flags = EV_TRIGGER, -#endif -#ifdef NOTE_TRIGGER - .fflags = NOTE_TRIGGER, -#endif - }; - - _dispatch_debug("waking up the _dispatch_mgr_q: %p", dq); - - _dispatch_update_kq(&kev); - - return false; -} - -void -_dispatch_update_kq(const struct kevent *kev) -{ - struct kevent kev_copy = *kev; - kev_copy.flags |= EV_RECEIPT; - - if (kev_copy.flags & EV_DELETE) { - switch (kev_copy.filter) { - case EVFILT_READ: - if (FD_ISSET((int)kev_copy.ident, &_dispatch_rfds)) { - FD_CLR((int)kev_copy.ident, &_dispatch_rfds); - _dispatch_rfd_ptrs[kev_copy.ident] = 0; - return; - } - case EVFILT_WRITE: - if (FD_ISSET((int)kev_copy.ident, &_dispatch_wfds)) { - FD_CLR((int)kev_copy.ident, &_dispatch_wfds); - _dispatch_wfd_ptrs[kev_copy.ident] = 0; - return; - } - default: - break; - } - } - - int rval = kevent(_dispatch_get_kq(), &kev_copy, 1, &kev_copy, 1, NULL); - if (rval == -1) { - // If we fail to register with kevents, for other reasons aside from - // changelist elements. - dispatch_assume_zero(errno); - //kev_copy.flags |= EV_ERROR; - //kev_copy.data = error; - return; - } - - // The following select workaround only applies to adding kevents - if (!(kev->flags & EV_ADD)) { - return; - } - - switch (kev_copy.data) { - case 0: - return; - case EBADF: - break; - default: - // If an error occurred while registering with kevent, and it was - // because of a kevent changelist processing && the kevent involved - // either doing a read or write, it would indicate we were trying - // to register a /dev/* port; fall back to select - switch (kev_copy.filter) { - case EVFILT_READ: - _dispatch_select_workaround = true; - FD_SET((int)kev_copy.ident, &_dispatch_rfds); - _dispatch_rfd_ptrs[kev_copy.ident] = kev_copy.udata; - break; - case EVFILT_WRITE: - _dispatch_select_workaround = true; - FD_SET((int)kev_copy.ident, &_dispatch_wfds); - _dispatch_wfd_ptrs[kev_copy.ident] = kev_copy.udata; - break; - default: - _dispatch_source_drain_kevent(&kev_copy); - break; - } - break; - } -} - -static const struct dispatch_queue_vtable_s _dispatch_queue_mgr_vtable = { - .do_type = DISPATCH_QUEUE_MGR_TYPE, - .do_kind = "mgr-queue", - .do_invoke = _dispatch_mgr_invoke, - .do_debug = dispatch_queue_debug, - .do_probe = _dispatch_mgr_wakeup, -}; - -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol -struct dispatch_queue_s _dispatch_mgr_q = { - .do_vtable = &_dispatch_queue_mgr_vtable, - .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, - .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, - .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, - .do_targetq = &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_COUNT - 1], - - .dq_label = "com.apple.libdispatch-manager", - .dq_width = 1, - .dq_serialnum = 2, -}; - -const struct dispatch_queue_offsets_s dispatch_queue_offsets = { - .dqo_version = 3, - .dqo_label = offsetof(struct dispatch_queue_s, dq_label), - .dqo_label_size = sizeof(_dispatch_main_q.dq_label), - .dqo_flags = 0, - .dqo_flags_size = 0, - .dqo_width = offsetof(struct dispatch_queue_s, dq_width), - .dqo_width_size = sizeof(_dispatch_main_q.dq_width), - .dqo_serialnum = offsetof(struct dispatch_queue_s, dq_serialnum), - .dqo_serialnum_size = sizeof(_dispatch_main_q.dq_serialnum), - .dqo_running = offsetof(struct dispatch_queue_s, dq_running), - .dqo_running_size = sizeof(_dispatch_main_q.dq_running), -}; - -#ifdef __BLOCKS__ -void -dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t work) -{ - // test before the copy of the block - if (when == DISPATCH_TIME_FOREVER) { -#if DISPATCH_DEBUG - DISPATCH_CLIENT_CRASH("dispatch_after() called with 'when' == infinity"); -#endif - return; - } - dispatch_after_f(when, queue, _dispatch_Block_copy(work), _dispatch_call_block_and_release); } -#endif -DISPATCH_NOINLINE -void -dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *ctxt, void (*func)(void *)) +DISPATCH_NORETURN +static dispatch_queue_t +_dispatch_mgr_thread(dispatch_queue_t dq DISPATCH_UNUSED) { - uint64_t delta; - if (when == DISPATCH_TIME_FOREVER) { -#if DISPATCH_DEBUG - DISPATCH_CLIENT_CRASH("dispatch_after_f() called with 'when' == infinity"); -#endif - return; - } - - // this function can and should be optimized to not use a dispatch source -again: - delta = _dispatch_timeout(when); - if (delta == 0) { - return dispatch_async_f(queue, ctxt, func); - } - if (!dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, delta, 0, NULL, queue, ^(dispatch_source_t ds) { - long err_dom, err_val; - if ((err_dom = dispatch_source_get_error(ds, &err_val))) { - dispatch_assert(err_dom == DISPATCH_ERROR_DOMAIN_POSIX); - dispatch_assert(err_val == ECANCELED); - func(ctxt); - dispatch_release(ds); // MUST NOT be _dispatch_release() - } else { - dispatch_source_cancel(ds); - } - })) { - goto again; - } + // never returns, so burn bridges behind us & clear stack 2k ahead + _dispatch_clear_stack(2048); + _dispatch_mgr_invoke(); } diff --git a/src/queue_internal.h b/src/queue_internal.h index 51b4fca..479ae60 100644 --- a/src/queue_internal.h +++ b/src/queue_internal.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -35,66 +35,83 @@ // If dc_vtable is less than 127, then the object is a continuation. // Otherwise, the object has a private layout and memory management rules. The // first two words must align with normal objects. -#define DISPATCH_CONTINUATION_HEADER(x) \ - const void * do_vtable; \ - struct x *volatile do_next; \ - dispatch_function_t dc_func; \ - void * dc_ctxt +#define DISPATCH_CONTINUATION_HEADER(x) \ + const void *do_vtable; \ + struct x *volatile do_next; \ + dispatch_function_t dc_func; \ + void *dc_ctxt -#define DISPATCH_OBJ_ASYNC_BIT 0x1 +#define DISPATCH_OBJ_ASYNC_BIT 0x1 #define DISPATCH_OBJ_BARRIER_BIT 0x2 -#define DISPATCH_OBJ_GROUP_BIT 0x4 +#define DISPATCH_OBJ_GROUP_BIT 0x4 +#define DISPATCH_OBJ_SYNC_SLOW_BIT 0x8 // vtables are pointers far away from the low page in memory -#define DISPATCH_OBJ_IS_VTABLE(x) ((unsigned long)(x)->do_vtable > 127ul) +#define DISPATCH_OBJ_IS_VTABLE(x) ((unsigned long)(x)->do_vtable > 127ul) struct dispatch_continuation_s { DISPATCH_CONTINUATION_HEADER(dispatch_continuation_s); - dispatch_group_t dc_group; - void * dc_data[3]; + dispatch_group_t dc_group; + void *dc_data[3]; }; typedef struct dispatch_continuation_s *dispatch_continuation_t; +struct dispatch_queue_attr_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_queue_attr_s); +}; + +struct dispatch_queue_attr_s { + DISPATCH_STRUCT_HEADER(dispatch_queue_attr_s, dispatch_queue_attr_vtable_s); +}; struct dispatch_queue_vtable_s { DISPATCH_VTABLE_HEADER(dispatch_queue_s); }; -#define DISPATCH_QUEUE_MIN_LABEL_SIZE 64 +#define DISPATCH_QUEUE_MIN_LABEL_SIZE 64 + +#ifdef __LP64__ +#define DISPATCH_QUEUE_CACHELINE_PAD 32 +#else +#define DISPATCH_QUEUE_CACHELINE_PAD 8 +#endif #define DISPATCH_QUEUE_HEADER \ - uint32_t dq_running; \ + uint32_t volatile dq_running; \ uint32_t dq_width; \ - struct dispatch_object_s *dq_items_tail; \ + struct dispatch_object_s *volatile dq_items_tail; \ struct dispatch_object_s *volatile dq_items_head; \ unsigned long dq_serialnum; \ - void *dq_finalizer_ctxt; \ - dispatch_queue_finalizer_function_t dq_finalizer_func + dispatch_queue_t dq_specific_q; struct dispatch_queue_s { DISPATCH_STRUCT_HEADER(dispatch_queue_s, dispatch_queue_vtable_s); DISPATCH_QUEUE_HEADER; - char dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE]; // must be last + char dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE]; // must be last + char _dq_pad[DISPATCH_QUEUE_CACHELINE_PAD]; // for static queues only }; extern struct dispatch_queue_s _dispatch_mgr_q; -void _dispatch_queue_init(dispatch_queue_t dq); -void _dispatch_queue_drain(dispatch_queue_t dq); void _dispatch_queue_dispose(dispatch_queue_t dq); -void _dispatch_queue_push_list_slow(dispatch_queue_t dq, struct dispatch_object_s *obj); +void _dispatch_queue_invoke(dispatch_queue_t dq); +void _dispatch_queue_push_list_slow(dispatch_queue_t dq, + struct dispatch_object_s *obj); -__attribute__((always_inline)) +DISPATCH_ALWAYS_INLINE static inline void -_dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, dispatch_object_t _tail) +_dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, + dispatch_object_t _tail) { struct dispatch_object_s *prev, *head = _head._do, *tail = _tail._do; tail->do_next = NULL; - prev = fastpath(dispatch_atomic_xchg(&dq->dq_items_tail, tail)); + dispatch_atomic_store_barrier(); + prev = fastpath(dispatch_atomic_xchg2o(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 + // 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 { _dispatch_queue_push_list_slow(dq, head); @@ -103,34 +120,91 @@ _dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, dispatch #define _dispatch_queue_push(x, y) _dispatch_queue_push_list((x), (y), (y)) -#define DISPATCH_QUEUE_PRIORITY_COUNT 3 - #if DISPATCH_DEBUG void dispatch_debug_queue(dispatch_queue_t dq, const char* str); #else -static inline void dispatch_debug_queue(dispatch_queue_t dq __attribute__((unused)), const char* str __attribute__((unused))) {} +static inline void dispatch_debug_queue(dispatch_queue_t dq DISPATCH_UNUSED, + const char* str DISPATCH_UNUSED) {} #endif size_t dispatch_queue_debug(dispatch_queue_t dq, char* buf, size_t bufsiz); -size_t dispatch_queue_debug_attr(dispatch_queue_t dq, char* buf, size_t bufsiz); +size_t _dispatch_queue_debug_attr(dispatch_queue_t dq, char* buf, + size_t bufsiz); +DISPATCH_ALWAYS_INLINE static inline dispatch_queue_t _dispatch_queue_get_current(void) { return _dispatch_thread_getspecific(dispatch_queue_key); } -__private_extern__ malloc_zone_t *_dispatch_ccache_zone; -dispatch_continuation_t _dispatch_continuation_alloc_from_heap(void); +#define DISPATCH_QUEUE_PRIORITY_COUNT 4 +#define DISPATCH_ROOT_QUEUE_COUNT (DISPATCH_QUEUE_PRIORITY_COUNT * 2) + +// overcommit priority index values need bit 1 set +enum { + DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY = 0, + DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY, + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY, + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY, + DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY, + DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY, + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY, + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY, +}; + +extern const struct dispatch_queue_attr_vtable_s dispatch_queue_attr_vtable; +extern const struct dispatch_queue_vtable_s _dispatch_queue_vtable; +extern unsigned long _dispatch_queue_serial_numbers; +extern struct dispatch_queue_s _dispatch_root_queues[]; -static inline dispatch_continuation_t -_dispatch_continuation_alloc_cacheonly(void) +DISPATCH_ALWAYS_INLINE DISPATCH_CONST +static inline dispatch_queue_t +_dispatch_get_root_queue(long priority, bool overcommit) { - dispatch_continuation_t dc = fastpath(_dispatch_thread_getspecific(dispatch_cache_key)); - if (dc) { - _dispatch_thread_setspecific(dispatch_cache_key, dc->do_next); + if (overcommit) switch (priority) { + case DISPATCH_QUEUE_PRIORITY_LOW: + return &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY]; + case DISPATCH_QUEUE_PRIORITY_DEFAULT: + return &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY]; + case DISPATCH_QUEUE_PRIORITY_HIGH: + return &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY]; + case DISPATCH_QUEUE_PRIORITY_BACKGROUND: + return &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY]; + } + switch (priority) { + case DISPATCH_QUEUE_PRIORITY_LOW: + return &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY]; + case DISPATCH_QUEUE_PRIORITY_DEFAULT: + return &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY]; + case DISPATCH_QUEUE_PRIORITY_HIGH: + return &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY]; + case DISPATCH_QUEUE_PRIORITY_BACKGROUND: + return &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY]; + default: + return NULL; } - return dc; +} + +// Note to later developers: ensure that any initialization changes are +// made for statically allocated queues (i.e. _dispatch_main_q). +static inline void +_dispatch_queue_init(dispatch_queue_t dq) +{ + dq->do_vtable = &_dispatch_queue_vtable; + dq->do_next = DISPATCH_OBJECT_LISTLESS; + dq->do_ref_cnt = 1; + dq->do_xref_cnt = 1; + // Default target queue is overcommit! + dq->do_targetq = _dispatch_get_root_queue(0, true); + dq->dq_running = 0; + dq->dq_width = 1; + dq->dq_serialnum = dispatch_atomic_inc(&_dispatch_queue_serial_numbers) - 1; } #endif diff --git a/src/semaphore.c b/src/semaphore.c index 9e36d4d..29585bd 100644 --- a/src/semaphore.c +++ b/src/semaphore.c @@ -1,40 +1,55 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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 "internal.h" // semaphores are too fundamental to use the dispatch_assume*() macros -#define DISPATCH_SEMAPHORE_VERIFY_KR(x) do { \ - if (x) { \ - DISPATCH_CRASH("flawed group/semaphore logic"); \ - } \ +#if USE_MACH_SEM +#define DISPATCH_SEMAPHORE_VERIFY_KR(x) do { \ + if (slowpath(x)) { \ + DISPATCH_CRASH("flawed group/semaphore logic"); \ + } \ + } while (0) +#elif USE_POSIX_SEM +#define DISPATCH_SEMAPHORE_VERIFY_RET(x) do { \ + if (slowpath((x) == -1)) { \ + DISPATCH_CRASH("flawed group/semaphore logic"); \ + } \ } while (0) +#endif -struct dispatch_semaphore_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_semaphore_s); -}; +DISPATCH_WEAK // rdar://problem/8503746 +long _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema); static void _dispatch_semaphore_dispose(dispatch_semaphore_t dsema); -static size_t _dispatch_semaphore_debug(dispatch_semaphore_t dsema, char *buf, size_t bufsiz); +static size_t _dispatch_semaphore_debug(dispatch_semaphore_t dsema, char *buf, + size_t bufsiz); static long _dispatch_group_wake(dispatch_semaphore_t dsema); +#pragma mark - +#pragma mark dispatch_semaphore_t + +struct dispatch_semaphore_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_semaphore_s); +}; + const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable = { .do_type = DISPATCH_SEMAPHORE_TYPE, .do_kind = "semaphore", @@ -42,64 +57,39 @@ const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable = { .do_debug = _dispatch_semaphore_debug, }; -dispatch_semaphore_t -_dispatch_get_thread_semaphore(void) -{ - dispatch_semaphore_t dsema; - - dsema = fastpath(_dispatch_thread_getspecific(dispatch_sema4_key)); - if (!dsema) { - while (!(dsema = dispatch_semaphore_create(0))) { - sleep(1); - } - } - _dispatch_thread_setspecific(dispatch_sema4_key, NULL); - return dsema; -} - -void -_dispatch_put_thread_semaphore(dispatch_semaphore_t dsema) -{ - dispatch_semaphore_t old_sema = _dispatch_thread_getspecific(dispatch_sema4_key); - _dispatch_thread_setspecific(dispatch_sema4_key, dsema); - if (old_sema) { - dispatch_release(old_sema); - } -} - -dispatch_group_t -dispatch_group_create(void) -{ - return (dispatch_group_t)dispatch_semaphore_create(LONG_MAX); -} - dispatch_semaphore_t dispatch_semaphore_create(long value) { dispatch_semaphore_t dsema; - + // If the internal value is negative, then the absolute of the value is // equal to the number of waiting threads. Therefore it is bogus to // initialize the semaphore with a negative value. if (value < 0) { return NULL; } - + dsema = calloc(1, sizeof(struct dispatch_semaphore_s)); - + if (fastpath(dsema)) { dsema->do_vtable = &_dispatch_semaphore_vtable; dsema->do_next = DISPATCH_OBJECT_LISTLESS; dsema->do_ref_cnt = 1; dsema->do_xref_cnt = 1; - dsema->do_targetq = dispatch_get_global_queue(0, 0); + dsema->do_targetq = dispatch_get_global_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dsema->dsema_value = value; dsema->dsema_orig = value; +#if USE_POSIX_SEM + int ret = sem_init(&dsema->dsema_sem, 0, 0); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); +#endif } - + return dsema; } +#if USE_MACH_SEM static void _dispatch_semaphore_create_port(semaphore_t *s4) { @@ -109,19 +99,20 @@ _dispatch_semaphore_create_port(semaphore_t *s4) if (*s4) { return; } - + // lazily allocate the semaphore port - + // Someday: // 1) Switch to a doubly-linked FIFO in user-space. // 2) User-space timers for the timeout. // 3) Use the per-thread semaphore port. - - while (dispatch_assume_zero(kr = semaphore_create(mach_task_self(), &tmp, SYNC_POLICY_FIFO, 0))) { + + while ((kr = semaphore_create(mach_task_self(), &tmp, + SYNC_POLICY_FIFO, 0))) { DISPATCH_VERIFY_MIG(kr); sleep(1); } - + if (!dispatch_atomic_cmpxchg(s4, 0, tmp)) { kr = semaphore_destroy(mach_task_self(), tmp); DISPATCH_SEMAPHORE_VERIFY_KR(kr); @@ -129,30 +120,117 @@ _dispatch_semaphore_create_port(semaphore_t *s4) _dispatch_safe_fork = false; } +#endif + +static void +_dispatch_semaphore_dispose(dispatch_semaphore_t dsema) +{ + if (dsema->dsema_value < dsema->dsema_orig) { + DISPATCH_CLIENT_CRASH( + "Semaphore/group object deallocated while in use"); + } + +#if USE_MACH_SEM + kern_return_t kr; + if (dsema->dsema_port) { + kr = semaphore_destroy(mach_task_self(), dsema->dsema_port); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + } + if (dsema->dsema_waiter_port) { + kr = semaphore_destroy(mach_task_self(), dsema->dsema_waiter_port); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + } +#elif USE_POSIX_SEM + int ret = sem_destroy(&dsema->dsema_sem); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); +#endif + + _dispatch_dispose(dsema); +} + +static size_t +_dispatch_semaphore_debug(dispatch_semaphore_t dsema, char *buf, size_t bufsiz) +{ + size_t offset = 0; + offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", + dx_kind(dsema), dsema); + offset += _dispatch_object_debug_attr(dsema, &buf[offset], bufsiz - offset); +#if USE_MACH_SEM + offset += snprintf(&buf[offset], bufsiz - offset, "port = 0x%u, ", + dsema->dsema_port); +#endif + offset += snprintf(&buf[offset], bufsiz - offset, + "value = %ld, orig = %ld }", dsema->dsema_value, dsema->dsema_orig); + return offset; +} + +DISPATCH_NOINLINE +long +_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema) +{ + // Before dsema_sent_ksignals is incremented we can rely on the reference + // held by the waiter. However, once this value is incremented the waiter + // may return between the atomic increment and the semaphore_signal(), + // therefore an explicit reference must be held in order to safely access + // dsema after the atomic increment. + _dispatch_retain(dsema); + + (void)dispatch_atomic_inc2o(dsema, dsema_sent_ksignals); + +#if USE_MACH_SEM + _dispatch_semaphore_create_port(&dsema->dsema_port); + kern_return_t kr = semaphore_signal(dsema->dsema_port); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); +#elif USE_POSIX_SEM + int ret = sem_post(&dsema->dsema_sem); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); +#endif + + _dispatch_release(dsema); + return 1; +} + +long +dispatch_semaphore_signal(dispatch_semaphore_t dsema) +{ + dispatch_atomic_release_barrier(); + long value = dispatch_atomic_inc2o(dsema, dsema_value); + if (fastpath(value > 0)) { + return 0; + } + if (slowpath(value == LONG_MIN)) { + DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_group_leave() or " + "dispatch_semaphore_signal()"); + } + return _dispatch_semaphore_signal_slow(dsema); +} DISPATCH_NOINLINE static long -_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout) +_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, + dispatch_time_t timeout) { - mach_timespec_t _timeout; - kern_return_t kr; - uint64_t nsec; long orig; - + again: - // Mach semaphores appear to sometimes spuriously wake up. Therefore, + // Mach semaphores appear to sometimes spuriously wake up. Therefore, // we keep a parallel count of the number of times a Mach semaphore is - // signaled. + // signaled (6880961). while ((orig = dsema->dsema_sent_ksignals)) { - if (dispatch_atomic_cmpxchg(&dsema->dsema_sent_ksignals, orig, orig - 1)) { + if (dispatch_atomic_cmpxchg2o(dsema, dsema_sent_ksignals, orig, + orig - 1)) { return 0; } } +#if USE_MACH_SEM + mach_timespec_t _timeout; + kern_return_t kr; + _dispatch_semaphore_create_port(&dsema->dsema_port); // From xnu/osfmk/kern/sync_sema.c: - // wait_semaphore->count = -1; /* we don't keep an actual count */ + // wait_semaphore->count = -1; /* we don't keep an actual count */ // // The code above does not match the documentation, and that fact is // not surprising. The documented semantics are clumsy to use in any @@ -162,8 +240,7 @@ again: switch (timeout) { default: do { - // timeout() already calculates relative time left - nsec = _dispatch_timeout(timeout); + uint64_t nsec = _dispatch_timeout(timeout); _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC); _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); kr = slowpath(semaphore_timedwait(dsema->dsema_port, _timeout)); @@ -173,10 +250,11 @@ again: DISPATCH_SEMAPHORE_VERIFY_KR(kr); break; } - // Fall through and try to undo what the fast path did to dsema->dsema_value + // Fall through and try to undo what the fast path did to + // dsema->dsema_value case DISPATCH_TIME_NOW: while ((orig = dsema->dsema_value) < 0) { - if (dispatch_atomic_cmpxchg(&dsema->dsema_value, orig, orig + 1)) { + if (dispatch_atomic_cmpxchg2o(dsema, dsema_value, orig, orig + 1)) { return KERN_OPERATION_TIMED_OUT; } } @@ -189,222 +267,186 @@ again: DISPATCH_SEMAPHORE_VERIFY_KR(kr); break; } +#elif USE_POSIX_SEM + struct timespec _timeout; + int ret; - goto again; -} + switch (timeout) { + default: + do { + uint64_t nsec = _dispatch_timeout(timeout); + _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC); + _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); + ret = slowpath(sem_timedwait(&dsema->dsema_sem, &_timeout)); + } while (ret == -1 && errno == EINTR); -DISPATCH_NOINLINE -void -dispatch_group_enter(dispatch_group_t dg) -{ - dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; -#if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) - // This assumes: - // 1) Way too much about the optimizer of GCC. - // 2) There will never be more than LONG_MAX threads. - // Therefore: no overflow detection - asm( -#ifdef __LP64__ - "lock decq %0\n\t" -#else - "lock decl %0\n\t" -#endif - "js 1f\n\t" - "xor %%eax, %%eax\n\t" - "ret\n\t" - "1:" - : "+m" (dsema->dsema_value) - : - : "cc" - ); - _dispatch_semaphore_wait_slow(dsema, DISPATCH_TIME_FOREVER); -#else - dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); + if (ret == -1 && errno != ETIMEDOUT) { + DISPATCH_SEMAPHORE_VERIFY_RET(ret); + break; + } + // Fall through and try to undo what the fast path did to + // dsema->dsema_value + case DISPATCH_TIME_NOW: + while ((orig = dsema->dsema_value) < 0) { + if (dispatch_atomic_cmpxchg2o(dsema, dsema_value, orig, orig + 1)) { + errno = ETIMEDOUT; + return -1; + } + } + // Another thread called semaphore_signal(). + // Fall through and drain the wakeup. + case DISPATCH_TIME_FOREVER: + do { + ret = sem_wait(&dsema->dsema_sem); + } while (ret != 0); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); + break; + } #endif + + goto again; } -DISPATCH_NOINLINE long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) { -#if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) - // This assumes: - // 1) Way too much about the optimizer of GCC. - // 2) There will never be more than LONG_MAX threads. - // Therefore: no overflow detection - asm( -#ifdef __LP64__ - "lock decq %0\n\t" -#else - "lock decl %0\n\t" -#endif - "js 1f\n\t" - "xor %%eax, %%eax\n\t" - "ret\n\t" - "1:" - : "+m" (dsema->dsema_value) - : - : "cc" - ); -#else - if (dispatch_atomic_dec(&dsema->dsema_value) >= 0) { + long value = dispatch_atomic_dec2o(dsema, dsema_value); + dispatch_atomic_acquire_barrier(); + if (fastpath(value >= 0)) { return 0; } -#endif return _dispatch_semaphore_wait_slow(dsema, timeout); } -DISPATCH_NOINLINE -static long -_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema) -{ - kern_return_t kr; - - _dispatch_semaphore_create_port(&dsema->dsema_port); - - // Before dsema_sent_ksignals is incremented we can rely on the reference - // held by the waiter. However, once this value is incremented the waiter - // may return between the atomic increment and the semaphore_signal(), - // therefore an explicit reference must be held in order to safely access - // dsema after the atomic increment. - _dispatch_retain(dsema); - - dispatch_atomic_inc(&dsema->dsema_sent_ksignals); - - kr = semaphore_signal(dsema->dsema_port); - DISPATCH_SEMAPHORE_VERIFY_KR(kr); +#pragma mark - +#pragma mark dispatch_group_t - _dispatch_release(dsema); - - return 1; +dispatch_group_t +dispatch_group_create(void) +{ + return (dispatch_group_t)dispatch_semaphore_create(LONG_MAX); } void -dispatch_group_leave(dispatch_group_t dg) +dispatch_group_enter(dispatch_group_t dg) { dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; - dispatch_semaphore_signal(dsema); - - if (dsema->dsema_value == dsema->dsema_orig) { - _dispatch_group_wake(dsema); - } -} - -DISPATCH_NOINLINE -long -dispatch_semaphore_signal(dispatch_semaphore_t dsema) -{ -#if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) - // overflow detection - // this assumes way too much about the optimizer of GCC - asm( -#ifdef __LP64__ - "lock incq %0\n\t" -#else - "lock incl %0\n\t" -#endif - "jo 1f\n\t" - "jle 2f\n\t" - "xor %%eax, %%eax\n\t" - "ret\n\t" - "1:\n\t" - "int $4\n\t" - "2:" - : "+m" (dsema->dsema_value) - : - : "cc" - ); -#else - if (dispatch_atomic_inc(&dsema->dsema_value) > 0) { - return 0; - } -#endif - return _dispatch_semaphore_signal_slow(dsema); + (void)dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); } DISPATCH_NOINLINE -long +static long _dispatch_group_wake(dispatch_semaphore_t dsema) { - struct dispatch_sema_notify_s *tmp, *head = dispatch_atomic_xchg(&dsema->dsema_notify_head, NULL); - long rval = dispatch_atomic_xchg(&dsema->dsema_group_waiters, 0); - bool do_rel = head; - long kr; + struct dispatch_sema_notify_s *next, *head, *tail = NULL; + long rval; - // wake any "group" waiter or notify blocks - + head = dispatch_atomic_xchg2o(dsema, dsema_notify_head, NULL); + if (head) { + // snapshot before anything is notified/woken + tail = dispatch_atomic_xchg2o(dsema, dsema_notify_tail, NULL); + } + rval = dispatch_atomic_xchg2o(dsema, dsema_group_waiters, 0); if (rval) { + // wake group waiters +#if USE_MACH_SEM _dispatch_semaphore_create_port(&dsema->dsema_waiter_port); do { - kr = semaphore_signal(dsema->dsema_waiter_port); + kern_return_t kr = semaphore_signal(dsema->dsema_waiter_port); DISPATCH_SEMAPHORE_VERIFY_KR(kr); } while (--rval); - } - while (head) { - dispatch_async_f(head->dsn_queue, head->dsn_ctxt, head->dsn_func); - _dispatch_release(head->dsn_queue); +#elif USE_POSIX_SEM do { - tmp = head->dsn_next; - } while (!tmp && !dispatch_atomic_cmpxchg(&dsema->dsema_notify_tail, head, NULL)); - free(head); - head = tmp; + int ret = sem_post(&dsema->dsema_sem); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); + } while (--rval); +#endif } - if (do_rel) { + if (head) { + // async group notify blocks + do { + dispatch_async_f(head->dsn_queue, head->dsn_ctxt, head->dsn_func); + _dispatch_release(head->dsn_queue); + next = fastpath(head->dsn_next); + if (!next && head != tail) { + while (!(next = fastpath(head->dsn_next))) { + _dispatch_hardware_pause(); + } + } + free(head); + } while ((head = next)); _dispatch_release(dsema); } return 0; } +void +dispatch_group_leave(dispatch_group_t dg) +{ + dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; + + dispatch_semaphore_signal(dsema); + if (dsema->dsema_value == dsema->dsema_orig) { + (void)_dispatch_group_wake(dsema); + } +} + DISPATCH_NOINLINE static long _dispatch_group_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout) { - mach_timespec_t _timeout; - kern_return_t kr; - uint64_t nsec; long orig; - + again: - // check before we cause another signal to be sent by incrementing dsema->dsema_group_waiters + // check before we cause another signal to be sent by incrementing + // dsema->dsema_group_waiters if (dsema->dsema_value == dsema->dsema_orig) { return _dispatch_group_wake(dsema); } - // Mach semaphores appear to sometimes spuriously wake up. Therefore, + // Mach semaphores appear to sometimes spuriously wake up. Therefore, // we keep a parallel count of the number of times a Mach semaphore is - // signaled. - dispatch_atomic_inc(&dsema->dsema_group_waiters); + // signaled (6880961). + (void)dispatch_atomic_inc2o(dsema, dsema_group_waiters); // check the values again in case we need to wake any threads if (dsema->dsema_value == dsema->dsema_orig) { return _dispatch_group_wake(dsema); } +#if USE_MACH_SEM + mach_timespec_t _timeout; + kern_return_t kr; + _dispatch_semaphore_create_port(&dsema->dsema_waiter_port); - + // From xnu/osfmk/kern/sync_sema.c: - // wait_semaphore->count = -1; /* we don't keep an actual count */ + // wait_semaphore->count = -1; /* we don't keep an actual count */ // // The code above does not match the documentation, and that fact is // not surprising. The documented semantics are clumsy to use in any // practical way. The above hack effectively tricks the rest of the // Mach semaphore logic to behave like the libdispatch algorithm. - + switch (timeout) { default: do { - nsec = _dispatch_timeout(timeout); + uint64_t nsec = _dispatch_timeout(timeout); _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC); _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); - kr = slowpath(semaphore_timedwait(dsema->dsema_waiter_port, _timeout)); + kr = slowpath(semaphore_timedwait(dsema->dsema_waiter_port, + _timeout)); } while (kr == KERN_ABORTED); + if (kr != KERN_OPERATION_TIMED_OUT) { DISPATCH_SEMAPHORE_VERIFY_KR(kr); break; } - // Fall through and try to undo the earlier change to dsema->dsema_group_waiters + // Fall through and try to undo the earlier change to + // dsema->dsema_group_waiters case DISPATCH_TIME_NOW: while ((orig = dsema->dsema_group_waiters)) { - if (dispatch_atomic_cmpxchg(&dsema->dsema_group_waiters, orig, orig - 1)) { + if (dispatch_atomic_cmpxchg2o(dsema, dsema_group_waiters, orig, + orig - 1)) { return KERN_OPERATION_TIMED_OUT; } } @@ -417,6 +459,43 @@ again: DISPATCH_SEMAPHORE_VERIFY_KR(kr); break; } +#elif USE_POSIX_SEM + struct timespec _timeout; + int ret; + + switch (timeout) { + default: + do { + uint64_t nsec = _dispatch_timeout(timeout); + _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC); + _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); + ret = slowpath(sem_timedwait(&dsema->dsema_sem, &_timeout)); + } while (ret == -1 && errno == EINTR); + + if (!(ret == -1 && errno == ETIMEDOUT)) { + DISPATCH_SEMAPHORE_VERIFY_RET(ret); + break; + } + // Fall through and try to undo the earlier change to + // dsema->dsema_group_waiters + case DISPATCH_TIME_NOW: + while ((orig = dsema->dsema_group_waiters)) { + if (dispatch_atomic_cmpxchg2o(dsema, dsema_group_waiters, orig, + orig - 1)) { + errno = ETIMEDOUT; + return -1; + } + } + // Another thread called semaphore_signal(). + // Fall through and drain the wakeup. + case DISPATCH_TIME_FOREVER: + do { + ret = sem_wait(&dsema->dsema_sem); + } while (ret == -1 && errno == EINTR); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); + break; + } +#endif goto again; } @@ -430,37 +509,35 @@ dispatch_group_wait(dispatch_group_t dg, dispatch_time_t timeout) return 0; } if (timeout == 0) { +#if USE_MACH_SEM return KERN_OPERATION_TIMED_OUT; +#elif USE_POSIX_SEM + errno = ETIMEDOUT; + return (-1); +#endif } return _dispatch_group_wait_slow(dsema, timeout); } -#ifdef __BLOCKS__ -void -dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq, dispatch_block_t db) -{ - dispatch_group_notify_f(dg, dq, _dispatch_Block_copy(db), _dispatch_call_block_and_release); -} -#endif - +DISPATCH_NOINLINE void -dispatch_group_notify_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, void (*func)(void *)) +dispatch_group_notify_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, + void (*func)(void *)) { dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; struct dispatch_sema_notify_s *dsn, *prev; // FIXME -- this should be updated to use the continuation cache - while (!(dsn = malloc(sizeof(*dsn)))) { + while (!(dsn = calloc(1, sizeof(*dsn)))) { sleep(1); } - dsn->dsn_next = NULL; dsn->dsn_queue = dq; dsn->dsn_ctxt = ctxt; dsn->dsn_func = func; _dispatch_retain(dq); - - prev = dispatch_atomic_xchg(&dsema->dsema_notify_tail, dsn); + dispatch_atomic_store_barrier(); + prev = dispatch_atomic_xchg2o(dsema, dsema_notify_tail, dsn); if (fastpath(prev)) { prev->dsn_next = dsn; } else { @@ -472,61 +549,108 @@ dispatch_group_notify_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, vo } } +#ifdef __BLOCKS__ void -_dispatch_semaphore_dispose(dispatch_semaphore_t dsema) +dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq, + dispatch_block_t db) { + dispatch_group_notify_f(dg, dq, _dispatch_Block_copy(db), + _dispatch_call_block_and_release); +} +#endif + +#pragma mark - +#pragma mark _dispatch_thread_semaphore_t + +DISPATCH_NOINLINE +static _dispatch_thread_semaphore_t +_dispatch_thread_semaphore_create(void) +{ +#if USE_MACH_SEM + semaphore_t s4; kern_return_t kr; - - if (dsema->dsema_value < dsema->dsema_orig) { - DISPATCH_CLIENT_CRASH("Semaphore/group object deallocated while in use"); - } - - if (dsema->dsema_port) { - kr = semaphore_destroy(mach_task_self(), dsema->dsema_port); - DISPATCH_SEMAPHORE_VERIFY_KR(kr); - } - if (dsema->dsema_waiter_port) { - kr = semaphore_destroy(mach_task_self(), dsema->dsema_waiter_port); - DISPATCH_SEMAPHORE_VERIFY_KR(kr); + while (slowpath(kr = semaphore_create(mach_task_self(), &s4, + SYNC_POLICY_FIFO, 0))) { + DISPATCH_VERIFY_MIG(kr); + sleep(1); } - - _dispatch_dispose(dsema); + return s4; +#elif USE_POSIX_SEM + sem_t s4; + int ret = sem_init(&s4, 0, 0); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); + return s4; +#endif } -size_t -_dispatch_semaphore_debug(dispatch_semaphore_t dsema, char *buf, size_t bufsiz) +DISPATCH_NOINLINE +void +_dispatch_thread_semaphore_dispose(_dispatch_thread_semaphore_t sema) { - size_t offset = 0; - offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dx_kind(dsema), dsema); - offset += dispatch_object_debug_attr(dsema, &buf[offset], bufsiz - offset); - offset += snprintf(&buf[offset], bufsiz - offset, "port = 0x%u, value = %ld, orig = %ld }", - dsema->dsema_port, dsema->dsema_value, dsema->dsema_orig); - return offset; +#if USE_MACH_SEM + semaphore_t s4 = (semaphore_t)sema; + kern_return_t kr = semaphore_destroy(mach_task_self(), s4); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); +#elif USE_POSIX_SEM + sem_t s4 = (sem_t)sema; + int ret = sem_destroy(&s4); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); +#endif } -#ifdef __BLOCKS__ void -dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, dispatch_block_t db) +_dispatch_thread_semaphore_signal(_dispatch_thread_semaphore_t sema) { - dispatch_group_async_f(dg, dq, _dispatch_Block_copy(db), _dispatch_call_block_and_release); -} +#if USE_MACH_SEM + semaphore_t s4 = (semaphore_t)sema; + kern_return_t kr = semaphore_signal(s4); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); +#elif USE_POSIX_SEM + sem_t s4 = (sem_t)sema; + int ret = sem_post(&s4); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); #endif +} -DISPATCH_NOINLINE void -dispatch_group_async_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, void (*func)(void *)) +_dispatch_thread_semaphore_wait(_dispatch_thread_semaphore_t sema) { - dispatch_continuation_t dc; - - _dispatch_retain(dg); - dispatch_group_enter(dg); - - dc = _dispatch_continuation_alloc_cacheonly() ?: _dispatch_continuation_alloc_from_heap(); +#if USE_MACH_SEM + semaphore_t s4 = (semaphore_t)sema; + kern_return_t kr; + do { + kr = semaphore_wait(s4); + } while (slowpath(kr == KERN_ABORTED)); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); +#elif USE_POSIX_SEM + sem_t s4 = (sem_t)sema; + int ret; + do { + ret = sem_wait(&s4); + } while (slowpath(ret != 0)); + DISPATCH_SEMAPHORE_VERIFY_RET(ret); +#endif +} - dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT|DISPATCH_OBJ_GROUP_BIT); - dc->dc_func = func; - dc->dc_ctxt = ctxt; - dc->dc_group = dg; +_dispatch_thread_semaphore_t +_dispatch_get_thread_semaphore(void) +{ + _dispatch_thread_semaphore_t sema = (_dispatch_thread_semaphore_t) + _dispatch_thread_getspecific(dispatch_sema4_key); + if (slowpath(!sema)) { + return _dispatch_thread_semaphore_create(); + } + _dispatch_thread_setspecific(dispatch_sema4_key, NULL); + return sema; +} - _dispatch_queue_push(dq, dc); +void +_dispatch_put_thread_semaphore(_dispatch_thread_semaphore_t sema) +{ + _dispatch_thread_semaphore_t old_sema = (_dispatch_thread_semaphore_t) + _dispatch_thread_getspecific(dispatch_sema4_key); + _dispatch_thread_setspecific(dispatch_sema4_key, (void*)sema); + if (slowpath(old_sema)) { + return _dispatch_thread_semaphore_dispose(old_sema); + } } diff --git a/src/semaphore_internal.h b/src/semaphore_internal.h index 3af28c0..e5b319e 100644 --- a/src/semaphore_internal.h +++ b/src/semaphore_internal.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -28,7 +28,7 @@ #define __DISPATCH_SEMAPHORE_INTERNAL__ struct dispatch_sema_notify_s { - struct dispatch_sema_notify_s *dsn_next; + struct dispatch_sema_notify_s *volatile dsn_next; dispatch_queue_t dsn_queue; void *dsn_ctxt; void (*dsn_func)(void *); @@ -39,8 +39,16 @@ struct dispatch_semaphore_s { long dsema_value; long dsema_orig; size_t dsema_sent_ksignals; +#if USE_MACH_SEM && USE_POSIX_SEM +#error "Too many supported semaphore types" +#elif USE_MACH_SEM semaphore_t dsema_port; semaphore_t dsema_waiter_port; +#elif USE_POSIX_SEM + sem_t dsema_sem; +#else +#error "No supported semaphore type" +#endif size_t dsema_group_waiters; struct dispatch_sema_notify_s *dsema_notify_head; struct dispatch_sema_notify_s *dsema_notify_tail; @@ -48,4 +56,11 @@ struct dispatch_semaphore_s { extern const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable; +typedef uintptr_t _dispatch_thread_semaphore_t; +_dispatch_thread_semaphore_t _dispatch_get_thread_semaphore(void); +void _dispatch_put_thread_semaphore(_dispatch_thread_semaphore_t); +void _dispatch_thread_semaphore_wait(_dispatch_thread_semaphore_t); +void _dispatch_thread_semaphore_signal(_dispatch_thread_semaphore_t); +void _dispatch_thread_semaphore_dispose(_dispatch_thread_semaphore_t); + #endif diff --git a/src/shims.c b/src/shims.c deleted file mode 100644 index a02d453..0000000 --- a/src/shims.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 "internal.h" - -void * -dispatch_mach_msg_get_context(mach_msg_header_t *msg) -{ - mach_msg_context_trailer_t *tp; - void *context = NULL; - - tp = (mach_msg_context_trailer_t *)((uint8_t *)msg + round_msg(msg->msgh_size)); - if (tp->msgh_trailer_size >= (mach_msg_size_t)sizeof(mach_msg_context_trailer_t)) { - context = (void *)(uintptr_t)tp->msgh_context; - } - - return context; -} - -/* - * Raw Mach message support - */ -boolean_t -_dispatch_machport_callback(mach_msg_header_t *msg, mach_msg_header_t *reply, - void (*callback)(mach_msg_header_t *)) -{ - mig_reply_setup(msg, reply); - ((mig_reply_error_t*)reply)->RetCode = MIG_NO_REPLY; - - callback(msg); - - return TRUE; -} - -/* - * CFMachPort compatibility - */ -boolean_t -_dispatch_CFMachPortCallBack(mach_msg_header_t *msg, mach_msg_header_t *reply, - void (*callback)(struct __CFMachPort *, void *msg, signed long size, void *)) -{ - mig_reply_setup(msg, reply); - ((mig_reply_error_t*)reply)->RetCode = MIG_NO_REPLY; - - callback(NULL, msg, msg->msgh_size, dispatch_mach_msg_get_context(msg)); - - return TRUE; -} diff --git a/src/shims.h b/src/shims.h new file mode 100644 index 0000000..73322be --- /dev/null +++ b/src/shims.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2008-2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_OS_SHIMS__ +#define __DISPATCH_OS_SHIMS__ + +#include +#if HAVE_PTHREAD_WORKQUEUES +#include +#endif +#if HAVE_PTHREAD_NP_H +#include +#endif + +#if !HAVE_DECL_FD_COPY +#define FD_COPY(f, t) (void)(*(t) = *(f)) +#endif + +#if !HAVE_NORETURN_BUILTIN_TRAP +/* + * XXXRW: Work-around for possible clang bug in which __builtin_trap() is not + * marked noreturn, leading to a build error as dispatch_main() *is* marked + * noreturn. Mask by marking __builtin_trap() as noreturn locally. + */ +DISPATCH_NORETURN +void __builtin_trap(void); +#endif + +#include "shims/atomic.h" +#include "shims/tsd.h" +#include "shims/hw_config.h" +#include "shims/perfmon.h" + +#include "shims/getprogname.h" +#include "shims/malloc_zone.h" +#include "shims/time.h" + +#ifdef __APPLE__ +// Clear the stack before calling long-running thread-handler functions that +// never return (and don't take arguments), to facilitate leak detection and +// provide cleaner backtraces. +#define _dispatch_clear_stack(s) do { \ + void *a[(s)/sizeof(void*) ? (s)/sizeof(void*) : 1]; \ + a[0] = pthread_get_stackaddr_np(pthread_self()); \ + bzero((void*)&a[1], a[0] - (void*)&a[1]); \ + } while (0) +#else +#define _dispatch_clear_stack(s) +#endif + +#endif diff --git a/src/shims/atomic.h b/src/shims/atomic.h new file mode 100644 index 0000000..fbc1171 --- /dev/null +++ b/src/shims/atomic.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2008-2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_SHIMS_ATOMIC__ +#define __DISPATCH_SHIMS_ATOMIC__ + +/* x86 & cortex-a8 have a 64 byte cacheline */ +#define DISPATCH_CACHELINE_SIZE 64 +#define ROUND_UP_TO_CACHELINE_SIZE(x) \ + (((x) + (DISPATCH_CACHELINE_SIZE - 1)) & ~(DISPATCH_CACHELINE_SIZE - 1)) +#define ROUND_UP_TO_VECTOR_SIZE(x) \ + (((x) + 15) & ~15) +#define DISPATCH_CACHELINE_ALIGN \ + __attribute__((__aligned__(DISPATCH_CACHELINE_SIZE))) + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) + +#define _dispatch_atomic_barrier() __sync_synchronize() +// see comment in dispatch_once.c +#define dispatch_atomic_maximally_synchronizing_barrier() \ + _dispatch_atomic_barrier() +// assume atomic builtins provide barriers +#define dispatch_atomic_barrier() +#define dispatch_atomic_acquire_barrier() +#define dispatch_atomic_release_barrier() +#define dispatch_atomic_store_barrier() + +#define _dispatch_hardware_pause() asm("") +#define _dispatch_debugger() asm("trap") + +#define dispatch_atomic_cmpxchg(p, e, n) \ + __sync_bool_compare_and_swap((p), (e), (n)) +#if __has_builtin(__sync_swap) +#define dispatch_atomic_xchg(p, n) \ + ((typeof(*(p)))__sync_swap((p), (n))) +#else +#define dispatch_atomic_xchg(p, n) \ + ((typeof(*(p)))__sync_lock_test_and_set((p), (n))) +#endif +#define dispatch_atomic_add(p, v) __sync_add_and_fetch((p), (v)) +#define dispatch_atomic_sub(p, v) __sync_sub_and_fetch((p), (v)) +#define dispatch_atomic_or(p, v) __sync_fetch_and_or((p), (v)) +#define dispatch_atomic_and(p, v) __sync_fetch_and_and((p), (v)) + +#define dispatch_atomic_inc(p) dispatch_atomic_add((p), 1) +#define dispatch_atomic_dec(p) dispatch_atomic_sub((p), 1) +// really just a low level abort() +#define _dispatch_hardware_crash() __builtin_trap() + +#define dispatch_atomic_cmpxchg2o(p, f, e, n) \ + dispatch_atomic_cmpxchg(&(p)->f, (e), (n)) +#define dispatch_atomic_xchg2o(p, f, n) \ + dispatch_atomic_xchg(&(p)->f, (n)) +#define dispatch_atomic_add2o(p, f, v) \ + dispatch_atomic_add(&(p)->f, (v)) +#define dispatch_atomic_sub2o(p, f, v) \ + dispatch_atomic_sub(&(p)->f, (v)) +#define dispatch_atomic_or2o(p, f, v) \ + dispatch_atomic_or(&(p)->f, (v)) +#define dispatch_atomic_and2o(p, f, v) \ + dispatch_atomic_and(&(p)->f, (v)) +#define dispatch_atomic_inc2o(p, f) \ + dispatch_atomic_add2o((p), f, 1) +#define dispatch_atomic_dec2o(p, f) \ + dispatch_atomic_sub2o((p), f, 1) + +#else +#error "Please upgrade to GCC 4.2 or newer." +#endif + +#if defined(__x86_64__) || defined(__i386__) + +// GCC emits nothing for __sync_synchronize() on x86_64 & i386 +#undef _dispatch_atomic_barrier +#define _dispatch_atomic_barrier() \ + __asm__ __volatile__( \ + "mfence" \ + : : : "memory") +#undef dispatch_atomic_maximally_synchronizing_barrier +#ifdef __LP64__ +#define dispatch_atomic_maximally_synchronizing_barrier() \ + do { unsigned long _clbr; __asm__ __volatile__( \ + "cpuid" \ + : "=a" (_clbr) : "0" (0) : "rbx", "rcx", "rdx", "cc", "memory" \ + ); } while(0) +#else +#ifdef __llvm__ +#define dispatch_atomic_maximally_synchronizing_barrier() \ + do { unsigned long _clbr; __asm__ __volatile__( \ + "cpuid" \ + : "=a" (_clbr) : "0" (0) : "ebx", "ecx", "edx", "cc", "memory" \ + ); } while(0) +#else // gcc does not allow inline i386 asm to clobber ebx +#define dispatch_atomic_maximally_synchronizing_barrier() \ + do { unsigned long _clbr; __asm__ __volatile__( \ + "pushl %%ebx\n\t" \ + "cpuid\n\t" \ + "popl %%ebx" \ + : "=a" (_clbr) : "0" (0) : "ecx", "edx", "cc", "memory" \ + ); } while(0) +#endif +#endif +#undef _dispatch_hardware_pause +#define _dispatch_hardware_pause() asm("pause") +#undef _dispatch_debugger +#define _dispatch_debugger() asm("int3") + +#elif defined(__ppc__) || defined(__ppc64__) + +// GCC emits "sync" for __sync_synchronize() on ppc & ppc64 +#undef _dispatch_atomic_barrier +#ifdef __LP64__ +#define _dispatch_atomic_barrier() \ + __asm__ __volatile__( \ + "isync\n\t" \ + "lwsync" + : : : "memory") +#else +#define _dispatch_atomic_barrier() \ + __asm__ __volatile__( \ + "isync\n\t" \ + "eieio" \ + : : : "memory") +#endif +#undef dispatch_atomic_maximally_synchronizing_barrier +#define dispatch_atomic_maximally_synchronizing_barrier() \ + __asm__ __volatile__( \ + "sync" \ + : : : "memory") + +#endif + + +#endif // __DISPATCH_SHIMS_ATOMIC__ diff --git a/src/shims/getprogname.h b/src/shims/getprogname.h new file mode 100644 index 0000000..74aba13 --- /dev/null +++ b/src/shims/getprogname.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009-2010 Mark Heily + * 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@ + */ + +#ifndef __DISPATCH_SHIMS_GETPROGNAME__ +#define __DISPATCH_SHIMS_GETPROGNAME__ + +#if !HAVE_GETPROGNAME +static inline char * +getprogname(void) +{ +# if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + return program_invocation_short_name; +# else +# error getprogname(3) is not available on this platform +# endif +} +#endif /* HAVE_GETPROGNAME */ + +#endif /* __DISPATCH_SHIMS_GETPROGNAME__ */ diff --git a/src/shims/hw_config.h b/src/shims/hw_config.h new file mode 100644 index 0000000..2d99759 --- /dev/null +++ b/src/shims/hw_config.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_SHIMS_HW_CONFIG__ +#define __DISPATCH_SHIMS_HW_CONFIG__ + +#if defined(__APPLE__) +#define DISPATCH_SYSCTL_LOGICAL_CPUS "hw.logicalcpu_max" +#define DISPATCH_SYSCTL_PHYSICAL_CPUS "hw.physicalcpu_max" +#define DISPATCH_SYSCTL_ACTIVE_CPUS "hw.activecpu" +#elif defined(__FreeBSD__) +#define DISPATCH_SYSCTL_LOGICAL_CPUS "kern.smp.cpus" +#define DISPATCH_SYSCTL_PHYSICAL_CPUS "kern.smp.cpus" +#define DISPATCH_SYSCTL_ACTIVE_CPUS "kern.smp.cpus" +#endif + +static inline uint32_t +_dispatch_get_logicalcpu_max() +{ + uint32_t val = 1; +#if defined(_COMM_PAGE_LOGICAL_CPUS) + uint8_t* u8val = (uint8_t*)(uintptr_t)_COMM_PAGE_LOGICAL_CPUS; + val = (uint32_t)*u8val; +#elif defined(DISPATCH_SYSCTL_LOGICAL_CPUS) + size_t valsz = sizeof(val); + int ret = sysctlbyname(DISPATCH_SYSCTL_LOGICAL_CPUS, + &val, &valsz, NULL, 0); + (void)dispatch_assume_zero(ret); + (void)dispatch_assume(valsz == sizeof(uint32_t)); +#elif HAVE_SYSCONF && defined(_SC_NPROCESSORS_ONLN) + int ret = (int)sysconf(_SC_NPROCESSORS_ONLN); + val = ret < 0 ? 1 : ret; +#else +#warning "no supported way to query logical CPU count" +#endif + return val; +} + +static inline uint32_t +_dispatch_get_physicalcpu_max() +{ + uint32_t val = 1; +#if defined(_COMM_PAGE_PHYSICAL_CPUS) + uint8_t* u8val = (uint8_t*)(uintptr_t)_COMM_PAGE_PHYSICAL_CPUS; + val = (uint32_t)*u8val; +#elif defined(DISPATCH_SYSCTL_PHYSICAL_CPUS) + size_t valsz = sizeof(val); + int ret = sysctlbyname(DISPATCH_SYSCTL_LOGICAL_CPUS, + &val, &valsz, NULL, 0); + (void)dispatch_assume_zero(ret); + (void)dispatch_assume(valsz == sizeof(uint32_t)); +#elif HAVE_SYSCONF && defined(_SC_NPROCESSORS_ONLN) + int ret = (int)sysconf(_SC_NPROCESSORS_ONLN); + val = ret < 0 ? 1 : ret; +#else +#warning "no supported way to query physical CPU count" +#endif + return val; +} + +static inline uint32_t +_dispatch_get_activecpu() +{ + uint32_t val = 1; +#if defined(_COMM_PAGE_ACTIVE_CPUS) + uint8_t* u8val = (uint8_t*)(uintptr_t)_COMM_PAGE_ACTIVE_CPUS; + val = (uint32_t)*u8val; +#elif defined(DISPATCH_SYSCTL_ACTIVE_CPUS) + size_t valsz = sizeof(val); + int ret = sysctlbyname(DISPATCH_SYSCTL_ACTIVE_CPUS, + &val, &valsz, NULL, 0); + (void)dispatch_assume_zero(ret); + (void)dispatch_assume(valsz == sizeof(uint32_t)); +#elif HAVE_SYSCONF && defined(_SC_NPROCESSORS_ONLN) + int ret = (int)sysconf(_SC_NPROCESSORS_ONLN); + val = ret < 0 ? 1 : ret; +#else +#warning "no supported way to query active CPU count" +#endif + return val; +} + +#endif /* __DISPATCH_SHIMS_HW_CONFIG__ */ diff --git a/src/shims/malloc_zone.h b/src/shims/malloc_zone.h new file mode 100644 index 0000000..3975b4f --- /dev/null +++ b/src/shims/malloc_zone.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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@ + */ + +#ifndef __DISPATCH_SHIMS_MALLOC_ZONE__ +#define __DISPATCH_SHIMS_MALLOC_ZONE__ + +#include + +#include + +/* + * Implement malloc zones as a simple wrapper around malloc(3) on systems + * that don't support them. + */ +#if !HAVE_MALLOC_CREATE_ZONE +typedef void * malloc_zone_t; + +static inline malloc_zone_t * +malloc_create_zone(size_t start_size, unsigned flags) +{ + + return ((void *)(-1)); +} + +static inline void +malloc_destroy_zone(malloc_zone_t *zone) +{ + +} + +static inline malloc_zone_t * +malloc_default_zone(void) +{ + + return ((void *)(-1)); +} + +static inline malloc_zone_t * +malloc_zone_from_ptr(const void *ptr) +{ + + return ((void *)(-1)); +} + +static inline void * +malloc_zone_malloc(malloc_zone_t *zone, size_t size) +{ + + return (malloc(size)); +} + +static inline void * +malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size) +{ + + return (calloc(num_items, size)); +} + +static inline void * +malloc_zone_realloc(malloc_zone_t *zone, void *ptr, size_t size) +{ + + return (realloc(ptr, size)); +} + +static inline void +malloc_zone_free(malloc_zone_t *zone, void *ptr) +{ + + free(ptr); +} + +static inline void +malloc_set_zone_name(malloc_zone_t *zone, const char *name) +{ + + /* No-op. */ +} +#endif + +#endif /* __DISPATCH_SHIMS_MALLOC_ZONE__ */ diff --git a/src/shims/perfmon.h b/src/shims/perfmon.h new file mode 100644 index 0000000..bf5eb28 --- /dev/null +++ b/src/shims/perfmon.h @@ -0,0 +1,97 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_SHIMS_PERFMON__ +#define __DISPATCH_SHIMS_PERFMON__ + +#if DISPATCH_PERF_MON + +#if defined (USE_APPLE_TSD_OPTIMIZATIONS) && defined(SIMULATE_5491082) && \ + (defined(__i386__) || defined(__x86_64__)) +#ifdef __LP64__ +#define _dispatch_workitem_inc() asm("incq %%gs:%0" : "+m" \ + (*(void **)(dispatch_bcounter_key * sizeof(void *) + \ + _PTHREAD_TSD_OFFSET)) :: "cc") +#define _dispatch_workitem_dec() asm("decq %%gs:%0" : "+m" \ + (*(void **)(dispatch_bcounter_key * sizeof(void *) + \ + _PTHREAD_TSD_OFFSET)) :: "cc") +#else +#define _dispatch_workitem_inc() asm("incl %%gs:%0" : "+m" \ + (*(void **)(dispatch_bcounter_key * sizeof(void *) + \ + _PTHREAD_TSD_OFFSET)) :: "cc") +#define _dispatch_workitem_dec() asm("decl %%gs:%0" : "+m" \ + (*(void **)(dispatch_bcounter_key * sizeof(void *) + \ + _PTHREAD_TSD_OFFSET)) :: "cc") +#endif +#else /* !USE_APPLE_TSD_OPTIMIZATIONS */ +static inline void +_dispatch_workitem_inc(void) +{ + unsigned long cnt; + cnt = (unsigned long)_dispatch_thread_getspecific(dispatch_bcounter_key); + _dispatch_thread_setspecific(dispatch_bcounter_key, (void *)++cnt); +} +static inline void +_dispatch_workitem_dec(void) +{ + unsigned long cnt; + cnt = (unsigned long)_dispatch_thread_getspecific(dispatch_bcounter_key); + _dispatch_thread_setspecific(dispatch_bcounter_key, (void *)--cnt); +} +#endif /* USE_APPLE_TSD_OPTIMIZATIONS */ + +// C99 doesn't define flsll() or ffsll() +#ifdef __LP64__ +#define flsll(x) flsl(x) +#else +static inline unsigned int +flsll(uint64_t val) +{ + union { + struct { +#ifdef __BIG_ENDIAN__ + unsigned int hi, low; +#else + unsigned int low, hi; +#endif + } words; + uint64_t word; + } _bucket = { + .word = val, + }; + if (_bucket.words.hi) { + return fls(_bucket.words.hi) + 32; + } + return fls(_bucket.words.low); +} +#endif + +#else +#define _dispatch_workitem_inc() +#define _dispatch_workitem_dec() +#endif // DISPATCH_PERF_MON + +#endif diff --git a/src/shims/time.h b/src/shims/time.h new file mode 100644 index 0000000..9ae9160 --- /dev/null +++ b/src/shims/time.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2008-2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_SHIMS_TIME__ +#define __DISPATCH_SHIMS_TIME__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#endif + +uint64_t _dispatch_get_nanoseconds(void); + +#if defined(__i386__) || defined(__x86_64__) || !HAVE_MACH_ABSOLUTE_TIME +// x86 currently implements mach time in nanoseconds +// this is NOT likely to change +#define _dispatch_time_mach2nano(x) ({x;}) +#define _dispatch_time_nano2mach(x) ({x;}) +#else +typedef struct _dispatch_host_time_data_s { + long double frac; + bool ratio_1_to_1; + dispatch_once_t pred; +} _dispatch_host_time_data_s; +extern _dispatch_host_time_data_s _dispatch_host_time_data; +void _dispatch_get_host_time_init(void *context); + +static inline uint64_t +_dispatch_time_mach2nano(uint64_t machtime) +{ + _dispatch_host_time_data_s *const data = &_dispatch_host_time_data; + dispatch_once_f(&data->pred, NULL, _dispatch_get_host_time_init); + + return machtime * data->frac; +} + +static inline int64_t +_dispatch_time_nano2mach(int64_t nsec) +{ + _dispatch_host_time_data_s *const data = &_dispatch_host_time_data; + dispatch_once_f(&data->pred, NULL, _dispatch_get_host_time_init); + + if (slowpath(_dispatch_host_time_data.ratio_1_to_1)) { + return nsec; + } + + long double big_tmp = nsec; + + // Divide by tbi.numer/tbi.denom to convert nsec to Mach absolute time + big_tmp /= data->frac; + + // Clamp to a 64bit signed int + if (slowpath(big_tmp > INT64_MAX)) { + return INT64_MAX; + } + if (slowpath(big_tmp < INT64_MIN)) { + return INT64_MIN; + } + return big_tmp; +} +#endif + +static inline uint64_t +_dispatch_absolute_time(void) +{ +#if !HAVE_MACH_ABSOLUTE_TIME + struct timespec ts; + int ret; + +#if HAVE_DECL_CLOCK_UPTIME + ret = clock_gettime(CLOCK_UPTIME, &ts); +#elif HAVE_DECL_CLOCK_MONOTONIC + ret = clock_gettime(CLOCK_MONOTONIC, &ts); +#else +#error "clock_gettime: no supported absolute time clock" +#endif + (void)dispatch_assume_zero(ret); + + /* XXXRW: Some kind of overflow detection needed? */ + return (ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec); +#else + return mach_absolute_time(); +#endif +} + +#endif diff --git a/src/shims/tsd.h b/src/shims/tsd.h new file mode 100644 index 0000000..b8c6640 --- /dev/null +++ b/src/shims/tsd.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2008-2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_SHIMS_TSD__ +#define __DISPATCH_SHIMS_TSD__ + +#if HAVE_PTHREAD_MACHDEP_H +#include +#endif + +#define DISPATCH_TSD_INLINE DISPATCH_ALWAYS_INLINE_NDEBUG + +#if USE_APPLE_TSD_OPTIMIZATIONS && HAVE_PTHREAD_KEY_INIT_NP && \ + !defined(DISPATCH_USE_DIRECT_TSD) +#define DISPATCH_USE_DIRECT_TSD 1 +#endif + +#if DISPATCH_USE_DIRECT_TSD +static const unsigned long dispatch_queue_key = __PTK_LIBDISPATCH_KEY0; +static const unsigned long dispatch_sema4_key = __PTK_LIBDISPATCH_KEY1; +static const unsigned long dispatch_cache_key = __PTK_LIBDISPATCH_KEY2; +static const unsigned long dispatch_io_key = __PTK_LIBDISPATCH_KEY3; +static const unsigned long dispatch_apply_key = __PTK_LIBDISPATCH_KEY4; +static const unsigned long dispatch_bcounter_key = __PTK_LIBDISPATCH_KEY5; +//__PTK_LIBDISPATCH_KEY5 + +DISPATCH_TSD_INLINE +static inline void +_dispatch_thread_key_create(const unsigned long *k, void (*d)(void *)) +{ + dispatch_assert_zero(pthread_key_init_np((int)*k, d)); +} +#else +pthread_key_t dispatch_queue_key; +pthread_key_t dispatch_sema4_key; +pthread_key_t dispatch_cache_key; +pthread_key_t dispatch_io_key; +pthread_key_t dispatch_apply_key; +pthread_key_t dispatch_bcounter_key; + +DISPATCH_TSD_INLINE +static inline void +_dispatch_thread_key_create(pthread_key_t *k, void (*d)(void *)) +{ + dispatch_assert_zero(pthread_key_create(k, d)); +} +#endif + +#if DISPATCH_USE_TSD_BASE && !DISPATCH_DEBUG +#else // DISPATCH_USE_TSD_BASE +DISPATCH_TSD_INLINE +static inline void +_dispatch_thread_setspecific(pthread_key_t k, void *v) +{ +#if DISPATCH_USE_DIRECT_TSD + if (_pthread_has_direct_tsd()) { + (void)_pthread_setspecific_direct(k, v); + return; + } +#endif + dispatch_assert_zero(pthread_setspecific(k, v)); +} + +DISPATCH_TSD_INLINE +static inline void * +_dispatch_thread_getspecific(pthread_key_t k) +{ +#if DISPATCH_USE_DIRECT_TSD + if (_pthread_has_direct_tsd()) { + return _pthread_getspecific_direct(k); + } +#endif + return pthread_getspecific(k); +} +#endif // DISPATCH_USE_TSD_BASE + +#define _dispatch_thread_self (uintptr_t)pthread_self + +#undef DISPATCH_TSD_INLINE + +#endif diff --git a/src/source.c b/src/source.c index 5a0dbc8..cf612aa 100644 --- a/src/source.c +++ b/src/source.c @@ -1,196 +1,203 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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 "internal.h" +#if HAVE_MACH #include "protocol.h" #include "protocolServer.h" +#endif #include -#define DISPATCH_EVFILT_TIMER (-EVFILT_SYSCOUNT - 1) -#define DISPATCH_EVFILT_CUSTOM_ADD (-EVFILT_SYSCOUNT - 2) -#define DISPATCH_EVFILT_CUSTOM_OR (-EVFILT_SYSCOUNT - 3) -#define DISPATCH_EVFILT_SYSCOUNT (EVFILT_SYSCOUNT + 3) - -#define DISPATCH_TIMER_INDEX_WALL 0 -#define DISPATCH_TIMER_INDEX_MACH 1 -static struct dispatch_kevent_s _dispatch_kevent_timer[] = { - { - .dk_kevent = { - .ident = DISPATCH_TIMER_INDEX_WALL, - .filter = DISPATCH_EVFILT_TIMER, - .udata = &_dispatch_kevent_timer[0], - }, - .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_timer[0].dk_sources), - }, - { - .dk_kevent = { - .ident = DISPATCH_TIMER_INDEX_MACH, - .filter = DISPATCH_EVFILT_TIMER, - .udata = &_dispatch_kevent_timer[1], - }, - .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_timer[1].dk_sources), - }, -}; -#define DISPATCH_TIMER_COUNT (sizeof _dispatch_kevent_timer / sizeof _dispatch_kevent_timer[0]) - -static struct dispatch_kevent_s _dispatch_kevent_data_or = { - .dk_kevent = { - .filter = DISPATCH_EVFILT_CUSTOM_OR, - .flags = EV_CLEAR, - .udata = &_dispatch_kevent_data_or, - }, - .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_data_or.dk_sources), -}; -static struct dispatch_kevent_s _dispatch_kevent_data_add = { - .dk_kevent = { - .filter = DISPATCH_EVFILT_CUSTOM_ADD, - .udata = &_dispatch_kevent_data_add, - }, - .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_data_add.dk_sources), -}; - -#ifndef DISPATCH_NO_LEGACY -struct dispatch_source_attr_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_source_attr_s); -}; - -struct dispatch_source_attr_s { - DISPATCH_STRUCT_HEADER(dispatch_source_attr_s, dispatch_source_attr_vtable_s); - void* finalizer_ctxt; - dispatch_source_finalizer_function_t finalizer_func; - void* context; -}; -#endif /* DISPATCH_NO_LEGACY */ - -#define _dispatch_source_call_block ((void *)-1) -static void _dispatch_source_latch_and_call(dispatch_source_t ds); -static void _dispatch_source_cancel_callout(dispatch_source_t ds); -static bool _dispatch_source_probe(dispatch_source_t ds); static void _dispatch_source_dispose(dispatch_source_t ds); -static void _dispatch_source_merge_kevent(dispatch_source_t ds, const struct kevent *ke); -static size_t _dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz); -static size_t dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz); static dispatch_queue_t _dispatch_source_invoke(dispatch_source_t ds); - -static void _dispatch_kevent_merge(dispatch_source_t ds); -static void _dispatch_kevent_release(dispatch_source_t ds); -static void _dispatch_kevent_resume(dispatch_kevent_t dk, uint32_t new_flags, uint32_t del_flags); -static void _dispatch_kevent_machport_resume(dispatch_kevent_t dk, uint32_t new_flags, uint32_t del_flags); -static void _dispatch_kevent_machport_enable(dispatch_kevent_t dk); -static void _dispatch_kevent_machport_disable(dispatch_kevent_t dk); - -static void _dispatch_drain_mach_messages(struct kevent *ke); +static bool _dispatch_source_probe(dispatch_source_t ds); +static void _dispatch_source_merge_kevent(dispatch_source_t ds, + const struct kevent *ke); +static void _dispatch_kevent_register(dispatch_source_t ds); +static void _dispatch_kevent_unregister(dispatch_source_t ds); +static bool _dispatch_kevent_resume(dispatch_kevent_t dk, uint32_t new_flags, + uint32_t del_flags); +static inline void _dispatch_source_timer_init(void); static void _dispatch_timer_list_update(dispatch_source_t ds); +static inline unsigned long _dispatch_source_timer_data( + dispatch_source_refs_t dr, unsigned long prev); +#if HAVE_MACH +static kern_return_t _dispatch_kevent_machport_resume(dispatch_kevent_t dk, + uint32_t new_flags, uint32_t del_flags); +static void _dispatch_drain_mach_messages(struct kevent *ke); +#endif +static size_t _dispatch_source_kevent_debug(dispatch_source_t ds, + char* buf, size_t bufsiz); +#if DISPATCH_DEBUG +static void _dispatch_kevent_debugger(void *context); +#endif -static void -_dispatch_mach_notify_source_init(void *context __attribute__((unused))); +#pragma mark - +#pragma mark dispatch_source_t -static const char * -_evfiltstr(short filt) +const struct dispatch_source_vtable_s _dispatch_source_kevent_vtable = { + .do_type = DISPATCH_SOURCE_KEVENT_TYPE, + .do_kind = "kevent-source", + .do_invoke = _dispatch_source_invoke, + .do_dispose = _dispatch_source_dispose, + .do_probe = _dispatch_source_probe, + .do_debug = _dispatch_source_kevent_debug, +}; + +dispatch_source_t +dispatch_source_create(dispatch_source_type_t type, + uintptr_t handle, + unsigned long mask, + dispatch_queue_t q) { - switch (filt) { -#define _evfilt2(f) case (f): return #f - _evfilt2(EVFILT_READ); - _evfilt2(EVFILT_WRITE); - _evfilt2(EVFILT_AIO); - _evfilt2(EVFILT_VNODE); - _evfilt2(EVFILT_PROC); - _evfilt2(EVFILT_SIGNAL); - _evfilt2(EVFILT_TIMER); - _evfilt2(EVFILT_MACHPORT); - _evfilt2(EVFILT_FS); - _evfilt2(EVFILT_USER); - _evfilt2(EVFILT_SESSION); + const struct kevent *proto_kev = &type->ke; + dispatch_source_t ds = NULL; + dispatch_kevent_t dk = NULL; - _evfilt2(DISPATCH_EVFILT_TIMER); - _evfilt2(DISPATCH_EVFILT_CUSTOM_ADD); - _evfilt2(DISPATCH_EVFILT_CUSTOM_OR); + // input validation + if (type == NULL || (mask & ~type->mask)) { + goto out_bad; + } + + switch (type->ke.filter) { + case EVFILT_SIGNAL: + if (handle >= NSIG) { + goto out_bad; + } + break; + case EVFILT_FS: +#if DISPATCH_USE_VM_PRESSURE + case EVFILT_VM: +#endif + case DISPATCH_EVFILT_CUSTOM_ADD: + case DISPATCH_EVFILT_CUSTOM_OR: + case DISPATCH_EVFILT_TIMER: + if (handle) { + goto out_bad; + } + break; default: - return "EVFILT_missing"; + break; } -} -#define DSL_HASH_SIZE 256u // must be a power of two -#define DSL_HASH(x) ((x) & (DSL_HASH_SIZE - 1)) + ds = calloc(1ul, sizeof(struct dispatch_source_s)); + if (slowpath(!ds)) { + goto out_bad; + } + dk = calloc(1ul, sizeof(struct dispatch_kevent_s)); + if (slowpath(!dk)) { + goto out_bad; + } -static TAILQ_HEAD(, dispatch_kevent_s) _dispatch_sources[DSL_HASH_SIZE]; + dk->dk_kevent = *proto_kev; + dk->dk_kevent.ident = handle; + dk->dk_kevent.flags |= EV_ADD|EV_ENABLE; + dk->dk_kevent.fflags |= (uint32_t)mask; + dk->dk_kevent.udata = dk; + TAILQ_INIT(&dk->dk_sources); -static dispatch_kevent_t -_dispatch_kevent_find(uintptr_t ident, short filter) -{ - uintptr_t hash = DSL_HASH(filter == EVFILT_MACHPORT ? MACH_PORT_INDEX(ident) : ident); - dispatch_kevent_t dki; + // Initialize as a queue first, then override some settings below. + _dispatch_queue_init((dispatch_queue_t)ds); + strlcpy(ds->dq_label, "source", sizeof(ds->dq_label)); - TAILQ_FOREACH(dki, &_dispatch_sources[hash], dk_list) { - if (dki->dk_kevent.ident == ident && dki->dk_kevent.filter == filter) { - break; + // Dispatch Object + ds->do_vtable = &_dispatch_source_kevent_vtable; + ds->do_ref_cnt++; // the reference the manger queue holds + ds->do_ref_cnt++; // since source is created suspended + ds->do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_INTERVAL; + // The initial target queue is the manager queue, in order to get + // the source installed. + ds->do_targetq = &_dispatch_mgr_q; + + // Dispatch Source + ds->ds_ident_hack = dk->dk_kevent.ident; + ds->ds_dkev = dk; + ds->ds_pending_data_mask = dk->dk_kevent.fflags; + if ((EV_DISPATCH|EV_ONESHOT) & proto_kev->flags) { + ds->ds_is_level = true; + ds->ds_needs_rearm = true; + } else if (!(EV_CLEAR & proto_kev->flags)) { + // we cheat and use EV_CLEAR to mean a "flag thingy" + ds->ds_is_adder = true; + } + + // Some sources require special processing + if (type->init != NULL) { + type->init(ds, type, handle, mask, q); + } + if (fastpath(!ds->ds_refs)) { + ds->ds_refs = calloc(1ul, sizeof(struct dispatch_source_refs_s)); + if (slowpath(!ds->ds_refs)) { + goto out_bad; } } - return dki; + ds->ds_refs->dr_source_wref = _dispatch_ptr2wref(ds); + dispatch_assert(!(ds->ds_is_level && ds->ds_is_adder)); + + // First item on the queue sets the user-specified target queue + dispatch_set_target_queue(ds, q); +#if DISPATCH_DEBUG + dispatch_debug(ds, "%s", __FUNCTION__); +#endif + return ds; + +out_bad: + free(ds); + free(dk); + return NULL; } static void -_dispatch_kevent_insert(dispatch_kevent_t dk) +_dispatch_source_dispose(dispatch_source_t ds) { - uintptr_t ident = dk->dk_kevent.ident; - uintptr_t hash = DSL_HASH(dk->dk_kevent.filter == EVFILT_MACHPORT ? MACH_PORT_INDEX(ident) : ident); + free(ds->ds_refs); + _dispatch_queue_dispose((dispatch_queue_t)ds); +} - TAILQ_INSERT_TAIL(&_dispatch_sources[hash], dk, dk_list); +void +_dispatch_source_xref_release(dispatch_source_t ds) +{ + if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) { + // Arguments for and against this assert are within 6705399 + DISPATCH_CLIENT_CRASH("Release of a suspended object"); + } + _dispatch_wakeup(ds); + _dispatch_release(ds); } void dispatch_source_cancel(dispatch_source_t ds) { #if DISPATCH_DEBUG - dispatch_debug(ds, __FUNCTION__); + dispatch_debug(ds, "%s", __FUNCTION__); #endif // Right after we set the cancel flag, someone else - // could potentially invoke the source, do the cancelation, + // could potentially invoke the source, do the cancelation, // unregister the source, and deallocate it. We would // need to therefore retain/release before setting the bit _dispatch_retain(ds); - dispatch_atomic_or(&ds->ds_atomic_flags, DSF_CANCELED); - _dispatch_wakeup(ds); - _dispatch_release(ds); -} - -#ifndef DISPATCH_NO_LEGACY -void -_dispatch_source_legacy_xref_release(dispatch_source_t ds) -{ - if (ds->ds_is_legacy) { - if (!(ds->ds_timer.flags & DISPATCH_TIMER_ONESHOT)) { - dispatch_source_cancel(ds); - } - - // Clients often leave sources suspended at the last release - dispatch_atomic_and(&ds->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK); - } else if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) { - // Arguments for and against this assert are within 6705399 - DISPATCH_CLIENT_CRASH("Release of a suspended object"); - } + (void)dispatch_atomic_or2o(ds, ds_atomic_flags, DSF_CANCELED); _dispatch_wakeup(ds); _dispatch_release(ds); } -#endif /* DISPATCH_NO_LEGACY */ long dispatch_source_testcancel(dispatch_source_t ds) @@ -217,113 +224,291 @@ dispatch_source_get_data(dispatch_source_t ds) return ds->ds_data; } -#if DISPATCH_DEBUG void -dispatch_debug_kevents(struct kevent* kev, size_t count, const char* str) +dispatch_source_merge_data(dispatch_source_t ds, unsigned long val) { - size_t i; - for (i = 0; i < count; ++i) { - _dispatch_log("kevent[%lu] = { ident = %p, filter = %s, flags = 0x%x, fflags = 0x%x, data = %p, udata = %p }: %s", - i, (void*)kev[i].ident, _evfiltstr(kev[i].filter), kev[i].flags, kev[i].fflags, (void*)kev[i].data, (void*)kev[i].udata, str); + struct kevent kev = { + .fflags = (typeof(kev.fflags))val, + .data = val, + }; + + dispatch_assert( + ds->ds_dkev->dk_kevent.filter == DISPATCH_EVFILT_CUSTOM_ADD || + ds->ds_dkev->dk_kevent.filter == DISPATCH_EVFILT_CUSTOM_OR); + + _dispatch_source_merge_kevent(ds, &kev); +} + +#pragma mark - +#pragma mark dispatch_source_handler + +#ifdef __BLOCKS__ +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +static void +_dispatch_source_set_event_handler2(void *context) +{ + struct Block_layout *bl = context; + + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + dispatch_source_refs_t dr = ds->ds_refs; + + if (ds->ds_handler_is_block && dr->ds_handler_ctxt) { + Block_release(dr->ds_handler_ctxt); } + dr->ds_handler_func = bl ? (void *)bl->invoke : NULL; + dr->ds_handler_ctxt = bl; + ds->ds_handler_is_block = true; } -#endif -static size_t -_dispatch_source_kevent_debug(dispatch_source_t ds, char* buf, size_t bufsiz) +void +dispatch_source_set_event_handler(dispatch_source_t ds, + dispatch_block_t handler) { - size_t offset = _dispatch_source_debug(ds, buf, bufsiz); - offset += snprintf(&buf[offset], bufsiz - offset, "filter = %s }", - ds->ds_dkev ? _evfiltstr(ds->ds_dkev->dk_kevent.filter) : "????"); - return offset; + handler = _dispatch_Block_copy(handler); + dispatch_barrier_async_f((dispatch_queue_t)ds, handler, + _dispatch_source_set_event_handler2); } +#endif /* __BLOCKS__ */ static void -_dispatch_source_init_tail_queue_array(void *context __attribute__((unused))) +_dispatch_source_set_event_handler_f(void *context) { - unsigned int i; - for (i = 0; i < DSL_HASH_SIZE; i++) { - TAILQ_INIT(&_dispatch_sources[i]); - } + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + dispatch_source_refs_t dr = ds->ds_refs; - TAILQ_INSERT_TAIL(&_dispatch_sources[DSL_HASH(DISPATCH_TIMER_INDEX_WALL)], &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_WALL], dk_list); - TAILQ_INSERT_TAIL(&_dispatch_sources[DSL_HASH(DISPATCH_TIMER_INDEX_MACH)], &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_MACH], dk_list); - TAILQ_INSERT_TAIL(&_dispatch_sources[0], &_dispatch_kevent_data_or, dk_list); - TAILQ_INSERT_TAIL(&_dispatch_sources[0], &_dispatch_kevent_data_add, dk_list); +#ifdef __BLOCKS__ + if (ds->ds_handler_is_block && dr->ds_handler_ctxt) { + Block_release(dr->ds_handler_ctxt); + } +#endif + dr->ds_handler_func = context; + dr->ds_handler_ctxt = ds->do_ctxt; + ds->ds_handler_is_block = false; } -// Find existing kevents, and merge any new flags if necessary void -_dispatch_kevent_merge(dispatch_source_t ds) +dispatch_source_set_event_handler_f(dispatch_source_t ds, + dispatch_function_t handler) { - static dispatch_once_t pred; - dispatch_kevent_t dk; - typeof(dk->dk_kevent.fflags) new_flags; - bool do_resume = false; + dispatch_barrier_async_f((dispatch_queue_t)ds, handler, + _dispatch_source_set_event_handler_f); +} - if (ds->ds_is_installed) { - return; +#ifdef __BLOCKS__ +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +static void +_dispatch_source_set_cancel_handler2(void *context) +{ + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + dispatch_source_refs_t dr = ds->ds_refs; + + if (ds->ds_cancel_is_block && dr->ds_cancel_handler) { + Block_release(dr->ds_cancel_handler); } - ds->ds_is_installed = true; + dr->ds_cancel_handler = context; + ds->ds_cancel_is_block = true; +} - dispatch_once_f(&pred, NULL, _dispatch_source_init_tail_queue_array); +void +dispatch_source_set_cancel_handler(dispatch_source_t ds, + dispatch_block_t handler) +{ + handler = _dispatch_Block_copy(handler); + dispatch_barrier_async_f((dispatch_queue_t)ds, handler, + _dispatch_source_set_cancel_handler2); +} +#endif /* __BLOCKS__ */ - dk = _dispatch_kevent_find(ds->ds_dkev->dk_kevent.ident, ds->ds_dkev->dk_kevent.filter); - - if (dk) { - // If an existing dispatch kevent is found, check to see if new flags - // need to be added to the existing kevent - new_flags = ~dk->dk_kevent.fflags & ds->ds_dkev->dk_kevent.fflags; - dk->dk_kevent.fflags |= ds->ds_dkev->dk_kevent.fflags; - free(ds->ds_dkev); - ds->ds_dkev = dk; - do_resume = new_flags; - } else { - dk = ds->ds_dkev; - _dispatch_kevent_insert(dk); - new_flags = dk->dk_kevent.fflags; - do_resume = true; +static void +_dispatch_source_set_cancel_handler_f(void *context) +{ + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + dispatch_source_refs_t dr = ds->ds_refs; + +#ifdef __BLOCKS__ + if (ds->ds_cancel_is_block && dr->ds_cancel_handler) { + Block_release(dr->ds_cancel_handler); } +#endif + dr->ds_cancel_handler = context; + ds->ds_cancel_is_block = false; +} - TAILQ_INSERT_TAIL(&dk->dk_sources, ds, ds_list); +void +dispatch_source_set_cancel_handler_f(dispatch_source_t ds, + dispatch_function_t handler) +{ + dispatch_barrier_async_f((dispatch_queue_t)ds, handler, + _dispatch_source_set_cancel_handler_f); +} - // Re-register the kevent with the kernel if new flags were added - // by the dispatch kevent - if (do_resume) { - dk->dk_kevent.flags |= EV_ADD; - _dispatch_kevent_resume(ds->ds_dkev, new_flags, 0); - ds->ds_is_armed = true; +#ifdef __BLOCKS__ +static void +_dispatch_source_set_registration_handler2(void *context) +{ + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + dispatch_source_refs_t dr = ds->ds_refs; + + if (ds->ds_registration_is_block && dr->ds_registration_handler) { + Block_release(dr->ds_registration_handler); } + dr->ds_registration_handler = context; + ds->ds_registration_is_block = true; +} + +void +dispatch_source_set_registration_handler(dispatch_source_t ds, + dispatch_block_t handler) +{ + handler = _dispatch_Block_copy(handler); + dispatch_barrier_async_f((dispatch_queue_t)ds, handler, + _dispatch_source_set_registration_handler2); } +#endif /* __BLOCKS__ */ + +static void +_dispatch_source_set_registration_handler_f(void *context) +{ + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + dispatch_source_refs_t dr = ds->ds_refs; +#ifdef __BLOCKS__ + if (ds->ds_registration_is_block && dr->ds_registration_handler) { + Block_release(dr->ds_registration_handler); + } +#endif + dr->ds_registration_handler = context; + ds->ds_registration_is_block = false; +} void -_dispatch_kevent_resume(dispatch_kevent_t dk, uint32_t new_flags, uint32_t del_flags) +dispatch_source_set_registration_handler_f(dispatch_source_t ds, + dispatch_function_t handler) { - switch (dk->dk_kevent.filter) { - case DISPATCH_EVFILT_TIMER: - case DISPATCH_EVFILT_CUSTOM_ADD: - case DISPATCH_EVFILT_CUSTOM_OR: - // these types not registered with kevent + dispatch_barrier_async_f((dispatch_queue_t)ds, handler, + _dispatch_source_set_registration_handler_f); +} + +#pragma mark - +#pragma mark dispatch_source_invoke + +static void +_dispatch_source_registration_callout(dispatch_source_t ds) +{ + dispatch_source_refs_t dr = ds->ds_refs; + + if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { + // no registration callout if source is canceled rdar://problem/8955246 +#ifdef __BLOCKS__ + if (ds->ds_registration_is_block) { + Block_release(dr->ds_registration_handler); + } + } else if (ds->ds_registration_is_block) { + dispatch_block_t b = dr->ds_registration_handler; + _dispatch_client_callout_block(b); + Block_release(dr->ds_registration_handler); +#endif + } else { + dispatch_function_t f = dr->ds_registration_handler; + _dispatch_client_callout(ds->do_ctxt, f); + } + ds->ds_registration_is_block = false; + dr->ds_registration_handler = NULL; +} + +static void +_dispatch_source_cancel_callout(dispatch_source_t ds) +{ + dispatch_source_refs_t dr = ds->ds_refs; + + ds->ds_pending_data_mask = 0; + ds->ds_pending_data = 0; + ds->ds_data = 0; + +#ifdef __BLOCKS__ + if (ds->ds_handler_is_block) { + Block_release(dr->ds_handler_ctxt); + ds->ds_handler_is_block = false; + dr->ds_handler_func = NULL; + dr->ds_handler_ctxt = NULL; + } + if (ds->ds_registration_is_block) { + Block_release(dr->ds_registration_handler); + ds->ds_registration_is_block = false; + dr->ds_registration_handler = NULL; + } +#endif + + if (!dr->ds_cancel_handler) { return; - case EVFILT_MACHPORT: - _dispatch_kevent_machport_resume(dk, new_flags, del_flags); - break; - case EVFILT_PROC: - if (dk->dk_kevent.flags & EV_ONESHOT) { - return; + } + if (ds->ds_cancel_is_block) { +#ifdef __BLOCKS__ + dispatch_block_t b = dr->ds_cancel_handler; + if (ds->ds_atomic_flags & DSF_CANCELED) { + _dispatch_client_callout_block(b); } - // fall through - default: - _dispatch_update_kq(&dk->dk_kevent); - if (dk->dk_kevent.flags & EV_DISPATCH) { - dk->dk_kevent.flags &= ~EV_ADD; + Block_release(dr->ds_cancel_handler); + ds->ds_cancel_is_block = false; +#endif + } else { + dispatch_function_t f = dr->ds_cancel_handler; + if (ds->ds_atomic_flags & DSF_CANCELED) { + _dispatch_client_callout(ds->do_ctxt, f); + } + } + dr->ds_cancel_handler = NULL; +} + +static void +_dispatch_source_latch_and_call(dispatch_source_t ds) +{ + unsigned long prev; + + if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { + return; + } + dispatch_source_refs_t dr = ds->ds_refs; + prev = dispatch_atomic_xchg2o(ds, ds_pending_data, 0); + if (ds->ds_is_level) { + ds->ds_data = ~prev; + } else if (ds->ds_is_timer && ds_timer(dr).target && prev) { + ds->ds_data = _dispatch_source_timer_data(dr, prev); + } else { + ds->ds_data = prev; + } + if (dispatch_assume(prev) && dr->ds_handler_func) { + _dispatch_client_callout(dr->ds_handler_ctxt, dr->ds_handler_func); + } +} + +static void +_dispatch_source_kevent_resume(dispatch_source_t ds, uint32_t new_flags) +{ + switch (ds->ds_dkev->dk_kevent.filter) { + case DISPATCH_EVFILT_TIMER: + // called on manager queue only + return _dispatch_timer_list_update(ds); + case EVFILT_MACHPORT: + if (ds->ds_pending_data_mask & DISPATCH_MACH_RECV_MESSAGE) { + new_flags |= DISPATCH_MACH_RECV_MESSAGE; // emulate EV_DISPATCH } break; } + if (_dispatch_kevent_resume(ds->ds_dkev, new_flags, 0)) { + _dispatch_kevent_unregister(ds); + } } -dispatch_queue_t +static dispatch_queue_t _dispatch_source_invoke(dispatch_source_t ds) { // This function performs all source actions. Each action is responsible @@ -332,15 +517,36 @@ _dispatch_source_invoke(dispatch_source_t ds) // will be returned and the invoke will be re-driven on that queue. // The order of tests here in invoke and in probe should be consistent. - + dispatch_queue_t dq = _dispatch_queue_get_current(); + dispatch_source_refs_t dr = ds->ds_refs; if (!ds->ds_is_installed) { // The source needs to be installed on the manager queue. if (dq != &_dispatch_mgr_q) { return &_dispatch_mgr_q; } - _dispatch_kevent_merge(ds); + _dispatch_kevent_register(ds); + if (dr->ds_registration_handler) { + return ds->do_targetq; + } + if (slowpath(ds->do_xref_cnt == 0)) { + return &_dispatch_mgr_q; // rdar://problem/9558246 + } + } else if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) { + // Source suspended by an item drained from the source queue. + return NULL; + } else if (dr->ds_registration_handler) { + // The source has been registered and the registration handler needs + // to be delivered on the target queue. + if (dq != ds->do_targetq) { + return ds->do_targetq; + } + // clears ds_registration_handler + _dispatch_source_registration_callout(ds); + if (slowpath(ds->do_xref_cnt == 0)) { + return &_dispatch_mgr_q; // rdar://problem/9558246 + } } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { // The source has been cancelled and needs to be uninstalled from the // manager queue. After uninstallation, the cancellation handler needs @@ -349,13 +555,13 @@ _dispatch_source_invoke(dispatch_source_t ds) if (dq != &_dispatch_mgr_q) { return &_dispatch_mgr_q; } - _dispatch_kevent_release(ds); + _dispatch_kevent_unregister(ds); return ds->do_targetq; - } else if (ds->ds_cancel_handler) { + } else if (dr->ds_cancel_handler) { if (dq != ds->do_targetq) { return ds->do_targetq; } - } + } _dispatch_source_cancel_callout(ds); } else if (ds->ds_pending_data) { // The source has pending data to deliver via the event handler callback @@ -368,38 +574,42 @@ _dispatch_source_invoke(dispatch_source_t ds) if (ds->ds_needs_rearm) { return &_dispatch_mgr_q; } - } else if (ds->ds_needs_rearm && !ds->ds_is_armed) { + } else if (ds->ds_needs_rearm && !(ds->ds_atomic_flags & DSF_ARMED)) { // The source needs to be rearmed on the manager queue. if (dq != &_dispatch_mgr_q) { return &_dispatch_mgr_q; } - _dispatch_kevent_resume(ds->ds_dkev, 0, 0); - ds->ds_is_armed = true; + _dispatch_source_kevent_resume(ds, 0); + (void)dispatch_atomic_or2o(ds, ds_atomic_flags, DSF_ARMED); } return NULL; } -bool +static bool _dispatch_source_probe(dispatch_source_t ds) { // This function determines whether the source needs to be invoked. // The order of tests here in probe and in invoke should be consistent. + dispatch_source_refs_t dr = ds->ds_refs; if (!ds->ds_is_installed) { // The source needs to be installed on the manager queue. return true; + } else if (dr->ds_registration_handler) { + // The registration handler needs to be delivered to the target queue. + return true; } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { // The source needs to be uninstalled from the manager queue, or the // cancellation handler needs to be delivered to the target queue. // Note: cancellation assumes installation. - if (ds->ds_dkev || ds->ds_cancel_handler) { + if (ds->ds_dkev || dr->ds_cancel_handler) { return true; } } else if (ds->ds_pending_data) { // The source has pending data to deliver to the target queue. return true; - } else if (ds->ds_needs_rearm && !ds->ds_is_armed) { + } else if (ds->ds_needs_rearm && !(ds->ds_atomic_flags & DSF_ARMED)) { // The source needs to be rearmed on the manager queue. return true; } @@ -407,175 +617,253 @@ _dispatch_source_probe(dispatch_source_t ds) return false; } -void -_dispatch_source_dispose(dispatch_source_t ds) -{ - _dispatch_queue_dispose((dispatch_queue_t)ds); -} +#pragma mark - +#pragma mark dispatch_source_kevent static void -_dispatch_kevent_debugger2(void *context, dispatch_source_t unused __attribute__((unused))) +_dispatch_source_merge_kevent(dispatch_source_t ds, const struct kevent *ke) { - struct sockaddr sa; - socklen_t sa_len = sizeof(sa); - int c, fd = (int)(long)context; - unsigned int i; - dispatch_kevent_t dk; - dispatch_source_t ds; - FILE *debug_stream; + struct kevent fake; - c = accept(fd, &sa, &sa_len); - if (c == -1) { - if (errno != EAGAIN) { - dispatch_assume_zero(errno); - } + if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { return; } -#if 0 - int r = fcntl(c, F_SETFL, 0); // disable non-blocking IO - if (r == -1) { - dispatch_assume_zero(errno); - } + + // EVFILT_PROC may fail with ESRCH when the process exists but is a zombie + // . As a workaround, we simulate an exit event for + // any EVFILT_PROC with an invalid pid . + if (ke->flags & EV_ERROR) { + if (ke->filter == EVFILT_PROC && ke->data == ESRCH) { + fake = *ke; + fake.flags &= ~EV_ERROR; + fake.fflags = NOTE_EXIT; + fake.data = 0; + ke = &fake; +#if DISPATCH_USE_VM_PRESSURE + } else if (ke->filter == EVFILT_VM && ke->data == ENOTSUP) { + // Memory pressure kevent is not supported on all platforms + // + return; #endif - debug_stream = fdopen(c, "a"); - if (!dispatch_assume(debug_stream)) { - close(c); - return; + } else { + // log the unexpected error + (void)dispatch_assume_zero(ke->data); + return; + } } - fprintf(debug_stream, "HTTP/1.0 200 OK\r\n"); - fprintf(debug_stream, "Content-type: text/html\r\n"); - fprintf(debug_stream, "Pragma: nocache\r\n"); - fprintf(debug_stream, "\r\n"); - fprintf(debug_stream, "\nPID %u\n\n

    \n", getpid()); - - //fprintf(debug_stream, "DKDKDKDKDKDKDK\n"); + if (ds->ds_is_level) { + // ke->data is signed and "negative available data" makes no sense + // zero bytes happens when EV_EOF is set + // 10A268 does not fail this assert with EVFILT_READ and a 10 GB file + dispatch_assert(ke->data >= 0l); + ds->ds_pending_data = ~ke->data; + } else if (ds->ds_is_adder) { + (void)dispatch_atomic_add2o(ds, ds_pending_data, ke->data); + } else if (ke->fflags & ds->ds_pending_data_mask) { + (void)dispatch_atomic_or2o(ds, ds_pending_data, + ke->fflags & ds->ds_pending_data_mask); + } - for (i = 0; i < DSL_HASH_SIZE; i++) { - if (TAILQ_EMPTY(&_dispatch_sources[i])) { - continue; - } - TAILQ_FOREACH(dk, &_dispatch_sources[i], dk_list) { - fprintf(debug_stream, "\t
  • DK %p ident %lu filter %s flags 0x%hx fflags 0x%x data 0x%lx udata %p\n", - dk, dk->dk_kevent.ident, _evfiltstr(dk->dk_kevent.filter), dk->dk_kevent.flags, - dk->dk_kevent.fflags, dk->dk_kevent.data, dk->dk_kevent.udata); - fprintf(debug_stream, "\t\t
      \n"); - TAILQ_FOREACH(ds, &dk->dk_sources, ds_list) { - fprintf(debug_stream, "\t\t\t
    • DS %p refcnt 0x%x suspend 0x%x data 0x%lx mask 0x%lx flags 0x%x
    • \n", - ds, ds->do_ref_cnt, ds->do_suspend_cnt, ds->ds_pending_data, ds->ds_pending_data_mask, - ds->ds_atomic_flags); - if (ds->do_suspend_cnt == DISPATCH_OBJECT_SUSPEND_LOCK) { - dispatch_queue_t dq = ds->do_targetq; - fprintf(debug_stream, "\t\t
      DQ: %p refcnt 0x%x suspend 0x%x label: %s\n", dq, dq->do_ref_cnt, dq->do_suspend_cnt, dq->dq_label); - } - } - fprintf(debug_stream, "\t\t
    \n"); - fprintf(debug_stream, "\t
  • \n"); - } + // EV_DISPATCH and EV_ONESHOT sources are no longer armed after delivery + if (ds->ds_needs_rearm) { + (void)dispatch_atomic_and2o(ds, ds_atomic_flags, ~DSF_ARMED); } - fprintf(debug_stream, "
\n\n\n"); - fflush(debug_stream); - fclose(debug_stream); + + _dispatch_wakeup(ds); } -static void -_dispatch_kevent_debugger(void *context __attribute__((unused))) +void +_dispatch_source_drain_kevent(struct kevent *ke) { - union { - struct sockaddr_in sa_in; - struct sockaddr sa; - } sa_u = { - .sa_in = { - .sin_family = AF_INET, - .sin_addr = { htonl(INADDR_LOOPBACK), }, - }, - }; - dispatch_source_t ds; - const char *valstr; - int val, r, fd, sock_opt = 1; - socklen_t slen = sizeof(sa_u); + dispatch_kevent_t dk = ke->udata; + dispatch_source_refs_t dri; - if (issetugid()) { - return; +#if DISPATCH_DEBUG + static dispatch_once_t pred; + dispatch_once_f(&pred, NULL, _dispatch_kevent_debugger); +#endif + + dispatch_debug_kevents(ke, 1, __func__); + +#if HAVE_MACH + if (ke->filter == EVFILT_MACHPORT) { + return _dispatch_drain_mach_messages(ke); } - valstr = getenv("LIBDISPATCH_DEBUGGER"); - if (!valstr) { - return; +#endif + dispatch_assert(dk); + + if (ke->flags & EV_ONESHOT) { + dk->dk_kevent.flags |= EV_ONESHOT; } - val = atoi(valstr); - if (val == 2) { - sa_u.sa_in.sin_addr.s_addr = 0; - } - fd = socket(PF_INET, SOCK_STREAM, 0); - if (fd == -1) { - dispatch_assume_zero(errno); - return; - } - r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, (socklen_t) sizeof sock_opt); - if (r == -1) { - dispatch_assume_zero(errno); - goto out_bad; - } -#if 0 - r = fcntl(fd, F_SETFL, O_NONBLOCK); - if (r == -1) { - dispatch_assume_zero(errno); - goto out_bad; + + TAILQ_FOREACH(dri, &dk->dk_sources, dr_list) { + _dispatch_source_merge_kevent(_dispatch_source_from_refs(dri), ke); } +} + +#pragma mark - +#pragma mark dispatch_kevent_t + +static struct dispatch_kevent_s _dispatch_kevent_data_or = { + .dk_kevent = { + .filter = DISPATCH_EVFILT_CUSTOM_OR, + .flags = EV_CLEAR, + .udata = &_dispatch_kevent_data_or, + }, + .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_data_or.dk_sources), +}; +static struct dispatch_kevent_s _dispatch_kevent_data_add = { + .dk_kevent = { + .filter = DISPATCH_EVFILT_CUSTOM_ADD, + .udata = &_dispatch_kevent_data_add, + }, + .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_data_add.dk_sources), +}; + +#if TARGET_OS_EMBEDDED +#define DSL_HASH_SIZE 64u // must be a power of two +#else +#define DSL_HASH_SIZE 256u // must be a power of two #endif - r = bind(fd, &sa_u.sa, sizeof(sa_u)); - if (r == -1) { - dispatch_assume_zero(errno); - goto out_bad; - } - r = listen(fd, SOMAXCONN); - if (r == -1) { - dispatch_assume_zero(errno); - goto out_bad; - } - r = getsockname(fd, &sa_u.sa, &slen); - if (r == -1) { - dispatch_assume_zero(errno); - goto out_bad; +#define DSL_HASH(x) ((x) & (DSL_HASH_SIZE - 1)) + +DISPATCH_CACHELINE_ALIGN +static TAILQ_HEAD(, dispatch_kevent_s) _dispatch_sources[DSL_HASH_SIZE]; + +static dispatch_once_t __dispatch_kevent_init_pred; + +static void +_dispatch_kevent_init(void *context DISPATCH_UNUSED) +{ + unsigned int i; + for (i = 0; i < DSL_HASH_SIZE; i++) { + TAILQ_INIT(&_dispatch_sources[i]); } - ds = dispatch_source_read_create_f(fd, NULL, &_dispatch_mgr_q, (void *)(long)fd, _dispatch_kevent_debugger2); - if (dispatch_assume(ds)) { - _dispatch_log("LIBDISPATCH: debug port: %hu", ntohs(sa_u.sa_in.sin_port)); - return; + + TAILQ_INSERT_TAIL(&_dispatch_sources[0], + &_dispatch_kevent_data_or, dk_list); + TAILQ_INSERT_TAIL(&_dispatch_sources[0], + &_dispatch_kevent_data_add, dk_list); + + _dispatch_source_timer_init(); +} + +static inline uintptr_t +_dispatch_kevent_hash(uintptr_t ident, short filter) +{ + uintptr_t value; +#if HAVE_MACH + value = (filter == EVFILT_MACHPORT ? MACH_PORT_INDEX(ident) : ident); +#else + value = ident; +#endif + return DSL_HASH(value); +} + +static dispatch_kevent_t +_dispatch_kevent_find(uintptr_t ident, short filter) +{ + uintptr_t hash = _dispatch_kevent_hash(ident, filter); + dispatch_kevent_t dki; + + TAILQ_FOREACH(dki, &_dispatch_sources[hash], dk_list) { + if (dki->dk_kevent.ident == ident && dki->dk_kevent.filter == filter) { + break; + } } -out_bad: - close(fd); + return dki; } -void -_dispatch_source_drain_kevent(struct kevent *ke) +static void +_dispatch_kevent_insert(dispatch_kevent_t dk) { - static dispatch_once_t pred; - dispatch_kevent_t dk = ke->udata; - dispatch_source_t dsi; + uintptr_t hash = _dispatch_kevent_hash(dk->dk_kevent.ident, + dk->dk_kevent.filter); - dispatch_once_f(&pred, NULL, _dispatch_kevent_debugger); + TAILQ_INSERT_TAIL(&_dispatch_sources[hash], dk, dk_list); +} - dispatch_debug_kevents(ke, 1, __func__); +// Find existing kevents, and merge any new flags if necessary +static void +_dispatch_kevent_register(dispatch_source_t ds) +{ + dispatch_kevent_t dk; + typeof(dk->dk_kevent.fflags) new_flags; + bool do_resume = false; - if (ke->filter == EVFILT_MACHPORT) { - return _dispatch_drain_mach_messages(ke); + if (ds->ds_is_installed) { + return; } - dispatch_assert(dk); + ds->ds_is_installed = true; - if (ke->flags & EV_ONESHOT) { - dk->dk_kevent.flags |= EV_ONESHOT; + dispatch_once_f(&__dispatch_kevent_init_pred, + NULL, _dispatch_kevent_init); + + dk = _dispatch_kevent_find(ds->ds_dkev->dk_kevent.ident, + ds->ds_dkev->dk_kevent.filter); + + if (dk) { + // If an existing dispatch kevent is found, check to see if new flags + // need to be added to the existing kevent + new_flags = ~dk->dk_kevent.fflags & ds->ds_dkev->dk_kevent.fflags; + dk->dk_kevent.fflags |= ds->ds_dkev->dk_kevent.fflags; + free(ds->ds_dkev); + ds->ds_dkev = dk; + do_resume = new_flags; + } else { + dk = ds->ds_dkev; + _dispatch_kevent_insert(dk); + new_flags = dk->dk_kevent.fflags; + do_resume = true; } - TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { - _dispatch_source_merge_kevent(dsi, ke); + TAILQ_INSERT_TAIL(&dk->dk_sources, ds->ds_refs, dr_list); + + // Re-register the kevent with the kernel if new flags were added + // by the dispatch kevent + if (do_resume) { + dk->dk_kevent.flags |= EV_ADD; + } + if (do_resume || ds->ds_needs_rearm) { + _dispatch_source_kevent_resume(ds, new_flags); + } + (void)dispatch_atomic_or2o(ds, ds_atomic_flags, DSF_ARMED); +} + +static bool +_dispatch_kevent_resume(dispatch_kevent_t dk, uint32_t new_flags, + uint32_t del_flags) +{ + long r; + switch (dk->dk_kevent.filter) { + case DISPATCH_EVFILT_TIMER: + case DISPATCH_EVFILT_CUSTOM_ADD: + case DISPATCH_EVFILT_CUSTOM_OR: + // these types not registered with kevent + return 0; +#if HAVE_MACH + case EVFILT_MACHPORT: + return _dispatch_kevent_machport_resume(dk, new_flags, del_flags); +#endif + case EVFILT_PROC: + if (dk->dk_kevent.flags & EV_ONESHOT) { + return 0; + } + // fall through + default: + r = _dispatch_update_kq(&dk->dk_kevent); + if (dk->dk_kevent.flags & EV_DISPATCH) { + dk->dk_kevent.flags &= ~EV_ADD; + } + return r; } } static void _dispatch_kevent_dispose(dispatch_kevent_t dk) { - uintptr_t key; + uintptr_t hash; switch (dk->dk_kevent.filter) { case DISPATCH_EVFILT_TIMER: @@ -583,12 +871,14 @@ _dispatch_kevent_dispose(dispatch_kevent_t dk) case DISPATCH_EVFILT_CUSTOM_OR: // these sources live on statically allocated lists return; +#if HAVE_MACH case EVFILT_MACHPORT: _dispatch_kevent_machport_resume(dk, 0, dk->dk_kevent.fflags); break; +#endif case EVFILT_PROC: if (dk->dk_kevent.flags & EV_ONESHOT) { - break; // implicitly deleted + break; // implicitly deleted } // fall through default: @@ -599,31 +889,28 @@ _dispatch_kevent_dispose(dispatch_kevent_t dk) break; } - if (dk->dk_kevent.filter == EVFILT_MACHPORT) { - key = MACH_PORT_INDEX(dk->dk_kevent.ident); - } else { - key = dk->dk_kevent.ident; - } - - TAILQ_REMOVE(&_dispatch_sources[DSL_HASH(key)], dk, dk_list); + hash = _dispatch_kevent_hash(dk->dk_kevent.ident, + dk->dk_kevent.filter); + TAILQ_REMOVE(&_dispatch_sources[hash], dk, dk_list); free(dk); } -void -_dispatch_kevent_release(dispatch_source_t ds) +static void +_dispatch_kevent_unregister(dispatch_source_t ds) { dispatch_kevent_t dk = ds->ds_dkev; - dispatch_source_t dsi; + dispatch_source_refs_t dri; uint32_t del_flags, fflags = 0; ds->ds_dkev = NULL; - TAILQ_REMOVE(&dk->dk_sources, ds, ds_list); + TAILQ_REMOVE(&dk->dk_sources, ds->ds_refs, dr_list); if (TAILQ_EMPTY(&dk->dk_sources)) { _dispatch_kevent_dispose(dk); } else { - TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { + TAILQ_FOREACH(dri, &dk->dk_sources, dr_list) { + dispatch_source_t dsi = _dispatch_source_from_refs(dri); fflags |= (uint32_t)dsi->ds_pending_data_mask; } del_flags = (uint32_t)ds->ds_pending_data_mask & ~fflags; @@ -634,714 +921,175 @@ _dispatch_kevent_release(dispatch_source_t ds) } } - ds->ds_is_armed = false; - ds->ds_needs_rearm = false; // re-arm is pointless and bad now - _dispatch_release(ds); // the retain is done at creation time -} - -void -_dispatch_source_merge_kevent(dispatch_source_t ds, const struct kevent *ke) -{ - struct kevent fake; - - if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { - return; - } - - // EVFILT_PROC may fail with ESRCH when the process exists but is a zombie. - // We simulate an exit event in this case. - if (ke->flags & EV_ERROR) { - if (ke->filter == EVFILT_PROC && ke->data == ESRCH) { - fake = *ke; - fake.flags &= ~EV_ERROR; - fake.fflags = NOTE_EXIT; - fake.data = 0; - ke = &fake; - } else { - // log the unexpected error - dispatch_assume_zero(ke->data); - return; - } - } - - if (ds->ds_is_level) { - // ke->data is signed and "negative available data" makes no sense - // zero bytes happens when EV_EOF is set - // 10A268 does not fail this assert with EVFILT_READ and a 10 GB file - dispatch_assert(ke->data >= 0l); - ds->ds_pending_data = ~ke->data; - } else if (ds->ds_is_adder) { - dispatch_atomic_add(&ds->ds_pending_data, ke->data); - } else { - dispatch_atomic_or(&ds->ds_pending_data, ke->fflags & ds->ds_pending_data_mask); - } - - // EV_DISPATCH and EV_ONESHOT sources are no longer armed after delivery - if (ds->ds_needs_rearm) { - ds->ds_is_armed = false; - } - - _dispatch_wakeup(ds); -} - -void -_dispatch_source_latch_and_call(dispatch_source_t ds) -{ - unsigned long prev; - - if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { - return; - } - prev = dispatch_atomic_xchg(&ds->ds_pending_data, 0); - if (ds->ds_is_level) { - ds->ds_data = ~prev; - } else { - ds->ds_data = prev; - } - if (dispatch_assume(prev)) { - if (ds->ds_handler_func) { - ds->ds_handler_func(ds->ds_handler_ctxt, ds); - } - } -} - -void -_dispatch_source_cancel_callout(dispatch_source_t ds) -{ - ds->ds_pending_data_mask = 0; - ds->ds_pending_data = 0; - ds->ds_data = 0; - -#ifdef __BLOCKS__ - if (ds->ds_handler_is_block) { - Block_release(ds->ds_handler_ctxt); - ds->ds_handler_is_block = false; - ds->ds_handler_func = NULL; - ds->ds_handler_ctxt = NULL; - } -#endif - - if (!ds->ds_cancel_handler) { - return; - } - if (ds->ds_cancel_is_block) { -#ifdef __BLOCKS__ - dispatch_block_t b = ds->ds_cancel_handler; - if (ds->ds_atomic_flags & DSF_CANCELED) { - b(); - } - Block_release(ds->ds_cancel_handler); - ds->ds_cancel_is_block = false; -#endif - } else { - dispatch_function_t f = ds->ds_cancel_handler; - if (ds->ds_atomic_flags & DSF_CANCELED) { - f(ds->do_ctxt); - } - } - ds->ds_cancel_handler = NULL; -} - -const struct dispatch_source_vtable_s _dispatch_source_kevent_vtable = { - .do_type = DISPATCH_SOURCE_KEVENT_TYPE, - .do_kind = "kevent-source", - .do_invoke = _dispatch_source_invoke, - .do_dispose = _dispatch_source_dispose, - .do_probe = _dispatch_source_probe, - .do_debug = _dispatch_source_kevent_debug, -}; - -void -dispatch_source_merge_data(dispatch_source_t ds, unsigned long val) -{ - struct kevent kev = { - .fflags = (typeof(kev.fflags))val, - .data = val, - }; - - dispatch_assert(ds->ds_dkev->dk_kevent.filter == DISPATCH_EVFILT_CUSTOM_ADD || - ds->ds_dkev->dk_kevent.filter == DISPATCH_EVFILT_CUSTOM_OR); - - _dispatch_source_merge_kevent(ds, &kev); -} - -size_t -dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz) -{ - dispatch_queue_t target = ds->do_targetq; - return snprintf(buf, bufsiz, - "target = %s[%p], pending_data = 0x%lx, pending_data_mask = 0x%lx, ", - target ? target->dq_label : "", target, - ds->ds_pending_data, ds->ds_pending_data_mask); -} - -size_t -_dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz) -{ - size_t offset = 0; - offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dx_kind(ds), ds); - offset += dispatch_object_debug_attr(ds, &buf[offset], bufsiz - offset); - offset += dispatch_source_debug_attr(ds, &buf[offset], bufsiz - offset); - return offset; -} - -#ifndef DISPATCH_NO_LEGACY -static void -dispatch_source_attr_dispose(dispatch_source_attr_t attr) -{ - // release the finalizer block if necessary - dispatch_source_attr_set_finalizer(attr, NULL); - _dispatch_dispose(attr); -} - -static const struct dispatch_source_attr_vtable_s dispatch_source_attr_vtable = { - .do_type = DISPATCH_SOURCE_ATTR_TYPE, - .do_kind = "source-attr", - .do_dispose = dispatch_source_attr_dispose, -}; - -dispatch_source_attr_t -dispatch_source_attr_create(void) -{ - dispatch_source_attr_t rval = calloc(1, sizeof(struct dispatch_source_attr_s)); - - if (rval) { - rval->do_vtable = &dispatch_source_attr_vtable; - rval->do_next = DISPATCH_OBJECT_LISTLESS; - rval->do_targetq = dispatch_get_global_queue(0, 0); - rval->do_ref_cnt = 1; - rval->do_xref_cnt = 1; - } - - return rval; -} - -void -dispatch_source_attr_set_finalizer_f(dispatch_source_attr_t attr, - void *context, dispatch_source_finalizer_function_t finalizer) -{ -#ifdef __BLOCKS__ - if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) { - Block_release(attr->finalizer_ctxt); - } -#endif - - attr->finalizer_ctxt = context; - attr->finalizer_func = finalizer; -} - -#ifdef __BLOCKS__ -long -dispatch_source_attr_set_finalizer(dispatch_source_attr_t attr, - dispatch_source_finalizer_t finalizer) -{ - void *ctxt; - dispatch_source_finalizer_function_t func; - - if (finalizer) { - if (!(ctxt = Block_copy(finalizer))) { - return 1; - } - func = (void *)_dispatch_call_block_and_release2; - } else { - ctxt = NULL; - func = NULL; - } - - dispatch_source_attr_set_finalizer_f(attr, ctxt, func); - - return 0; -} - -dispatch_source_finalizer_t -dispatch_source_attr_get_finalizer(dispatch_source_attr_t attr) -{ - if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) { - return (dispatch_source_finalizer_t)attr->finalizer_ctxt; - } else if (attr->finalizer_func == NULL) { - return NULL; - } else { - abort(); // finalizer is not a block... - } + (void)dispatch_atomic_and2o(ds, ds_atomic_flags, ~DSF_ARMED); + ds->ds_needs_rearm = false; // re-arm is pointless and bad now + _dispatch_release(ds); // the retain is done at creation time } -#endif -void -dispatch_source_attr_set_context(dispatch_source_attr_t attr, void *context) -{ - attr->context = context; -} - -dispatch_source_attr_t -dispatch_source_attr_copy(dispatch_source_attr_t proto) -{ - dispatch_source_attr_t rval = NULL; - - if (proto && (rval = malloc(sizeof(struct dispatch_source_attr_s)))) { - memcpy(rval, proto, sizeof(struct dispatch_source_attr_s)); -#ifdef __BLOCKS__ - if (rval->finalizer_func == (void*)_dispatch_call_block_and_release2) { - rval->finalizer_ctxt = Block_copy(rval->finalizer_ctxt); - } -#endif - } else if (!proto) { - rval = dispatch_source_attr_create(); - } - return rval; -} -#endif /* DISPATCH_NO_LEGACY */ - - -struct dispatch_source_type_s { - struct kevent ke; - uint64_t mask; -}; - -const struct dispatch_source_type_s _dispatch_source_type_timer = { - .ke = { - .filter = DISPATCH_EVFILT_TIMER, - }, - .mask = DISPATCH_TIMER_INTERVAL|DISPATCH_TIMER_ONESHOT|DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_WALL_CLOCK, -}; +#pragma mark - +#pragma mark dispatch_timer -const struct dispatch_source_type_s _dispatch_source_type_read = { - .ke = { - .filter = EVFILT_READ, - .flags = EV_DISPATCH, - }, -}; - -const struct dispatch_source_type_s _dispatch_source_type_write = { - .ke = { - .filter = EVFILT_WRITE, - .flags = EV_DISPATCH, - }, -}; - -const struct dispatch_source_type_s _dispatch_source_type_proc = { - .ke = { - .filter = EVFILT_PROC, - .flags = EV_CLEAR, - }, - .mask = NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_SIGNAL|NOTE_REAP, -}; - -const struct dispatch_source_type_s _dispatch_source_type_signal = { - .ke = { - .filter = EVFILT_SIGNAL, - }, -}; - -const struct dispatch_source_type_s _dispatch_source_type_vnode = { - .ke = { - .filter = EVFILT_VNODE, - .flags = EV_CLEAR, - }, - .mask = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK|NOTE_RENAME|NOTE_REVOKE|NOTE_NONE, -}; - -const struct dispatch_source_type_s _dispatch_source_type_vfs = { - .ke = { - .filter = EVFILT_FS, - .flags = EV_CLEAR, - }, - .mask = VQ_NOTRESP|VQ_NEEDAUTH|VQ_LOWDISK|VQ_MOUNT|VQ_UNMOUNT|VQ_DEAD|VQ_ASSIST|VQ_NOTRESPLOCK|VQ_UPDATE|VQ_VERYLOWDISK, -}; - -const struct dispatch_source_type_s _dispatch_source_type_mach_send = { - .ke = { - .filter = EVFILT_MACHPORT, - .flags = EV_DISPATCH, - .fflags = DISPATCH_MACHPORT_DEAD, - }, - .mask = DISPATCH_MACH_SEND_DEAD, -}; - -const struct dispatch_source_type_s _dispatch_source_type_mach_recv = { - .ke = { - .filter = EVFILT_MACHPORT, - .flags = EV_DISPATCH, - .fflags = DISPATCH_MACHPORT_RECV, +DISPATCH_CACHELINE_ALIGN +static struct dispatch_kevent_s _dispatch_kevent_timer[] = { + [DISPATCH_TIMER_INDEX_WALL] = { + .dk_kevent = { + .ident = DISPATCH_TIMER_INDEX_WALL, + .filter = DISPATCH_EVFILT_TIMER, + .udata = &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_WALL], + }, + .dk_sources = TAILQ_HEAD_INITIALIZER( + _dispatch_kevent_timer[DISPATCH_TIMER_INDEX_WALL].dk_sources), }, -}; - -const struct dispatch_source_type_s _dispatch_source_type_data_add = { - .ke = { - .filter = DISPATCH_EVFILT_CUSTOM_ADD, + [DISPATCH_TIMER_INDEX_MACH] = { + .dk_kevent = { + .ident = DISPATCH_TIMER_INDEX_MACH, + .filter = DISPATCH_EVFILT_TIMER, + .udata = &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_MACH], + }, + .dk_sources = TAILQ_HEAD_INITIALIZER( + _dispatch_kevent_timer[DISPATCH_TIMER_INDEX_MACH].dk_sources), }, -}; - -const struct dispatch_source_type_s _dispatch_source_type_data_or = { - .ke = { - .filter = DISPATCH_EVFILT_CUSTOM_OR, - .flags = EV_CLEAR, - .fflags = ~0, + [DISPATCH_TIMER_INDEX_DISARM] = { + .dk_kevent = { + .ident = DISPATCH_TIMER_INDEX_DISARM, + .filter = DISPATCH_EVFILT_TIMER, + .udata = &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_DISARM], + }, + .dk_sources = TAILQ_HEAD_INITIALIZER( + _dispatch_kevent_timer[DISPATCH_TIMER_INDEX_DISARM].dk_sources), }, -}; - -dispatch_source_t -dispatch_source_create(dispatch_source_type_t type, - uintptr_t handle, - unsigned long mask, - dispatch_queue_t q) -{ - const struct kevent *proto_kev = &type->ke; - dispatch_source_t ds = NULL; - dispatch_kevent_t dk = NULL; - - // input validation - if (type == NULL || (mask & ~type->mask)) { - goto out_bad; - } - - switch (type->ke.filter) { - case EVFILT_SIGNAL: - if (handle >= NSIG) { - goto out_bad; - } - break; - case EVFILT_FS: - case DISPATCH_EVFILT_CUSTOM_ADD: - case DISPATCH_EVFILT_CUSTOM_OR: - case DISPATCH_EVFILT_TIMER: - if (handle) { - goto out_bad; - } - break; - default: - break; - } - - ds = calloc(1ul, sizeof(struct dispatch_source_s)); - if (slowpath(!ds)) { - goto out_bad; - } - dk = calloc(1ul, sizeof(struct dispatch_kevent_s)); - if (slowpath(!dk)) { - goto out_bad; - } - - dk->dk_kevent = *proto_kev; - dk->dk_kevent.ident = handle; - dk->dk_kevent.flags |= EV_ADD|EV_ENABLE; - dk->dk_kevent.fflags |= (uint32_t)mask; - dk->dk_kevent.udata = dk; - TAILQ_INIT(&dk->dk_sources); - - // Initialize as a queue first, then override some settings below. - _dispatch_queue_init((dispatch_queue_t)ds); - strlcpy(ds->dq_label, "source", sizeof(ds->dq_label)); - - // Dispatch Object - ds->do_vtable = &_dispatch_source_kevent_vtable; - ds->do_ref_cnt++; // the reference the manger queue holds - ds->do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_INTERVAL; - // do_targetq will be retained below, past point of no-return - ds->do_targetq = q; - - // Dispatch Source - ds->ds_ident_hack = dk->dk_kevent.ident; - ds->ds_dkev = dk; - ds->ds_pending_data_mask = dk->dk_kevent.fflags; - if ((EV_DISPATCH|EV_ONESHOT) & proto_kev->flags) { - if (proto_kev->filter != EVFILT_MACHPORT) { - ds->ds_is_level = true; - } - ds->ds_needs_rearm = true; - } else if (!(EV_CLEAR & proto_kev->flags)) { - // we cheat and use EV_CLEAR to mean a "flag thingy" - ds->ds_is_adder = true; - } - - // If its a timer source, it needs to be re-armed - if (type->ke.filter == DISPATCH_EVFILT_TIMER) { - ds->ds_needs_rearm = true; - } - - dispatch_assert(!(ds->ds_is_level && ds->ds_is_adder)); -#if DISPATCH_DEBUG - dispatch_debug(ds, __FUNCTION__); -#endif - - // Some sources require special processing - if (type == DISPATCH_SOURCE_TYPE_MACH_SEND) { - static dispatch_once_t pred; - dispatch_once_f(&pred, NULL, _dispatch_mach_notify_source_init); - } else if (type == DISPATCH_SOURCE_TYPE_TIMER) { - ds->ds_timer.flags = mask; - } - - _dispatch_retain(ds->do_targetq); - return ds; - -out_bad: - free(ds); - free(dk); - return NULL; -} - -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol -static void -_dispatch_source_set_event_handler2(void *context) -{ - struct Block_layout *bl = context; - - dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); - dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); - - if (ds->ds_handler_is_block && ds->ds_handler_ctxt) { - Block_release(ds->ds_handler_ctxt); - } - ds->ds_handler_func = bl ? (void *)bl->invoke : NULL; - ds->ds_handler_ctxt = bl; - ds->ds_handler_is_block = true; -} - -void -dispatch_source_set_event_handler(dispatch_source_t ds, dispatch_block_t handler) -{ - dispatch_assert(!ds->ds_is_legacy); - handler = _dispatch_Block_copy(handler); - dispatch_barrier_async_f((dispatch_queue_t)ds, - handler, _dispatch_source_set_event_handler2); -} - -static void -_dispatch_source_set_event_handler_f(void *context) -{ - dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); - dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); - - if (ds->ds_handler_is_block && ds->ds_handler_ctxt) { - Block_release(ds->ds_handler_ctxt); - } - ds->ds_handler_func = context; - ds->ds_handler_ctxt = ds->do_ctxt; - ds->ds_handler_is_block = false; -} - -void -dispatch_source_set_event_handler_f(dispatch_source_t ds, - dispatch_function_t handler) -{ - dispatch_assert(!ds->ds_is_legacy); - dispatch_barrier_async_f((dispatch_queue_t)ds, - handler, _dispatch_source_set_event_handler_f); -} - -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol -static void -_dispatch_source_set_cancel_handler2(void *context) -{ - dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); - dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); - - if (ds->ds_cancel_is_block && ds->ds_cancel_handler) { - Block_release(ds->ds_cancel_handler); - } - ds->ds_cancel_handler = context; - ds->ds_cancel_is_block = true; -} - -void -dispatch_source_set_cancel_handler(dispatch_source_t ds, - dispatch_block_t handler) -{ - dispatch_assert(!ds->ds_is_legacy); - handler = _dispatch_Block_copy(handler); - dispatch_barrier_async_f((dispatch_queue_t)ds, - handler, _dispatch_source_set_cancel_handler2); -} - -static void -_dispatch_source_set_cancel_handler_f(void *context) -{ - dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); - dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); - - if (ds->ds_cancel_is_block && ds->ds_cancel_handler) { - Block_release(ds->ds_cancel_handler); - } - ds->ds_cancel_handler = context; - ds->ds_cancel_is_block = false; -} +}; +// Don't count disarmed timer list +#define DISPATCH_TIMER_COUNT ((sizeof(_dispatch_kevent_timer) \ + / sizeof(_dispatch_kevent_timer[0])) - 1) -void -dispatch_source_set_cancel_handler_f(dispatch_source_t ds, - dispatch_function_t handler) +static inline void +_dispatch_source_timer_init(void) { - dispatch_assert(!ds->ds_is_legacy); - dispatch_barrier_async_f((dispatch_queue_t)ds, - handler, _dispatch_source_set_cancel_handler_f); + TAILQ_INSERT_TAIL(&_dispatch_sources[DSL_HASH(DISPATCH_TIMER_INDEX_WALL)], + &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_WALL], dk_list); + TAILQ_INSERT_TAIL(&_dispatch_sources[DSL_HASH(DISPATCH_TIMER_INDEX_MACH)], + &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_MACH], dk_list); + TAILQ_INSERT_TAIL(&_dispatch_sources[DSL_HASH(DISPATCH_TIMER_INDEX_DISARM)], + &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_DISARM], dk_list); } -#ifndef DISPATCH_NO_LEGACY -// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol -dispatch_source_t -_dispatch_source_create2(dispatch_source_t ds, - dispatch_source_attr_t attr, - void *context, - dispatch_source_handler_function_t handler) +DISPATCH_ALWAYS_INLINE +static inline unsigned int +_dispatch_source_timer_idx(dispatch_source_refs_t dr) { - if (ds == NULL || handler == NULL) { - return NULL; - } - - ds->ds_is_legacy = true; - - ds->ds_handler_func = handler; - ds->ds_handler_ctxt = context; - - if (attr && attr != DISPATCH_SOURCE_CREATE_SUSPENDED) { - ds->dq_finalizer_ctxt = attr->finalizer_ctxt; - ds->dq_finalizer_func = (typeof(ds->dq_finalizer_func))attr->finalizer_func; - ds->do_ctxt = attr->context; - } -#ifdef __BLOCKS__ - if (ds->dq_finalizer_func == (void*)_dispatch_call_block_and_release2) { - ds->dq_finalizer_ctxt = Block_copy(ds->dq_finalizer_ctxt); - if (!ds->dq_finalizer_ctxt) { - goto out_bad; - } - } - if (handler == _dispatch_source_call_block) { - struct Block_layout *bl = ds->ds_handler_ctxt = Block_copy(context); - if (!ds->ds_handler_ctxt) { - if (ds->dq_finalizer_func == (void*)_dispatch_call_block_and_release2) { - Block_release(ds->dq_finalizer_ctxt); - } - goto out_bad; - } - ds->ds_handler_func = (void *)bl->invoke; - ds->ds_handler_is_block = true; - } + return ds_timer(dr).flags & DISPATCH_TIMER_WALL_CLOCK ? + DISPATCH_TIMER_INDEX_WALL : DISPATCH_TIMER_INDEX_MACH; +} - // all legacy sources get a cancellation event on the normal event handler. - dispatch_source_handler_function_t func = ds->ds_handler_func; - dispatch_source_handler_t block = ds->ds_handler_ctxt; - void *ctxt = ds->ds_handler_ctxt; - bool handler_is_block = ds->ds_handler_is_block; - - ds->ds_cancel_is_block = true; - if (handler_is_block) { - ds->ds_cancel_handler = _dispatch_Block_copy(^{ - block(ds); - }); - } else { - ds->ds_cancel_handler = _dispatch_Block_copy(^{ - func(ctxt, ds); - }); - } -#endif - if (attr != DISPATCH_SOURCE_CREATE_SUSPENDED) { - dispatch_resume(ds); +DISPATCH_ALWAYS_INLINE +static inline uint64_t +_dispatch_source_timer_now2(unsigned int timer) +{ + switch (timer) { + case DISPATCH_TIMER_INDEX_MACH: + return _dispatch_absolute_time(); + case DISPATCH_TIMER_INDEX_WALL: + return _dispatch_get_nanoseconds(); + default: + DISPATCH_CRASH("Invalid timer"); } - - return ds; - -out_bad: - free(ds); - return NULL; } -long -dispatch_source_get_error(dispatch_source_t ds, long *err_out) +DISPATCH_ALWAYS_INLINE +static inline uint64_t +_dispatch_source_timer_now(dispatch_source_refs_t dr) { - // 6863892 don't report ECANCELED until kevent is unregistered - if ((ds->ds_atomic_flags & DSF_CANCELED) && !ds->ds_dkev) { - if (err_out) { - *err_out = ECANCELED; - } - return DISPATCH_ERROR_DOMAIN_POSIX; - } else { - return DISPATCH_ERROR_DOMAIN_NO_ERROR; - } + return _dispatch_source_timer_now2(_dispatch_source_timer_idx(dr)); } -#endif /* DISPATCH_NO_LEGACY */ // Updates the ordered list of timers based on next fire date for changes to ds. // Should only be called from the context of _dispatch_mgr_q. -void +static void _dispatch_timer_list_update(dispatch_source_t ds) { - dispatch_source_t dsi = NULL; - int idx; - + dispatch_source_refs_t dr = ds->ds_refs, dri = NULL; + dispatch_assert(_dispatch_queue_get_current() == &_dispatch_mgr_q); - // do not reschedule timers unregistered with _dispatch_kevent_release() + // do not reschedule timers unregistered with _dispatch_kevent_unregister() if (!ds->ds_dkev) { return; } // Ensure the source is on the global kevent lists before it is removed and // readded below. - _dispatch_kevent_merge(ds); - - TAILQ_REMOVE(&ds->ds_dkev->dk_sources, ds, ds_list); + _dispatch_kevent_register(ds); + + TAILQ_REMOVE(&ds->ds_dkev->dk_sources, dr, dr_list); + + // Move timers that are disabled, suspended or have missed intervals to the + // disarmed list, rearm after resume resp. source invoke will reenable them + if (!ds_timer(dr).target || DISPATCH_OBJECT_SUSPENDED(ds) || + ds->ds_pending_data) { + (void)dispatch_atomic_and2o(ds, ds_atomic_flags, ~DSF_ARMED); + ds->ds_dkev = &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_DISARM]; + TAILQ_INSERT_TAIL(&ds->ds_dkev->dk_sources, (dispatch_source_refs_t)dr, + dr_list); + return; + } // change the list if the clock type has changed - if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) { - idx = DISPATCH_TIMER_INDEX_WALL; - } else { - idx = DISPATCH_TIMER_INDEX_MACH; - } - ds->ds_dkev = &_dispatch_kevent_timer[idx]; + ds->ds_dkev = &_dispatch_kevent_timer[_dispatch_source_timer_idx(dr)]; - if (ds->ds_timer.target) { - TAILQ_FOREACH(dsi, &ds->ds_dkev->dk_sources, ds_list) { - if (dsi->ds_timer.target == 0 || ds->ds_timer.target < dsi->ds_timer.target) { - break; - } + TAILQ_FOREACH(dri, &ds->ds_dkev->dk_sources, dr_list) { + if (ds_timer(dri).target == 0 || + ds_timer(dr).target < ds_timer(dri).target) { + break; } } - - if (dsi) { - TAILQ_INSERT_BEFORE(dsi, ds, ds_list); + + if (dri) { + TAILQ_INSERT_BEFORE(dri, dr, dr_list); } else { - TAILQ_INSERT_TAIL(&ds->ds_dkev->dk_sources, ds, ds_list); + TAILQ_INSERT_TAIL(&ds->ds_dkev->dk_sources, dr, dr_list); } } -static void +static inline void _dispatch_run_timers2(unsigned int timer) { + dispatch_source_refs_t dr; dispatch_source_t ds; uint64_t now, missed; - if (timer == DISPATCH_TIMER_INDEX_MACH) { - now = mach_absolute_time(); - } else { - now = _dispatch_get_nanoseconds(); - } - - while ((ds = TAILQ_FIRST(&_dispatch_kevent_timer[timer].dk_sources))) { + now = _dispatch_source_timer_now2(timer); + while ((dr = TAILQ_FIRST(&_dispatch_kevent_timer[timer].dk_sources))) { + ds = _dispatch_source_from_refs(dr); // We may find timers on the wrong list due to a pending update from // dispatch_source_set_timer. Force an update of the list in that case. if (timer != ds->ds_ident_hack) { _dispatch_timer_list_update(ds); continue; } - if (!ds->ds_timer.target) { + if (!ds_timer(dr).target) { // no configured timers on the list break; } - if (ds->ds_timer.target > now) { + if (ds_timer(dr).target > now) { // Done running timers for now. break; } - - if (ds->ds_timer.flags & (DISPATCH_TIMER_ONESHOT|DISPATCH_TIMER_ABSOLUTE)) { - dispatch_atomic_inc(&ds->ds_pending_data); - ds->ds_timer.target = 0; - } else { - // Calculate number of missed intervals. - missed = (now - ds->ds_timer.target) / ds->ds_timer.interval; - dispatch_atomic_add(&ds->ds_pending_data, missed + 1); - ds->ds_timer.target += (missed + 1) * ds->ds_timer.interval; + // Remove timers that are suspended or have missed intervals from the + // list, rearm after resume resp. source invoke will reenable them + if (DISPATCH_OBJECT_SUSPENDED(ds) || ds->ds_pending_data) { + _dispatch_timer_list_update(ds); + continue; } - + // Calculate number of missed intervals. + missed = (now - ds_timer(dr).target) / ds_timer(dr).interval; + if (++missed > INT_MAX) { + missed = INT_MAX; + } + ds_timer(dr).target += missed * ds_timer(dr).interval; _dispatch_timer_list_update(ds); + ds_timer(dr).last_fire = now; + (void)dispatch_atomic_add2o(ds, ds_pending_data, (int)missed); _dispatch_wakeup(ds); } } @@ -1349,65 +1097,32 @@ _dispatch_run_timers2(unsigned int timer) void _dispatch_run_timers(void) { + dispatch_once_f(&__dispatch_kevent_init_pred, + NULL, _dispatch_kevent_init); + unsigned int i; for (i = 0; i < DISPATCH_TIMER_COUNT; i++) { - _dispatch_run_timers2(i); + if (!TAILQ_EMPTY(&_dispatch_kevent_timer[i].dk_sources)) { + _dispatch_run_timers2(i); + } } } -#if defined(__i386__) || defined(__x86_64__) -// these architectures always return mach_absolute_time() in nanoseconds -#define _dispatch_convert_mach2nano(x) (x) -#define _dispatch_convert_nano2mach(x) (x) -#else -static mach_timebase_info_data_t tbi; -static dispatch_once_t tbi_pred; - -static void -_dispatch_convert_init(void *context __attribute__((unused))) -{ - dispatch_assume_zero(mach_timebase_info(&tbi)); -} - -static uint64_t -_dispatch_convert_mach2nano(uint64_t val) -{ -#ifdef __LP64__ - __uint128_t tmp; -#else - long double tmp; -#endif - - dispatch_once_f(&tbi_pred, NULL, _dispatch_convert_init); - - tmp = val; - tmp *= tbi.numer; - tmp /= tbi.denom; - - return tmp; -} - -static uint64_t -_dispatch_convert_nano2mach(uint64_t val) +static inline unsigned long +_dispatch_source_timer_data(dispatch_source_refs_t dr, unsigned long prev) { -#ifdef __LP64__ - __uint128_t tmp; -#else - long double tmp; -#endif - - dispatch_once_f(&tbi_pred, NULL, _dispatch_convert_init); - - tmp = val; - tmp *= tbi.denom; - tmp /= tbi.numer; - - return tmp; + // calculate the number of intervals since last fire + unsigned long data, missed; + uint64_t now = _dispatch_source_timer_now(dr); + missed = (unsigned long)((now - ds_timer(dr).last_fire) / + ds_timer(dr).interval); + // correct for missed intervals already delivered last time + data = prev - ds_timer(dr).missed + missed; + ds_timer(dr).missed = missed; + return data; } -#endif // approx 1 year (60s * 60m * 24h * 365d) -#define FOREVER_SEC 3153600l #define FOREVER_NSEC 31536000000000000ull struct timespec * @@ -1416,45 +1131,28 @@ _dispatch_get_next_timer_fire(struct timespec *howsoon) // // kevent(2) does not allow large timeouts, so we use a long timeout // instead (approximately 1 year). - dispatch_source_t ds = NULL; + dispatch_source_refs_t dr = NULL; unsigned int timer; uint64_t now, delta_tmp, delta = UINT64_MAX; - // We are looking for the first unsuspended timer which has its target - // time set. Given timers are kept in order, if we hit an timer that's - // unset there's no point in continuing down the list. for (timer = 0; timer < DISPATCH_TIMER_COUNT; timer++) { - TAILQ_FOREACH(ds, &_dispatch_kevent_timer[timer].dk_sources, ds_list) { - if (!ds->ds_timer.target) { - break; - } - if (DISPATCH_OBJECT_SUSPENDED(ds)) { - ds->ds_is_armed = false; - } else { - break; - } - } - - if (!ds || !ds->ds_timer.target) { + // Timers are kept in order, first one will fire next + dr = TAILQ_FIRST(&_dispatch_kevent_timer[timer].dk_sources); + if (!dr || !ds_timer(dr).target) { + // Empty list or disabled timer continue; } - - if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) { - now = _dispatch_get_nanoseconds(); - } else { - now = mach_absolute_time(); - } - if (ds->ds_timer.target <= now) { + now = _dispatch_source_timer_now(dr); + if (ds_timer(dr).target <= now) { howsoon->tv_sec = 0; howsoon->tv_nsec = 0; return howsoon; } - // the subtraction cannot go negative because the previous "if" // verified that the target is greater than now. - delta_tmp = ds->ds_timer.target - now; - if (!(ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK)) { - delta_tmp = _dispatch_convert_mach2nano(delta_tmp); + delta_tmp = ds_timer(dr).target - now; + if (!(ds_timer(dr).flags & DISPATCH_TIMER_WALL_CLOCK)) { + delta_tmp = _dispatch_time_mach2nano(delta_tmp); } if (delta_tmp < delta) { delta = delta_tmp; @@ -1475,28 +1173,45 @@ struct dispatch_set_timer_params { struct dispatch_timer_source_s values; }; -// To be called from the context of the _dispatch_mgr_q static void -_dispatch_source_set_timer2(void *context) +_dispatch_source_set_timer3(void *context) { + // Called on the _dispatch_mgr_q struct dispatch_set_timer_params *params = context; dispatch_source_t ds = params->ds; ds->ds_ident_hack = params->ident; - ds->ds_timer = params->values; + ds_timer(ds->ds_refs) = params->values; + // Clear any pending data that might have accumulated on + // older timer params + ds->ds_pending_data = 0; _dispatch_timer_list_update(ds); dispatch_resume(ds); dispatch_release(ds); free(params); } +static void +_dispatch_source_set_timer2(void *context) +{ + // Called on the source queue + struct dispatch_set_timer_params *params = context; + dispatch_suspend(params->ds); + dispatch_barrier_async_f(&_dispatch_mgr_q, params, + _dispatch_source_set_timer3); +} + void dispatch_source_set_timer(dispatch_source_t ds, dispatch_time_t start, uint64_t interval, uint64_t leeway) { + if (slowpath(!ds->ds_is_timer)) { + DISPATCH_CLIENT_CRASH("Attempt to set timer on a non-timer source"); + } + struct dispatch_set_timer_params *params; - + // we use zero internally to mean disabled if (interval == 0) { interval = 1; @@ -1504,121 +1219,89 @@ dispatch_source_set_timer(dispatch_source_t ds, // 6866347 - make sure nanoseconds won't overflow interval = INT64_MAX; } + if ((int64_t)leeway < 0) { + leeway = INT64_MAX; + } - // Suspend the source so that it doesn't fire with pending changes - // The use of suspend/resume requires the external retain/release - dispatch_retain(ds); - dispatch_suspend(ds); - if (start == DISPATCH_TIME_NOW) { - start = mach_absolute_time(); + start = _dispatch_absolute_time(); } else if (start == DISPATCH_TIME_FOREVER) { start = INT64_MAX; } - while (!(params = malloc(sizeof(struct dispatch_set_timer_params)))) { + while (!(params = calloc(1ul, sizeof(struct dispatch_set_timer_params)))) { sleep(1); } params->ds = ds; - params->values.flags = ds->ds_timer.flags; + params->values.flags = ds_timer(ds->ds_refs).flags; if ((int64_t)start < 0) { // wall clock params->ident = DISPATCH_TIMER_INDEX_WALL; - params->values.start = -((int64_t)start); params->values.target = -((int64_t)start); params->values.interval = interval; params->values.leeway = leeway; params->values.flags |= DISPATCH_TIMER_WALL_CLOCK; } else { - // mach clock + // absolute clock params->ident = DISPATCH_TIMER_INDEX_MACH; - params->values.start = start; params->values.target = start; - params->values.interval = _dispatch_convert_nano2mach(interval); - params->values.leeway = _dispatch_convert_nano2mach(leeway); - params->values.flags &= ~DISPATCH_TIMER_WALL_CLOCK; - } - - dispatch_barrier_async_f(&_dispatch_mgr_q, params, _dispatch_source_set_timer2); -} - -#ifndef DISPATCH_NO_LEGACY -// LEGACY -long -dispatch_source_timer_set_time(dispatch_source_t ds, uint64_t nanoseconds, uint64_t leeway) -{ - dispatch_time_t start; - if (nanoseconds == 0) { - nanoseconds = 1; - } - if (ds->ds_timer.flags == (DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_WALL_CLOCK)) { - static const struct timespec t0; - start = dispatch_walltime(&t0, nanoseconds); - } else if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) { - start = dispatch_walltime(DISPATCH_TIME_NOW, nanoseconds); - } else { - start = dispatch_time(DISPATCH_TIME_NOW, nanoseconds); - } - if (ds->ds_timer.flags & (DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_ONESHOT)) { - // 6866347 - make sure nanoseconds won't overflow - nanoseconds = INT64_MAX; // non-repeating (~292 years) - } - dispatch_source_set_timer(ds, start, nanoseconds, leeway); - return 0; -} + params->values.interval = _dispatch_time_nano2mach(interval); + + // rdar://problem/7287561 interval must be at least one in + // in order to avoid later division by zero when calculating + // the missed interval count. (NOTE: the wall clock's + // interval is already "fixed" to be 1 or more) + if (params->values.interval < 1) { + params->values.interval = 1; + } -// LEGACY -uint64_t -dispatch_event_get_nanoseconds(dispatch_source_t ds) -{ - if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) { - return ds->ds_timer.interval; - } else { - return _dispatch_convert_mach2nano(ds->ds_timer.interval); + params->values.leeway = _dispatch_time_nano2mach(leeway); + params->values.flags &= ~DISPATCH_TIMER_WALL_CLOCK; } + // Suspend the source so that it doesn't fire with pending changes + // The use of suspend/resume requires the external retain/release + dispatch_retain(ds); + dispatch_barrier_async_f((dispatch_queue_t)ds, params, + _dispatch_source_set_timer2); } -#endif /* DISPATCH_NO_LEGACY */ -static dispatch_source_t _dispatch_mach_notify_source; -static mach_port_t _dispatch_port_set; -static mach_port_t _dispatch_event_port; +#pragma mark - +#pragma mark dispatch_mach -#define _DISPATCH_IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v) -#define _DISPATCH_HASH(x, y) (_DISPATCH_IS_POWER_OF_TWO(y) ? (MACH_PORT_INDEX(x) & ((y) - 1)) : (MACH_PORT_INDEX(x) % (y))) +#if HAVE_MACH -#define _DISPATCH_MACHPORT_HASH_SIZE 32 -#define _DISPATCH_MACHPORT_HASH(x) _DISPATCH_HASH((x), _DISPATCH_MACHPORT_HASH_SIZE) +#if DISPATCH_DEBUG && DISPATCH_MACHPORT_DEBUG +#define _dispatch_debug_machport(name) \ + dispatch_debug_machport((name), __func__) +#else +#define _dispatch_debug_machport(name) +#endif -static void _dispatch_port_set_init(void *); -static mach_port_t _dispatch_get_port_set(void); +// Flags for all notifications that are registered/unregistered when a +// send-possible notification is requested/delivered +#define _DISPATCH_MACH_SP_FLAGS (DISPATCH_MACH_SEND_POSSIBLE| \ + DISPATCH_MACH_SEND_DEAD|DISPATCH_MACH_SEND_DELETED) -void -_dispatch_drain_mach_messages(struct kevent *ke) -{ - dispatch_source_t dsi; - dispatch_kevent_t dk; - struct kevent ke2; +#define _DISPATCH_IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v) +#define _DISPATCH_HASH(x, y) (_DISPATCH_IS_POWER_OF_TWO(y) ? \ + (MACH_PORT_INDEX(x) & ((y) - 1)) : (MACH_PORT_INDEX(x) % (y))) - if (!dispatch_assume(ke->data)) { - return; - } - dk = _dispatch_kevent_find(ke->data, EVFILT_MACHPORT); - if (!dispatch_assume(dk)) { - return; - } - _dispatch_kevent_machport_disable(dk); // emulate EV_DISPATCH +#define _DISPATCH_MACHPORT_HASH_SIZE 32 +#define _DISPATCH_MACHPORT_HASH(x) \ + _DISPATCH_HASH((x), _DISPATCH_MACHPORT_HASH_SIZE) - EV_SET(&ke2, ke->data, EVFILT_MACHPORT, EV_ADD|EV_ENABLE|EV_DISPATCH, DISPATCH_MACHPORT_RECV, 0, dk); +static dispatch_source_t _dispatch_mach_notify_source; +static mach_port_t _dispatch_port_set; +static mach_port_t _dispatch_event_port; - TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { - _dispatch_source_merge_kevent(dsi, &ke2); - } -} +static kern_return_t _dispatch_mach_notify_update(dispatch_kevent_t dk, + uint32_t new_flags, uint32_t del_flags, uint32_t mask, + mach_msg_id_t notify_msgid, mach_port_mscount_t notify_sync); -void -_dispatch_port_set_init(void *context __attribute__((unused))) +static void +_dispatch_port_set_init(void *context DISPATCH_UNUSED) { struct kevent kev = { .filter = EVFILT_MACHPORT, @@ -1626,22 +1309,39 @@ _dispatch_port_set_init(void *context __attribute__((unused))) }; kern_return_t kr; - kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &_dispatch_port_set); + kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, + &_dispatch_port_set); DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); - kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_dispatch_event_port); + if (kr) { + _dispatch_bug_mach_client( + "_dispatch_port_set_init: mach_port_allocate() failed", kr); + DISPATCH_CLIENT_CRASH( + "mach_port_allocate() failed: cannot create port set"); + } + kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, + &_dispatch_event_port); DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); - kr = mach_port_move_member(mach_task_self(), _dispatch_event_port, _dispatch_port_set); + if (kr) { + _dispatch_bug_mach_client( + "_dispatch_port_set_init: mach_port_allocate() failed", kr); + DISPATCH_CLIENT_CRASH( + "mach_port_allocate() failed: cannot create receive right"); + } + kr = mach_port_move_member(mach_task_self(), _dispatch_event_port, + _dispatch_port_set); DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); + if (kr) { + _dispatch_bug_mach_client( + "_dispatch_port_set_init: mach_port_move_member() failed", kr); + DISPATCH_CLIENT_CRASH("mach_port_move_member() failed"); + } kev.ident = _dispatch_port_set; _dispatch_update_kq(&kev); } -mach_port_t +static mach_port_t _dispatch_get_port_set(void) { static dispatch_once_t pred; @@ -1651,284 +1351,334 @@ _dispatch_get_port_set(void) return _dispatch_port_set; } -void -_dispatch_kevent_machport_resume(dispatch_kevent_t dk, uint32_t new_flags, uint32_t del_flags) -{ - mach_port_t previous, port = (mach_port_t)dk->dk_kevent.ident; - kern_return_t kr; - - if ((new_flags & DISPATCH_MACHPORT_RECV) || (!new_flags && !del_flags && dk->dk_kevent.fflags & DISPATCH_MACHPORT_RECV)) { - _dispatch_kevent_machport_enable(dk); - } - if (new_flags & DISPATCH_MACHPORT_DEAD) { - kr = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 1, - _dispatch_event_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); - DISPATCH_VERIFY_MIG(kr); - - - switch(kr) { - case KERN_INVALID_NAME: - case KERN_INVALID_RIGHT: - // Supress errors - break; - default: - // Else, we dont expect any errors from mach. Log any errors if we do - if (dispatch_assume_zero(kr)) { - // log the error - } else if (dispatch_assume_zero(previous)) { - // Another subsystem has beat libdispatch to requesting the Mach - // dead-name notification on this port. We should technically cache the - // previous port and message it when the kernel messages our port. Or - // we can just say screw those subsystems and drop the previous port. - // They should adopt libdispatch :-P - kr = mach_port_deallocate(mach_task_self(), previous); - DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); - } - } - } - - if (del_flags & DISPATCH_MACHPORT_RECV) { - _dispatch_kevent_machport_disable(dk); - } - if (del_flags & DISPATCH_MACHPORT_DEAD) { - kr = mach_port_request_notification(mach_task_self(), (mach_port_t)dk->dk_kevent.ident, - MACH_NOTIFY_DEAD_NAME, 1, MACH_PORT_NULL, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); - DISPATCH_VERIFY_MIG(kr); - - switch (kr) { - case KERN_INVALID_NAME: - case KERN_INVALID_RIGHT: - case KERN_INVALID_ARGUMENT: - break; - default: - if (dispatch_assume_zero(kr)) { - // log the error - } else if (previous) { - // the kernel has not consumed the right yet - dispatch_assume_zero(_dispatch_send_consume_send_once_right(previous)); - } - } - } -} - -void +static kern_return_t _dispatch_kevent_machport_enable(dispatch_kevent_t dk) { mach_port_t mp = (mach_port_t)dk->dk_kevent.ident; kern_return_t kr; + _dispatch_debug_machport(mp); kr = mach_port_move_member(mach_task_self(), mp, _dispatch_get_port_set()); - DISPATCH_VERIFY_MIG(kr); - switch (kr) { - case KERN_INVALID_NAME: + if (slowpath(kr)) { + DISPATCH_VERIFY_MIG(kr); + switch (kr) { + case KERN_INVALID_NAME: #if DISPATCH_DEBUG - _dispatch_log("Corruption: Mach receive right 0x%x destroyed prematurely", mp); + _dispatch_log("Corruption: Mach receive right 0x%x destroyed " + "prematurely", mp); #endif - break; - default: - dispatch_assume_zero(kr); + break; + case KERN_INVALID_RIGHT: + _dispatch_bug_mach_client("_dispatch_kevent_machport_enable: " + "mach_port_move_member() failed ", kr); + break; + default: + (void)dispatch_assume_zero(kr); + break; + } } + return kr; } -void +static void _dispatch_kevent_machport_disable(dispatch_kevent_t dk) { mach_port_t mp = (mach_port_t)dk->dk_kevent.ident; kern_return_t kr; + _dispatch_debug_machport(mp); kr = mach_port_move_member(mach_task_self(), mp, 0); - DISPATCH_VERIFY_MIG(kr); - switch (kr) { - case KERN_INVALID_RIGHT: - case KERN_INVALID_NAME: + if (slowpath(kr)) { + DISPATCH_VERIFY_MIG(kr); + switch (kr) { + case KERN_INVALID_RIGHT: + case KERN_INVALID_NAME: #if DISPATCH_DEBUG - _dispatch_log("Corruption: Mach receive right 0x%x destroyed prematurely", mp); + _dispatch_log("Corruption: Mach receive right 0x%x destroyed " + "prematurely", mp); #endif - break; - case 0: - break; - default: - dispatch_assume_zero(kr); - break; + break; + default: + (void)dispatch_assume_zero(kr); + break; + } } } -#define _DISPATCH_MIN_MSG_SZ (8ul * 1024ul - MAX_TRAILER_SIZE) -#ifndef DISPATCH_NO_LEGACY -dispatch_source_t -dispatch_source_mig_create(mach_port_t mport, size_t max_msg_size, dispatch_source_attr_t attr, - dispatch_queue_t dq, dispatch_mig_callback_t mig_callback) -{ - if (max_msg_size < _DISPATCH_MIN_MSG_SZ) { - max_msg_size = _DISPATCH_MIN_MSG_SZ; - } - return dispatch_source_machport_create(mport, DISPATCH_MACHPORT_RECV, attr, dq, - ^(dispatch_source_t ds) { - if (!dispatch_source_get_error(ds, NULL)) { - if (dq->dq_width != 1) { - dispatch_retain(ds); // this is a shim -- use the external retain - dispatch_async(dq, ^{ - dispatch_mig_server(ds, max_msg_size, mig_callback); - dispatch_release(ds); // this is a shim -- use the external release - }); - } else { - dispatch_mig_server(ds, max_msg_size, mig_callback); - } - } - }); +kern_return_t +_dispatch_kevent_machport_resume(dispatch_kevent_t dk, uint32_t new_flags, + uint32_t del_flags) +{ + kern_return_t kr_recv = 0, kr_sp = 0; + + dispatch_assert_zero(new_flags & del_flags); + if (new_flags & DISPATCH_MACH_RECV_MESSAGE) { + kr_recv = _dispatch_kevent_machport_enable(dk); + } else if (del_flags & DISPATCH_MACH_RECV_MESSAGE) { + _dispatch_kevent_machport_disable(dk); + } + if ((new_flags & _DISPATCH_MACH_SP_FLAGS) || + (del_flags & _DISPATCH_MACH_SP_FLAGS)) { + // Requesting a (delayed) non-sync send-possible notification + // registers for both immediate dead-name notification and delayed-arm + // send-possible notification for the port. + // The send-possible notification is armed when a mach_msg() with the + // the MACH_SEND_NOTIFY to the port times out. + // If send-possible is unavailable, fall back to immediate dead-name + // registration rdar://problem/2527840&9008724 + kr_sp = _dispatch_mach_notify_update(dk, new_flags, del_flags, + _DISPATCH_MACH_SP_FLAGS, MACH_NOTIFY_SEND_POSSIBLE, + MACH_NOTIFY_SEND_POSSIBLE == MACH_NOTIFY_DEAD_NAME ? 1 : 0); + } + + return (kr_recv ? kr_recv : kr_sp); } -#endif /* DISPATCH_NO_LEGACY */ -static void -_dispatch_mach_notify_source_init(void *context __attribute__((unused))) +void +_dispatch_drain_mach_messages(struct kevent *ke) { - size_t maxsz = sizeof(union __RequestUnion___dispatch_send_libdispatch_internal_protocol_subsystem); + mach_port_t name = (mach_port_name_t)ke->data; + dispatch_source_refs_t dri; + dispatch_kevent_t dk; + struct kevent kev; - if (sizeof(union __ReplyUnion___dispatch_libdispatch_internal_protocol_subsystem) > maxsz) { - maxsz = sizeof(union __ReplyUnion___dispatch_libdispatch_internal_protocol_subsystem); + if (!dispatch_assume(name)) { + return; } + _dispatch_debug_machport(name); + dk = _dispatch_kevent_find(name, EVFILT_MACHPORT); + if (!dispatch_assume(dk)) { + return; + } + _dispatch_kevent_machport_disable(dk); // emulate EV_DISPATCH - _dispatch_get_port_set(); - - _dispatch_mach_notify_source = dispatch_source_mig_create(_dispatch_event_port, - maxsz, NULL, &_dispatch_mgr_q, libdispatch_internal_protocol_server); + EV_SET(&kev, name, EVFILT_MACHPORT, EV_ADD|EV_ENABLE|EV_DISPATCH, + DISPATCH_MACH_RECV_MESSAGE, 0, dk); - dispatch_assert(_dispatch_mach_notify_source); + TAILQ_FOREACH(dri, &dk->dk_sources, dr_list) { + _dispatch_source_merge_kevent(_dispatch_source_from_refs(dri), &kev); + } } -kern_return_t -_dispatch_mach_notify_port_deleted(mach_port_t notify __attribute__((unused)), mach_port_name_t name) +static inline void +_dispatch_mach_notify_merge(mach_port_t name, uint32_t flag, uint32_t unreg, + bool final) { - dispatch_source_t dsi; + dispatch_source_refs_t dri; dispatch_kevent_t dk; struct kevent kev; -#if DISPATCH_DEBUG - _dispatch_log("Corruption: Mach send/send-once/dead-name right 0x%x deleted prematurely", name); -#endif + dk = _dispatch_kevent_find(name, EVFILT_MACHPORT); + if (!dk) { + return; + } + + // Update notification registration state. + dk->dk_kevent.data &= ~unreg; + if (!final) { + // Re-register for notification before delivery + _dispatch_kevent_resume(dk, flag, 0); + } + + EV_SET(&kev, name, EVFILT_MACHPORT, EV_ADD|EV_ENABLE, flag, 0, dk); + + TAILQ_FOREACH(dri, &dk->dk_sources, dr_list) { + _dispatch_source_merge_kevent(_dispatch_source_from_refs(dri), &kev); + if (final) { + // this can never happen again + // this must happen after the merge + // this may be racy in the future, but we don't provide a 'setter' + // API for the mask yet + _dispatch_source_from_refs(dri)->ds_pending_data_mask &= ~unreg; + } + } + + if (final) { + // no more sources have these flags + dk->dk_kevent.fflags &= ~unreg; + } +} + +static kern_return_t +_dispatch_mach_notify_update(dispatch_kevent_t dk, uint32_t new_flags, + uint32_t del_flags, uint32_t mask, mach_msg_id_t notify_msgid, + mach_port_mscount_t notify_sync) +{ + mach_port_t previous, port = (mach_port_t)dk->dk_kevent.ident; + typeof(dk->dk_kevent.data) prev = dk->dk_kevent.data; + kern_return_t kr, krr = 0; + + // Update notification registration state. + dk->dk_kevent.data |= (new_flags | dk->dk_kevent.fflags) & mask; + dk->dk_kevent.data &= ~(del_flags & mask); + + _dispatch_debug_machport(port); + if ((dk->dk_kevent.data & mask) && !(prev & mask)) { + previous = MACH_PORT_NULL; + krr = mach_port_request_notification(mach_task_self(), port, + notify_msgid, notify_sync, _dispatch_event_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + DISPATCH_VERIFY_MIG(krr); + + switch(krr) { + case KERN_INVALID_NAME: + case KERN_INVALID_RIGHT: + // Supress errors & clear registration state + dk->dk_kevent.data &= ~mask; + break; + default: + // Else, we dont expect any errors from mach. Log any errors + if (dispatch_assume_zero(krr)) { + // log the error & clear registration state + dk->dk_kevent.data &= ~mask; + } else if (dispatch_assume_zero(previous)) { + // Another subsystem has beat libdispatch to requesting the + // specified Mach notification on this port. We should + // technically cache the previous port and message it when the + // kernel messages our port. Or we can just say screw those + // subsystems and deallocate the previous port. + // They should adopt libdispatch :-P + kr = mach_port_deallocate(mach_task_self(), previous); + DISPATCH_VERIFY_MIG(kr); + (void)dispatch_assume_zero(kr); + previous = MACH_PORT_NULL; + } + } + } else if (!(dk->dk_kevent.data & mask) && (prev & mask)) { + previous = MACH_PORT_NULL; + kr = mach_port_request_notification(mach_task_self(), port, + notify_msgid, notify_sync, MACH_PORT_NULL, + MACH_MSG_TYPE_MOVE_SEND_ONCE, &previous); + DISPATCH_VERIFY_MIG(kr); - dk = _dispatch_kevent_find(name, EVFILT_MACHPORT); - if (!dk) { - goto out; + switch (kr) { + case KERN_INVALID_NAME: + case KERN_INVALID_RIGHT: + case KERN_INVALID_ARGUMENT: + break; + default: + if (dispatch_assume_zero(kr)) { + // log the error + } + } + } else { + return 0; } - - EV_SET(&kev, name, EVFILT_MACHPORT, EV_ADD|EV_ENABLE|EV_DISPATCH|EV_EOF, DISPATCH_MACHPORT_DELETED, 0, dk); - - TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { - _dispatch_source_merge_kevent(dsi, &kev); - // this can never happen again - // this must happen after the merge - // this may be racy in the future, but we don't provide a 'setter' API for the mask yet - dsi->ds_pending_data_mask &= ~DISPATCH_MACHPORT_DELETED; + if (slowpath(previous)) { + // the kernel has not consumed the send-once right yet + (void)dispatch_assume_zero( + _dispatch_send_consume_send_once_right(previous)); } - - // no more sources have this flag - dk->dk_kevent.fflags &= ~DISPATCH_MACHPORT_DELETED; - -out: - return KERN_SUCCESS; + return krr; } -kern_return_t -_dispatch_mach_notify_port_destroyed(mach_port_t notify __attribute__((unused)), mach_port_t name) +static void +_dispatch_mach_notify_source2(void *context) { - kern_return_t kr; - // this function should never be called - dispatch_assume_zero(name); - kr = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE, -1); - DISPATCH_VERIFY_MIG(kr); - dispatch_assume_zero(kr); - return KERN_SUCCESS; + dispatch_source_t ds = context; + size_t maxsz = MAX(sizeof(union + __RequestUnion___dispatch_send_libdispatch_internal_protocol_subsystem), + sizeof(union + __ReplyUnion___dispatch_libdispatch_internal_protocol_subsystem)); + + dispatch_mig_server(ds, maxsz, libdispatch_internal_protocol_server); } -kern_return_t -_dispatch_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscnt __attribute__((unused))) +void +_dispatch_mach_notify_source_init(void *context DISPATCH_UNUSED) { - // this function should never be called - dispatch_assume_zero(notify); - return KERN_SUCCESS; + _dispatch_get_port_set(); + + _dispatch_mach_notify_source = dispatch_source_create( + DISPATCH_SOURCE_TYPE_MACH_RECV, _dispatch_event_port, 0, + &_dispatch_mgr_q); + dispatch_assert(_dispatch_mach_notify_source); + dispatch_set_context(_dispatch_mach_notify_source, + _dispatch_mach_notify_source); + dispatch_source_set_event_handler_f(_dispatch_mach_notify_source, + _dispatch_mach_notify_source2); + dispatch_resume(_dispatch_mach_notify_source); } kern_return_t -_dispatch_mach_notify_send_once(mach_port_t notify __attribute__((unused))) +_dispatch_mach_notify_port_deleted(mach_port_t notify DISPATCH_UNUSED, + mach_port_name_t name) { - // we only register for dead-name notifications - // some code deallocated our send-once right without consuming it #if DISPATCH_DEBUG - _dispatch_log("Corruption: An app/library deleted a libdispatch dead-name notification"); + _dispatch_log("Corruption: Mach send/send-once/dead-name right 0x%x " + "deleted prematurely", name); #endif + + _dispatch_debug_machport(name); + _dispatch_mach_notify_merge(name, DISPATCH_MACH_SEND_DELETED, + _DISPATCH_MACH_SP_FLAGS, true); + return KERN_SUCCESS; } kern_return_t -_dispatch_mach_notify_dead_name(mach_port_t notify __attribute__((unused)), mach_port_name_t name) +_dispatch_mach_notify_dead_name(mach_port_t notify DISPATCH_UNUSED, + mach_port_name_t name) { - dispatch_source_t dsi; - dispatch_kevent_t dk; - struct kevent kev; kern_return_t kr; - dk = _dispatch_kevent_find(name, EVFILT_MACHPORT); - if (!dk) { - goto out; - } - - EV_SET(&kev, name, EVFILT_MACHPORT, EV_ADD|EV_ENABLE|EV_DISPATCH|EV_EOF, DISPATCH_MACHPORT_DEAD, 0, dk); - - TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { - _dispatch_source_merge_kevent(dsi, &kev); - // this can never happen again - // this must happen after the merge - // this may be racy in the future, but we don't provide a 'setter' API for the mask yet - dsi->ds_pending_data_mask &= ~DISPATCH_MACHPORT_DEAD; - } - - // no more sources have this flag - dk->dk_kevent.fflags &= ~DISPATCH_MACHPORT_DEAD; +#if DISPATCH_DEBUG + _dispatch_log("machport[0x%08x]: dead-name notification: %s", + name, __func__); +#endif + _dispatch_debug_machport(name); + _dispatch_mach_notify_merge(name, DISPATCH_MACH_SEND_DEAD, + _DISPATCH_MACH_SP_FLAGS, true); -out: - // the act of receiving a dead name notification allocates a dead-name right that must be deallocated + // the act of receiving a dead name notification allocates a dead-name + // right that must be deallocated kr = mach_port_deallocate(mach_task_self(), name); DISPATCH_VERIFY_MIG(kr); - //dispatch_assume_zero(kr); + //(void)dispatch_assume_zero(kr); return KERN_SUCCESS; } kern_return_t -_dispatch_wakeup_main_thread(mach_port_t mp __attribute__((unused))) +_dispatch_mach_notify_send_possible(mach_port_t notify DISPATCH_UNUSED, + mach_port_name_t name) { - // dummy function just to pop out the main thread out of mach_msg() - return 0; -} +#if DISPATCH_DEBUG + _dispatch_log("machport[0x%08x]: send-possible notification: %s", + name, __func__); +#endif + _dispatch_debug_machport(name); + _dispatch_mach_notify_merge(name, DISPATCH_MACH_SEND_POSSIBLE, + _DISPATCH_MACH_SP_FLAGS, false); -kern_return_t -_dispatch_consume_send_once_right(mach_port_t mp __attribute__((unused))) -{ - // dummy function to consume a send-once right - return 0; + return KERN_SUCCESS; } mach_msg_return_t -dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback_t callback) +dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, + dispatch_mig_callback_t callback) { mach_msg_options_t options = MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0); - mach_msg_options_t tmp_options = options; + mach_msg_options_t tmp_options; mig_reply_error_t *bufTemp, *bufRequest, *bufReply; mach_msg_return_t kr = 0; - unsigned int cnt = 1000; // do not stall out serial queues + unsigned int cnt = 1000; // do not stall out serial queues int demux_success; - - maxmsgsz += MAX_TRAILER_SIZE; + bool received = false; + size_t rcv_size = maxmsgsz + MAX_TRAILER_SIZE; // XXX FIXME -- allocate these elsewhere - bufRequest = alloca(maxmsgsz); - bufReply = alloca(maxmsgsz); - bufReply->Head.msgh_size = 0; // make CLANG happy + bufRequest = alloca(rcv_size); + bufReply = alloca(rcv_size); + bufReply->Head.msgh_size = 0; // make CLANG happy + bufRequest->RetCode = 0; +#if DISPATCH_DEBUG + options |= MACH_RCV_LARGE; // rdar://problem/8422992 +#endif + tmp_options = options; // XXX FIXME -- change this to not starve out the target queue for (;;) { if (DISPATCH_OBJECT_SUSPENDED(ds) || (--cnt == 0)) { @@ -1939,9 +1689,8 @@ dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback break; } } - kr = mach_msg(&bufReply->Head, tmp_options, bufReply->Head.msgh_size, - (mach_msg_size_t)maxmsgsz, (mach_port_t)ds->ds_ident_hack, 0, 0); + (mach_msg_size_t)rcv_size, (mach_port_t)ds->ds_ident_hack, 0,0); tmp_options = options; @@ -1954,10 +1703,43 @@ dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback } break; case MACH_RCV_TIMED_OUT: + // Don't return an error if a message was sent this time or + // a message was successfully received previously + // rdar://problems/7363620&7791738 + if(bufReply->Head.msgh_remote_port || received) { + kr = MACH_MSG_SUCCESS; + } + break; case MACH_RCV_INVALID_NAME: break; +#if DISPATCH_DEBUG + case MACH_RCV_TOO_LARGE: + // receive messages that are too large and log their id and size + // rdar://problem/8422992 + tmp_options &= ~MACH_RCV_LARGE; + size_t large_size = bufReply->Head.msgh_size + MAX_TRAILER_SIZE; + void *large_buf = malloc(large_size); + if (large_buf) { + rcv_size = large_size; + bufReply = large_buf; + } + if (!mach_msg(&bufReply->Head, tmp_options, 0, + (mach_msg_size_t)rcv_size, + (mach_port_t)ds->ds_ident_hack, 0, 0)) { + _dispatch_log("BUG in libdispatch client: " + "dispatch_mig_server received message larger than " + "requested size %zd: id = 0x%x, size = %d", + maxmsgsz, bufReply->Head.msgh_id, + bufReply->Head.msgh_size); + } + if (large_buf) { + free(large_buf); + } + // fall through +#endif default: - dispatch_assume_zero(kr); + _dispatch_bug_mach_client( + "dispatch_mig_server: mach_msg() failed", kr); break; } break; @@ -1966,6 +1748,7 @@ dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback if (!(tmp_options & MACH_RCV_MSG)) { break; } + received = true; bufTemp = bufRequest; bufRequest = bufReply; @@ -1978,7 +1761,8 @@ dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback bufRequest->Head.msgh_remote_port = 0; mach_msg_destroy(&bufRequest->Head); } else if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { - // if MACH_MSGH_BITS_COMPLEX is _not_ set, then bufReply->RetCode is present + // if MACH_MSGH_BITS_COMPLEX is _not_ set, then bufReply->RetCode + // is present if (slowpath(bufReply->RetCode)) { if (bufReply->RetCode == MIG_NO_REPLY) { continue; @@ -1992,7 +1776,8 @@ dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback if (bufReply->Head.msgh_remote_port) { tmp_options |= MACH_SEND_MSG; - if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) { + if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != + MACH_MSG_TYPE_MOVE_SEND_ONCE) { tmp_options |= MACH_SEND_TIMEOUT; } } @@ -2000,3 +1785,329 @@ dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback return kr; } + +#endif /* HAVE_MACH */ + +#pragma mark - +#pragma mark dispatch_source_debug + +DISPATCH_NOINLINE +static const char * +_evfiltstr(short filt) +{ + switch (filt) { +#define _evfilt2(f) case (f): return #f + _evfilt2(EVFILT_READ); + _evfilt2(EVFILT_WRITE); + _evfilt2(EVFILT_AIO); + _evfilt2(EVFILT_VNODE); + _evfilt2(EVFILT_PROC); + _evfilt2(EVFILT_SIGNAL); + _evfilt2(EVFILT_TIMER); +#ifdef EVFILT_VM + _evfilt2(EVFILT_VM); +#endif +#if HAVE_MACH + _evfilt2(EVFILT_MACHPORT); +#endif + _evfilt2(EVFILT_FS); + _evfilt2(EVFILT_USER); + + _evfilt2(DISPATCH_EVFILT_TIMER); + _evfilt2(DISPATCH_EVFILT_CUSTOM_ADD); + _evfilt2(DISPATCH_EVFILT_CUSTOM_OR); + default: + return "EVFILT_missing"; + } +} + +static size_t +_dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz) +{ + dispatch_queue_t target = ds->do_targetq; + return snprintf(buf, bufsiz, "target = %s[%p], pending_data = 0x%lx, " + "pending_data_mask = 0x%lx, ", + target ? target->dq_label : "", target, + ds->ds_pending_data, ds->ds_pending_data_mask); +} + +static size_t +_dispatch_timer_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz) +{ + dispatch_source_refs_t dr = ds->ds_refs; + return snprintf(buf, bufsiz, "timer = { target = 0x%llx, " + "last_fire = 0x%llx, interval = 0x%llx, flags = 0x%llx }, ", + ds_timer(dr).target, ds_timer(dr).last_fire, ds_timer(dr).interval, + ds_timer(dr).flags); +} + +static size_t +_dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz) +{ + size_t offset = 0; + offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", + dx_kind(ds), ds); + offset += _dispatch_object_debug_attr(ds, &buf[offset], bufsiz - offset); + offset += _dispatch_source_debug_attr(ds, &buf[offset], bufsiz - offset); + if (ds->ds_is_timer) { + offset += _dispatch_timer_debug_attr(ds, &buf[offset], bufsiz - offset); + } + return offset; +} + +static size_t +_dispatch_source_kevent_debug(dispatch_source_t ds, char* buf, size_t bufsiz) +{ + size_t offset = _dispatch_source_debug(ds, buf, bufsiz); + offset += snprintf(&buf[offset], bufsiz - offset, "filter = %s }", + ds->ds_dkev ? _evfiltstr(ds->ds_dkev->dk_kevent.filter) : "????"); + return offset; +} + +#if DISPATCH_DEBUG +void +dispatch_debug_kevents(struct kevent* kev, size_t count, const char* str) +{ + size_t i; + for (i = 0; i < count; ++i) { + _dispatch_log("kevent[%lu] = { ident = %p, filter = %s, flags = 0x%x, " + "fflags = 0x%x, data = %p, udata = %p }: %s", + i, (void*)kev[i].ident, _evfiltstr(kev[i].filter), kev[i].flags, + kev[i].fflags, (void*)kev[i].data, (void*)kev[i].udata, str); + } +} + +static void +_dispatch_kevent_debugger2(void *context) +{ + struct sockaddr sa; + socklen_t sa_len = sizeof(sa); + int c, fd = (int)(long)context; + unsigned int i; + dispatch_kevent_t dk; + dispatch_source_t ds; + dispatch_source_refs_t dr; + FILE *debug_stream; + + c = accept(fd, &sa, &sa_len); + if (c == -1) { + if (errno != EAGAIN) { + (void)dispatch_assume_zero(errno); + } + return; + } +#if 0 + int r = fcntl(c, F_SETFL, 0); // disable non-blocking IO + if (r == -1) { + (void)dispatch_assume_zero(errno); + } +#endif + debug_stream = fdopen(c, "a"); + if (!dispatch_assume(debug_stream)) { + close(c); + return; + } + + fprintf(debug_stream, "HTTP/1.0 200 OK\r\n"); + fprintf(debug_stream, "Content-type: text/html\r\n"); + fprintf(debug_stream, "Pragma: nocache\r\n"); + fprintf(debug_stream, "\r\n"); + fprintf(debug_stream, "\n"); + fprintf(debug_stream, "PID %u\n", getpid()); + fprintf(debug_stream, "\n
    \n"); + + //fprintf(debug_stream, "DKDKDKDK" + // "DKDKDK\n"); + + for (i = 0; i < DSL_HASH_SIZE; i++) { + if (TAILQ_EMPTY(&_dispatch_sources[i])) { + continue; + } + TAILQ_FOREACH(dk, &_dispatch_sources[i], dk_list) { + fprintf(debug_stream, "\t
  • DK %p ident %lu filter %s flags " + "0x%hx fflags 0x%x data 0x%lx udata %p\n", + dk, (unsigned long)dk->dk_kevent.ident, + _evfiltstr(dk->dk_kevent.filter), dk->dk_kevent.flags, + dk->dk_kevent.fflags, (unsigned long)dk->dk_kevent.data, + dk->dk_kevent.udata); + fprintf(debug_stream, "\t\t
      \n"); + TAILQ_FOREACH(dr, &dk->dk_sources, dr_list) { + ds = _dispatch_source_from_refs(dr); + fprintf(debug_stream, "\t\t\t
    • DS %p refcnt 0x%x suspend " + "0x%x data 0x%lx mask 0x%lx flags 0x%x
    • \n", + ds, ds->do_ref_cnt, ds->do_suspend_cnt, + ds->ds_pending_data, ds->ds_pending_data_mask, + ds->ds_atomic_flags); + if (ds->do_suspend_cnt == DISPATCH_OBJECT_SUSPEND_LOCK) { + dispatch_queue_t dq = ds->do_targetq; + fprintf(debug_stream, "\t\t
      DQ: %p refcnt 0x%x suspend " + "0x%x label: %s\n", dq, dq->do_ref_cnt, + dq->do_suspend_cnt, dq->dq_label); + } + } + fprintf(debug_stream, "\t\t
    \n"); + fprintf(debug_stream, "\t
  • \n"); + } + } + fprintf(debug_stream, "
\n\n\n"); + fflush(debug_stream); + fclose(debug_stream); +} + +static void +_dispatch_kevent_debugger2_cancel(void *context) +{ + int ret, fd = (int)(long)context; + + ret = close(fd); + if (ret != -1) { + (void)dispatch_assume_zero(errno); + } +} + +static void +_dispatch_kevent_debugger(void *context DISPATCH_UNUSED) +{ + union { + struct sockaddr_in sa_in; + struct sockaddr sa; + } sa_u = { + .sa_in = { + .sin_family = AF_INET, + .sin_addr = { htonl(INADDR_LOOPBACK), }, + }, + }; + dispatch_source_t ds; + const char *valstr; + int val, r, fd, sock_opt = 1; + socklen_t slen = sizeof(sa_u); + + if (issetugid()) { + return; + } + valstr = getenv("LIBDISPATCH_DEBUGGER"); + if (!valstr) { + return; + } + val = atoi(valstr); + if (val == 2) { + sa_u.sa_in.sin_addr.s_addr = 0; + } + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd == -1) { + (void)dispatch_assume_zero(errno); + return; + } + r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, + (socklen_t) sizeof sock_opt); + if (r == -1) { + (void)dispatch_assume_zero(errno); + goto out_bad; + } +#if 0 + r = fcntl(fd, F_SETFL, O_NONBLOCK); + if (r == -1) { + (void)dispatch_assume_zero(errno); + goto out_bad; + } +#endif + r = bind(fd, &sa_u.sa, sizeof(sa_u)); + if (r == -1) { + (void)dispatch_assume_zero(errno); + goto out_bad; + } + r = listen(fd, SOMAXCONN); + if (r == -1) { + (void)dispatch_assume_zero(errno); + goto out_bad; + } + r = getsockname(fd, &sa_u.sa, &slen); + if (r == -1) { + (void)dispatch_assume_zero(errno); + goto out_bad; + } + + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, + &_dispatch_mgr_q); + if (dispatch_assume(ds)) { + _dispatch_log("LIBDISPATCH: debug port: %hu", + (in_port_t)ntohs(sa_u.sa_in.sin_port)); + + /* ownership of fd transfers to ds */ + dispatch_set_context(ds, (void *)(long)fd); + dispatch_source_set_event_handler_f(ds, _dispatch_kevent_debugger2); + dispatch_source_set_cancel_handler_f(ds, + _dispatch_kevent_debugger2_cancel); + dispatch_resume(ds); + + return; + } +out_bad: + close(fd); +} + +#if HAVE_MACH + +#ifndef MACH_PORT_TYPE_SPREQUEST +#define MACH_PORT_TYPE_SPREQUEST 0x40000000 +#endif + +void +dispatch_debug_machport(mach_port_t name, const char* str) +{ + mach_port_type_t type; + mach_msg_bits_t ns = 0, nr = 0, nso = 0, nd = 0; + unsigned int dnreqs = 0, dnrsiz; + kern_return_t kr = mach_port_type(mach_task_self(), name, &type); + if (kr) { + _dispatch_log("machport[0x%08x] = { error(0x%x) \"%s\" }: %s", name, + kr, mach_error_string(kr), str); + return; + } + if (type & MACH_PORT_TYPE_SEND) { + (void)dispatch_assume_zero(mach_port_get_refs(mach_task_self(), name, + MACH_PORT_RIGHT_SEND, &ns)); + } + if (type & MACH_PORT_TYPE_SEND_ONCE) { + (void)dispatch_assume_zero(mach_port_get_refs(mach_task_self(), name, + MACH_PORT_RIGHT_SEND_ONCE, &nso)); + } + if (type & MACH_PORT_TYPE_DEAD_NAME) { + (void)dispatch_assume_zero(mach_port_get_refs(mach_task_self(), name, + MACH_PORT_RIGHT_DEAD_NAME, &nd)); + } + if (type & (MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_SEND| + MACH_PORT_TYPE_SEND_ONCE)) { + (void)dispatch_assume_zero(mach_port_dnrequest_info(mach_task_self(), + name, &dnrsiz, &dnreqs)); + } + if (type & MACH_PORT_TYPE_RECEIVE) { + mach_port_status_t status = { .mps_pset = 0, }; + mach_msg_type_number_t cnt = MACH_PORT_RECEIVE_STATUS_COUNT; + (void)dispatch_assume_zero(mach_port_get_refs(mach_task_self(), name, + MACH_PORT_RIGHT_RECEIVE, &nr)); + (void)dispatch_assume_zero(mach_port_get_attributes(mach_task_self(), + name, MACH_PORT_RECEIVE_STATUS, (void*)&status, &cnt)); + _dispatch_log("machport[0x%08x] = { R(%03u) S(%03u) SO(%03u) D(%03u) " + "dnreqs(%03u) spreq(%s) nsreq(%s) pdreq(%s) srights(%s) " + "sorights(%03u) qlim(%03u) msgcount(%03u) mkscount(%03u) " + "seqno(%03u) }: %s", name, nr, ns, nso, nd, dnreqs, + type & MACH_PORT_TYPE_SPREQUEST ? "Y":"N", + status.mps_nsrequest ? "Y":"N", status.mps_pdrequest ? "Y":"N", + status.mps_srights ? "Y":"N", status.mps_sorights, + status.mps_qlimit, status.mps_msgcount, status.mps_mscount, + status.mps_seqno, str); + } else if (type & (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_SEND_ONCE| + MACH_PORT_TYPE_DEAD_NAME)) { + _dispatch_log("machport[0x%08x] = { R(%03u) S(%03u) SO(%03u) D(%03u) " + "dnreqs(%03u) spreq(%s) }: %s", name, nr, ns, nso, nd, dnreqs, + type & MACH_PORT_TYPE_SPREQUEST ? "Y":"N", str); + } else { + _dispatch_log("machport[0x%08x] = { type(0x%08x) }: %s", name, type, + str); + } +} + +#endif // HAVE_MACH + +#endif // DISPATCH_DEBUG diff --git a/src/source_internal.h b/src/source_internal.h index e7126db..a44eef7 100644 --- a/src/source_internal.h +++ b/src/source_internal.h @@ -1,20 +1,20 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 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@ */ @@ -32,6 +32,45 @@ #include // for HeaderDoc #endif +// NOTE: dispatch_source_mach_send_flags_t and dispatch_source_mach_recv_flags_t +// bit values must not overlap as they share the same kevent fflags ! + +/*! + * @enum dispatch_source_mach_send_flags_t + * + * @constant DISPATCH_MACH_SEND_DELETED + * Port-deleted notification. Disabled for source registration. + */ +enum { + DISPATCH_MACH_SEND_DELETED = 0x4, +}; +/*! + * @enum dispatch_source_mach_recv_flags_t + * + * @constant DISPATCH_MACH_RECV_MESSAGE + * Receive right has pending messages + * + * @constant DISPATCH_MACH_RECV_NO_SENDERS + * Receive right has no more senders. TODO + */ +enum { + DISPATCH_MACH_RECV_MESSAGE = 0x2, + DISPATCH_MACH_RECV_NO_SENDERS = 0x10, +}; + +enum { + DISPATCH_TIMER_WALL_CLOCK = 0x4, +}; + +#define DISPATCH_EVFILT_TIMER (-EVFILT_SYSCOUNT - 1) +#define DISPATCH_EVFILT_CUSTOM_ADD (-EVFILT_SYSCOUNT - 2) +#define DISPATCH_EVFILT_CUSTOM_OR (-EVFILT_SYSCOUNT - 3) +#define DISPATCH_EVFILT_SYSCOUNT ( EVFILT_SYSCOUNT + 3) + +#define DISPATCH_TIMER_INDEX_WALL 0 +#define DISPATCH_TIMER_INDEX_MACH 1 +#define DISPATCH_TIMER_INDEX_DISARM 2 + struct dispatch_source_vtable_s { DISPATCH_VTABLE_HEADER(dispatch_source_s); }; @@ -40,21 +79,56 @@ extern const struct dispatch_source_vtable_s _dispatch_source_kevent_vtable; struct dispatch_kevent_s { TAILQ_ENTRY(dispatch_kevent_s) dk_list; - TAILQ_HEAD(, dispatch_source_s) dk_sources; + TAILQ_HEAD(, dispatch_source_refs_s) dk_sources; struct kevent dk_kevent; }; typedef struct dispatch_kevent_s *dispatch_kevent_t; +struct dispatch_source_type_s { + struct kevent ke; + uint64_t mask; + void (*init)(dispatch_source_t ds, dispatch_source_type_t type, + uintptr_t handle, unsigned long mask, dispatch_queue_t q); +}; + struct dispatch_timer_source_s { uint64_t target; - uint64_t start; + uint64_t last_fire; uint64_t interval; uint64_t leeway; uint64_t flags; // dispatch_timer_flags_t + unsigned long missed; +}; + +// Source state which may contain references to the source object +// Separately allocated so that 'leaks' can see sources +struct dispatch_source_refs_s { + TAILQ_ENTRY(dispatch_source_refs_s) dr_list; + uintptr_t dr_source_wref; // "weak" backref to dispatch_source_t + dispatch_function_t ds_handler_func; + void *ds_handler_ctxt; + void *ds_cancel_handler; + void *ds_registration_handler; }; +typedef struct dispatch_source_refs_s *dispatch_source_refs_t; + +struct dispatch_timer_source_refs_s { + struct dispatch_source_refs_s _ds_refs; + struct dispatch_timer_source_s _ds_timer; +}; + +#define _dispatch_ptr2wref(ptr) (~(uintptr_t)(ptr)) +#define _dispatch_wref2ptr(ref) ((void*)~(ref)) +#define _dispatch_source_from_refs(dr) \ + ((dispatch_source_t)_dispatch_wref2ptr((dr)->dr_source_wref)) +#define ds_timer(dr) \ + (((struct dispatch_timer_source_refs_s *)(dr))->_ds_timer) + +// ds_atomic_flags bits #define DSF_CANCELED 1u // cancellation has been requested +#define DSF_ARMED 2u // source is armed struct dispatch_source_s { DISPATCH_STRUCT_HEADER(dispatch_source_s, dispatch_source_vtable_s); @@ -66,37 +140,26 @@ struct dispatch_source_s { struct { char dq_label[8]; dispatch_kevent_t ds_dkev; - - dispatch_source_handler_function_t ds_handler_func; - void *ds_handler_ctxt; - - void *ds_cancel_handler; - - unsigned int ds_is_level:1, - ds_is_adder:1, - ds_is_installed:1, - ds_needs_rearm:1, - ds_is_armed:1, - ds_is_legacy:1, - ds_cancel_is_block:1, - ds_handler_is_block:1; - + dispatch_source_refs_t ds_refs; unsigned int ds_atomic_flags; - + unsigned int + ds_is_level:1, + ds_is_adder:1, + ds_is_installed:1, + ds_needs_rearm:1, + ds_is_timer:1, + ds_cancel_is_block:1, + ds_handler_is_block:1, + ds_registration_is_block:1; unsigned long ds_data; unsigned long ds_pending_data; unsigned long ds_pending_data_mask; - - TAILQ_ENTRY(dispatch_source_s) ds_list; - unsigned long ds_ident_hack; - - struct dispatch_timer_source_s ds_timer; }; }; }; - -void _dispatch_source_legacy_xref_release(dispatch_source_t ds); +void _dispatch_source_xref_release(dispatch_source_t ds); +void _dispatch_mach_notify_source_init(void *context); #endif /* __DISPATCH_SOURCE_INTERNAL__ */ diff --git a/src/time.c b/src/time.c index 07506f2..4c0285a 100644 --- a/src/time.c +++ b/src/time.c @@ -1,26 +1,26 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 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 "internal.h" -uint64_t +uint64_t _dispatch_get_nanoseconds(void) { struct timeval now; @@ -31,77 +31,17 @@ _dispatch_get_nanoseconds(void) return now.tv_sec * NSEC_PER_SEC + now.tv_usec * NSEC_PER_USEC; } -#if defined(__i386__) || defined(__x86_64__) -// x86 currently implements mach time in nanoseconds; this is NOT likely to change -#define _dispatch_time_mach2nano(x) (x) -#define _dispatch_time_nano2mach(x) (x) -#else -static struct _dispatch_host_time_data_s { - mach_timebase_info_data_t tbi; - uint64_t safe_numer_math; - dispatch_once_t pred; -} _dispatch_host_time_data; - -static void -_dispatch_get_host_time_init(void *context __attribute__((unused))) -{ - dispatch_assume_zero(mach_timebase_info(&_dispatch_host_time_data.tbi)); - _dispatch_host_time_data.safe_numer_math = DISPATCH_TIME_FOREVER / _dispatch_host_time_data.tbi.numer; -} +#if !(defined(__i386__) || defined(__x86_64__) || !HAVE_MACH_ABSOLUTE_TIME) +DISPATCH_CACHELINE_ALIGN _dispatch_host_time_data_s _dispatch_host_time_data; -static uint64_t -_dispatch_time_mach2nano(uint64_t nsec) +void +_dispatch_get_host_time_init(void *context DISPATCH_UNUSED) { - struct _dispatch_host_time_data_s *const data = &_dispatch_host_time_data; - uint64_t small_tmp = nsec; -#ifdef __LP64__ - __uint128_t big_tmp = nsec; -#else - long double big_tmp = nsec; -#endif - - dispatch_once_f(&data->pred, NULL, _dispatch_get_host_time_init); - - if (slowpath(data->tbi.numer != data->tbi.denom)) { - if (nsec < data->safe_numer_math) { - small_tmp *= data->tbi.numer; - small_tmp /= data->tbi.denom; - } else { - big_tmp *= data->tbi.numer; - big_tmp /= data->tbi.denom; - small_tmp = big_tmp; - } - } - return small_tmp; -} - -static int64_t -_dispatch_time_nano2mach(int64_t nsec) -{ - struct _dispatch_host_time_data_s *const data = &_dispatch_host_time_data; -#ifdef __LP64__ - __int128_t big_tmp = nsec; -#else - long double big_tmp = nsec; -#endif - - dispatch_once_f(&data->pred, NULL, _dispatch_get_host_time_init); - - if (fastpath(data->tbi.numer == data->tbi.denom)) { - return nsec; - } - - // Multiply by the inverse to convert nsec to Mach absolute time - big_tmp *= data->tbi.denom; - big_tmp /= data->tbi.numer; - - if (big_tmp > INT64_MAX) { - return INT64_MAX; - } - if (big_tmp < INT64_MIN) { - return INT64_MIN; - } - return big_tmp; + mach_timebase_info_data_t tbi; + (void)dispatch_assume_zero(mach_timebase_info(&tbi)); + _dispatch_host_time_data.frac = tbi.numer; + _dispatch_host_time_data.frac /= tbi.denom; + _dispatch_host_time_data.ratio_1_to_1 = (tbi.numer == tbi.denom); } #endif @@ -115,29 +55,29 @@ dispatch_time(dispatch_time_t inval, int64_t delta) // wall clock if (delta >= 0) { if ((int64_t)(inval -= delta) >= 0) { - return DISPATCH_TIME_FOREVER; // overflow + return DISPATCH_TIME_FOREVER; // overflow } return inval; } if ((int64_t)(inval -= delta) >= -1) { // -1 is special == DISPATCH_TIME_FOREVER == forever - return -2; // underflow + return -2; // underflow } return inval; } // mach clock delta = _dispatch_time_nano2mach(delta); - if (inval == 0) { - inval = mach_absolute_time(); + if (inval == 0) { + inval = _dispatch_absolute_time(); } if (delta >= 0) { if ((int64_t)(inval += delta) <= 0) { - return DISPATCH_TIME_FOREVER; // overflow + return DISPATCH_TIME_FOREVER; // overflow } return inval; } if ((int64_t)(inval += delta) < 1) { - return 1; // underflow + return 1; // underflow } return inval; } @@ -146,7 +86,7 @@ dispatch_time_t dispatch_walltime(const struct timespec *inval, int64_t delta) { int64_t nsec; - + if (inval) { nsec = inval->tv_sec * 1000000000ull + inval->tv_nsec; } else { @@ -178,6 +118,6 @@ _dispatch_timeout(dispatch_time_t when) now = _dispatch_get_nanoseconds(); return now >= when ? 0 : when - now; } - now = mach_absolute_time(); + now = _dispatch_absolute_time(); return now >= when ? 0 : _dispatch_time_mach2nano(when - now); } diff --git a/src/trace.h b/src/trace.h new file mode 100644 index 0000000..0d9bc3d --- /dev/null +++ b/src/trace.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2010-2011 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_TRACE__ +#define __DISPATCH_TRACE__ + +#if DISPATCH_USE_DTRACE + +#include "provider.h" + +#define _dispatch_trace_callout(_c, _f, _dcc) do { \ + if (slowpath(DISPATCH_CALLOUT_ENTRY_ENABLED()) || \ + slowpath(DISPATCH_CALLOUT_RETURN_ENABLED())) { \ + dispatch_queue_t _dq = _dispatch_queue_get_current(); \ + char *_label = _dq ? _dq->dq_label : ""; \ + dispatch_function_t _func = (dispatch_function_t)(_f); \ + void *_ctxt = (_c); \ + DISPATCH_CALLOUT_ENTRY(_dq, _label, _func, _ctxt); \ + _dcc; \ + DISPATCH_CALLOUT_RETURN(_dq, _label, _func, _ctxt); \ + return; \ + } \ + return _dcc; \ + } while (0) + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_trace_client_callout(void *ctxt, dispatch_function_t f) +{ + _dispatch_trace_callout(ctxt, f == _dispatch_call_block_and_release && + ctxt ? ((struct Block_basic *)ctxt)->Block_invoke : f, + _dispatch_client_callout(ctxt, f)); +} + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_trace_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) +{ + _dispatch_trace_callout(ctxt, f, _dispatch_client_callout2(ctxt, i, f)); +} + +#ifdef __BLOCKS__ +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_trace_client_callout_block(dispatch_block_t b) +{ + struct Block_basic *bb = (void*)b; + _dispatch_trace_callout(b, bb->Block_invoke, + _dispatch_client_callout(b, (dispatch_function_t)bb->Block_invoke)); +} +#endif + +#define _dispatch_client_callout _dispatch_trace_client_callout +#define _dispatch_client_callout2 _dispatch_trace_client_callout2 +#define _dispatch_client_callout_block _dispatch_trace_client_callout_block + +#define _dispatch_trace_continuation(_q, _o, _t) do { \ + dispatch_queue_t _dq = (_q); \ + char *_label = _dq ? _dq->dq_label : ""; \ + struct dispatch_object_s *_do = (_o); \ + char *_kind; \ + dispatch_function_t _func; \ + void *_ctxt; \ + if (DISPATCH_OBJ_IS_VTABLE(_do)) { \ + _ctxt = _do->do_ctxt; \ + _kind = (char*)dx_kind(_do); \ + if (dx_type(_do) == DISPATCH_SOURCE_KEVENT_TYPE && \ + (_dq) != &_dispatch_mgr_q) { \ + _func = ((dispatch_source_t)_do)->ds_refs->ds_handler_func; \ + } else { \ + _func = (dispatch_function_t)_dispatch_queue_invoke; \ + } \ + } else { \ + struct dispatch_continuation_s *_dc = (void*)(_do); \ + _ctxt = _dc->dc_ctxt; \ + if ((long)_dc->do_vtable & DISPATCH_OBJ_SYNC_SLOW_BIT) { \ + _kind = "semaphore"; \ + _func = (dispatch_function_t)dispatch_semaphore_signal; \ + } else if (_dc->dc_func == _dispatch_call_block_and_release) { \ + _kind = "block"; \ + _func = ((struct Block_basic *)_dc->dc_ctxt)->Block_invoke;\ + } else { \ + _kind = "function"; \ + _func = _dc->dc_func; \ + } \ + } \ + _t(_dq, _label, _do, _kind, _func, _ctxt); \ + } while (0) + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_trace_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, + dispatch_object_t _tail) +{ + if (slowpath(DISPATCH_QUEUE_PUSH_ENABLED())) { + struct dispatch_object_s *dou = _head._do; + do { + _dispatch_trace_continuation(dq, dou, DISPATCH_QUEUE_PUSH); + } while (dou != _tail._do && (dou = dou->do_next)); + } + _dispatch_queue_push_list(dq, _head, _tail); +} + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_queue_push_notrace(dispatch_queue_t dq, dispatch_object_t dou) +{ + _dispatch_queue_push_list(dq, dou, dou); +} + +#define _dispatch_queue_push_list _dispatch_trace_queue_push_list + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_trace_continuation_pop(dispatch_queue_t dq, + dispatch_object_t dou) +{ + if (slowpath(DISPATCH_QUEUE_POP_ENABLED())) { + _dispatch_trace_continuation(dq, dou._do, DISPATCH_QUEUE_POP); + } +} +#else + +#define _dispatch_queue_push_notrace _dispatch_queue_push +#define _dispatch_trace_continuation_pop(dq, dou) + +#endif // DISPATCH_USE_DTRACE + +#endif // __DISPATCH_TRACE__ diff --git a/testing/Makefile b/testing/Makefile deleted file mode 100644 index 1f2ec73..0000000 --- a/testing/Makefile +++ /dev/null @@ -1,127 +0,0 @@ -# No workie: dispatch_sema -TESTS= dispatch_apply \ - dispatch_api \ - dispatch_c99 \ - dispatch_cffd \ - dispatch_deadname \ - dispatch_debug \ - queue_finalizer \ - dispatch_group \ - dispatch_overcommit \ - dispatch_pingpong \ - dispatch_plusplus \ - dispatch_priority \ - dispatch_priority2 \ - dispatch_proc \ - dispatch_read \ - dispatch_read2 \ - dispatch_after \ - dispatch_timer \ - dispatch_sema \ - dispatch_suspend_timer \ - dispatch_timer_bit31 \ - dispatch_timer_bit63 \ - dispatch_timer_oneshot \ - dispatch_timer_set_time \ - dispatch_starfish \ - dispatch_cascade \ - dispatch_drift \ - dispatch_readsync \ - nsoperation - -all: harness summarize bench $(TESTS) - @lipo -remove x86_64 -output dispatch_timer_bit31 dispatch_timer_bit31 2>/dev/null || true - -logs: $(addsuffix .testlog, $(TESTS)) -debuglogs: $(addsuffix .debuglog, $(TESTS)) - -testbots: - $(MAKE) test - -test: clean-logs - $(MAKE) _test - -_test: all logs debuglogs - @cat *.testlog *.debuglog - @cat *.testlog *.debuglog | ./summarize - -# Override ARCHS and SDKROOT to cross-build test suite - -SRCS = dispatch_test.c -OBJS = $(SRCS:%.c=%.o) -ARCHS=i386 x86_64 ppc -CFLAGS = -Werror -Wall -Wextra -Wshadow -mdynamic-no-pic -Os -g $(patsubst %, -arch %,$(ARCHS)) -CPPFLAGS = $(CFLAGS) -LDFLAGS = $(patsubst %, -arch %,$(ARCHS)) -LDLIBS = -lstdc++ - -ifneq ($(SDKROOT),) -CFLAGS += -isysroot $(SDKROOT) -LDFLAGS += -isysroot $(SDKROOT) -CC = xcrun -sdk $(SDKROOT) gcc -endif - -harness: harness.o $(OBJS) -summarize: summarize.o -bench: bench.o func.o - $(CC) $(LDFLAGS) -framework Foundation $(LDLIBS) -o $@ $^ - -bench.o: bench.mm - $(CC) -x objective-c++ $(CFLAGS) -c $^ -o $@ -func.o: func.c - $(CC) -x c++ $(CFLAGS) -c $^ -o $@ - -dispatch_apply: dispatch_apply.o $(OBJS) -dispatch_api: dispatch_api.o $(OBJS) -dispatch_c99: dispatch_c99.o $(OBJS) -dispatch_cffd: dispatch_cffd.o $(OBJS) - $(CC) $(LDFLAGS) -framework CoreFoundation -o $@ $^ -dispatch_deadname: dispatch_deadname.o $(OBJS) -dispatch_debug: dispatch_debug.o $(OBJS) -dispatch_group: dispatch_group.o $(OBJS) -dispatch_overcommit: dispatch_overcommit.o $(OBJS) -dispatch_pingpong: dispatch_pingpong.o $(OBJS) -dispatch_plusplus: dispatch_plusplus.o $(OBJS) -dispatch_priority: dispatch_priority.o $(OBJS) -dispatch_priority2: dispatch_priority2.o $(OBJS) -dispatch_proc: dispatch_proc.o $(OBJS) -queue_finalizer: queue_finalizer.o $(OBJS) -dispatch_read: dispatch_read.o $(OBJS) -dispatch_read2: dispatch_read2.o $(OBJS) -dispatch_after: dispatch_after.o $(OBJS) -dispatch_timer: dispatch_timer.o $(OBJS) -dispatch_suspend_timer: dispatch_suspend_timer.o $(OBJS) -dispatch_sema: dispatch_sema.o $(OBJS) -dispatch_timer_bit31: dispatch_timer_bit31.o $(OBJS) -dispatch_timer_bit63: dispatch_timer_bit63.o $(OBJS) -dispatch_timer_oneshot: dispatch_timer_oneshot.o $(OBJS) -dispatch_timer_set_time: dispatch_timer_set_time.o $(OBJS) -dispatch_drift: dispatch_drift.o $(OBJS) -dispatch_starfish: dispatch_starfish.o $(OBJS) -dispatch_cascade: dispatch_cascade.o $(OBJS) -dispatch_timer_bit31: dispatch_timer_bit31.o $(OBJS) -dispatch_readsync: dispatch_readsync.o $(OBJS) -ENVIRON_nsoperation = NOLEAKS=1 -nsoperation: nsoperation.o $(OBJS) - $(CC) $(LDFLAGS) -framework Foundation -o $@ $^ - -dispatch_api.o: dispatch_api.c - $(CC) -c $(CFLAGS) -include $(SDKROOT)/usr/include/dispatch/dispatch.h -pendantic -o $@ $^ - -dispatch_c99.o: dispatch_c99.c - $(CC) -c $(CFLAGS) -std=c99 -pedantic -o $@ $^ - -dispatch_priority2.o: dispatch_priority.c - $(CC) -c $(CFLAGS) -DUSE_SET_TARGET_QUEUE=1 -o $@ $^ - -$(addsuffix .testlog, $(TESTS)): harness $(TESTS) - $(ENVIRON_$(basename $@)) ./harness ./$(basename $@) > $@ - -$(addsuffix .debuglog, $(TESTS)): harness $(TESTS) - $(ENVIRON_$(basename $@)) DYLD_IMAGE_SUFFIX=_debug ./harness ./$(basename $@) > $@ - -clean-logs: - rm -f *.testlog *.debuglog *.leakslog - -clean: clean-logs - rm -f *.o *.dSYM bench harness summarize $(TESTS) diff --git a/testing/apply_strtoull.c b/testing/apply_strtoull.c deleted file mode 100644 index 82671a0..0000000 --- a/testing/apply_strtoull.c +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 \n", argv[0]); - exit(EXIT_FAILURE); - } - - fd = open(argv[1], O_RDONLY); - assert(fd != -1); - - r = fstat(fd, &sb); - assert(r != -1); - - if (sb.st_len == 0) { - fprintf(stderr, "The file is zero length.\n"); - exit(EXIT_FAILURE); - } - - map = mmap(NULL, sb.st_len, PROT_READ, MAP_FILE, fd, 0); - assert(map != MAP_FAILED); - - numbers = malloc(sb.st_len * sizeof(void *)); /* more than enough */ - assert(numbers); - - /* XXX finish me */ - numbers[0] = map; - j = 1; - for (i = 0; i < sb.st_len; i++) { - if (map[i] == '\n') { - numbers[j] = map + i; - j++; - continue; - } - i++; - } - i - ; i < sb.st_len; i++) { - if (map[i] - } - - dispatch_apply(b, cnt); - - exit(EXIT_SUCCESS); -} diff --git a/testing/bench.mm b/testing/bench.mm deleted file mode 100644 index 51809b2..0000000 --- a/testing/bench.mm +++ /dev/null @@ -1,488 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __BLOCKS__ -#include -#endif -#include -#include - -extern "C" { -__private_extern__ void func(void); -#ifdef __BLOCKS__ -__private_extern__ void (^block)(void); -#endif -static void backflip(void *ctxt); -static void backflip_done(void); -} - -@interface BasicObject : NSObject -{ -} -- (void) method; -@end - -@implementation BasicObject -- (void) method -{ -} -@end - -class BasicClass { -public: - virtual void virtfunc(void) { - }; -}; - -static void * -force_a_thread(void *arg) -{ - pause(); - abort(); - return arg; -} - -static volatile int32_t global; - -static const size_t cnt = 10000000; -static const size_t cnt2 = 100000; - -static uint64_t bfs; -static long double loop_cost; -static long double cycles_per_nanosecond; -static mach_timebase_info_data_t tbi; - -//static void func2(void *, dispatch_item_t di); - -static void __attribute__((noinline)) -print_result(uint64_t s, const char *str) -{ - uint64_t d, e = mach_absolute_time(); - long double dd; - - d = e - s; - - if (tbi.numer != tbi.denom) { - d *= tbi.numer; - d /= tbi.denom; - } - - dd = (typeof(dd))d / (typeof(dd))cnt; - - dd -= loop_cost; - - if (loop_cost == 0.0) { - loop_cost = dd; - } - - dd *= cycles_per_nanosecond; - - printf("%-45s%15.3Lf cycles\n", str, dd); -} - -static void __attribute__((noinline)) -print_result2(uint64_t s, const char *str) -{ - uint64_t d, e = mach_absolute_time(); - long double dd; - - d = e - s; - - if (tbi.numer != tbi.denom) { - d *= tbi.numer; - d /= tbi.denom; - } - - dd = (typeof(dd))d / (typeof(dd))cnt2; - - dd -= loop_cost; - dd *= cycles_per_nanosecond; - - printf("%-45s%15.3Lf cycles\n", str, dd); -} - -#if defined(__i386__) || defined(__x86_64__) -static inline uint64_t -rdtsc(void) -{ - uint32_t lo, hi; - - asm volatile("rdtsc" : "=a" (lo), "=d" (hi)); - - return (uint64_t)hi << 32 | lo; -} -#endif - -static struct fml { - struct fml *fml_next; -} *fixed_malloc_lifo_head; - -struct fml *fixed_malloc_lifo(void);// __attribute__((noinline)); -void fixed_free_lifo(struct fml *fml);// __attribute__((noinline)); - -struct fml * -fixed_malloc_lifo(void) -{ - struct fml *fml_r = fixed_malloc_lifo_head; - - if (fml_r) { - fixed_malloc_lifo_head = fml_r->fml_next; - return fml_r; - } else { - return (struct fml *)malloc(32); - } -} - -void -fixed_free_lifo(struct fml *fml) -{ - fml->fml_next = fixed_malloc_lifo_head; - fixed_malloc_lifo_head = fml; -} - -int -main(void) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - pthread_mutex_t plock = PTHREAD_MUTEX_INITIALIZER; - OSSpinLock slock = OS_SPINLOCK_INIT; - BasicObject *bo; - BasicClass *bc; - pthread_t pthr_pause; - dispatch_queue_t q, mq; - kern_return_t kr; - semaphore_t sem; - uint64_t freq; - uint64_t s; - size_t freq_len = sizeof(freq); - size_t bf_cnt = cnt; - unsigned i; - int r; - - r = sysctlbyname("hw.cpufrequency", &freq, &freq_len, NULL, 0); - assert(r != -1); - assert(freq_len == sizeof(freq)); - - cycles_per_nanosecond = (long double)freq / (long double)NSEC_PER_SEC; - - assert(pool); - - /* Malloc has different logic for threaded apps. */ - r = pthread_create(&pthr_pause, NULL, force_a_thread, NULL); - assert(r == 0); - - kr = mach_timebase_info(&tbi); - assert(kr == 0); -#if defined(__i386__) || defined(__x86_64__) - assert(tbi.numer == tbi.denom); /* This will fail on PowerPC. */ -#endif - - bo = [[BasicObject alloc] init]; - assert(bo); - - bc = new BasicClass(); - assert(bc); - - q = dispatch_queue_create("com.apple.bench-dispatch", NULL); - assert(q); - - mq = dispatch_get_main_queue(); - assert(mq); - - printf("%-45s%15Lf\n\n", "Cycles per nanosecond:", cycles_per_nanosecond); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - asm volatile(""); - } - print_result(s, "Empty loop:"); - - printf("\nLoop cost subtracted from the following:\n\n"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - mach_absolute_time(); - } - print_result(s, "mach_absolute_time():"); - -#if defined(__i386__) || defined(__x86_64__) - s = mach_absolute_time(); - for (i = cnt; i; i--) { - rdtsc(); - } - print_result(s, "rdtsc():"); -#endif - - s = mach_absolute_time(); - for (i = cnt2; i; i--) { - pthread_t pthr; - void *pr; - - r = pthread_create(&pthr, NULL, (void *(*)(void *))func, NULL); - assert(r == 0); - r = pthread_join(pthr, &pr); - assert(r == 0); - } - print_result2(s, "pthread create+join:"); - - s = mach_absolute_time(); - for (i = cnt2; i; i--) { - kr = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0); - assert(kr == 0); - kr = semaphore_destroy(mach_task_self(), sem); - assert(kr == 0); - } - print_result2(s, "Mach semaphore create/destroy:"); - - kr = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0); - assert(kr == 0); - s = mach_absolute_time(); - for (i = cnt2; i; i--) { - kr = semaphore_signal(sem); - assert(kr == 0); - } - print_result2(s, "Mach semaphore signal:"); - kr = semaphore_destroy(mach_task_self(), sem); - assert(kr == 0); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - pthread_yield_np(); - } - print_result(s, "pthread_yield_np():"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - free(malloc(32)); - } - print_result(s, "free(malloc(32)):"); - - s = mach_absolute_time(); - for (i = cnt / 2; i; i--) { - void *m1 = malloc(32); - void *m2 = malloc(32); - free(m1); - free(m2); - } - print_result(s, "Avoiding the MRU cache of free(malloc(32)):"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - fixed_free_lifo(fixed_malloc_lifo()); - } - print_result(s, "per-thread/fixed free(malloc(32)):"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - assert(strtoull("18446744073709551615", NULL, 0) == ~0ull); - } - print_result(s, "strtoull(\"18446744073709551615\") == ~0ull:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - func(); - } - print_result(s, "Empty function call:"); - -#ifdef __BLOCKS__ - s = mach_absolute_time(); - for (i = cnt; i; i--) { - block(); - } - print_result(s, "Empty block call:"); -#endif - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - [bo method]; - } - print_result(s, "Empty ObjC call:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - bc->virtfunc(); - } - print_result(s, "Empty C++ virtual call:"); - - s = mach_absolute_time(); - for (i = cnt2; i; i--) { - [bo description]; - } - print_result2(s, "\"description\" ObjC call:"); - - [pool release]; - - pool = NULL; - -#if defined(__i386__) || defined(__x86_64__) - s = mach_absolute_time(); - for (i = cnt; i; i--) { - asm("nop"); - } - print_result(s, "raw 'nop':"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - asm("pause"); - } - print_result(s, "raw 'pause':"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - asm("mfence"); - } - print_result(s, "Atomic mfence:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - asm("lfence"); - } - print_result(s, "Atomic lfence:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - asm("sfence"); - } - print_result(s, "Atomic sfence:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - uint64_t sidt_rval; - asm("sidt %0" : "=m" (sidt_rval)); - } - print_result(s, "'sidt' instruction:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - int prev; - asm volatile("cmpxchg %1,%2" : "=a" (prev) : "r" (0l), "m" (global), "0" (1l)); - } - print_result(s, "'cmpxchg' without the 'lock' prefix:"); -#endif - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - __sync_lock_test_and_set(&global, 0); - } - print_result(s, "Atomic xchg:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - __sync_val_compare_and_swap(&global, 1, 0); - } - print_result(s, "Atomic cmpxchg:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - __sync_fetch_and_add(&global, 1); - } - print_result(s, "Atomic increment:"); - - global = 0; - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - OSAtomicIncrement32Barrier(&global); - } - print_result(s, "OSAtomic increment:"); - - global = 0; - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - while (!__sync_bool_compare_and_swap(&global, 0, 1)) { - do { -#if defined(__i386__) || defined(__x86_64__) - asm("pause"); -#endif - } while (global); - } - global = 0; - } - print_result(s, "Inlined spin lock/unlock:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - OSSpinLockLock(&slock); - OSSpinLockUnlock(&slock); - } - print_result(s, "OS spin lock/unlock:"); - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - r = pthread_mutex_lock(&plock); - assert(r == 0); - r = pthread_mutex_unlock(&plock); - assert(r == 0); - } - print_result(s, "pthread lock/unlock:"); - -#ifdef __BLOCKS__ - s = mach_absolute_time(); - for (i = cnt; i; i--) { - dispatch_sync(q, ^{ }); - } - print_result(s, "dispatch_sync:"); -#endif - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - dispatch_sync_f(q, NULL, (void (*)(void *))func); - } - print_result(s, "dispatch_sync_f:"); - -#ifdef __BLOCKS__ - s = mach_absolute_time(); - for (i = cnt; i; i--) { - dispatch_barrier_sync(q, ^{ }); - } - print_result(s, "dispatch_barrier_sync:"); -#endif - - s = mach_absolute_time(); - for (i = cnt; i; i--) { - dispatch_barrier_sync_f(q, NULL, (void (*)(void *))func); - } - print_result(s, "dispatch_barrier_sync_f:"); - - s = mach_absolute_time(); - dispatch_apply_f(cnt, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL, (void (*)(void *, size_t))func); - s += loop_cost; /* cancel out the implicit subtraction done by the next line */ - print_result(s, "dispatch_apply_f():"); - - // we do a "double backflip" to hit the fast-path of the enqueue/dequeue logic - bfs = mach_absolute_time(); - dispatch_async_f(dispatch_get_main_queue(), &bf_cnt, backflip); - dispatch_async_f(dispatch_get_main_queue(), &bf_cnt, backflip); - - dispatch_main(); -} - -__attribute__((noinline)) -void -backflip_done(void) -{ - print_result(bfs, "dispatch_async_f():"); - exit(EXIT_SUCCESS); -} - -void -backflip(void *ctxt) -{ - size_t *bf_cnt = (size_t *)ctxt; - if (--(*bf_cnt)) { - return dispatch_async_f(dispatch_get_main_queue(), ctxt, backflip); - } - backflip_done(); -} diff --git a/testing/conc.c b/testing/conc.c deleted file mode 100644 index 40766e2..0000000 --- a/testing/conc.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int -main(void) -{ - dispatch_block_t wb = ^(dispatch_item_t di) { printf("\t\t%p\tstart\n", pthread_self()); sleep(3); }; - dispatch_block_t cb = ^(dispatch_item_t di) { printf("\t\t%p\tdone\n", pthread_self()); }; - dispatch_queue_t dq; - bool r; - int i; - - dq = dispatch_queue_new("conc", DISPATCH_QUEUE_CONCURRENT, NULL, NULL, NULL); - assert(dq); - - for (i = 0; i < 10; i++) { - r = dispatch_call(dq, wb, cb, NULL, NULL); - assert(r); - } - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_after.c b/testing/dispatch_after.c deleted file mode 100644 index 7bb174a..0000000 --- a/testing/dispatch_after.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" -#include - -void done(void *arg __unused) { - sleep(1); - test_stop(); -} - -int -main(void) -{ - __block dispatch_time_t time_a_min, time_a, time_a_max; - __block dispatch_time_t time_b_min, time_b, time_b_max; - __block dispatch_time_t time_c_min, time_c, time_c_max; - - - test_start("Dispatch After"); - - dispatch_async(dispatch_get_main_queue(), ^{ - time_a_min = dispatch_time(0, 5.5*NSEC_PER_SEC); - time_a = dispatch_time(0, 6*NSEC_PER_SEC); - time_a_max = dispatch_time(0, 6.5*NSEC_PER_SEC); - dispatch_after(time_a, dispatch_get_current_queue(), ^{ - dispatch_time_t now_a = dispatch_time(0, 0); - test_long_less_than("can't finish faster than 5.5s", 0, now_a - time_a_min); - test_long_less_than("must finish faster than 6.5s", 0, time_a_max - now_a); - - time_b_min = dispatch_time(0, 1.5*NSEC_PER_SEC); - time_b = dispatch_time(0, 2*NSEC_PER_SEC); - time_b_max = dispatch_time(0, 2.5*NSEC_PER_SEC); - dispatch_after(time_b, dispatch_get_current_queue(), ^{ - dispatch_time_t now_b = dispatch_time(0, 0); - test_long_less_than("can't finish faster than 1.5s", 0, now_b - time_b_min); - test_long_less_than("must finish faster than 2.5s", 0, time_b_max - now_b); - - time_c_min = dispatch_time(0, 0*NSEC_PER_SEC); - time_c = dispatch_time(0, 0*NSEC_PER_SEC); - time_c_max = dispatch_time(0, .5*NSEC_PER_SEC); - dispatch_after(time_c, dispatch_get_current_queue(), ^{ - dispatch_time_t now_c = dispatch_time(0, 0); - test_long_less_than("can't finish faster than 0s", 0, now_c - time_c_min); - test_long_less_than("must finish faster than .5s", 0, time_c_max - now_c); - - dispatch_async_f(dispatch_get_current_queue(), NULL, done); - }); - }); - }); - }); - - dispatch_main(); - return 0; -} diff --git a/testing/dispatch_api.c b/testing/dispatch_api.c deleted file mode 100644 index e1fb916..0000000 --- a/testing/dispatch_api.c +++ /dev/null @@ -1,20 +0,0 @@ -#include - -#include "dispatch_test.h" - -void -work(void *context __attribute__((unused))) -{ - test_stop(); - exit(0); -} - -int main(void) { - test_start("Dispatch (Public) API"); - dispatch_queue_t q = dispatch_get_main_queue(); - test_ptr_notnull("dispatch_get_main_queue", q); - - dispatch_async_f(dispatch_get_main_queue(), NULL, work); - dispatch_main(); - return 0; -} diff --git a/testing/dispatch_apply.c b/testing/dispatch_apply.c deleted file mode 100644 index 60e075f..0000000 --- a/testing/dispatch_apply.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -int -main(void) -{ - test_start("Dispatch Apply"); - - volatile __block int32_t count = 0; - const int32_t final = 32; - - dispatch_queue_t queue = dispatch_get_concurrent_queue(0); - test_ptr_notnull("dispatch_get_concurrent_queue", queue); - - dispatch_apply(final, queue, ^(size_t i __attribute__((unused))) { - OSAtomicIncrement32(&count); - }); - - test_long("count", count, final); - test_stop(); - - return 0; -} diff --git a/testing/dispatch_c99.c b/testing/dispatch_c99.c deleted file mode 100644 index 2205c9e..0000000 --- a/testing/dispatch_c99.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -#include "dispatch_test.h" - -void -work(void *context __attribute__((unused))) -{ - test_stop(); - exit(0); -} - -int main(void) { - test_start("Dispatch C99"); - dispatch_queue_t q = dispatch_get_main_queue(); - test_ptr_notnull("dispatch_get_main_queue", q); - - dispatch_async_f(dispatch_get_main_queue(), NULL, work); - dispatch_main(); - return 0; -} diff --git a/testing/dispatch_cascade.c b/testing/dispatch_cascade.c deleted file mode 100644 index 7e09e41..0000000 --- a/testing/dispatch_cascade.c +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include -#include - -#include "dispatch_test.h" - -int done = 0; - -#define QUEUES 80 -dispatch_queue_t queues[QUEUES]; - - -#define BLOCKS 10000 -union { - size_t index; - char padding[64]; -} indices[BLOCKS]; - -size_t iterations = QUEUES * BLOCKS * 0.25; - -void -histogram(void) { - size_t counts[QUEUES] = {}; - size_t maxcount = 0; - - size_t q; - for (q = 0; q < QUEUES; ++q) { - size_t i; - for (i = 0; i < BLOCKS; ++i) { - if (indices[i].index == q) { - ++counts[q]; - } - } - } - - for (q = 0; q < QUEUES; ++q) { - if (counts[q] > maxcount) { - maxcount = counts[q]; - } - } - - printf("maxcount = %ld\n", maxcount); - - size_t x,y; - for (y = 20; y > 0; --y) { - for (x = 0; x < QUEUES; ++x) { - double fraction = (double)counts[x] / (double)maxcount; - double value = fraction * (double)20; - printf("%s", (value > y) ? "*" : " "); - } - printf("\n"); - } -} - -void -cascade(void* context) { - size_t idx, *idxptr = context; - - if (done) return; - - idx = *idxptr + 1; - - if (idx < QUEUES) { - *idxptr = idx; - dispatch_async_f(queues[idx], context, cascade); - } - - if (__sync_sub_and_fetch(&iterations, 1) == 0) { - done = 1; - histogram(); - test_stop(); - exit(0); - } -} - -int -main(int argc __attribute__((unused)), char* argv[] __attribute__((unused))) { - int i; - - test_start("Dispatch Cascade"); - - for (i = 0; i < QUEUES; ++i) { - queues[i] = dispatch_queue_create(NULL, NULL); - } - - for (i = 0; i < BLOCKS; ++i) { - cascade(&indices[i].index); - } - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_cffd.c b/testing/dispatch_cffd.c deleted file mode 100644 index 13e4ac3..0000000 --- a/testing/dispatch_cffd.c +++ /dev/null @@ -1,139 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "dispatch_test.h" - -int debug = 0; - -#define DEBUG(...) do { \ - if (debug) fprintf(stderr, __VA_ARGS__); \ - } while(0); - -#define assert_errno(str, expr) do { \ - if (!(expr)) { \ - fprintf(stderr, "%s: %s\n", (str), strerror(errno)); \ - exit(1); \ - } } while(0); - -int -init_kqueue(void) -{ - int kq; - int res; - struct kevent ke; - static struct timespec t0; - - kq = kqueue(); - assert_errno("kqueue", kq >= 0); - - EV_SET(&ke, 1, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 1, 0); - - res = kevent(kq, &ke, 1, NULL, 0, &t0); - assert_errno("kevent", res == 0); - - return kq; -} - -int -read_kevent(int kq) -{ - int res; - struct kevent ke; - //static struct timespec t0; - - res = kevent(kq, NULL, 0, &ke, 1, NULL); - assert_errno("kevent", res >= 0); - - fprintf(stdout, "kevent.data = %ld\n", ke.data); - - return (res < 0); -} - - -static void -cffd_callback(CFFileDescriptorRef cffd, - CFOptionFlags callBackTypes __attribute__((unused)), - void *info __attribute__((unused))) -{ - int kq; - - kq = CFFileDescriptorGetNativeDescriptor(cffd); - if (read_kevent(kq) == 0) { - // ... - } - - CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack); -} - -void -timer() -{ - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - assert(ds); - dispatch_source_set_timer(ds, dispatch_time(0, 1*NSEC_PER_SEC), NSEC_PER_SEC, 0); - dispatch_source_set_event_handler(ds, ^{ - printf("ping\n"); - }); - dispatch_resume(ds); -} - -void -hangup() -{ - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, dispatch_get_main_queue()); - assert(ds); - dispatch_source_set_event_handler(ds, ^{ - printf("hangup\n"); - }); - dispatch_resume(ds); -} - -int -main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) -{ - int kq; - CFFileDescriptorRef cffd; - CFRunLoopSourceRef rls; - CFFileDescriptorContext ctx; - - test_start("CFFileDescriptor"); - - signal(SIGHUP, SIG_IGN); - - kq = init_kqueue(); - - memset(&ctx, 0, sizeof(CFFileDescriptorContext)); - cffd = CFFileDescriptorCreate(NULL, kq, 1, cffd_callback, &ctx); - assert(cffd); - - rls = CFFileDescriptorCreateRunLoopSource(NULL, cffd, 0); - assert(rls); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack); - -// timer(); -// hangup(); - - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10.0, false); - - test_stop(); - - return 0; -} - diff --git a/testing/dispatch_deadname.c b/testing/dispatch_deadname.c deleted file mode 100644 index 1dfc1c7..0000000 --- a/testing/dispatch_deadname.c +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -int -main(void) -{ - test_start("Dispatch dead-name notification"); - - dispatch_async(dispatch_get_concurrent_queue(0), ^{ - mach_port_t mp = pthread_mach_thread_np(pthread_self()); - dispatch_source_t ds0; - kern_return_t kr; - - assert(mp); - - kr = mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1); - - assert(kr == 0); - - ds0 = dispatch_source_machport_create(mp, DISPATCH_MACHPORT_DEAD, NULL, dispatch_get_main_queue(), - ^(dispatch_event_t de) { - dispatch_release(dispatch_event_get_source(de)); - test_stop(); - exit(EXIT_SUCCESS); - }); - - test_ptr_notnull("dispatch_source_machport_create", ds0); - - // give the mgr queue time to start, otherwise the mgr queue will run - // on this thread, thus defeating the test which assumes that this - // thread will die. - sleep(1); - }); - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_debug.c b/testing/dispatch_debug.c deleted file mode 100644 index 8f55106..0000000 --- a/testing/dispatch_debug.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include - -#include - -#include "dispatch_test.h" - -int main(void) -{ - test_start("Dispatch Debug"); - - dispatch_queue_t main_q = dispatch_get_main_queue(); - dispatch_debug(main_q, "dispatch_queue_t"); - - dispatch_queue_t default_q = dispatch_get_concurrent_queue(0); - dispatch_debug(default_q, "dispatch_queue_t"); - - dispatch_source_attr_t attr = dispatch_source_attr_create(); - dispatch_debug(attr, "dispatch_source_attr_t"); - - dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, - 1000000000ull, 0, attr, main_q, ^(dispatch_event_t ev __attribute__((unused))) {}); - dispatch_debug(s, "dispatch_source_t"); - - dispatch_group_t g = dispatch_group_create(); - dispatch_debug(g, "dispatch_group_t"); - - return 0; -} diff --git a/testing/dispatch_drift.c b/testing/dispatch_drift.c deleted file mode 100644 index 902d278..0000000 --- a/testing/dispatch_drift.c +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "dispatch_test.h" - -int -main(int argc __attribute__((unused)), char* argv[] __attribute__((unused))) -{ - __block uint32_t count = 0; - __block uint64_t first_time_m = 0ULL; - __block double first_time_d; - __block double last_jitter = 0; - // 10 times a second - uint64_t interval = 1000000000 / 10; - double interval_d = interval / 1000000000.0; - // for 25 seconds - unsigned int target = 25 / interval_d; - - test_start("Timer drift test"); - - dispatch_source_t t = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, interval, 0, NULL, dispatch_get_main_queue(), - ^(dispatch_event_t event __attribute__((unused))) { - struct timeval now_tv; - static double first = 0; - gettimeofday(&now_tv, NULL); - double now = now_tv.tv_sec + now_tv.tv_usec / 1000000.0; - - if (count == 0) { - // Because this is taken at 1st timer fire, - // later jitter values may be negitave. - // This doesn't effect the drift calculation. - first = now; - } - double goal = first + interval_d * count; - double jitter = goal - now; - double drift = jitter - last_jitter; - - printf("%4d: jitter %f, drift %f\n", count, jitter, drift); -#if 0 - test_double_less_than("drift", now_d - expected_fire_time_d, .001); -#endif - - if (target <= ++count) { - if (drift < 0) { - drift = -drift; - } - test_double_less_than("drift", drift, 0.0001); - test_stop(); - } - last_jitter = jitter; - }); - - test_ptr_notnull("timer source", t); - - dispatch_main(); - return 0; -} - diff --git a/testing/dispatch_group.c b/testing/dispatch_group.c deleted file mode 100644 index bda3a03..0000000 --- a/testing/dispatch_group.c +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -#ifndef NSEC_PER_SEC -#define NSEC_PER_SEC 1000000000 -#endif - -dispatch_group_t -create_group(size_t count, int delay) -{ - size_t i; - - dispatch_group_t group = dispatch_group_create(); - - for (i = 0; i < count; ++i) { - dispatch_queue_t queue = dispatch_queue_create(NULL, NULL); - assert(queue); - - dispatch_group_async(group, queue, ^{ - if (delay) { - fprintf(stderr, "sleeping...\n"); - sleep(delay); - fprintf(stderr, "done.\n"); - } - }); - - dispatch_release(queue); - } - return group; -} - -int -main(void) -{ - long res; - - test_start("Dispatch Group"); - - dispatch_group_t group; - - group = create_group(100, 0); - test_ptr_notnull("dispatch_group_async", group); - - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - - // should be OK to re-use a group - dispatch_group_async(group, dispatch_get_concurrent_queue(0), ^{}); - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - - dispatch_release(group); - group = NULL; - - group = create_group(3, 7); - test_ptr_notnull("dispatch_group_async", group); - - res = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC)); - test_long("dispatch_group_wait", !res, 0); - - // retry after timeout (this time succeed) - res = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC)); - test_long("dispatch_group_wait", res, 0); - - dispatch_release(group); - group = NULL; - - group = create_group(100, 0); - test_ptr_notnull("dispatch_group_async", group); - - dispatch_group_notify(group, dispatch_get_main_queue(), ^{ - dispatch_queue_t m = dispatch_get_main_queue(); - dispatch_queue_t c = dispatch_get_current_queue(); - test_ptr("Notification Received", m, c); - test_stop(); - }); - - dispatch_release(group); - group = NULL; - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_overcommit.c b/testing/dispatch_overcommit.c deleted file mode 100644 index af5e59a..0000000 --- a/testing/dispatch_overcommit.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -int32_t count = 0; -const int32_t final = 32; - -int -main(void) -{ - test_start("Dispatch Overcommit"); - - dispatch_queue_attr_t attr = dispatch_queue_attr_create(); - test_ptr_notnull("dispatch_queue_attr_create", attr); - dispatch_queue_attr_set_flags(attr, DISPATCH_QUEUE_OVERCOMMIT); - - int i; - for (i = 0; i < final; ++i) { - char* name; - asprintf(&name, "test.overcommit.%d", i); - - dispatch_queue_t queue = dispatch_queue_create(name, attr); - test_ptr_notnull("dispatch_queue_create", queue); - free(name); - - dispatch_async(queue, ^{ - OSAtomicIncrement32(&count); - if (count == final) { - test_long("count", count, final); - test_stop(); - } else { - while (1); // spin - } - }); - } - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_pingpong.c b/testing/dispatch_pingpong.c deleted file mode 100644 index f40825e..0000000 --- a/testing/dispatch_pingpong.c +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include - -#include "dispatch_test.h" - -uint32_t count = 0; -const uint32_t final = 1000000; // 10M - -void pingpongloop(dispatch_group_t group, dispatch_queue_t ping, dispatch_queue_t pong, size_t counter) { - //printf("[%p] %s: %lu\n", (void*)(uintptr_t)pthread_self(), dispatch_queue_get_label(dispatch_get_current_queue()), counter); - if (counter < final) { - dispatch_group_async(group, pong, ^{ pingpongloop(group, pong, ping, counter+1); }); - } else { - count = counter; - } -} - -int main(void) { - - test_start("Dispatch Ping Pong"); - - dispatch_queue_t ping = dispatch_queue_create("ping", NULL); - test_ptr_notnull("dispatch_queue_create(ping)", ping); - dispatch_queue_t pong = dispatch_queue_create("pong", NULL); - test_ptr_notnull("dispatch_queue_create(pong)", pong); - - dispatch_group_t group = dispatch_group_create(); - test_ptr_notnull("dispatch_group_create", group); - - pingpongloop(group, ping, pong, 0); - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - - test_long("count", count, final); - test_stop(); - - return 0; -} diff --git a/testing/dispatch_plusplus.cpp b/testing/dispatch_plusplus.cpp deleted file mode 100644 index 6baccab..0000000 --- a/testing/dispatch_plusplus.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include - -#include "dispatch_test.h" - -int main(void) { - test_start("Dispatch C++"); - dispatch_queue_t q = dispatch_get_main_queue(); - test_ptr_notnull("dispatch_get_main_queue", q); - - dispatch_async(dispatch_get_main_queue(), ^{ - test_stop(); - exit(0); - }); - dispatch_main(); - return 0; -} diff --git a/testing/dispatch_priority.c b/testing/dispatch_priority.c deleted file mode 100644 index a964765..0000000 --- a/testing/dispatch_priority.c +++ /dev/null @@ -1,126 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -int done = 0; - -#define BLOCKS 128 -#define PRIORITIES 3 - -#if TARGET_OS_EMBEDDED -#define LOOP_COUNT 2000000 -#else -#define LOOP_COUNT 100000000 -#endif - -char *labels[PRIORITIES] = { "LOW", "DEFAULT", "HIGH" }; -int priorities[PRIORITIES] = { DISPATCH_QUEUE_PRIORITY_LOW, DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_PRIORITY_HIGH }; - -union { - size_t count; - char padding[64]; -} counts[PRIORITIES]; - -#define ITERATIONS (size_t)(PRIORITIES * BLOCKS * 0.50) -size_t iterations = ITERATIONS; - -void -histogram(void) { - size_t maxcount = BLOCKS; - size_t sc[PRIORITIES]; - - size_t total = 0; - - size_t x,y; - for (y = 0; y < PRIORITIES; ++y) { - sc[y] = counts[y].count; - } - - for (y = 0; y < PRIORITIES; ++y) { - printf("%s: %ld\n", labels[y], sc[y]); - total += sc[y]; - - double fraction = (double)sc[y] / (double)maxcount; - double value = fraction * (double)80; - for (x = 0; x < 80; ++x) { - printf("%s", (value > x) ? "*" : " "); - } - printf("\n"); - } - - test_long("blocks completed", total, ITERATIONS); - test_long_less_than("high priority precedence", (long)sc[0], (long)sc[2]); -} - -void -cpubusy(void* context) -{ - size_t *count = context; - size_t iterdone; - - size_t idx; - for (idx = 0; idx < LOOP_COUNT; ++idx) { - if (done) break; - } - - if ((iterdone = __sync_sub_and_fetch(&iterations, 1)) == 0) { - __sync_add_and_fetch(&done, 1); - __sync_add_and_fetch(count, 1); - histogram(); - test_stop(); - exit(0); - } else if (iterdone > 0) { - __sync_add_and_fetch(count, 1); - } -} - -void -submit_work(dispatch_queue_t queue, void* context) -{ - int i; - - for (i = 0; i < BLOCKS; ++i) { - dispatch_async_f(queue, context, cpubusy); - } - -#if USE_SET_TARGET_QUEUE - dispatch_release(queue); -#endif -} - -int -main(int argc __attribute__((unused)), char* argv[] __attribute__((unused))) -{ - dispatch_queue_t q[PRIORITIES]; - int i; - -#if USE_SET_TARGET_QUEUE - test_start("Dispatch Priority (Set Target Queue)"); - for(i = 0; i < PRIORITIES; i++) { - q[i] = dispatch_queue_create(labels[i], NULL); - test_ptr_notnull("q[i]", q[i]); - assert(q[i]); - dispatch_set_target_queue(q[i], dispatch_get_global_queue(priorities[i], 0)); - dispatch_queue_set_width(q[i], DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS); - } -#else - test_start("Dispatch Priority"); - for(i = 0; i < PRIORITIES; i++) { - q[i] = dispatch_get_global_queue(priorities[i], 0); - } -#endif - - for(i = 0; i < PRIORITIES; i++) { - submit_work(q[i], &counts[i].count); - } - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_proc.c b/testing/dispatch_proc.c deleted file mode 100644 index 6f5ef02..0000000 --- a/testing/dispatch_proc.c +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -#define PID_CNT 5 - -static long event_cnt; -static long cancel_cnt; - -int -main(void) -{ - dispatch_source_t proc; - int res; - pid_t pid; - - test_start("Dispatch Proc"); - - // Creates a process and register multiple observers. Send a signal, - // exit the process, etc., and verify all observers were notified. - - posix_spawnattr_t attr; - res = posix_spawnattr_init(&attr); - assert(res == 0); - res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); - assert(res == 0); - - char* args[] = { - "/bin/sleep", "2", NULL - }; - - res = posix_spawnp(&pid, args[0], NULL, &attr, args, NULL); - if (res < 0) { - perror(args[0]); - exit(127); - } - - res = posix_spawnattr_destroy(&attr); - assert(res == 0); - - dispatch_queue_t semaphore = dispatch_queue_create("semaphore", NULL); - - assert(pid > 0); - - int i; - for (i = 0; i < PID_CNT; ++i) { - dispatch_suspend(semaphore); - proc = dispatch_source_proc_create(pid, DISPATCH_PROC_EXIT, NULL, dispatch_get_main_queue(), - ^(dispatch_event_t ev) { - long err_dom, err_val; - if ((err_dom = dispatch_event_get_error(ev, &err_val))) { - test_long("PROC error domain", err_dom, DISPATCH_ERROR_DOMAIN_POSIX); - test_long("PROC error value", err_val, ECANCELED); - cancel_cnt++; - dispatch_resume(semaphore); - } else { - long flags = dispatch_event_get_flags(ev); - test_long("DISPATCH_PROC_EXIT", flags, DISPATCH_PROC_EXIT); - event_cnt++; - dispatch_release(dispatch_event_get_source(ev)); - } - }); - test_ptr_notnull("dispatch_source_proc_create", proc); - } - - dispatch_async(semaphore, ^{ - int status; - int res2 = waitpid(pid, &status, 0); - assert(res2 != -1); - //int passed = (WIFEXITED(status) && WEXITSTATUS(status) == 0); - test_long("Sub-process exited", WEXITSTATUS(status) | WTERMSIG(status), 0); - test_long("Event count", event_cnt, PID_CNT); - test_long("Cancel count", cancel_cnt, PID_CNT); - test_stop(); - }); - - kill(pid, SIGCONT); - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_rdlock_bmark.c b/testing/dispatch_rdlock_bmark.c deleted file mode 100644 index ec87100..0000000 --- a/testing/dispatch_rdlock_bmark.c +++ /dev/null @@ -1,140 +0,0 @@ -#include -#include -#include -#include -#include - -#define LAPS (1024 * 1024) -#define THREADS 1 - -static pthread_rwlock_t pthr_rwlock = PTHREAD_RWLOCK_INITIALIZER; -static semaphore_t wake_port; -static void (*test_func)(void *); -static unsigned int thr_count; - -static void -reader(void *ctxt __attribute__((unused))) -{ -} - -static void -pthr_worker(void *ctxt __attribute__((unused))) -{ - size_t i; - int r; - - for (i = 0; i < (LAPS / THREADS); i++) { - r = pthread_rwlock_rdlock(&pthr_rwlock); - assert(r == 0); - r = pthread_rwlock_unlock(&pthr_rwlock); - assert(r == 0); - } -} - -static void -gcd_worker(void *ctxt) -{ - dispatch_queue_t dq = ctxt; - size_t i; - - for (i = 0; i < (LAPS / THREADS); i++) { - dispatch_read_sync_f(dq, NULL, reader); - } -} - -static void * -worker(void *ctxt) -{ - kern_return_t kr; - - __sync_add_and_fetch(&thr_count, 1); - - kr = semaphore_wait(wake_port); - assert(kr == 0); - - test_func(ctxt); - - if (__sync_sub_and_fetch(&thr_count, 1) == 0) { - kr = semaphore_signal(wake_port); - assert(kr == 0); - } - - return NULL; -} - -static uint64_t -benchmark(void *ctxt) -{ - pthread_t pthr[THREADS]; - uint64_t cycles; - void *rval; - size_t i; - int r; - - for (i = 0; i < THREADS; i++) { - r = pthread_create(&pthr[i], NULL, worker, ctxt); - assert(r == 0); - } - - while (thr_count != THREADS) { - sleep(1); - } - - sleep(1); - - cycles = dispatch_benchmark(1, ^{ - kern_return_t kr; - - kr = semaphore_signal_all(wake_port); - assert(kr == 0); - kr = semaphore_wait(wake_port); - assert(kr == 0); - }); - - for (i = 0; i < THREADS; i++) { - r = pthread_join(pthr[i], &rval); - assert(r == 0); - } - - return cycles; -} - -int -main(void) -{ - uint64_t pthr_cycles, gcd_cycles; - long double ratio; - dispatch_queue_t dq; - kern_return_t kr; - int r; - - dq = dispatch_queue_create("test", NULL); - assert(dq); - - // pthreads lazily inits the object - // do not benchmark that fact - r = pthread_rwlock_rdlock(&pthr_rwlock); - assert(r == 0); - r = pthread_rwlock_unlock(&pthr_rwlock); - assert(r == 0); - - kr = semaphore_create(mach_task_self(), &wake_port, SYNC_POLICY_FIFO, 0); - assert(kr == 0); - - test_func = pthr_worker; - pthr_cycles = benchmark(NULL); - - test_func = gcd_worker; - gcd_cycles = benchmark(dq); - - dispatch_release(dq); - - ratio = pthr_cycles; - ratio /= gcd_cycles; - - printf("Cycles:\n\tPOSIX\t%llu\n", pthr_cycles); - printf("\tGCD\t%llu\n", gcd_cycles); - printf("Ratio:\t%Lf\n", ratio); - - return 0; -} diff --git a/testing/dispatch_read.c b/testing/dispatch_read.c deleted file mode 100644 index 02ede77..0000000 --- a/testing/dispatch_read.c +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "dispatch_test.h" - -static size_t bytes_total; -static size_t bytes_read; - -int main(void) -{ - const char *path = "/usr/share/dict/words"; - struct stat sb; - - test_start("Dispatch Source Read"); - - int infd = open(path, O_RDONLY); - if (infd == -1) { - perror(path); - exit(EXIT_FAILURE); - } - if (fstat(infd, &sb) == -1) { - perror(path); - exit(EXIT_FAILURE); - } - bytes_total = sb.st_size; - - if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) { - perror(path); - exit(EXIT_FAILURE); - } - - dispatch_queue_t main_q = dispatch_get_main_queue(); - test_ptr_notnull("dispatch_get_main_queue", main_q); - - dispatch_source_attr_t attr = dispatch_source_attr_create(); - test_ptr_notnull("dispatch_source_attr_create", attr); - - dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) { - test_ptr_notnull("finalizer ran", ds); - int res = close(infd); - test_errno("close", res == -1 ? errno : 0, 0); - test_stop(); - }); - - dispatch_source_t reader = dispatch_source_read_create(infd, attr, - main_q, ^(dispatch_event_t ev) { - long err_val; - long err_dom = dispatch_event_get_error(ev, &err_val); - if (!err_dom) { - size_t estimated = dispatch_event_get_bytes_available(ev); - printf("bytes available: %zu\n", estimated); - const ssize_t bufsiz = 1024*500; // 500 KB buffer - static char buffer[1024*500]; // 500 KB buffer - ssize_t actual = read(infd, buffer, sizeof(buffer)); - bytes_read += actual; - printf("bytes read: %zd\n", actual); - if (actual < bufsiz) { - actual = read(infd, buffer, sizeof(buffer)); - bytes_read += actual; - // confirm EOF condition - test_long("EOF", actual, 0); - dispatch_release(dispatch_event_get_source(ev)); - } - } else { - test_long("Error domain", err_dom, DISPATCH_ERROR_DOMAIN_POSIX); - test_long("Error value", err_val, ECANCELED); - test_long("Bytes read", bytes_read, bytes_total); - } - }); - - printf("reader = %p\n", reader); - assert(reader); - - dispatch_release(attr); - - dispatch_main(); -} diff --git a/testing/dispatch_read2.c b/testing/dispatch_read2.c deleted file mode 100644 index 1be8150..0000000 --- a/testing/dispatch_read2.c +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "dispatch_test.h" - -static size_t bytes_total; -static size_t bytes_read; - -int main(void) -{ - const char *path = "/usr/share/dict/words"; - struct stat sb; - - test_start("Dispatch Source Read"); - - int infd = open(path, O_RDONLY); - if (infd == -1) { - perror(path); - exit(EXIT_FAILURE); - } - if (fstat(infd, &sb) == -1) { - perror(path); - exit(EXIT_FAILURE); - } - bytes_total = sb.st_size; - - if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) { - perror(path); - exit(EXIT_FAILURE); - } - - dispatch_queue_t main_q = dispatch_get_main_queue(); - test_ptr_notnull("dispatch_get_main_queue", main_q); - - dispatch_source_t reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, main_q); - test_ptr_notnull("dispatch_source_create", reader); - assert(reader); - - dispatch_source_set_event_handler(reader, ^{ - size_t estimated = dispatch_source_get_data(reader); - printf("bytes available: %zu\n", estimated); - const ssize_t bufsiz = 1024*500; // 500 KB buffer - static char buffer[1024*500]; // 500 KB buffer - ssize_t actual = read(infd, buffer, sizeof(buffer)); - bytes_read += actual; - printf("bytes read: %zd\n", actual); - if (actual < bufsiz) { - actual = read(infd, buffer, sizeof(buffer)); - bytes_read += actual; - // confirm EOF condition - test_long("EOF", actual, 0); - dispatch_source_cancel(reader); - } - }); - dispatch_source_set_cancel_handler(reader, ^{ - test_long("Bytes read", bytes_read, bytes_total); - test_stop(); - }); - dispatch_resume(reader); - - dispatch_main(); -} diff --git a/testing/dispatch_readsync.c b/testing/dispatch_readsync.c deleted file mode 100644 index 64da538..0000000 --- a/testing/dispatch_readsync.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -#define LAPS 10000 -#define INTERVAL 100 - -static size_t r_count = LAPS; -static size_t w_count = LAPS / INTERVAL; - -static void -writer(void *ctxt __attribute__((unused))) -{ - if (--w_count == 0) { - if (r_count == 0) { - test_stop(); - } - } -} - -static void -reader(void *ctxt __attribute__((unused))) -{ - if (__sync_sub_and_fetch(&r_count, 1) == 0) { - if (r_count == 0) { - test_stop(); - } - } -} - -int -main(void) -{ - dispatch_queue_t dq; - - test_start("Dispatch Reader/Writer Queues"); - - dq = dispatch_queue_create("com.apple.libdispatch.test_readsync", NULL); - assert(dq); - - dispatch_queue_set_width(dq, LONG_MAX); - - dispatch_apply(LAPS, dispatch_get_concurrent_queue(0), ^(size_t idx) { - dispatch_sync_f(dq, NULL, reader); - - if (idx % INTERVAL) { - dispatch_barrier_async_f(dq, NULL, writer); - } - }); - - dispatch_release(dq); - - dispatch_main(); -} diff --git a/testing/dispatch_sema.c b/testing/dispatch_sema.c deleted file mode 100644 index f30a2c8..0000000 --- a/testing/dispatch_sema.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include -#include - -#include "dispatch_test.h" - -#define LAPS 10000 - -int -main(void) -{ - static size_t total; - dispatch_semaphore_t dsema; - - test_start("Dispatch Semaphore"); - - dsema = dispatch_semaphore_create(1); - assert(dsema); - - dispatch_apply(LAPS, dispatch_get_concurrent_queue(0), ^(size_t idx __attribute__((unused))) { - dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); - total++; - dispatch_semaphore_signal(dsema); - }); - - dispatch_release(dsema); - - test_long("count", total, LAPS); - test_stop(); - - return 0; -} diff --git a/testing/dispatch_starfish.c b/testing/dispatch_starfish.c deleted file mode 100644 index fb1f709..0000000 --- a/testing/dispatch_starfish.c +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -#if TARGET_OS_EMBEDDED -#define COUNT 300ul -#define LAPS 10ul -#else -#define COUNT 1000ul -#define LAPS 10ul -#endif - -static dispatch_queue_t queues[COUNT]; -static size_t lap_count_down = LAPS; -static size_t count_down; -static uint64_t start; -static mach_timebase_info_data_t tbi; - -static void do_test(void); - -static void -collect(void *context __attribute__((unused))) -{ - uint64_t delta; - long double math; - size_t i; - - if (--count_down) { - return; - } - - delta = mach_absolute_time() - start; - delta *= tbi.numer; - delta /= tbi.denom; - math = delta; - math /= COUNT * COUNT * 2ul + COUNT * 2ul; - - printf("lap: %ld\n", lap_count_down); - printf("count: %lu\n", COUNT); - printf("delta: %llu ns\n", delta); - printf("math: %Lf ns / lap\n", math); - - for (i = 0; i < COUNT; i++) { - dispatch_release(queues[i]); - } - - // our malloc could be a lot better, - // this result is really a malloc torture test - test_long_less_than("Latency" , (unsigned long)math, 1000); - - if (--lap_count_down) { - return do_test(); - } - - // give the threads some time to settle before test_stop() runs "leaks" - // ...also note, this is a total cheat. dispatch_after lets this - // thread go idle, so dispatch cleans up the continuations cache. - // Doign the "old style" sleep left that stuff around and leaks - // took a LONG TIME to complete. Long enough that the test harness - // decided to kill us. - dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), NULL, test_stop_after_delay); -} - -static void -pong(void *context) -{ - dispatch_queue_t this_q = context; - size_t replies = (size_t)dispatch_get_context(this_q); - - dispatch_set_context(this_q, (void *)--replies); - if (!replies) { - //printf("collect from: %s\n", dispatch_queue_get_label(dispatch_get_current_queue())); - dispatch_async_f(dispatch_get_main_queue(), NULL, collect); - } -} - -static void -ping(void *context) -{ - dispatch_queue_t reply_q = context; - - dispatch_async_f(reply_q, reply_q, pong); -} - -static void -start_node(void *context) -{ - dispatch_queue_t this_q = context; - size_t i; - - dispatch_set_context(this_q, (void *)COUNT); - - for (i = 0; i < COUNT; i++) { - dispatch_async_f(queues[i], this_q, ping); - } -} - -void -do_test(void) -{ - size_t i; - kern_return_t kr; - - count_down = COUNT; - - kr = mach_timebase_info(&tbi); - assert(kr == 0); - - start = mach_absolute_time(); - - for (i = 0; i < COUNT; i++) { - char buf[1000]; - snprintf(buf, sizeof(buf), "com.example.starfish-node#%ld", i); - queues[i] = dispatch_queue_create(buf, NULL); - dispatch_suspend(queues[i]); - } - - for (i = 0; i < COUNT; i++) { - dispatch_async_f(queues[i], queues[i], start_node); - } - - for (i = 0; i < COUNT; i++) { - dispatch_resume(queues[i]); - } -} - -int -main(void) -{ - test_start("Dispatch Starfish"); - - do_test(); - - dispatch_main(); -} diff --git a/testing/dispatch_suspend_timer.c b/testing/dispatch_suspend_timer.c deleted file mode 100644 index f271f39..0000000 --- a/testing/dispatch_suspend_timer.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include - -#include - -#include "dispatch_test.h" - -dispatch_source_t tweedledee; -dispatch_source_t tweedledum; - -int main(void) -{ - test_start("Dispatch Suspend Timer"); - - dispatch_queue_t main_q = dispatch_get_main_queue(); - test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue()); - - __block int i = 0; - __block int j = 0; - - dispatch_source_attr_t attr = dispatch_source_attr_create(); - test_ptr_notnull("dispatch_source_attr_create", attr); - - dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) { - test_ptr_notnull("finalizer ran", ds); - if (ds == tweedledum) test_stop(); - }); - - tweedledee = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, - 1000000000ull, 0, attr, main_q, ^(dispatch_event_t ev) { - long err; - if (dispatch_event_get_error(ev, &err)) { - test_errno("dispatch_event_get_error", err, ECANCELED); - dispatch_release(dispatch_event_get_source(ev)); - } else { - fprintf(stderr, "%d\n", ++i); - if (i == 10) { - dispatch_cancel(dispatch_event_get_source(ev)); - } - } - }); - test_ptr_notnull("dispatch_source_timer_create", tweedledee); - dispatch_retain(tweedledee); - - tweedledum = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, - 3000000000ull, 0, attr, main_q, ^(dispatch_event_t ev) { - long err; - if (dispatch_event_get_error(ev, &err)) { - test_errno("dispatch_event_get_error", err, ECANCELED); - dispatch_release(dispatch_event_get_source(ev)); - } else { - switch(++j) { - case 1: - fprintf(stderr, "suspending timer for 3 seconds\n"); - dispatch_suspend(tweedledee); - break; - case 2: - fprintf(stderr, "resuming timer\n"); - dispatch_resume(tweedledee); - dispatch_release(tweedledee); - break; - default: - dispatch_cancel(dispatch_event_get_source(ev)); - break; - } - } - }); - test_ptr_notnull("dispatch_source_timer_create", tweedledum); - - dispatch_release(attr); - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_test.c b/testing/dispatch_test.c deleted file mode 100644 index 82b404f..0000000 --- a/testing/dispatch_test.c +++ /dev/null @@ -1,148 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#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 - -extern char **environ; - -void -test_stop(void) { - test_stop_after_delay((void *)(intptr_t)0); -} - -void -test_stop_after_delay(void *delay) { - int res; - pid_t pid; - char pidstr[10]; - - if (delay != NULL) { - sleep((int)(intptr_t)delay); - } - - if (getenv("NOLEAKS")) _exit(EXIT_SUCCESS); - - /* leaks doesn't work against debug variant malloc */ - if (getenv("DYLD_IMAGE_SUFFIX")) _exit(EXIT_SUCCESS); - - snprintf(pidstr, sizeof(pidstr), "%d", getpid()); - char* args[] = { "./leaks-wrapper", pidstr, NULL }; - res = posix_spawnp(&pid, args[0], NULL, NULL, args, environ); - if (res == 0 && pid > 0) { - int status; - waitpid(pid, &status, 0); - test_long("Leaks", status, 0); - } else { - perror(args[0]); - } - - _exit(EXIT_SUCCESS); -} diff --git a/testing/dispatch_test.h b/testing/dispatch_test.h deleted file mode 100644 index 59547f4..0000000 --- a/testing/dispatch_test.h +++ /dev/null @@ -1,33 +0,0 @@ -#include - -__BEGIN_DECLS - -void test_start(const char* desc); -void test_stop(void); -void test_stop_after_delay(void *delay); - -void _test_ptr_null(const char* file, long line, const char* desc, const void* ptr); -#define test_ptr_null(a,b) _test_ptr_null(__FILE__, __LINE__, a, b) - -void _test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr); -#define test_ptr_notnull(a,b) _test_ptr_notnull(__FILE__, __LINE__, a, b) - -void _test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected); -#define test_ptr(a,b,c) _test_ptr(__FILE__, __LINE__, a, b, c) - -void _test_long(const char* file, long line, const char* desc, long actual, long expected); -#define test_long(a,b,c) _test_long(__FILE__, __LINE__, a, b, c) - -void _test_long_less_than(const char* file, long line, const char* desc, long actual, long max_expected); -#define test_long_less_than(a,b,c) _test_long_less_than(__FILE__, __LINE__, a, b, c) - -void _test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected); -#define test_double_less_than_or_equal(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m) - -void _test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected); -#define test_double_less_than(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m) - -void _test_errno(const char* file, long line, const char* desc, long actual, long expected); -#define test_errno(a,b,c) _test_errno(__FILE__, __LINE__, a, b, c) - -__END_DECLS diff --git a/testing/dispatch_test_sync_on_main.c b/testing/dispatch_test_sync_on_main.c deleted file mode 100644 index 6adba96..0000000 --- a/testing/dispatch_test_sync_on_main.c +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -int global_count; - -void -main_work(void* ctxt) -{ - if (global_count == 20) { - exit(0); - } - uint64_t time = random() % NSEC_PER_SEC; - printf("Firing timer on main %d\n", ++global_count); - dispatch_after_f(dispatch_time(0, time), dispatch_get_main_queue(), NULL, main_work); -} - - -int main(void) { - global_count = 0; - - dispatch_queue_t dq = dispatch_queue_create("foo.bar", NULL); - dispatch_async(dq, ^{ - - dispatch_async_f(dispatch_get_main_queue(), NULL, main_work); - - int i; - for (i=0; i<5; ++i) { - dispatch_sync(dispatch_get_main_queue(), ^{ - printf("Calling sync %d\n", i); - assert(pthread_main_np() == 1); - if (i==4) { - global_count = 20; - } - }); - } - }); - - //dispatch_main(); - CFRunLoopRun(); - return 0; -} diff --git a/testing/dispatch_timer.c b/testing/dispatch_timer.c deleted file mode 100644 index ddda1a9..0000000 --- a/testing/dispatch_timer.c +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "dispatch_test.h" - -int main(void) -{ - test_start("Dispatch Source Timer"); - - dispatch_queue_t main_q = dispatch_get_main_queue(); - test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue()); - - uint64_t j; - - // create several timers and release them. - for (j = 1; j <= 5; ++j) - { - dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, - (uint64_t)j * (uint64_t)1000000000ull, 0, NULL, dispatch_get_concurrent_queue(0), - ^(dispatch_event_t ev) { - if (!dispatch_event_get_error(ev, NULL)) { - fprintf(stderr, "timer[%lld]\n", j); - dispatch_release(dispatch_event_get_source(ev)); - } - }); - test_ptr_notnull("dispatch_source_timer_create", s); - } - - dispatch_source_attr_t attr = dispatch_source_attr_create(); - dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) { - test_ptr_notnull("finalizer ran", ds); - test_stop(); - }); - - __block int i = 0; - - dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, - 1000000000ull, - 0, - attr, - main_q, - ^(dispatch_event_t ev) { - long err; - if (dispatch_event_get_error(ev, &err)) { - test_errno("dispatch_event_get_error", err, ECANCELED); - dispatch_release(dispatch_event_get_source(ev)); - } else { - fprintf(stderr, "%d\n", ++i); - if (i >= 3) { - dispatch_cancel(dispatch_event_get_source(ev)); - } - } - }); - test_ptr_notnull("dispatch_source_timer_create", s); - - dispatch_release(attr); - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_timer_bit31.c b/testing/dispatch_timer_bit31.c deleted file mode 100644 index 131c8c0..0000000 --- a/testing/dispatch_timer_bit31.c +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "dispatch_test.h" - -int main(void) -{ - test_start("Dispatch Source Timer, bit 31"); - - dispatch_queue_t main_q = dispatch_get_main_queue(); - test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue()); - - __block int i = 0; - struct timeval start_time; - - gettimeofday(&start_time, NULL); - dispatch_source_attr_t attr = dispatch_source_attr_create(); - dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) { - struct timeval end_time; - gettimeofday(&end_time, NULL); - test_ptr_notnull("finalizer ran", ds); - // XXX: check, s/b 2.0799... seconds, which is <4 seconds - // when it could end on a bad boundry. - test_long_less_than("needs to finish faster than 4 seconds", end_time.tv_sec - start_time.tv_sec, 4); - // And it has to take at least two seconds... - test_long_less_than("can't finish faster than 2 seconds", 1, end_time.tv_sec - start_time.tv_sec); - test_stop(); - }); - - dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, - 0x80000000ull, - 0, - attr, - main_q, - ^(dispatch_event_t ev) { - long err; - if (dispatch_event_get_error(ev, &err)) { - test_errno("dispatch_event_get_error", err, ECANCELED); - dispatch_release(dispatch_event_get_source(ev)); - } else { - fprintf(stderr, "%d\n", ++i); - dispatch_cancel(dispatch_event_get_source(ev)); - } - }); - test_ptr_notnull("dispatch_source_timer_create", s); - - dispatch_release(attr); - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_timer_bit63.c b/testing/dispatch_timer_bit63.c deleted file mode 100644 index 6630794..0000000 --- a/testing/dispatch_timer_bit63.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "dispatch_test.h" - -int main(void) -{ - test_start("Dispatch Source Timer, bit 63"); - - //uint64_t interval = 0xffffffffffffffffull; - uint64_t interval = 0x8000000000000001ull; - - dispatch_queue_t mainq = dispatch_get_main_queue(); - - __block int i = 0; - struct timeval start_time; - - gettimeofday(&start_time, NULL); - - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, mainq); - assert(ds); - dispatch_source_set_event_handler(ds, ^{ - assert(i < 1); - printf("%d\n", i++); - }); - dispatch_source_set_timer(ds, DISPATCH_TIME_NOW, interval, 0); - dispatch_resume(ds); - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), - dispatch_get_main_queue(), ^{ - test_stop(); - }); - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_timer_oneshot.c b/testing/dispatch_timer_oneshot.c deleted file mode 100644 index 9860601..0000000 --- a/testing/dispatch_timer_oneshot.c +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "dispatch_test.h" - -static void -oneshot(void* context __attribute__((unused)), dispatch_event_t de) -{ - dispatch_source_t ds = dispatch_event_get_source(de); - test_ptr_notnull("dispatch_event_get_source", ds); - - if (!dispatch_event_get_error(de, NULL)) { - long canceled = dispatch_testcancel(ds); - test_long("dispatch_testcancel", canceled, 0); - - dispatch_release(ds); - test_stop(); - } -} - -int -main(void) -{ - test_start("Dispatch Timer One-Shot"); - - dispatch_source_t s; - - s = dispatch_source_timer_create_f(DISPATCH_TIMER_ONESHOT, - (uint64_t)1000000000ull, // 1s - 0, - NULL, - dispatch_get_concurrent_queue(0), - NULL, - &oneshot); - - dispatch_main(); - - return 0; -} diff --git a/testing/dispatch_timer_set_time.c b/testing/dispatch_timer_set_time.c deleted file mode 100644 index 189db33..0000000 --- a/testing/dispatch_timer_set_time.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include "dispatch_test.h" - -int main(void) -{ - test_start("Dispatch Update Timer"); - - dispatch_queue_t main_q = dispatch_get_main_queue(); - test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue()); - - __block int i = 0; - struct timeval start_time; - - gettimeofday(&start_time, NULL); - dispatch_source_attr_t attr = dispatch_source_attr_create(); - dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) { - struct timeval end_time; - gettimeofday(&end_time, NULL); - // Make sure we actually managed to adjust the interval - // duration. Seven one second ticks would blow past - // this. - test_long_less_than("total duration", end_time.tv_sec - start_time.tv_sec, 3); - test_ptr_notnull("finalizer ran", ds); - test_stop(); - }); - - dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, - 1000000000ull, - 0, - attr, - main_q, - ^(dispatch_event_t ev) { - long err; - if (dispatch_event_get_error(ev, &err)) { - test_errno("dispatch_event_get_error", err, ECANCELED); - dispatch_release(dispatch_event_get_source(ev)); - } else { - fprintf(stderr, "%d\n", ++i); - if (i >= 7) { - dispatch_cancel(dispatch_event_get_source(ev)); - } else if (i == 1) { - dispatch_source_timer_set_time(dispatch_event_get_source(ev), 100, 0); - } - } - }); - test_ptr_notnull("dispatch_source_timer_create", s); - - dispatch_release(attr); - - dispatch_main(); - - return 0; -} diff --git a/testing/fast_apply_bench.c b/testing/fast_apply_bench.c deleted file mode 100644 index 599845e..0000000 --- a/testing/fast_apply_bench.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -static inline uint64_t -rdtsc(void) -{ - uint32_t lo, hi; - - asm volatile("rdtsc" : "=a" (lo), "=d" (hi)); - - return (uint64_t)hi << 32 | lo; -} - -__attribute__((noinline)) void -apply_p(void (^b)(size_t), size_t offset, size_t count) -{ - /* This would feed through to the existing dispatch_apply() */ - abort(); -} - -/* a dynamically variable to eventually be added to the kernel/user 'commpage' */ -size_t total_active_cpus = 8; - -__attribute__((noinline)) void -apply(void (^b)(size_t), size_t offset, size_t count) -{ - const size_t too_long = 100000; /* 100 us */ - const size_t laps = 16; - uint64_t delta, tmp, now; - size_t i; - - if (total_active_cpus == 1) { - for (i = 0; i < count; i++) { - b(offset + i); - } - return; - } - - now = mach_absolute_time(); - - for (i = 0; i < count; i++) { - b(offset + i); - - if (i % laps) { - continue; - } - - tmp = mach_absolute_time(); - delta = tmp - now; - now = tmp; - - if (delta > (too_long * laps) || (i == 0 && delta > too_long)) { - apply_p(b, offset + i + 1, count - (i + 1)); - return; - } - } -} - -int -main(void) -{ - void (^b)(size_t) = ^(size_t index) { - asm volatile(""); /* defeat compiler optimizations */ - }; - const size_t laps = 10000000; - mach_timebase_info_data_t tbi; - kern_return_t kr; - long double dd; - uint64_t s, e; - size_t i; - - kr = mach_timebase_info(&tbi); - assert(kr == 0); - assert(tbi.numer == tbi.denom); /* This will fail on PowerPC. */ - - s = mach_absolute_time(); - for (i = 0; i < laps; i++) { - b(i); - } - e = mach_absolute_time(); - dd = e - s; - dd /= laps; - printf("direct:\t%Lf ns\n", dd); - - s = mach_absolute_time(); - apply(b, 0, laps); - e = mach_absolute_time(); - dd = e - s; - dd /= laps; - printf("apply:\t%Lf ns\n", dd); - - return 0; -} diff --git a/testing/fd_stress.c b/testing/fd_stress.c deleted file mode 100644 index fb2cd5a..0000000 --- a/testing/fd_stress.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * fd_stress.c - * - * Stress test for dispatch read and write sources. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static inline size_t max(size_t a, size_t b) { - return (a > b) ? a : b; -} - -static inline size_t min(size_t a, size_t b) { - return (a < b) ? a : b; -} - -int debug = 0; - -#define DEBUG(...) do { \ - if (debug) fprintf(stderr, __VA_ARGS__); \ - } while(0); - -#define assert_errno(str, expr) do { \ - if (!(expr)) { \ - fprintf(stderr, "%s: %s\n", (str), strerror(errno)); \ - exit(1); \ - } } while(0); - -#define assert_gai_errno(str, expr) do { \ - if (!(expr)) { \ - fprintf(stderr, "%s: %s\n", (str), gai_strerror(errno)); \ - exit(1); \ - } } while(0); - - -/* sock_context - * - * Context structure used by the reader and writer queues. - * - * Writers begin by generating a random length and writing it to the descriptor. - * The write buffer is filled with a random byte value and written until empty - * or until the total length is reached. The write buffer is refilled with more - * random data when empty. Each write updates an MD5 digest which is written to - * the descriptor once the total length is reached. - * - * Readers begin by reading the total length of data. The read buffer is filled - * and an MD5 digest is computed on the bytes as they are received. Once the - * total length of data has be read, an MD5 digest is read from the descriptor - * and compared with the computed value. - */ -struct sock_context { - enum { - LENGTH, - DATA, - CKSUM, - DONE, - } state; - char label[64]; - uint32_t len; - off_t offset; - char buf[8192]; - size_t buflen; - CC_MD5_CTX md5ctx; - char md5[CC_MD5_DIGEST_LENGTH]; -}; - -dispatch_source_t -create_writer(int wfd, dispatch_block_t completion) -{ - dispatch_source_t ds; - struct sock_context *ctx = calloc(1, sizeof(struct sock_context)); - assert(ctx); - - snprintf(ctx->label, sizeof(ctx->label), "writer.fd.%d", wfd); - dispatch_queue_t queue = dispatch_queue_create(ctx->label, 0); - - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, wfd, 0, queue); - assert(ds); - dispatch_release(queue); - - uint32_t len; - do { - len = (arc4random() & 0x7FFF); - } while (len == 0); - ctx->state = LENGTH; - CC_MD5_Init(&ctx->md5ctx); - ctx->len = len; - ctx->buflen = sizeof(len); - len = htonl(len); - memcpy(ctx->buf, &len, ctx->buflen); - DEBUG("%s: LENGTH %d\n", ctx->label, ctx->len); - - dispatch_source_set_event_handler(ds, ^{ - DEBUG("%s: available %ld\n", ctx->label, dispatch_source_get_data(ds)); - ssize_t res; - size_t wrsz = min(ctx->len, ctx->buflen); - res = write(wfd, &ctx->buf[ctx->offset], wrsz); - DEBUG("%s: write(%d, %p, %ld): %ld\n", ctx->label, wfd, &ctx->buf[ctx->offset], wrsz, res); - if (res > 0) { - if (ctx->state == DATA) { - CC_MD5_Update(&ctx->md5ctx, &ctx->buf[ctx->offset], res); - ctx->len -= res; - } - ctx->offset += res; - ctx->buflen -= res; - assert(ctx->offset >= 0); - assert(ctx->len >= 0); - assert(ctx->buflen >= 0); - if (ctx->buflen == 0 || ctx->len == 0) { - if (ctx->state == LENGTH) { - // finished writing length, move on to data. - ctx->state = DATA; - ctx->buflen = sizeof(ctx->buf); - char pattern = arc4random() & 0xFF; - memset(ctx->buf, pattern, ctx->buflen); - } else if (ctx->state == DATA && ctx->len == 0) { - // finished writing data, move on to cksum. - ctx->state = CKSUM; - ctx->len = sizeof(ctx->md5); - ctx->buflen = sizeof(ctx->md5); - CC_MD5_Final(ctx->md5, &ctx->md5ctx); - memcpy(ctx->buf, ctx->md5, ctx->buflen); - } else if (ctx->state == DATA) { - ctx->buflen = sizeof(ctx->buf); - char pattern = arc4random() & 0xFF; - memset(ctx->buf, pattern, ctx->buflen); - } else if (ctx->state == CKSUM) { - ctx->state = DONE; - dispatch_source_cancel(ds); - } else { - assert(0); - } - ctx->offset = 0; - } - } else if (res == 0) { - assert(ctx->state == DONE); - assert(0); - } else if (res == -1 && errno == EAGAIN) { - DEBUG("%s: EAGAIN\n", ctx->label); - } else { - assert_errno("write", res >= 0); - } - }); - dispatch_source_set_cancel_handler(ds, ^{ - DEBUG("%s: close(%d)\n", ctx->label, wfd); - int res = close(wfd); - assert_errno("close", res == 0); - completion(); - dispatch_release(ds); - free(ctx); - }); - dispatch_resume(ds); - return ds; -} - -dispatch_source_t -create_reader(int rfd, dispatch_block_t completion) -{ - dispatch_source_t ds; - struct sock_context *ctx = calloc(1, sizeof(struct sock_context)); - assert(ctx); - - snprintf(ctx->label, sizeof(ctx->label), "reader.fd.%d", rfd); - dispatch_queue_t queue = dispatch_queue_create(ctx->label, 0); - - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, rfd, 0, queue); - assert(ds); - dispatch_release(queue); - - ctx->state = LENGTH; - ctx->len = sizeof(ctx->len); - ctx->buflen = sizeof(ctx->len); - CC_MD5_Init(&ctx->md5ctx); - - dispatch_source_set_event_handler(ds, ^{ - DEBUG("%s: available %ld\n", ctx->label, dispatch_source_get_data(ds)); - ssize_t res; - size_t rdsz = min(ctx->len, ctx->buflen); - res = read(rfd, &ctx->buf[ctx->offset], rdsz); - DEBUG("%s: read(%d,%p,%ld): %ld\n", ctx->label, rfd, &ctx->buf[ctx->offset], rdsz, res); - - // log unexpected data lengths... - long expected = dispatch_source_get_data(ds); - long actual = res; - if (actual >= 0 && (actual != expected && actual != rdsz)) { - fprintf(stderr, "%s: expected %ld, actual %ld (rdsz = %ld)\n", ctx->label, expected, actual, rdsz); - } - - if (res > 0) { - if (ctx->state == DATA) { - CC_MD5_Update(&ctx->md5ctx, &ctx->buf[ctx->offset], res); - ctx->len -= res; - } - ctx->offset += res; - ctx->buflen -= res; - if (ctx->buflen == 0 || ctx->len == 0) { - if (ctx->state == LENGTH) { - // buffer is full, interpret as uint32_t - memcpy(&ctx->len, ctx->buf, sizeof(ctx->len)); - ctx->len = ntohl(ctx->len); - ctx->buflen = sizeof(ctx->buf); - ctx->state = DATA; - } else if (ctx->state == DATA && ctx->len == 0) { - CC_MD5_Final(ctx->md5, &ctx->md5ctx); - ctx->state = CKSUM; - ctx->len = CC_MD5_DIGEST_LENGTH; - ctx->buflen = ctx->len; - } else if (ctx->state == DATA) { - ctx->buflen = sizeof(ctx->buf); - } else if (ctx->state == CKSUM) { - ctx->state = DONE; - res = memcmp(ctx->buf, ctx->md5, sizeof(ctx->md5)); - if (res != 0) { - DEBUG("%s: MD5 FAILURE\n", ctx->label); - } - assert(res == 0); - } - ctx->offset = 0; - } - } else if (res == 0) { - assert(ctx->state == DONE); - DEBUG("%s: EOF\n", ctx->label); - dispatch_source_cancel(ds); - } else { - assert_errno("read", res >= 0); - } - }); - dispatch_source_set_cancel_handler(ds, ^{ - DEBUG("%s: close(%d)\n", ctx->label, rfd); - int res = close(rfd); - assert_errno("close", res == 0); - completion(); - dispatch_release(ds); - free(ctx); - }); - dispatch_resume(ds); - return ds; -} - -void -set_nonblock(int fd) -{ - int res, flags; - flags = fcntl(fd, F_GETFL); - - flags |= O_NONBLOCK; - res = fcntl(fd, F_SETFL, flags); - assert_errno("fcntl(F_SETFL,O_NONBLOCK)", res == 0); -} - -void -create_fifo(int *rfd, int *wfd) -{ - int res; - char *name; - - char path[MAXPATHLEN]; - strlcpy(path, "/tmp/fd_stress.fifo.XXXXXX", sizeof(path)); - name = mktemp(path); - - res = unlink(name); - - res = mkfifo(name, 0700); - assert_errno(name, res == 0); - - *rfd = open(name, O_RDONLY | O_NONBLOCK); - assert_errno(name, *rfd >= 0); - - *wfd = open(name, O_WRONLY | O_NONBLOCK); - assert_errno(name, *wfd >= 0); -} - -void -create_pipe(int *rfd, int *wfd) -{ - int res; - int fildes[2]; - - res = pipe(fildes); - assert_errno("pipe", res == 0); - - *rfd = fildes[0]; - *wfd = fildes[1]; - - set_nonblock(*rfd); - set_nonblock(*wfd); -} - -void -create_server_socket(int *rfd, struct sockaddr_in *sa) -{ - int res; - int value; - socklen_t salen = sizeof(*sa); - - memset(sa, 0, salen); - sa->sin_len = salen; - sa->sin_family = AF_INET; - sa->sin_port = htons(12345); - sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - *rfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - assert_errno("socket", *rfd >= 0); - - value = 1; - res = setsockopt(*rfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); - assert_errno("setsockopt(SO_REUSEADDR)", res == 0); - - value = 1; - res = setsockopt(*rfd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)); - assert_errno("setsockopt(SO_REUSEPORT)", res == 0); - - res = bind(*rfd, (const struct sockaddr *)sa, salen); - assert_errno("bind", res == 0); - - res = listen(*rfd, 128); - assert_errno("listen", res == 0); -} - -void -create_client_socket(int *wfd, const struct sockaddr_in *sa) -{ - int res; - - *wfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - assert_errno("socket", *wfd >= 0); - - set_nonblock(*wfd); - - res = connect(*wfd, (const struct sockaddr *)sa, sa->sin_len); - assert_errno("connect", res == 0 || errno == EINPROGRESS); -} - -extern int optind; - -void -usage(void) -{ - fprintf(stderr, "usage: fd_stress [-d] iterations width\n"); - exit(1); -} - -int -main(int argc, char* argv[]) -{ - int serverfd; - struct sockaddr_in sa; - create_server_socket(&serverfd, &sa); - - int ch; - - while ((ch = getopt(argc, argv, "d")) != -1) { - switch (ch) { - case 'd': - debug = 1; - break; - case '?': - default: - usage(); - break; - } - } - argc -= optind; - argv += optind; - - if (argc != 2) { - usage(); - } - - size_t iterations = strtol(argv[0], NULL, 10); - size_t width = strtol(argv[1], NULL, 10); - - if (iterations == 0 || width == 0) { - usage(); - } - - fprintf(stdout, "pid %d\n", getpid()); - - dispatch_group_t group; - group = dispatch_group_create(); - assert(group); - -#if 0 - dispatch_queue_t queue = dispatch_queue_create("server", NULL); - - dispatch_source_t ds; - ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, serverfd, 0, queue); - assert(ds); - dispatch_source_set_event_handler(ds, ^{ - int res; - int fd; - struct sockaddr peer; - socklen_t peerlen; - - fd = accept(serverfd, &peer, &peerlen); - assert_errno("accept", fd >= 0); - - set_nonblock(fd); - - char host[NI_MAXHOST], serv[NI_MAXSERV]; - host[0] = 0; - serv[0] = 0; - res = getnameinfo(&peer, peerlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST|NI_NUMERICSERV); - DEBUG("ACCEPTED %d (%s:%s)\n", fd, host, serv); - - create_reader(fd, ^{ dispatch_group_leave(group); }); - }); - dispatch_resume(ds); -#endif - - size_t i; - for (i = 1; i < iterations; ++i) { - fprintf(stderr, "iteration %ld\n", i); - - size_t j; - for (j = 0; j < width; ++j) { - int rfd, wfd; - dispatch_group_enter(group); - create_pipe(&rfd, &wfd); - DEBUG("PIPE %d %d\n", rfd, wfd); - dispatch_source_t reader; - reader = create_reader(rfd, ^{ dispatch_group_leave(group); }); - create_writer(wfd, ^{}); - } - -#if 0 - int clientfd; - dispatch_group_enter(group); - create_client_socket(&clientfd, &sa); - DEBUG("CLIENT %d\n", clientfd); - create_writer(clientfd, ^{}); - - dispatch_group_enter(group); - create_fifo(&rfd, &wfd); - DEBUG("FIFO %d %d\n", rfd, wfd); - create_writer(wfd, ^{}); - create_reader(rfd, ^{ dispatch_group_leave(group); }); -#endif - - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - } - fprintf(stdout, "pid %d\n", getpid()); - dispatch_main(); - - return 0; -} diff --git a/testing/float_parsing.c b/testing/float_parsing.c deleted file mode 100644 index 716bf50..0000000 --- a/testing/float_parsing.c +++ /dev/null @@ -1,136 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAXLAPS (512 * 1024) - -static void -test(size_t LAPS, char *nums) -{ - uint64_t concurrent_cycles; - uint64_t serial_cycles; - char **result_strings; - char *nums_off; - double *results; - size_t i; - - result_strings = calloc(1, sizeof(char *) * LAPS); - assert(result_strings); - - results = calloc(1, sizeof(double) * LAPS); - assert(results); - - printf("%zu random floats\n", LAPS); - - i = 0; - nums_off = nums; - do { - result_strings[i] = nums_off; - do { - nums_off++; - assert(*nums_off); - } while (*nums_off != '\n'); - i++; - nums_off++; - } while (i < LAPS); - - for (i = 0; i < LAPS; i++) { - assert(result_strings[i]); - } - - concurrent_cycles = dispatch_benchmark(10, ^{ - dispatch_apply(LAPS, dispatch_get_concurrent_queue(0), ^(size_t idx) { - results[idx] = strtod(result_strings[idx], NULL); - }); - }); - - for (i = 0; i < LAPS; i++) { - assert(results[i]); - } - - serial_cycles = dispatch_benchmark(10, ^{ - size_t k = 0; - do { - results[k] = strtod(result_strings[k], NULL); - k++; - } while (k < LAPS); - }); - - for (i = 0; i < LAPS; i++) { - assert(results[i]); - } - - printf( "\tserial cycles:\t%llu\n" - "\tapply() cycles:\t%llu\n" - "\tserial / concurrent: %.2Lf\n", - serial_cycles, concurrent_cycles, - (long double)serial_cycles / (long double)concurrent_cycles); - - free(result_strings); - free(results); -} - -int -main(void) -{ - char path[PATH_MAX] = "/tmp/random_floats_XXXXXX"; - struct stat sb; - double words[1000]; - char buf[1024]; - char *nums; - int fd, rfd; - size_t i, j; - ssize_t r; - - rfd = open("/dev/random", O_RDONLY); - assert(rfd != -1); - - fd = mkstemp(path); - assert(fd != -1); - - r = unlink(path); - assert(r != -1); - - i = 0; - do { - r = read(rfd, words, sizeof(words)); - assert(r == sizeof(words)); - for (j = 0; j < 1000; j++) { - if (isnormal(words[j])) { - r = write(fd, buf, snprintf(buf, sizeof(buf), "%.20e\n", words[j])); - assert(r != -1); - i++; - } - } - } while (i < MAXLAPS); - - r = close(rfd); - assert(r != -1); - - r = fstat(fd, &sb); - assert(r != -1); - - nums = mmap(NULL, sb.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0); - assert(nums != MAP_FAILED); - - for (i = MAXLAPS; i > 0; i /= 2) { - test(i, nums); - } - - r = munmap(nums, sb.st_size); - assert(r != -1); - - r = close(fd); - assert(r != -1); - - return 0; -} diff --git a/testing/fork-join.c b/testing/fork-join.c deleted file mode 100644 index d1b0e57..0000000 --- a/testing/fork-join.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -int -main(void) -{ - long double nano_per_lap; - size_t i, cnt = 1000000; - dispatch_future_t *df; - uint64_t s, e; - - df = malloc(cnt * sizeof(df)); - assert(df); - - s = mach_absolute_time(); - - for (i = 0; i < cnt; i++) { - df[i] = dispatch_fork(dispatch_get_concurrent_queue(0), ^{ - }); - assert(df[i]); - } - - for (i = 0; i < cnt; i++) { - dispatch_join(df[i]); - } - - e = mach_absolute_time(); - - nano_per_lap = (e - s); - nano_per_lap /= cnt; - - printf("%Lf nanoseconds per lap\n", nano_per_lap); - - return 0; -} diff --git a/testing/func.c b/testing/func.c deleted file mode 100644 index 223fd84..0000000 --- a/testing/func.c +++ /dev/null @@ -1,9 +0,0 @@ -extern "C" { -void -func(void) -{ -} -#ifdef __BLOCKS__ -void (^block)(void) = ^{ }; -#endif -}; diff --git a/testing/harness.c b/testing/harness.c deleted file mode 100644 index d0430c2..0000000 --- a/testing/harness.c +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -extern char **environ; - -int -main(int argc, char *argv[]) -{ - dispatch_source_t tmp_ds; - int res; - pid_t pid; - - if (argc < 2) { - fprintf(stderr, "usage: harness [...]\n"); - exit(1); - } - - posix_spawnattr_t attr; - res = posix_spawnattr_init(&attr); - assert(res == 0); - res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); - assert(res == 0); - - int i; - char** newargv = calloc(argc, sizeof(void*)); - for (i = 1; i < argc; ++i) { - newargv[i-1] = argv[i]; - } - newargv[i-1] = NULL; - - res = posix_spawnp(&pid, newargv[0], NULL, &attr, newargv, environ); - if (res) { - errno = res; - perror(newargv[0]); - exit(EXIT_FAILURE); - } - //fprintf(stderr, "pid = %d\n", pid); - assert(pid > 0); - - dispatch_queue_t main_q = dispatch_get_main_queue(); - - tmp_ds = dispatch_source_proc_create(pid, DISPATCH_PROC_EXIT, NULL, main_q, - ^(dispatch_event_t ev __attribute__((unused))) { - int status; - int res2 = waitpid(pid, &status, 0); - assert(res2 != -1); - //int passed = (WIFEXITED(status) && WEXITSTATUS(status) == 0); - test_long("Process exited", WEXITSTATUS(status) | WTERMSIG(status), 0); - exit(0); - }); - assert(tmp_ds); - - uint64_t timeout = 30LL * NSEC_PER_SEC; - - tmp_ds = dispatch_source_timer_create(DISPATCH_TIMER_ONESHOT, timeout, 0, NULL, main_q, - ^(dispatch_event_t ev __attribute__((unused))) { - kill(pid, SIGKILL); - fprintf(stderr, "Terminating unresponsive process (%0.1lfs)\n", (double)timeout/NSEC_PER_SEC); - }); - assert(tmp_ds); - - signal(SIGINT, SIG_IGN); - tmp_ds = dispatch_source_signal_create(SIGINT, NULL, main_q, - ^(dispatch_event_t ev __attribute__((unused))) { - fprintf(stderr, "Terminating process due to signal\n"); - kill(pid, SIGKILL); - }); - assert(tmp_ds); - - kill(pid, SIGCONT); - - dispatch_main(); - - return 0; -} diff --git a/testing/leaks-wrapper b/testing/leaks-wrapper deleted file mode 100755 index 6e9f9ed..0000000 --- a/testing/leaks-wrapper +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -/usr/bin/leaks "$@" 2>&1 | tee "$@.leakslog" | grep -q " 0 leaks for 0 total leaked bytes" - -if [ $? -eq 0 ]; then - rm "$@.leakslog" - exit 0 -else - exit $? -fi diff --git a/testing/mach_server.c b/testing/mach_server.c deleted file mode 100644 index 24535ae..0000000 --- a/testing/mach_server.c +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 - -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] : ""; - - kr = bootstrap_look_up(bootstrap_port, "com.apple.test.mach-server", - &mp); - printf("lookup %s\n", !kr ? "successful" : mach_error_string(kr)); - - strlcpy(msg.payload, str, sizeof(msg.payload)); - msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE); - msg.header.msgh_size = round_msg(sizeof(msg.header) + strlen(msg.payload) + 1); - msg.header.msgh_remote_port = mp; - msg.header.msgh_local_port = MACH_PORT_NULL; - msg.header.msgh_id = 0x12345678; - kr = mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - printf("client send %s\n", !kr ? "successful" : mach_error_string(kr)); -} - -int main(int argc, char* argv[]) -{ - if (argc > 1 && strcmp(argv[1], "-launchd") == 0) { - mach_port_t mp = checkin(); - printf("checkin %s\n", mp ? "successful" : "failed"); - if (mp) { - kern_return_t kr; - kr = mach_port_set_context(mach_task_self(), mp, (mach_vm_address_t)"phear"); - - dispatch_source_t mig = dispatch_source_mig_create(mp, 0 /* max msg size */, - NULL, dispatch_get_main_queue(), my_mig_callback); - dispatch_main(); - } - } else if (argc > 1) { - client(argc, argv); - } else { - // load the job - load(); - } - - return 0; -} - diff --git a/testing/mig/Makefile b/testing/mig/Makefile deleted file mode 100644 index bd44a2f..0000000 --- a/testing/mig/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -CFLAGS = -g -Os -Wall -Wextra -Wshadow -Wmissing-prototypes -Wmissing-declarations -Werror - -all: client server - -hello_logger.h hello_loggerServer.c hello_loggerUser.c hello_loggerServer.h: hello_logger.defs - mig -sheader hello_loggerServer.h hello_logger.defs - -client.o: hello_logger.h - -server.o: hello_loggerServer.h - -client: hello_loggerUser.o client.o - -server: hello_loggerServer.o server.o - -clean: - -rm -f *.o client server hello_logger.h hello_loggerServer.c hello_loggerUser.c hello_loggerServer.h diff --git a/testing/mig/client.c b/testing/mig/client.c deleted file mode 100644 index 15fc515..0000000 --- a/testing/mig/client.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "hello_logger.h" - -int main(int argc, char *argv[]) -{ - kern_return_t kr; - mach_port_t mp; - - if (argc != 2) { - fprintf(stderr, "I need a string to send!\n"); - exit(EXIT_FAILURE); - } - - kr = bootstrap_look_up(bootstrap_port, HELLO_LOGGER_BOOTSTRAP_NAME, &mp); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "bootstrap_look_up(): %s\n", bootstrap_strerror(kr)); - exit(EXIT_FAILURE); - } - - kr = example(mp, argv[1]); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "test_hello_logger(): %s\n", mach_error_string(kr)); - exit(EXIT_FAILURE); - } - - exit(EXIT_SUCCESS); -} diff --git a/testing/mig/hello_logger.defs b/testing/mig/hello_logger.defs deleted file mode 100644 index 0eb8c38..0000000 --- a/testing/mig/hello_logger.defs +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -import "hello_logger_types.h"; - -type string_t = c_string[*:1024]; - -subsystem hello_logger 12345; -serverprefix do_; - -routine example( - test_port : mach_port_t; - some_string : string_t); diff --git a/testing/mig/hello_logger_types.h b/testing/mig/hello_logger_types.h deleted file mode 100644 index a3bafb2..0000000 --- a/testing/mig/hello_logger_types.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _HELLO_WORLD_TYPES_H_ -#define _HELLO_WORLD_TYPES_H_ - -#define HELLO_LOGGER_BOOTSTRAP_NAME "com.example.hello_logger" - -typedef char *string_t; - -#endif diff --git a/testing/mig/server.c b/testing/mig/server.c deleted file mode 100644 index fb1e19f..0000000 --- a/testing/mig/server.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hello_logger.h" -#include "hello_loggerServer.h" - -static mach_port_t checkin_or_register(char *bname); - -int main(void) -{ - mach_port_t mp = checkin_or_register(HELLO_LOGGER_BOOTSTRAP_NAME); - dispatch_source_t ds = dispatch_source_mig_new(mp, do_hello_logger_subsystem.maxsize, hello_logger_server, NULL, NULL, NULL); - - assert(ds); - - dispatch_main(); - - exit(EXIT_SUCCESS); -} - -kern_return_t -do_example(mach_port_t test_port __attribute__((unused)), string_t somestring) -{ - fprintf(stdout, "%s\n", somestring); - - return KERN_SUCCESS; -} - -mach_port_t -checkin_or_register(char *bname) -{ - kern_return_t kr; - mach_port_t mp; - - /* If we're started by launchd or the old mach_init */ - kr = bootstrap_check_in(bootstrap_port, bname, &mp); - if (kr == KERN_SUCCESS) - return mp; - - abort(); -} diff --git a/testing/nsoperation.m b/testing/nsoperation.m deleted file mode 100644 index bb5b065..0000000 --- a/testing/nsoperation.m +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#include "dispatch_test.h" - -@interface MYOperation : NSOperation -{ -} -@end - -@implementation MYOperation - -- (id) init -{ - self = [super init]; - return self; -} - -- (void)main -{ - test_stop(); -} - -@end - -int -main(void) -{ - test_start("NSOperation"); - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease]; - test_ptr_notnull("NSOperationQueue", queue); - - MYOperation *operation = [[MYOperation alloc] init]; - test_ptr_notnull("NSOperation", operation); - - [queue addOperation:operation]; - [operation release]; - - [[NSRunLoop mainRunLoop] run]; - - [pool release]; - - return 0; -} diff --git a/testing/queue_finalizer.c b/testing/queue_finalizer.c deleted file mode 100644 index 6411b9a..0000000 --- a/testing/queue_finalizer.c +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dispatch_test.h" - -int main(void) { - long res; - - test_start("Dispatch Queue Finalizer"); - -#ifdef __LP64__ - void* ctxt_magic = (void*)((uintptr_t)arc4random() << 32 | arc4random()); -#else - void* ctxt_magic = (void*)arc4random(); -#endif - - dispatch_queue_attr_t attr = dispatch_queue_attr_create(); - test_ptr_notnull("dispatch_queue_attr_create", attr); - - __block long finalizer_ran = 0; - - res = dispatch_queue_attr_set_finalizer(attr, ^(dispatch_queue_t dq) { - void* ctxt = dispatch_queue_get_context(dq); - test_ptr("dispatch_queue_get_context", ctxt, ctxt_magic); - test_ptr_notnull("finalizer ran", dq); - test_stop(); - }); - test_long("dispatch_queue_attr_set_finalizer", res, 0); - - dispatch_queue_t q = dispatch_queue_create(NULL, attr); - test_ptr_notnull("dispatch_queue_new", q); - - dispatch_queue_set_context(q, ctxt_magic); - - dispatch_release(attr); - - dispatch_release(q); - - dispatch_main(); - - return 0; -} diff --git a/testing/slice_benchmarks.c b/testing/slice_benchmarks.c deleted file mode 100644 index 0f9c054..0000000 --- a/testing/slice_benchmarks.c +++ /dev/null @@ -1,445 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// #include "../src/private.h" -#include - -// "normal" loop size -#define LOOP 100000 -#define SMALL_LOOP 1000 - -void report(const char *func, char *full_name, double x, unsigned long loops, char *unit) { - // XXX: make cols pretty & stuff - const char *prefix = "bench_"; - const int plen = strlen(prefix); - assert(!strncmp(func, prefix, plen)); - func += plen; - char *name; - asprintf(&name, "[%s] %s", func, full_name); - assert(name); - - x /= loops; - - if (!strcmp("mach", unit)) { - static mach_timebase_info_data_t mtb; - if (!mtb.denom) { - (void)mach_timebase_info(&mtb); - } - x = (x * mtb.numer) / mtb.denom; - unit = "ns"; - } - - printf("%-64s %13f%-2s\n", name, x, unit); - free(name); -} - -void bench_queue_mem_use(void) { - struct proc_taskinfo pti; - uint64_t target_size; - - // The 1st call eats a little memory that isn't accounted for - // until the 2nd call. Also the _first_ printf eats >1M, so - // if you insert some for debugging make sure it isn't the first! - proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &pti, sizeof(pti)); - proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &pti, sizeof(pti)); - target_size = pti.pti_virtual_size + 1024*1024; - int n; - - for(n = 0; target_size >= pti.pti_virtual_size; n++) { - dispatch_queue_t leak = dispatch_queue_create("to be deleted", NULL); - assert(leak); - proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &pti, sizeof(pti)); - //printf("pti_virtual_size %qd; togo %qd, n %d\n", pti.pti_virtual_size, target_size - pti.pti_virtual_size, n); - } - - report(__FUNCTION__, "#queues to grow VSIZE 1Mbyte", n-1, 1, "x"); -} - -void bench_message_round_trip(void) { - dispatch_queue_t q1 = dispatch_queue_create("q1", NULL); - dispatch_queue_t q2 = dispatch_queue_create("q2", NULL); - uint64_t start = mach_absolute_time(); - - int i; - for(i = 0; i < LOOP; i++) { - // make sure we don't build up too much of a backlog - if (i && !(i & 0x3ff)) { - dispatch_sync(q2, ^{}); - } - dispatch_queue_retain(q2); - dispatch_async(q1, ^{ - dispatch_async(q2, ^{ - dispatch_queue_release(q2); - }); - }); - } - - // Make sure eveything has drained before we take the end timestamp - dispatch_sync(q1, ^{}); - dispatch_sync(q2, ^{}); - - uint64_t end = mach_absolute_time(); - report(__FUNCTION__, "round trip (async async - implicit copy)", (end - start), LOOP, "mach"); - dispatch_queue_release(q1); - dispatch_queue_release(q2); -} - -void bench_precopy_message_round_trip(void) { - dispatch_queue_t q1 = dispatch_queue_create("q1", NULL); - dispatch_queue_t q2 = dispatch_queue_create("q2", NULL); - assert(q1 && q2); - - unsigned long rc; - - dispatch_block_t b2 = Block_copy(^{ - }); - dispatch_block_t b1 = Block_copy(^{ - unsigned long rc = dispatch_async(q2, b2); - assert(!rc); - dispatch_queue_release(q2); - }); - dispatch_block_t be = Block_copy(^{}); - assert(b1 && b2); - uint64_t start = mach_absolute_time(); - - int i; - for(i = 0; i < LOOP; i++) { - // make sure we don't build up too much of a backlog - if (i && !(i & 0x3ff)) { - dispatch_sync(q2, be); - } - dispatch_queue_retain(q2); - rc = dispatch_async(q1, b1); - assert(!rc); - } - - // Make sure eveything has drained before we take the end timestamp - dispatch_sync(q1, be); - dispatch_sync(q2, be); - - uint64_t end = mach_absolute_time(); - report(__FUNCTION__, "round trip (a/a - precopy)", (end - start), LOOP, "mach"); - dispatch_queue_release(q1); - dispatch_queue_release(q2); -} - -void bench_message_round_type_syncasync(void) { - dispatch_queue_t q1 = dispatch_queue_create("q1", NULL); - dispatch_queue_t q2 = dispatch_queue_create("q2", NULL); - uint64_t start = mach_absolute_time(); - - int i; - for(i = 0; i < LOOP; i++) { - dispatch_queue_retain(q2); - dispatch_sync(q1, ^{ - dispatch_async(q2, ^{ - dispatch_queue_release(q2); - }); - }); - } - - // Make sure eveything has drained before we take the end timestamp - dispatch_sync(q1, ^{}); - dispatch_sync(q2, ^{}); - - uint64_t end = mach_absolute_time(); - report(__FUNCTION__, "round trip (s/a - implicit copy)", (end - start), LOOP, "mach"); - dispatch_queue_release(q1); - dispatch_queue_release(q2); -} - -void nothing_f(void *ignored) { -} - -void brt_f_q1(void *vq2) { - unsigned long rc = dispatch_async_f((dispatch_queue_t)vq2, NULL, nothing_f); - assert(!rc); -} - -void bench_message_round_trip_f(void) { - dispatch_queue_t q1 = dispatch_queue_create("q1", NULL); - dispatch_queue_t q2 = dispatch_queue_create("q2", NULL); - uint64_t start = mach_absolute_time(); - unsigned long rc; - - int i; - for(i = 0; i < LOOP; i++) { - // make sure we don't build up too much of a backlog - if (i && !(i & 0x3ff)) { - dispatch_sync_f(q2, NULL, nothing_f); - } - rc = dispatch_async_f(q1, q2, brt_f_q1); - assert(!rc); - } - - // Make sure eveything has drained before we take the end timestamp - dispatch_sync_f(q1, NULL, nothing_f); - dispatch_sync_f(q2, NULL, nothing_f); - - uint64_t end = mach_absolute_time(); - report(__FUNCTION__, "round trip (a/a - no blocks)", (end - start), LOOP, "mach"); - dispatch_queue_release(q1); - dispatch_queue_release(q2); -} - -void bench_message_round_type_syncasync_f(void) { -} - -struct baton { - // should extend to keep data on times for latency calc - int passes_left; - int at_q; - int baton_number; - - // Avoid false ache line shares. Big speed difference on a Mac Pro - char pad[128 - sizeof(int)*3]; -}; - -pthread_mutex_t kludge; -static int n_baton_kludge; - -void pass(dispatch_queue_t *q, struct baton *bat, const int n_queues, dispatch_queue_t complete_q) { - //fprintf(stderr, "bat#%d q#%d, passes left: %d\n", bat->baton_number, bat->at_q, bat->baton_number); - if (0 == --(bat->passes_left)) { - dispatch_queue_resume(complete_q); - // XXX: atomic - if (!__sync_sub_and_fetch(&n_baton_kludge, 1)) { - pthread_mutex_unlock(&kludge); - } - return; - } - bat->at_q = (bat->at_q + 1) % n_queues; - unsigned long rc = dispatch_async(q[bat->at_q], ^{ pass(q, bat, n_queues, complete_q); }); - assert(rc == 0); -} - -void bench_baton() { - const int n_queues = 128; - const int q_div_b = 4; - const int n_batons = n_queues / q_div_b; - assert(q_div_b * n_batons == n_queues); - n_baton_kludge = n_batons; - dispatch_queue_t *q; - dispatch_queue_t complete_q = dispatch_queue_create("completion q", NULL);; - char *q_labels[n_queues]; - int i; - unsigned long rc; - - // creting a queue ("C"), suspending it, blocking in a dispatch_sync, and - // having another queue resume C does not appear to ever unblock the - // dispatch_sync. XXX: make test case and file radar. (if it still - // works that way on recent builds, with dispatch inside libsystem, and - // such) - - - pthread_mutex_init(&kludge, NULL); - rc = pthread_mutex_trylock(&kludge); - assert(!rc); - q = alloca(n_queues * sizeof(dispatch_queue_t)); - - for(i = 0; i < n_queues; i++) { - asprintf(q_labels + i, "relay#%d (%s)", i, __FUNCTION__); - assert(q_labels[i]); - q[i] = dispatch_queue_create(q_labels[i], NULL); - assert(q[i]); - } - - uint64_t start_time = mach_absolute_time(); - - for(i = 0; i < n_queues; i += q_div_b) { - struct baton *bat = valloc(sizeof(struct baton)); - assert(bat); - bat->passes_left = SMALL_LOOP; - bat->at_q = i; - bat->baton_number = i / q_div_b; - dispatch_queue_suspend(complete_q); - rc = dispatch_async(q[i], ^{ - pass(q, bat, n_queues, complete_q); - }); - assert(rc == 0); - } - - // XXX: dispatch_sync(complete_q, ^{}); - rc = pthread_mutex_lock(&kludge); - assert(!rc); - uint64_t end_time = mach_absolute_time(); - report(__FUNCTION__, "baton pass", (end_time - start_time), SMALL_LOOP*n_batons, "mach"); - // dispatch_queue_release(q); -} - -void bench_overload2() { - const int n_queues = 128; - const int q_div_b = 1; - const int n_batons = n_queues / q_div_b; - n_baton_kludge = n_batons; - assert(q_div_b * n_batons == n_queues); - dispatch_queue_t *q = alloca(n_queues * sizeof(dispatch_queue_t)); - dispatch_source_t *ds = alloca(n_queues * sizeof(dispatch_source_t)); - dispatch_queue_t complete_q = dispatch_queue_create("completion q", NULL); - __block uint64_t start_time = 0; - uint64_t time_to_start; - uint64_t end_time; - char *q_labels[n_queues]; - int i; - unsigned int rc; - - rc = pthread_mutex_unlock(&kludge); - assert(!rc); - rc = pthread_mutex_trylock(&kludge); - assert(!rc); - - // Start all batons one to two seconds from now. - time_to_start = (2 + time(NULL)) * 1000000000; - - for(i = 0; i < n_queues; i++) { - asprintf(q_labels + i, "queue#%d (%s)", i, __FUNCTION__); - assert(q_labels[i]); - q[i] = dispatch_queue_create(q_labels[i], NULL); - assert(q[i]); - struct baton *bat = valloc(sizeof(struct baton)); - assert(bat); - bat->passes_left = SMALL_LOOP; - bat->at_q = i; - bat->baton_number = i / q_div_b; - dispatch_queue_suspend(complete_q); - ds[i] = dispatch_source_timer_create(DISPATCH_TIMER_ABSOLUTE, time_to_start, 0, NULL, q[i], ^(dispatch_event_t event){ - assert(!dispatch_event_get_error(event, NULL)); - // We want to measure the time from the first - // baton pass, and NOT include hte wait time - // for eveyone to start to fire - if (!start_time) { - uint64_t s = mach_absolute_time(); - __sync_bool_compare_and_swap(&start_time, 0, s); - } - pass(q, bat, n_queues, complete_q); - }); - assert(ds[i]); - } - - // XXX: dispatch_sync(complete_q, ^{}); - rc = pthread_mutex_lock(&kludge); - assert(!rc); - - end_time = mach_absolute_time(); - report(__FUNCTION__, "overload#2", (end_time - start_time), SMALL_LOOP*n_batons, "mach"); - // Many releases and free()s - -} - -void bench_overload1() { - const int n_queues = 128; - const int q_div_b = 1; - const int n_batons = n_queues / q_div_b; - n_baton_kludge = n_batons; - assert(q_div_b * n_batons == n_queues); - dispatch_queue_t *q = alloca(n_queues * sizeof(dispatch_queue_t)); - dispatch_queue_t complete_q = dispatch_queue_create("completion q", NULL); - __block uint64_t start_time = 0; - struct timeval time_to_start; - uint64_t end_time; - char *q_labels[n_queues]; - int i; - unsigned int rc; - - rc = pthread_mutex_unlock(&kludge); - assert(!rc); - rc = pthread_mutex_trylock(&kludge); - assert(!rc); - - // Start all batons one to two seconds from now. - gettimeofday(&time_to_start, NULL); - time_to_start.tv_sec += 2; - - for(i = 0; i < n_queues; i++) { - asprintf(q_labels + i, "queue#%d (%s)", i, __FUNCTION__); - assert(q_labels[i]); - q[i] = dispatch_queue_create(q_labels[i], NULL); - assert(q[i]); - struct baton *bat = valloc(sizeof(struct baton)); - assert(bat); - bat->passes_left = SMALL_LOOP; - bat->at_q = i; - bat->baton_number = i / q_div_b; - dispatch_queue_suspend(complete_q); - dispatch_async(q[i], ^(void) { - struct timeval now; - gettimeofday(&now, NULL); - int sec = time_to_start.tv_sec - now.tv_sec; - if (sec >= 0) { - int usec = time_to_start.tv_usec + now.tv_usec; - if (usec > 0 || sec > 0) { - usleep(1000000 * sec + usec); - } else { - // XXX: log here - } - } - - // We want to measure the time from the first - // baton pass, and NOT include hte wait time - // for eveyone to start to fire - if (!start_time) { - uint64_t s = mach_absolute_time(); - __sync_bool_compare_and_swap(&start_time, 0, s); - } - - pass(q, bat, n_queues, complete_q); - }); - } - - // XXX: dispatch_sync(complete_q, ^{}); - rc = pthread_mutex_lock(&kludge); - assert(!rc); - - end_time = mach_absolute_time(); - report(__FUNCTION__, "overload#1", (end_time - start_time), SMALL_LOOP*n_batons, "mach"); - // Many releases and free()s - -} - -int main(int argc, char *argv[]) { - // Someday we will be able to take a list of tests to run, or exclude, or something. - - // There are somewhat diffrent perfomance chararistics when using the - // main queue, so we use a "normal" queue for all our tests. - dispatch_queue_t bench_q = dispatch_queue_create("benhmark Q", NULL); - - dispatch_async(bench_q, ^{ - // These two aren't as intresting in duel core, they queue all - // the calls before making them which isn't really what we - // want to test, is it? It also limites the number of loops - // we can spin around. -#if 1 - bench_message_round_trip(); - bench_precopy_message_round_trip(); - - bench_message_round_type_syncasync(); - bench_message_round_trip_f(); - bench_message_round_type_syncasync_f(); -#endif - bench_baton(); - bench_overload1(); - bench_overload2(); - - // This leaks, so we run it last. Also it gives - // wrong results if stdio hasn't been started already, - // so we definitly don't want to run it first even if - // the leaks are fixed (or ignored) - bench_queue_mem_use(); - - exit(0); - }); - - dispatch_main(); -} diff --git a/testing/summarize.c b/testing/summarize.c deleted file mode 100644 index 6eeb0d0..0000000 --- a/testing/summarize.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include - -int -has_prefix(const char* str, const char* prefix) { - return (strncmp(str, prefix, strlen(prefix)) == 0); -} - -int -print_summary(FILE* f, long total, long pass, long fail) { - fprintf(f, "Total: %ld\n", total); - fprintf(f, "Passed: %ld (%0.0lf%%)\n", pass, ((double)pass / (double)total) * (double)100.0); - fprintf(f, "Failed: %ld (%0.0lf%%)\n", fail, ((double)fail / (double)total) * (double)100.0); - fprintf(f, "\n"); - return 0; -} - -int main(int argc, char* argv[]) { - if (argc > 1) { - fprintf(stderr, "%s: usage: summarize\n", argv[0]); - exit(1); - } - - /* - FILE* f = fopen(argv[1], "w"); - if (f == NULL) { - perror(argv[1]); - exit(1); - } - */ - FILE* f = stdout; - - fprintf(f, "\n==================================================\n"); - fprintf(f, "[SUMMARY] Test Summary\n"); - fprintf(f, "==================================================\n\n"); - - size_t len; - char* ln; - long total = 0; - long pass = 0; - long fail = 0; - long total_total = 0; - long total_pass = 0; - long total_fail = 0; - for(;;) { - ln = fgetln(stdin, &len); - //if (ln) fprintf(stdout, "%.*s", (int)len, ln); - if (ln == NULL || has_prefix(ln, "[TEST]")) { - if (total) { - print_summary(f, total, pass, fail); - } - total_total += total; - total_pass += pass; - total_fail += fail; - total = 0; - pass = 0; - fail = 0; - if (ln) { - fprintf(f, "%.*s", (int)len, ln); - } else { - fprintf(f, "[TOTAL]\n"); - print_summary(f, total_total, total_pass, total_fail); - break; - } - } else if (has_prefix(ln, "[PASS]")) { - ++total; - ++pass; - } else if (has_prefix(ln, "[FAIL]")) { - ++total; - ++fail; - } - } - - return (total_fail ? EXIT_FAILURE : EXIT_SUCCESS); -} diff --git a/testing/test.c b/testing/test.c deleted file mode 100644 index 631cb3b..0000000 --- a/testing/test.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -int -main(void) -{ - void (^wb)(dispatch_item_t) = ^(dispatch_item_t di) { - printf("%p\t%p\t%s:\t%llu\n", pthread_self(), di, __func__, mach_absolute_time()); - }; - void (^cb)(dispatch_item_t) = ^(dispatch_item_t di) { - printf("%p\t%p\t%s:\t%llu\n", pthread_self(), di, __func__, mach_absolute_time()); - }; - dispatch_queue_t q; - dispatch_item_t di_r; - size_t i; - bool r; - - q = dispatch_queue_new("test", 0, NULL, NULL, NULL); - assert(q != NULL); - - for (i = 0; i < 1000; i++) { - r = dispatch_call_wait(q, wb, NULL); - assert(r); - } - - printf("done with dispatch_call_wait()\n"); - - r = dispatch_apply_wait(wb, 10, NULL); - assert(r); - - r = dispatch_call(q, wb, cb, NULL, &di_r); - assert(r); - assert(di_r); - - printf("waiting for dispatch_call() callback\n"); - - dispatch_main(); - - return 0; -} diff --git a/testing/yet-another-apply-test.c b/testing/yet-another-apply-test.c deleted file mode 100644 index 7329cdf..0000000 --- a/testing/yet-another-apply-test.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 \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); -} diff --git a/tools/dispatch_trace.d b/tools/dispatch_trace.d new file mode 100755 index 0000000..9059e4e --- /dev/null +++ b/tools/dispatch_trace.d @@ -0,0 +1,76 @@ +#!/usr/sbin/dtrace -Z -s + +/* + * Copyright (c) 2010-2011 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@ + */ + +/* + * Usage: dispatch_dtrace.d -p [pid] + * traced process must have been executed with + * DYLD_IMAGE_SUFFIX=_profile or DYLD_IMAGE_SUFFIX=_debug + */ + +#pragma D option quiet +#pragma D option bufsize=16m + +BEGIN { + printf("%-8s %-3s %-8s %-35s%-15s%-?s %-43s%-?s %-14s%-?s %s\n", + "Time us", "CPU", "Thread", "Function", "Probe", "Queue", "Label", + "Item", "Kind", "Context", "Symbol"); +} + +dispatch$target:libdispatch_profile.dylib::queue-push, +dispatch$target:libdispatch_debug.dylib::queue-push, +dispatch$target:libdispatch_profile.dylib::queue-pop, +dispatch$target:libdispatch_debug.dylib::queue-pop, +dispatch$target:libdispatch_profile.dylib::callout-entry, +dispatch$target:libdispatch_debug.dylib::callout-entry, +dispatch$target:libdispatch_profile.dylib::callout-return, +dispatch$target:libdispatch_debug.dylib::callout-return /!start/ { + start = walltimestamp; +} + +/* probe queue-push/-pop(dispatch_queue_t queue, const char *label, + * dispatch_object_t item, const char *kind, + * dispatch_function_t function, void *context) + */ +dispatch$target:libdispatch_profile.dylib::queue-push, +dispatch$target:libdispatch_debug.dylib::queue-push, +dispatch$target:libdispatch_profile.dylib::queue-pop, +dispatch$target:libdispatch_debug.dylib::queue-pop { + printf("%-8d %-3d 0x%08p %-35s%-15s0x%0?p %-43s0x%0?p %-14s0x%0?p", + (walltimestamp-start)/1000, cpu, tid, probefunc, probename, arg0, + copyinstr(arg1, 42), arg2, copyinstr(arg3, 13), arg5); + usym(arg4); + printf("\n"); +} + +/* probe callout-entry/-return(dispatch_queue_t queue, const char *label, + * dispatch_function_t function, void *context) + */ +dispatch$target:libdispatch_profile.dylib::callout-entry, +dispatch$target:libdispatch_debug.dylib::callout-entry, +dispatch$target:libdispatch_profile.dylib::callout-return, +dispatch$target:libdispatch_debug.dylib::callout-return { + printf("%-8d %-3d 0x%08p %-35s%-15s0x%0?p %-43s%-?s %-14s0x%0?p", + (walltimestamp-start)/1000, cpu, tid, probefunc, probename, arg0, + copyinstr(arg1, 42), "", "", arg3); + usym(arg2); + printf("\n"); +} diff --git a/xcodeconfig/libdispatch-resolved.xcconfig b/xcodeconfig/libdispatch-resolved.xcconfig new file mode 100644 index 0000000..70e405f --- /dev/null +++ b/xcodeconfig/libdispatch-resolved.xcconfig @@ -0,0 +1,25 @@ +// +// Copyright (c) 2010-2011 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@ +// + +SUPPORTED_PLATFORMS = iphoneos +PRODUCT_NAME = libdispatch_$(DISPATCH_RESOLVED_VARIANT) +OTHER_LDFLAGS = +SKIP_INSTALL = YES +VERSIONING_SYSTEM = diff --git a/xcodeconfig/libdispatch-resolver.xcconfig b/xcodeconfig/libdispatch-resolver.xcconfig new file mode 100644 index 0000000..d8abe3d --- /dev/null +++ b/xcodeconfig/libdispatch-resolver.xcconfig @@ -0,0 +1,20 @@ +// +// Copyright (c) 2010-2011 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@ +// + diff --git a/xcodeconfig/libdispatch.xcconfig b/xcodeconfig/libdispatch.xcconfig new file mode 100644 index 0000000..e7d44f4 --- /dev/null +++ b/xcodeconfig/libdispatch.xcconfig @@ -0,0 +1,67 @@ +// +// Copyright (c) 2010-2011 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 "/Makefiles/CoreOS/Xcode/BSD.xcconfig" +SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator +ARCHS[sdk=iphonesimulator*] = $(NATIVE_ARCH_32_BIT) // Override BSD.xcconfig ARCHS +PRODUCT_NAME = libdispatch +PRODUCT_NAME[sdk=iphonesimulator*] = libdispatch_sim +EXECUTABLE_PREFIX = +LD_DYLIB_INSTALL_NAME = /usr/lib/system/$(EXECUTABLE_NAME) +INSTALL_PATH = /usr/lib/system +INSTALL_PATH[sdk=iphonesimulator*] = $(SDKROOT)/usr/lib/system +PUBLIC_HEADERS_FOLDER_PATH = /usr/include/dispatch +PUBLIC_HEADERS_FOLDER_PATH[sdk=iphonesimulator*] = $(SDKROOT)/usr/include/dispatch +PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/dispatch +PRIVATE_HEADERS_FOLDER_PATH[sdk=iphonesimulator*] = $(SDKROOT)/usr/local/include/dispatch +HEADER_SEARCH_PATHS = $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders $(PROJECT_DIR) +INSTALLHDRS_SCRIPT_PHASE = YES +ALWAYS_SEARCH_USER_PATHS = NO +BUILD_VARIANTS = normal debug profile +ONLY_ACTIVE_ARCH = NO +GCC_VERSION = com.apple.compilers.llvm.clang.1_0 +GCC_STRICT_ALIASING = YES +GCC_SYMBOLS_PRIVATE_EXTERN = YES +GCC_CW_ASM_SYNTAX = NO +GCC_ENABLE_CPP_EXCEPTIONS = NO +GCC_ENABLE_CPP_RTTI = NO +GCC_ENABLE_OBJC_EXCEPTIONS = NO +GCC_ENABLE_PASCAL_STRINGS = NO +GCC_WARN_SHADOW = YES +GCC_WARN_64_TO_32_BIT_CONVERSION = YES +GCC_WARN_ABOUT_RETURN_TYPE = YES +GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES +GCC_WARN_ABOUT_MISSING_NEWLINE = YES +GCC_WARN_UNUSED_VARIABLE = YES +GCC_TREAT_WARNINGS_AS_ERRORS = YES +GCC_OPTIMIZATION_LEVEL = s +GCC_THUMB_SUPPORT[arch=armv6] = NO +GCC_PREPROCESSOR_DEFINITIONS = __DARWIN_NON_CANCELABLE=1 +GCC_PREPROCESSOR_DEFINITIONS[sdk=iphonesimulator*] = $(GCC_PREPROCESSOR_DEFINITIONS) USE_LIBDISPATCH_INIT_CONSTRUCTOR=1 DISPATCH_USE_PTHREAD_ATFORK=1 DISPATCH_USE_DIRECT_TSD=0 +WARNING_CFLAGS = -Wall -Wextra -Waggregate-return -Wfloat-equal -Wpacked -Wmissing-declarations -Wstrict-overflow=4 -Wstrict-aliasing=2 +OTHER_CFLAGS = -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-exceptions -fdiagnostics-show-option -fverbose-asm -momit-leaf-frame-pointer +OTHER_CFLAGS_debug = -fstack-protector -fno-inline -O0 -DDISPATCH_DEBUG=1 +OTHER_CFLAGS_profile = -DDISPATCH_PROFILE=1 +GENERATE_PROFILING_CODE = NO +GENERATE_MASTER_OBJECT_FILE = NO +DYLIB_CURRENT_VERSION = $(CURRENT_PROJECT_VERSION) +UMBRELLA_LDFLAGS = -umbrella System +UMBRELLA_LDFLAGS[sdk=iphonesimulator*] = +OTHER_LDFLAGS = $(OTHER_LDFLAGS) $(UMBRELLA_LDFLAGS) $(CR_LDFLAGS) diff --git a/xcodescripts/install-manpages.sh b/xcodescripts/install-manpages.sh new file mode 100755 index 0000000..2d88a26 --- /dev/null +++ b/xcodescripts/install-manpages.sh @@ -0,0 +1,107 @@ +#!/bin/bash -e +# +# Copyright (c) 2010-2011 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@ +# + +if [ "$ACTION" = installhdrs ]; then exit 0; fi +if [ "${RC_ProjectName%_Sim}" != "${RC_ProjectName}" ]; then exit 0; fi + +mkdir -p "$DSTROOT"/usr/share/man/man3 || true +mkdir -p "$DSTROOT"/usr/local/share/man/man3 || true + +# Copy man pages +cd "$SRCROOT"/man +BASE_PAGES="dispatch.3 dispatch_after.3 dispatch_api.3 dispatch_apply.3 \ + dispatch_async.3 dispatch_group_create.3 dispatch_object.3 \ + dispatch_once.3 dispatch_queue_create.3 dispatch_semaphore_create.3 \ + dispatch_source_create.3 dispatch_time.3 dispatch_data_create.3 \ + dispatch_io_create.3 dispatch_io_read.3 dispatch_read.3" + +PRIVATE_PAGES="dispatch_benchmark.3" + +cp ${BASE_PAGES} "$DSTROOT"/usr/share/man/man3 +cp ${PRIVATE_PAGES} "$DSTROOT"/usr/local/share/man/man3 + +# Make hard links (lots of hard links) + +cd "$DSTROOT"/usr/local/share/man/man3 +ln -f dispatch_benchmark.3 dispatch_benchmark_f.3 +chown ${INSTALL_OWNER}:${INSTALL_GROUP} $PRIVATE_PAGES +chmod $INSTALL_MODE_FLAG $PRIVATE_PAGES + +cd $DSTROOT/usr/share/man/man3 + +chown ${INSTALL_OWNER}:${INSTALL_GROUP} $BASE_PAGES +chmod $INSTALL_MODE_FLAG $BASE_PAGES + +ln -f dispatch_after.3 dispatch_after_f.3 +ln -f dispatch_apply.3 dispatch_apply_f.3 +ln -f dispatch_once.3 dispatch_once_f.3 + +for m in dispatch_async_f dispatch_sync dispatch_sync_f; do + ln -f dispatch_async.3 ${m}.3 +done + +for m in dispatch_group_enter dispatch_group_leave dispatch_group_wait \ + dispatch_group_async dispatch_group_async_f dispatch_group_notify \ + dispatch_group_notify_f; do + ln -f dispatch_group_create.3 ${m}.3 +done + +for m in dispatch_retain dispatch_release dispatch_suspend dispatch_resume \ + dispatch_get_context dispatch_set_context dispatch_set_finalizer_f; do + ln -f dispatch_object.3 ${m}.3 +done + +for m in dispatch_semaphore_signal dispatch_semaphore_wait; do + ln -f dispatch_semaphore_create.3 ${m}.3 +done + +for m in dispatch_get_current_queue dispatch_main dispatch_get_main_queue \ + dispatch_get_global_queue dispatch_queue_get_label \ + dispatch_set_target_queue; do + ln -f dispatch_queue_create.3 ${m}.3 +done + +for m in dispatch_source_set_event_handler dispatch_source_set_event_handler_f \ + dispatch_source_set_cancel_handler dispatch_source_set_cancel_handler_f \ + dispatch_source_cancel dispatch_source_testcancel \ + dispatch_source_get_handle dispatch_source_get_mask \ + dispatch_source_get_data dispatch_source_merge_data \ + dispatch_source_set_timer; do + ln -f dispatch_source_create.3 ${m}.3 +done + +ln -f dispatch_time.3 dispatch_walltime.3 + +for m in dispatch_data_create_concat dispatch_data_create_subrange \ + dispatch_data_create_map dispatch_data_apply \ + dispatch_data_copy_region dispatch_data_get_size; do + ln -f dispatch_data_create.3 ${m}.3 +done + +for m in dispatch_io_create_with_path dispatch_io_set_high_water \ + dispatch_io_set_low_water dispatch_io_set_interval \ + dispatch_io_close; do + ln -f dispatch_io_create.3 ${m}.3 +done + +ln -f dispatch_io_read.3 dispatch_io_write.3 + +ln -f dispatch_read.3 dispatch_write.3 diff --git a/xcodescripts/mig-headers.sh b/xcodescripts/mig-headers.sh new file mode 100755 index 0000000..3669ec2 --- /dev/null +++ b/xcodescripts/mig-headers.sh @@ -0,0 +1,29 @@ +#!/bin/bash -e +# +# Copyright (c) 2010-2011 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@ +# + +export MIGCC="$(xcrun -find cc)" +export MIGCOM="$(xcrun -find migcom)" +export PATH="${PLATFORM_DEVELOPER_BIN_DIR}:${DEVELOPER_BIN_DIR}:${PATH}" +for a in ${ARCHS}; do + xcrun mig -arch $a -header "${SCRIPT_OUTPUT_FILE_0}" \ + -sheader "${SCRIPT_OUTPUT_FILE_1}" -user /dev/null \ + -server /dev/null "${SCRIPT_INPUT_FILE_0}" +done diff --git a/xcodescripts/postprocess-headers.sh b/xcodescripts/postprocess-headers.sh new file mode 100755 index 0000000..41f4669 --- /dev/null +++ b/xcodescripts/postprocess-headers.sh @@ -0,0 +1,21 @@ +#!/bin/bash -e +# +# Copyright (c) 2010-2011 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@ +# + diff --git a/xcodescripts/symlink-headers.sh b/xcodescripts/symlink-headers.sh new file mode 100755 index 0000000..a062a6f --- /dev/null +++ b/xcodescripts/symlink-headers.sh @@ -0,0 +1,29 @@ +#!/bin/bash -e +# +# Copyright (c) 2010-2011 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@ +# + +if [ "$DEPLOYMENT_LOCATION" != YES ]; then + DSTROOT="$CONFIGURATION_BUILD_DIR" + [ -L "$DSTROOT$PRIVATE_HEADERS_FOLDER_PATH"/private.h ] && exit +fi + +mv "$DSTROOT$PRIVATE_HEADERS_FOLDER_PATH"/private.h \ + "$DSTROOT$PRIVATE_HEADERS_FOLDER_PATH"/dispatch.h +ln -sf dispatch.h "$DSTROOT$PRIVATE_HEADERS_FOLDER_PATH"/private.h -- 2.45.2