From c093abd6701fa4c95f99104066f1f80a1c3c39e0 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 27 Jul 2012 17:24:27 +0000 Subject: [PATCH] libdispatch-228.18.tar.gz --- INSTALL | 40 +- config/config.h | 6 + configure.ac | 99 +- dispatch/base.h | 41 +- dispatch/data.h | 24 +- dispatch/dispatch.h | 4 +- dispatch/group.h | 6 +- dispatch/io.h | 11 +- dispatch/object.h | 82 +- dispatch/queue.h | 20 +- dispatch/semaphore.h | 3 +- dispatch/source.h | 3 +- dispatch/time.h | 14 +- libdispatch.xcodeproj/project.pbxproj | 117 +- man/Makefile.am | 67 +- man/dispatch.3 | 3 +- man/dispatch_data_create.3 | 14 + man/dispatch_group_create.3 | 30 + man/dispatch_io_create.3 | 38 +- man/dispatch_object.3 | 97 +- man/dispatch_queue_create.3 | 22 +- man/dispatch_semaphore_create.3 | 21 - man/dispatch_source_create.3 | 48 +- man/dispatch_time.3 | 2 +- os/object.h | 108 ++ os/object_private.h | 134 +++ private/benchmark.h | 2 +- private/data_private.h | 178 +++ private/dispatch.h | 39 + private/private.h | 23 +- private/queue_private.h | 17 +- private/source_private.h | 53 +- src/Makefile.am | 12 +- src/apply.c | 130 ++- src/benchmark.c | 1 - src/data.c | 84 +- src/data_internal.h | 22 +- src/init.c | 390 +++++-- src/internal.h | 95 +- src/io.c | 67 +- src/io_internal.h | 28 +- src/object.c | 199 +++- src/object.m | 286 +++++ src/object_internal.h | 166 ++- src/queue.c | 716 +++++++----- src/queue_internal.h | 198 +++- src/semaphore.c | 83 +- src/semaphore_internal.h | 13 +- src/shims/atomic.h | 8 +- src/shims/tsd.h | 13 +- src/source.c | 87 +- src/source_internal.h | 15 +- src/trace.h | 24 +- src/transform.c | 1015 +++++++++++++++++ xcodeconfig/libdispatch.aliases | 12 + xcodeconfig/libdispatch.order | 40 + xcodeconfig/libdispatch.unexport | 12 + xcodeconfig/libdispatch.xcconfig | 25 +- ...{symlink-headers.sh => install-headers.sh} | 14 +- xcodescripts/install-manpages.sh | 3 +- 60 files changed, 4026 insertions(+), 1098 deletions(-) create mode 100644 os/object.h create mode 100644 os/object_private.h create mode 100644 private/data_private.h create mode 100644 private/dispatch.h create mode 100644 src/object.m create mode 100644 src/transform.c create mode 100644 xcodeconfig/libdispatch.aliases create mode 100644 xcodeconfig/libdispatch.order create mode 100644 xcodeconfig/libdispatch.unexport rename xcodescripts/{symlink-headers.sh => install-headers.sh} (61%) diff --git a/INSTALL b/INSTALL index 69fd5a6..bac7e27 100644 --- a/INSTALL +++ b/INSTALL @@ -22,8 +22,8 @@ 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. + Specify the path to Apple's Libc package, so that appropriate headers can + be found and used. --with-apple-libclosure-source @@ -32,32 +32,31 @@ The following configure options may be of general interest: --with-apple-xnu-source - Specify the path to Apple's XNU package, so that appropriate headers - can be found and used. + 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. + 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: +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. + Do not tag libdispatch's init routine as __constructor, in which case it + must be run manually before libdispatch routines can be called. This is the + default when building on Mac OS X. For /usr/lib/system/libdispatch.dylib + 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. + 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 @@ -65,17 +64,18 @@ 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' \ + ./configure CFLAGS='-arch x86_64 -arch i386 -g -Os' \ --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 + make check 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 + make check diff --git a/config/config.h b/config/config.h index 040bf21..7fe2d63 100644 --- a/config/config.h +++ b/config/config.h @@ -96,6 +96,9 @@ /* Define if pthread work queues are present */ #define HAVE_PTHREAD_WORKQUEUES 1 +/* Define to 1 if you have the `pthread_workqueue_setdispatch_np' function. */ +#define HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP 1 + /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 @@ -160,6 +163,9 @@ /* Define to use Mach semaphores */ #define USE_MACH_SEM 1 +/* Define to use Objective-C runtime */ +#define USE_OBJC 1 + /* Define to use POSIX semaphores */ /* #undef USE_POSIX_SEM */ diff --git a/configure.ac b/configure.ac index eeba91b..9e751e7 100644 --- a/configure.ac +++ b/configure.ac @@ -8,45 +8,38 @@ AC_REVISION([$$]) AC_CONFIG_AUX_DIR(config) AC_CONFIG_HEADER([config/config.h]) AC_CONFIG_MACRO_DIR([m4]) +ac_clean_files=a.out.dSYM AM_MAINTAINER_MODE +AC_PROG_CC([clang gcc cc]) +AC_PROG_CXX([clang++ g++ c++]) +AC_PROG_OBJC([clang gcc cc]) + # # 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]) + [Specify path to Apple Libc source])], [ + apple_libc_source_pthreads_path=${withval}/pthreads + CPPFLAGS="$CPPFLAGS -I$apple_libc_source_pthreads_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]) + [Specify path to Apple libclosure source])], [ + apple_libclosure_source_path=${withval} + CPPFLAGS="$CPPFLAGS -I$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"]) + [Specify path to Apple XNU source])], [ + apple_xnu_source_libkern_path=${withval}/libkern + apple_xnu_source_osfmk_path=${withval}/osfmk + CPPFLAGS="$CPPFLAGS -I$apple_xnu_source_libkern_path" +]) AC_CACHE_CHECK([for System.framework/PrivateHeaders], dispatch_cv_system_privateheaders, [AS_IF([test -d /System/Library/Frameworks/System.framework/PrivateHeaders], @@ -57,15 +50,16 @@ AS_IF([test "x$dispatch_cv_system_privateheaders" != "xno"], ) # -# On Mac OS X, libpispatch_init is automatically invoked during libSystem +# On Mac OS X, libdispatch_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])] + [Disable libdispatch_init as a constructor])],, + [AS_IF([test -f /usr/lib/system/libdispatch.dylib], + [enable_libdispatch_init_constructor=no])] ) - AS_IF([test "x$enable_libdispatch_init_constructor" != "xno"], [AC_DEFINE(USE_LIBDISPATCH_INIT_CONSTRUCTOR, 1, [Define to tag libdispatch_init as a constructor])] @@ -78,15 +72,12 @@ 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) @@ -127,26 +118,34 @@ 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"], [ +AS_IF([test -n "$apple_xnu_source_osfmk_path"], [ saveCPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I." - ln -fsh "$apple_xnu_source_system_path" System + ln -fsh "$apple_xnu_source_osfmk_path" System ]) AC_CHECK_HEADERS([pthread_machdep.h]) -AS_IF([test -n "$apple_xnu_source_system_path"], [ +AS_IF([test -n "$apple_xnu_source_osfmk_path"], [ rm -f System CPPFLAGS="$saveCPPFLAGS" + AC_CONFIG_COMMANDS([src/System], + [ln -fsh "$apple_xnu_source_osfmk_path" src/System], + [apple_xnu_source_osfmk_path="$apple_xnu_source_osfmk_path"]) ]) # -# Core Services is tested in one of the GCD regression tests, so test for its -# presence using its header file. +# Parts of the testsuite use CoreFoundation and Foundation # -AC_CHECK_HEADER([CoreServices/CoreServices.h], - [have_coreservices=true], - [have_coreservices=false] +AC_CHECK_HEADER([CoreFoundation/CoreFoundation.h], + [have_corefoundation=true], [have_corefoundation=false] ) -AM_CONDITIONAL(HAVE_CORESERVICES, $have_coreservices) +AM_CONDITIONAL(HAVE_COREFOUNDATION, $have_corefoundation) +AC_LANG_PUSH([Objective C]) +AC_CHECK_HEADER([Foundation/Foundation.h], [ + AC_DEFINE(USE_OBJC, 1, [Define to use Objective-C runtime]) + have_foundation=true], [have_foundation=false] +) +AM_CONDITIONAL(HAVE_FOUNDATION, $have_foundation) +AC_LANG_POP([Objective C]) # # We use the availability of mach.h to decide whether to compile in all sorts @@ -155,8 +154,7 @@ AM_CONDITIONAL(HAVE_CORESERVICES, $have_coreservices) 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] + have_mach=true], [have_mach=false] ) AM_CONDITIONAL(USE_MIG, $have_mach) @@ -167,6 +165,7 @@ AM_CONDITIONAL(USE_MIG, $have_mach) AC_CHECK_HEADER([pthread_workqueue.h], [AC_DEFINE(HAVE_PTHREAD_WORKQUEUES, 1, [Define if pthread work queues are present])] ) +AC_CHECK_FUNCS([pthread_workqueue_setdispatch_np]) # # Find functions and declarations we care about. @@ -182,15 +181,13 @@ 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], + [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] + [have_sem_init=true], [have_sem_init=false] ) # @@ -236,7 +233,7 @@ AC_CACHE_CHECK([for -momit-leaf-frame-pointer], [dispatch_cv_cc_omit_leaf_fp], [ CFLAGS="$saveCFLAGS" ]) AS_IF([test "x$dispatch_cv_cc_omit_leaf_fp" != "xno"], [ - OMIT_LEAF_FP_FLAGS="-momit-leaf-frame-pointer" + OMIT_LEAF_FP_FLAGS="-momit-leaf-frame-pointer" ]) AC_SUBST([OMIT_LEAF_FP_FLAGS]) @@ -254,10 +251,10 @@ 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]) - ], []) +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. diff --git a/dispatch/base.h b/dispatch/base.h index 029e3e0..2af340e 100644 --- a/dispatch/base.h +++ b/dispatch/base.h @@ -25,45 +25,6 @@ #error "Please #include instead of this file directly." #endif -#ifdef __cplusplus -/* - * Dispatch objects are NOT C++ objects. Nevertheless, we can at least keep C++ - * aware of type compatibility. - */ -typedef struct dispatch_object_s { -private: - dispatch_object_s(); - ~dispatch_object_s(); - dispatch_object_s(const dispatch_object_s &); - void operator=(const dispatch_object_s &); -} *dispatch_object_t; -#else -typedef union { - struct dispatch_object_s *_do; - struct dispatch_continuation_s *_dc; - struct dispatch_queue_s *_dq; - struct dispatch_queue_attr_s *_dqa; - struct dispatch_group_s *_dg; - 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 -#else -/*! @parseOnly */ -#define DISPATCH_DECL(name) typedef struct name##_s *name##_t -#endif - #if __GNUC__ #define DISPATCH_NORETURN __attribute__((__noreturn__)) #define DISPATCH_NOTHROW __attribute__((__nothrow__)) @@ -139,4 +100,6 @@ typedef void (*dispatch_function_t)(void *); #define DISPATCH_EXPECT(x, v) (x) #endif +typedef void (*dispatch_function_t)(void *); + #endif diff --git a/dispatch/data.h b/dispatch/data.h index 2222e1b..ddba5dc 100644 --- a/dispatch/data.h +++ b/dispatch/data.h @@ -46,7 +46,8 @@ DISPATCH_DECL(dispatch_data); * @discussion The singleton dispatch data object representing a zero-length * memory region. */ -#define dispatch_data_empty (&_dispatch_data_empty) +#define dispatch_data_empty \ + DISPATCH_GLOBAL_OBJECT(dispatch_data_t, _dispatch_data_empty) __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) DISPATCH_EXPORT struct dispatch_data_s _dispatch_data_empty; @@ -92,7 +93,7 @@ DISPATCH_EXPORT const dispatch_block_t _dispatch_data_destructor_free; * @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_EXPORT DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_data_t dispatch_data_create(const void *buffer, size_t size, @@ -118,8 +119,10 @@ dispatch_data_get_size(dispatch_data_t data); * 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. + * read access to the represented memory, but are only valid until the returned + * object is released. Under ARC, if that object is held in a variable with + * automatic storage, care needs to be taken to ensure that it is not released + * by the compiler before memory access via the pointer has been completed. * * @param data The dispatch data object to map. * @param buffer_ptr A pointer to a pointer variable to be filled with the @@ -130,7 +133,8 @@ dispatch_data_get_size(dispatch_data_t data); * @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_EXPORT DISPATCH_NONNULL1 DISPATCH_RETURNS_RETAINED +DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_data_t dispatch_data_create_map(dispatch_data_t data, const void **buffer_ptr, @@ -152,7 +156,8 @@ dispatch_data_create_map(dispatch_data_t data, * data1 and data2 objects. */ __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) -DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_RETURNS_RETAINED +DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_data_t dispatch_data_create_concat(dispatch_data_t data1, dispatch_data_t data2); @@ -172,7 +177,8 @@ dispatch_data_create_concat(dispatch_data_t data1, dispatch_data_t data2); * subrange of the data object. */ __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) -DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_WARN_RESULT DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_RETURNS_RETAINED +DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_data_t dispatch_data_create_subrange(dispatch_data_t data, size_t offset, @@ -234,8 +240,8 @@ dispatch_data_apply(dispatch_data_t data, dispatch_data_applier_t applier); * @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_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_RETURNS_RETAINED +DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_data_t dispatch_data_copy_region(dispatch_data_t data, size_t location, diff --git a/dispatch/dispatch.h b/dispatch/dispatch.h index 2ba2cce..119b413 100644 --- a/dispatch/dispatch.h +++ b/dispatch/dispatch.h @@ -31,12 +31,13 @@ #include #include #include +#include #ifndef __OSX_AVAILABLE_STARTING #define __OSX_AVAILABLE_STARTING(x, y) #endif -#define DISPATCH_API_VERSION 20110201 +#define DISPATCH_API_VERSION 20111201 #ifndef __DISPATCH_BUILDING_DISPATCH__ @@ -44,6 +45,7 @@ #define __DISPATCH_INDIRECT__ #endif +#include #include #include #include diff --git a/dispatch/group.h b/dispatch/group.h index 4e6e11d..88e8087 100644 --- a/dispatch/group.h +++ b/dispatch/group.h @@ -50,7 +50,8 @@ __BEGIN_DECLS * The newly created group, or NULL on failure. */ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) -DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT +DISPATCH_NOTHROW dispatch_group_t dispatch_group_create(void); @@ -113,7 +114,8 @@ dispatch_group_async(dispatch_group_t group, * dispatch_group_async_f(). */ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) -DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NONNULL4 +DISPATCH_NOTHROW void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t queue, diff --git a/dispatch/io.h b/dispatch/io.h index f8fb2ff..dd83e7d 100644 --- a/dispatch/io.h +++ b/dispatch/io.h @@ -220,7 +220,8 @@ typedef unsigned long dispatch_io_type_t; * occurred. */ __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) -DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT +DISPATCH_NOTHROW dispatch_io_t dispatch_io_create(dispatch_io_type_t type, dispatch_fd_t fd, @@ -255,8 +256,8 @@ dispatch_io_create(dispatch_io_type_t type, * occurred. */ __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) -DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_NONNULL2 DISPATCH_WARN_RESULT -DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED +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, @@ -295,8 +296,8 @@ dispatch_io_create_with_path(dispatch_io_type_t type, * occurred. */ __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0) -DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_MALLOC DISPATCH_WARN_RESULT -DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED +DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_io_t dispatch_io_create_with_io(dispatch_io_type_t type, dispatch_io_t io, diff --git a/dispatch/object.h b/dispatch/object.h index 2ecf251..bc7257a 100644 --- a/dispatch/object.h +++ b/dispatch/object.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2010 Apple Inc. All rights reserved. + * Copyright (c) 2008-2012 Apple Inc. All rights reserved. * * @APPLE_APACHE_LICENSE_HEADER_START@ * @@ -26,6 +26,76 @@ #include // for HeaderDoc #endif +/*! + * @typedef dispatch_object_t + * + * @abstract + * Abstract base type for all dispatch objects. + * The details of the type definition are language-specific. + * + * @discussion + * Dispatch objects are reference counted via calls to dispatch_retain() and + * dispatch_release(). + */ + +#if OS_OBJECT_USE_OBJC +/* + * By default, dispatch objects are declared as Objective-C types when building + * with an Objective-C compiler. This allows them to participate in ARC, in RR + * management by the Blocks runtime and in leaks checking by the static + * analyzer, and enables them to be added to Cocoa collections. + * See for details. + */ +OS_OBJECT_DECL(dispatch_object); +#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object) +#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object)) +#define DISPATCH_RETURNS_RETAINED OS_OBJECT_RETURNS_RETAINED +DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +_dispatch_object_validate(dispatch_object_t object) { + void *isa = *(void* volatile*)(OS_OBJECT_BRIDGE void*)object; + (void)isa; +} +#elif defined(__cplusplus) +/* + * Dispatch objects are NOT C++ objects. Nevertheless, we can at least keep C++ + * aware of type compatibility. + */ +typedef struct dispatch_object_s { +private: + dispatch_object_s(); + ~dispatch_object_s(); + dispatch_object_s(const dispatch_object_s &); + void operator=(const dispatch_object_s &); +} *dispatch_object_t; +#define DISPATCH_DECL(name) \ + typedef struct name##_s : public dispatch_object_s {} *name##_t +#define DISPATCH_GLOBAL_OBJECT(type, object) (&(object)) +#define DISPATCH_RETURNS_RETAINED +#else /* Plain C */ +typedef union { + struct _os_object_s *_os_obj; + struct dispatch_object_s *_do; + struct dispatch_continuation_s *_dc; + struct dispatch_queue_s *_dq; + struct dispatch_queue_attr_s *_dqa; + struct dispatch_group_s *_dg; + 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__)); +/*! @parseOnly */ +#define DISPATCH_DECL(name) typedef struct name##_s *name##_t +/*! @parseOnly */ +#define DISPATCH_GLOBAL_OBJECT(t, x) (&(x)) +/*! @parseOnly */ +#define DISPATCH_RETURNS_RETAINED +#endif + __BEGIN_DECLS /*! @@ -77,6 +147,11 @@ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_retain(dispatch_object_t object); +#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE +#undef dispatch_retain +#define dispatch_retain(object) ({ dispatch_object_t _o = (object); \ + _dispatch_object_validate(_o); (void)[_o retain]; }) +#endif /*! * @function dispatch_release @@ -98,6 +173,11 @@ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW void dispatch_release(dispatch_object_t object); +#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE +#undef dispatch_release +#define dispatch_release(object) ({ dispatch_object_t _o = (object); \ + _dispatch_object_validate(_o); [_o release]; }) +#endif /*! * @function dispatch_get_context diff --git a/dispatch/queue.h b/dispatch/queue.h index d767771..b8050f9 100644 --- a/dispatch/queue.h +++ b/dispatch/queue.h @@ -330,6 +330,11 @@ dispatch_apply_f(size_t iterations, dispatch_queue_t queue, * from deadlock if that queue is not the one returned by * dispatch_get_current_queue(). * + * When dispatch_get_current_queue() is called on the main thread, it may + * or may not return the same value as dispatch_get_main_queue(). Comparing + * the two is not a valid way to test whether code is executing on the + * main thread. + * * @result * Returns the current queue. */ @@ -355,7 +360,8 @@ dispatch_get_current_queue(void); */ __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) +#define dispatch_get_main_queue() \ + DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q) /*! * @typedef dispatch_queue_priority_t @@ -430,7 +436,9 @@ dispatch_get_global_queue(dispatch_queue_priority_t priority, * @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) +#define DISPATCH_QUEUE_CONCURRENT \ + DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, \ + _dispatch_queue_attr_concurrent) __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) DISPATCH_EXPORT struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent; @@ -470,7 +478,8 @@ struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent; * The newly created dispatch queue. */ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) -DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT +DISPATCH_NOTHROW dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); @@ -525,7 +534,10 @@ dispatch_queue_get_label(dispatch_queue_t queue); * cancellation handler blocks will be submitted. * * A dispatch I/O channel's target queue specifies where where its I/O - * operations are executed. + * operations are executed. If the channel's target queue's priority is set to + * DISPATCH_QUEUE_PRIORITY_BACKGROUND, then the I/O operations performed by + * dispatch_io_read() or dispatch_io_write() on that queue will be + * throttled when there is I/O contention. * * For all other dispatch object types, the only function of the target queue * is to determine where an object's finalizer function is invoked. diff --git a/dispatch/semaphore.h b/dispatch/semaphore.h index 19b50af..312a2c2 100644 --- a/dispatch/semaphore.h +++ b/dispatch/semaphore.h @@ -56,7 +56,8 @@ __BEGIN_DECLS * The newly created semaphore, or NULL on failure. */ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) -DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT +DISPATCH_NOTHROW dispatch_semaphore_t dispatch_semaphore_create(long value); diff --git a/dispatch/source.h b/dispatch/source.h index 4c9f601..e37ecec 100644 --- a/dispatch/source.h +++ b/dispatch/source.h @@ -288,7 +288,8 @@ __BEGIN_DECLS * handler block to the default priority global queue. */ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) -DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT +DISPATCH_NOTHROW dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, diff --git a/dispatch/time.h b/dispatch/time.h index d39578d..e0bc2f6 100644 --- a/dispatch/time.h +++ b/dispatch/time.h @@ -28,11 +28,11 @@ #include -__BEGIN_DECLS - -struct timespec; +// +#if TARGET_OS_MAC +#include +#endif -// 6368156 #ifdef NSEC_PER_SEC #undef NSEC_PER_SEC #endif @@ -50,6 +50,10 @@ struct timespec; #define USEC_PER_SEC 1000000ull #define NSEC_PER_USEC 1000ull +__BEGIN_DECLS + +struct timespec; + /*! * @typedef dispatch_time_t * @@ -60,7 +64,7 @@ struct timespec; */ typedef uint64_t dispatch_time_t; -#define DISPATCH_TIME_NOW 0 +#define DISPATCH_TIME_NOW (0ull) #define DISPATCH_TIME_FOREVER (~0ull) /*! diff --git a/libdispatch.xcodeproj/project.pbxproj b/libdispatch.xcodeproj/project.pbxproj index e36948c..59d706e 100644 --- a/libdispatch.xcodeproj/project.pbxproj +++ b/libdispatch.xcodeproj/project.pbxproj @@ -56,6 +56,11 @@ 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 */; }; + C913AC0F143BD34800B78976 /* data_private.h in Headers */ = {isa = PBXBuildFile; fileRef = C913AC0E143BD34800B78976 /* data_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C93D6165143E190E00EB9023 /* transform.c in Sources */ = {isa = PBXBuildFile; fileRef = C9C5F80D143C1771006DC718 /* transform.c */; }; + C93D6166143E190F00EB9023 /* transform.c in Sources */ = {isa = PBXBuildFile; fileRef = C9C5F80D143C1771006DC718 /* transform.c */; }; + C93D6167143E190F00EB9023 /* transform.c in Sources */ = {isa = PBXBuildFile; fileRef = C9C5F80D143C1771006DC718 /* transform.c */; }; + C9C5F80E143C1771006DC718 /* transform.c in Sources */ = {isa = PBXBuildFile; fileRef = C9C5F80D143C1771006DC718 /* transform.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 */; }; @@ -64,11 +69,15 @@ 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 */; }; + E4407FAE143CC984003A9E80 /* dispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = E4407FAD143CC984003A9E80 /* dispatch.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E4407FAF143CC984003A9E80 /* dispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = E4407FAD143CC984003A9E80 /* dispatch.h */; settings = {ATTRIBUTES = (Private, ); }; }; 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 */; }; + E454569314746F1B00106147 /* object_private.h in Headers */ = {isa = PBXBuildFile; fileRef = E454569214746F1B00106147 /* object_private.h */; settings = {ATTRIBUTES = (); }; }; + E454569414746F1B00106147 /* object_private.h in Headers */ = {isa = PBXBuildFile; fileRef = E454569214746F1B00106147 /* object_private.h */; settings = {ATTRIBUTES = (); }; }; 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 */; }; @@ -118,6 +127,8 @@ 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 */; }; + E4EB4A2714C35ECE00AA0FA9 /* object.h in Headers */ = {isa = PBXBuildFile; fileRef = E4EB4A2614C35ECE00AA0FA9 /* object.h */; }; + E4EB4A2814C35ECE00AA0FA9 /* object.h in Headers */ = {isa = PBXBuildFile; fileRef = E4EB4A2614C35ECE00AA0FA9 /* object.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 */; }; @@ -138,6 +149,10 @@ 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 */; }; + E4FC3264145F46C9002FBDDB /* object.m in Sources */ = {isa = PBXBuildFile; fileRef = E4FC3263145F46C9002FBDDB /* object.m */; }; + E4FC3265145F46C9002FBDDB /* object.m in Sources */ = {isa = PBXBuildFile; fileRef = E4FC3263145F46C9002FBDDB /* object.m */; }; + E4FC3266145F46C9002FBDDB /* object.m in Sources */ = {isa = PBXBuildFile; fileRef = E4FC3263145F46C9002FBDDB /* object.m */; }; + E4FC3267145F46C9002FBDDB /* object.m in Sources */ = {isa = PBXBuildFile; fileRef = E4FC3263145F46C9002FBDDB /* object.m */; }; 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 */; }; @@ -224,21 +239,27 @@ 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; }; + C913AC0E143BD34800B78976 /* data_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data_private.h; sourceTree = ""; }; C927F35F10FD7F1000C5AB8B /* ddt.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ddt.xcodeproj; path = tools/ddt/ddt.xcodeproj; sourceTree = ""; }; + C9C5F80D143C1771006DC718 /* transform.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = transform.c; 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 = ""; }; + E422DA3614D2A7E7003C6EE4 /* libdispatch.aliases */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libdispatch.aliases; sourceTree = ""; }; + E422DA3714D2AE11003C6EE4 /* libdispatch.unexport */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libdispatch.unexport; 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 = ""; }; + E4407FAD143CC984003A9E80 /* dispatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dispatch.h; sourceTree = ""; }; + E448727914C6215D00BB45C2 /* libdispatch.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libdispatch.order; 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 = ""; }; + E454569214746F1B00106147 /* object_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = object_private.h; 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 = ""; }; @@ -248,8 +269,11 @@ 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 = ""; }; + E4EB4A2614C35ECE00AA0FA9 /* object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = object.h; sourceTree = ""; }; + E4EB4A2A14C36F4E00AA0FA9 /* install-headers.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "install-headers.sh"; 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; }; + E4FC3263145F46C9002FBDDB /* object.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = object.m; sourceTree = ""; }; 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 = ""; }; @@ -294,6 +318,7 @@ isa = PBXGroup; children = ( E44DB71E11D2FF080074F2AD /* Build Support */, + E4EB4A2914C35F1800AA0FA9 /* OS Object */, FC7BEDAA0E83625200161930 /* Public Headers */, FC7BEDAF0E83626100161930 /* Private Headers */, FC7BEDB60E8363DC00161930 /* Project Headers */, @@ -316,11 +341,13 @@ E44EBE3B1251659900645D88 /* init.c */, 5A27262510F26F1900751FBC /* io.c */, 9661E56A0F3E7DDF00749F3E /* object.c */, + E4FC3263145F46C9002FBDDB /* object.m */, 96DF70BD0F38FE3C0074BD99 /* once.c */, FC7BED8A0E8361E600161930 /* queue.c */, 721F5CCE0F15553500FF03A6 /* semaphore.c */, 96A8AA860F41E7A400CD570B /* source.c */, 96032E4A0F5CC8C700241C5F /* time.c */, + C9C5F80D143C1771006DC718 /* transform.c */, FC7BED950E8361E600161930 /* protocol.defs */, E43570B8126E93380097AB9F /* provider.d */, ); @@ -378,6 +405,9 @@ E43D93F11097917E004F6A62 /* libdispatch.xcconfig */, E40041AA125D705F0022B135 /* libdispatch-resolver.xcconfig */, E40041A9125D70590022B135 /* libdispatch-resolved.xcconfig */, + E422DA3614D2A7E7003C6EE4 /* libdispatch.aliases */, + E448727914C6215D00BB45C2 /* libdispatch.order */, + E422DA3714D2AE11003C6EE4 /* libdispatch.unexport */, ); path = xcodeconfig; sourceTree = ""; @@ -408,9 +438,9 @@ isa = PBXGroup; children = ( E49F251D125D630A0057C971 /* install-manpages.sh */, + E4EB4A2A14C36F4E00AA0FA9 /* install-headers.sh */, E49F251E125D631D0057C971 /* mig-headers.sh */, E482F1CD12DBAB590030614D /* postprocess-headers.sh */, - E49F251C125D629F0057C971 /* symlink-headers.sh */, ); path = xcodescripts; sourceTree = ""; @@ -423,6 +453,16 @@ path = config; sourceTree = ""; }; + E4EB4A2914C35F1800AA0FA9 /* OS Object */ = { + isa = PBXGroup; + children = ( + E4EB4A2614C35ECE00AA0FA9 /* object.h */, + E454569214746F1B00106147 /* object_private.h */, + ); + name = "OS Object"; + path = os; + sourceTree = ""; + }; FC1832A0109923B3003403D5 /* shims */ = { isa = PBXGroup; children = ( @@ -459,10 +499,12 @@ FC7BEDAF0E83626100161930 /* Private Headers */ = { isa = PBXGroup; children = ( + E4407FAD143CC984003A9E80 /* dispatch.h */, FC7BED930E8361E600161930 /* private.h */, 96BC39BC0F3EBAB100C59689 /* queue_private.h */, FCEF047F0F5661960067401F /* source_private.h */, 961B99350F3E83980006BC96 /* benchmark.h */, + C913AC0E143BD34800B78976 /* data_private.h */, ); name = "Private Headers"; path = private; @@ -505,6 +547,7 @@ 5AAB45C610D30D0C004407EA /* data.h in Headers */, 96032E4D0F5CC8D100241C5F /* time.h in Headers */, FC7BEDA20E8361E600161930 /* private.h in Headers */, + C913AC0F143BD34800B78976 /* data_private.h in Headers */, 96BC39BD0F3EBAB100C59689 /* queue_private.h in Headers */, FCEF04800F5661960067401F /* source_private.h in Headers */, 961B99360F3E83980006BC96 /* benchmark.h in Headers */, @@ -525,6 +568,9 @@ E4BA743B13A8911B0095BDF1 /* getprogname.h in Headers */, E4BA743F13A8911B0095BDF1 /* malloc_zone.h in Headers */, E4128ED613BA9A1700ABB2CB /* hw_config.h in Headers */, + E4407FAE143CC984003A9E80 /* dispatch.h in Headers */, + E454569314746F1B00106147 /* object_private.h in Headers */, + E4EB4A2714C35ECE00AA0FA9 /* object.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -564,6 +610,9 @@ E4BA743C13A8911B0095BDF1 /* getprogname.h in Headers */, E4BA744013A8911B0095BDF1 /* malloc_zone.h in Headers */, E4128ED713BA9A1700ABB2CB /* hw_config.h in Headers */, + E4407FAF143CC984003A9E80 /* dispatch.h in Headers */, + E454569414746F1B00106147 /* object_private.h in Headers */, + E4EB4A2814C35ECE00AA0FA9 /* object.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -577,8 +626,8 @@ D2AAC043055464E500DB518D /* Headers */, D2AAC044055464E500DB518D /* Sources */, D289987405E68DCB004EDB86 /* Frameworks */, + E4EB4A2B14C3720B00AA0FA9 /* Install Headers */, E482F1C512DBAA110030614D /* Postprocess Headers */, - 2EC9C9800E846B5200E2499A /* Symlink Headers */, 4CED8B9D0EEDF8B600AF99AB /* Install Manpages */, ); buildRules = ( @@ -599,8 +648,8 @@ E49F24AA125D57FA0057C971 /* Headers */, E49F24C7125D57FA0057C971 /* Sources */, E49F24D5125D57FA0057C971 /* Frameworks */, + E4EB4A3014C3A14000AA0FA9 /* Install Headers */, E4128EB213B9612700ABB2CB /* Postprocess Headers */, - E49F24D6125D57FA0057C971 /* Symlink Headers */, E49F24D7125D57FA0057C971 /* Install Manpages */, ); buildRules = ( @@ -696,31 +745,31 @@ /* End PBXReferenceProxy section */ /* Begin PBXShellScriptBuildPhase section */ - 2EC9C9800E846B5200E2499A /* Symlink Headers */ = { + 4CED8B9D0EEDF8B600AF99AB /* Install Manpages */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 12; + buildActionMask = 8; files = ( ); inputPaths = ( - "$(SRCROOT)/xcodescripts/symlink-headers.sh", + "$(SRCROOT)/xcodescripts/install-manpages.sh", ); - name = "Symlink Headers"; + name = "Install Manpages"; outputPaths = ( ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = "/bin/bash -e"; shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; showEnvVarsInLog = 0; }; - 4CED8B9D0EEDF8B600AF99AB /* Install Manpages */ = { + E4128EB213B9612700ABB2CB /* Postprocess Headers */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( - "$(SRCROOT)/xcodescripts/install-manpages.sh", + "$(SRCROOT)/xcodescripts/postprocess-headers.sh", ); - name = "Install Manpages"; + name = "Postprocess Headers"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; @@ -728,7 +777,7 @@ shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; showEnvVarsInLog = 0; }; - E4128EB213B9612700ABB2CB /* Postprocess Headers */ = { + E482F1C512DBAA110030614D /* Postprocess Headers */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( @@ -741,18 +790,18 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = "/bin/bash -e"; - shellScript = ". \"${SCRIPT_INPUT_FILE_0}\" "; + shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; showEnvVarsInLog = 0; }; - E482F1C512DBAA110030614D /* Postprocess Headers */ = { + E49F24D7125D57FA0057C971 /* Install Manpages */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( - "$(SRCROOT)/xcodescripts/postprocess-headers.sh", + "$(SRCROOT)/xcodescripts/install-manpages.sh", ); - name = "Postprocess Headers"; + name = "Install Manpages"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; @@ -760,34 +809,42 @@ shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; showEnvVarsInLog = 0; }; - E49F24D6125D57FA0057C971 /* Symlink Headers */ = { + E4EB4A2B14C3720B00AA0FA9 /* Install Headers */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 12; + buildActionMask = 2147483647; files = ( ); inputPaths = ( - "$(SRCROOT)/xcodescripts/symlink-headers.sh", + "$(SRCROOT)/xcodescripts/install-headers.sh", + "$(SRCROOT)/os/object.h", + "$(SRCROOT)/os/object_private.h", ); - name = "Symlink Headers"; + name = "Install Headers"; outputPaths = ( + "$(CONFIGURATION_BUILD_DIR)/$(OS_PUBLIC_HEADERS_FOLDER_PATH)/object.h", + "$(CONFIGURATION_BUILD_DIR)/$(OS_PRIVATE_HEADERS_FOLDER_PATH)/object_private.h", ); runOnlyForDeploymentPostprocessing = 0; shellPath = "/bin/bash -e"; shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; showEnvVarsInLog = 0; }; - E49F24D7125D57FA0057C971 /* Install Manpages */ = { + E4EB4A3014C3A14000AA0FA9 /* Install Headers */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 8; + buildActionMask = 2147483647; files = ( ); inputPaths = ( - "$(SRCROOT)/xcodescripts/install-manpages.sh", + "$(SRCROOT)/xcodescripts/install-headers.sh", + "$(SRCROOT)/os/object.h", + "$(SRCROOT)/os/object_private.h", ); - name = "Install Manpages"; + name = "Install Headers"; outputPaths = ( + "$(CONFIGURATION_BUILD_DIR)/$(OS_PUBLIC_HEADERS_FOLDER_PATH)/object.h", + "$(CONFIGURATION_BUILD_DIR)/$(OS_PRIVATE_HEADERS_FOLDER_PATH)/object_private.h", ); - runOnlyForDeploymentPostprocessing = 1; + runOnlyForDeploymentPostprocessing = 0; shellPath = "/bin/bash -e"; shellScript = ". \"${SCRIPT_INPUT_FILE_0}\""; showEnvVarsInLog = 0; @@ -883,6 +940,8 @@ 96032E4B0F5CC8C700241C5F /* time.c in Sources */, 5AAB45C010D30B79004407EA /* data.c in Sources */, 5A27262610F26F1900751FBC /* io.c in Sources */, + C9C5F80E143C1771006DC718 /* transform.c in Sources */, + E4FC3264145F46C9002FBDDB /* object.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -904,6 +963,8 @@ E49F24D2125D57FA0057C971 /* time.c in Sources */, E49F24D3125D57FA0057C971 /* data.c in Sources */, E49F24D4125D57FA0057C971 /* io.c in Sources */, + C93D6165143E190E00EB9023 /* transform.c in Sources */, + E4FC3265145F46C9002FBDDB /* object.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -925,6 +986,8 @@ E4EC11B512514302000DDBD1 /* time.c in Sources */, E4EC11B712514302000DDBD1 /* data.c in Sources */, E4EC11B812514302000DDBD1 /* io.c in Sources */, + C93D6166143E190F00EB9023 /* transform.c in Sources */, + E4FC3266145F46C9002FBDDB /* object.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -946,6 +1009,8 @@ E4EC122112514715000DDBD1 /* time.c in Sources */, E4EC122312514715000DDBD1 /* data.c in Sources */, E4EC122412514715000DDBD1 /* io.c in Sources */, + C93D6167143E190F00EB9023 /* transform.c in Sources */, + E4FC3267145F46C9002FBDDB /* object.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/man/Makefile.am b/man/Makefile.am index f57453a..7ad94e2 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -30,7 +30,7 @@ EXTRA_DIST= \ LN=ln install-data-hook: - cd $(DESTDIR)$(mandir)/man3 && \ + 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 && \ @@ -61,6 +61,8 @@ install-data-hook: $(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_registration_handler.3 && \ + $(LN) -f dispatch_source_create.3 dispatch_source_set_registration_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 && \ @@ -85,5 +87,68 @@ install-data-hook: $(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_create.3 dispatch_io_barrier.3 && \ $(LN) -f dispatch_io_read.3 dispatch_io_write.3 && \ $(LN) -f dispatch_read.3 dispatch_write.3 + +uninstall-hook: + cd $(DESTDIR)$(mandir)/man3 && \ + rm -f dispatch_after_f.3 \ + dispatch_apply_f.3 \ + dispatch_sync.3 \ + dispatch_async_f.3 \ + dispatch_sync_f.3 \ + dispatch_group_enter.3 \ + dispatch_group_leave.3 \ + dispatch_group_wait.3 \ + dispatch_group_notify.3 \ + dispatch_group_notify_f.3 \ + dispatch_group_async.3 \ + dispatch_group_async_f.3 \ + dispatch_retain.3 \ + dispatch_release.3 \ + dispatch_suspend.3 \ + dispatch_resume.3 \ + dispatch_get_context.3 \ + dispatch_set_context.3 \ + dispatch_set_finalizer_f.3 \ + dispatch_once_f.3 \ + dispatch_queue_get_label.3 \ + dispatch_get_current_queue.3 \ + dispatch_get_global_queue.3 \ + dispatch_get_main_queue.3 \ + dispatch_main.3 \ + dispatch_set_target_queue.3 \ + dispatch_semaphore_signal.3 \ + dispatch_semaphore_wait.3 \ + dispatch_source_set_event_handler.3 \ + dispatch_source_set_event_handler_f.3 \ + dispatch_source_set_registration_handler.3 \ + dispatch_source_set_registration_handler_f.3 \ + dispatch_source_set_cancel_handler.3 \ + dispatch_source_set_cancel_handler_f.3 \ + dispatch_source_cancel.3 \ + dispatch_source_testcancel.3 \ + dispatch_source_get_handle.3 \ + dispatch_source_get_mask.3 \ + dispatch_source_get_data.3 \ + dispatch_source_merge_data.3 \ + dispatch_source_set_timer.3 \ + dispatch_walltime.3 \ + dispatch_data_create_concat.3 \ + dispatch_data_create_subrange.3 \ + dispatch_data_create_map.3 \ + dispatch_data_apply.3 \ + dispatch_data_copy_subrange.3 \ + dispatch_data_copy_region.3 \ + dispatch_data_get_size.3 \ + dispatch_data_copy_subrange.3 \ + dispatch_data_empty.3 \ + dispatch_io_create_with_path.3 \ + dispatch_io_set_high_water.3 \ + dispatch_io_set_low_water.3 \ + dispatch_io_set_interval.3 \ + dispatch_io_close.3 \ + dispatch_io_barrier.3 \ + dispatch_io_write.3 \ + dispatch_write.3 diff --git a/man/dispatch.3 b/man/dispatch.3 index c55be96..d25e083 100644 --- a/man/dispatch.3 +++ b/man/dispatch.3 @@ -31,8 +31,7 @@ events and automatically submit event handler blocks to dispatch queues. .Xr dispatch_api 3 , .Xr dispatch_apply 3 , .Xr dispatch_async 3 , -.Xr dispatch_benchmark 3 , -.Xr dispatch_data_create 3, +.Xr dispatch_data_create 3 , .Xr dispatch_group_create 3 , .Xr dispatch_io_create 3 , .Xr dispatch_io_read 3 , diff --git a/man/dispatch_data_create.3 b/man/dispatch_data_create.3 index 96965f2..b941b34 100644 --- a/man/dispatch_data_create.3 +++ b/man/dispatch_data_create.3 @@ -200,6 +200,20 @@ 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. +.Pp +The functions +.Fn dispatch_data_create_map +and +.Fn dispatch_data_apply +return an interior pointer to represented memory that is only valid as long as +an associated object has not been released. When Objective-C Automated +Reference Counting is enabled, care needs to be taken if that object is held in +a variable with automatic storage. It may need to be annotated with the +.Li objc_precise_lifetime +attribute, or stored in a +.Li __strong +instance variable instead, to ensure that the object is not released +prematurely before memory accesses via the interor pointer have been completed. .Sh SEE ALSO .Xr dispatch 3 , .Xr dispatch_object 3 , diff --git a/man/dispatch_group_create.3 b/man/dispatch_group_create.3 index 1dae0ef..4b8063c 100644 --- a/man/dispatch_group_create.3 +++ b/man/dispatch_group_create.3 @@ -141,6 +141,36 @@ functions are wrappers around and .Fn dispatch_group_notify_f respectively. +.Sh CAVEATS +In order to ensure deterministic behavior, it is recommended to call +.Fn dispatch_group_wait +only once all blocks have been submitted to the group. If it is later +determined that new blocks should be run, it is recommended not to reuse an +already-running group, but to create a new group. +.Pp +.Fn dispatch_group_wait +returns as soon as there are exactly zero +.Em enqueued or running +blocks associated with a group (more precisely, as soon as every +.Fn dispatch_group_enter +call has been balanced by a +.Fn dispatch_group_leave +call). If one thread waits for a group while another thread submits +new blocks to the group, then the count of associated blocks might +momentarily reach zero before all blocks have been submitted. If this happens, +.Fn dispatch_group_wait +will return too early: some blocks associated with the group have finished, +but some have not yet been submitted or run. +.Pp +However, as a special case, a block associated with a group may submit new +blocks associated with its own group. In this case, the behavior is +deterministic: a waiting thread will +.Em not +wake up until the newly submitted blocks have also finished. +.Pp +All of the foregoing also applies to +.Fn dispath_group_notify +as well, with "block to be submitted" substituted for "waiting thread". .Sh SEE ALSO .Xr dispatch 3 , .Xr dispatch_async 3 , diff --git a/man/dispatch_io_create.3 b/man/dispatch_io_create.3 index 9087442..7af5b6d 100644 --- a/man/dispatch_io_create.3 +++ b/man/dispatch_io_create.3 @@ -8,7 +8,8 @@ .Nm dispatch_io_close , .Nm dispatch_io_set_high_water , .Nm dispatch_io_set_low_water , -.Nm dispatch_io_set_interval +.Nm dispatch_io_set_interval , +.Nm dispatch_io_barrier .Nd open, close and configure dispatch I/O channels .Sh SYNOPSIS .Fd #include @@ -49,6 +50,11 @@ .Fa "uint64_t interval" .Fa "dispatch_io_interval_flags_t flags" .Fc +.Ft void +.Fo dispatch_io_barrier +.Fa "dispatch_io_t channel" +.Fa "void (^barrier)(void)" +.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 @@ -224,6 +230,36 @@ 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. +.Pp +.Sh BARRIER OPERATIONS +The +.Fn dispatch_io_barrier +function schedules a barrier operation on an I/O channel. The specified barrier +block will be run once, after all current I/O operations (such as +.Xr read 2 or +.Xr write 2 ) +on the underlying +file descriptor have finished. No new I/O operations will start until the +barrier block finishes. +.Pp +The barrier block may operate on the underlying file descriptor with functions +like +.Xr fsync 2 +or +.Xr lseek 2 . +As discussed in the +.Sx FILEDESCRIPTOR OWNERSHIP +section, the barrier block must not +.Xr close 2 +the file descriptor, and if it changes any flags on the file descriptor, it +must restore them before finishing. +.Pp +There is no synchronization between a barrier block and any +.Xr dispatch_io_read 3 +or +.Xr dispatch_io_write 3 +handler blocks; they may be running at the same time. The barrier block itself +is responsible for any required synchronization. .Sh MEMORY MODEL Dispatch I/O channel objects are retained and released via calls to .Fn dispatch_retain diff --git a/man/dispatch_object.3 b/man/dispatch_object.3 index 29c1621..21b3d95 100644 --- a/man/dispatch_object.3 +++ b/man/dispatch_object.3 @@ -1,5 +1,5 @@ -.\" Copyright (c) 2008-2010 Apple Inc. All rights reserved. -.Dd May 1, 2009 +.\" Copyright (c) 2008-2012 Apple Inc. All rights reserved. +.Dd March 1, 2012 .Dt dispatch_object 3 .Os Darwin .Sh NAME @@ -39,9 +39,7 @@ .Fc .Sh DESCRIPTION Dispatch objects share functions for coordinating memory management, suspension, -cancellation and context pointers. While all dispatch objects are retainable, -not all objects support suspension, context pointers or finalizers (currently -only queues and sources support these additional interfaces). +cancellation and context pointers. .Sh MEMORY MANGEMENT Objects returned by creation functions in the dispatch framework may be uniformly retained and released with the functions @@ -53,13 +51,87 @@ respectively. The dispatch framework does not guarantee that any given client has the last or only reference to a given object. Objects may be retained internally by the system. +.Ss INTEGRATION WITH OBJECTIVE-C +.Bd -filled -offset indent +When building with an Objective-C or Objective-C++ compiler, dispatch objects +are declared as Objective-C types. This results in the following differences +compared to building as plain C/C++: +.Bl -dash +.It +if Objective-C Automated Reference Counting is enabled, dispatch objects are +memory managed by the Objective-C runtime and explicit calls to the +.Fn dispatch_retain +and +.Fn dispatch_release +functions will produce build errors. +.Pp +.Em Note : +when ARC is enabled, care needs to be taken with dispatch API returning an +interior pointer that is only valid as long as an associated object has not +been released. If that object is held in a variable with automatic storage, it +may need to be annotated with the +.Li objc_precise_lifetime +attribute, or stored in a +.Li __strong +instance variable instead, to ensure that the object is not prematurely +released. The functions returning interior pointers are +.Xr dispatch_data_create_map 3 +and +.Xr dispatch_data_apply 3 . +.It +the Blocks runtime automatically retains and releases dispatch objects captured +by blocks upon +.Fn Block_copy +and +.Fn Block_release , +e.g.\& as performed during asynchronous execution of a block via +.Xr dispatch_async 3 . +.Pp +.Em Note : +retain cycles may be encountered if dispatch source objects are captured by +their handler blocks; these cycles can be broken by declaring the captured +object +.Li __weak +or by calling +.Xr dispatch_source_cancel 3 +to cause its handler blocks to be released explicitly. +.It +dispatch objects can be added directly to Cocoa collections, and their +lifetime is tracked by the Objective-C static analyzer. +.El +.Pp +Integration of dispatch objects with Objective-C requires targeting Mac\ OS\ X +10.8 or later, and is disabled when building with Objective-C Garbage +Collection or for the legacy Objective-C runtime. It can also be disabled +manually by using compiler options to define the +.Dv OS_OBJECT_USE_OBJC +preprocessor macro to +.Li 0 . +.Ed +.Pp +.Em Important : +When building with a plain C/C++ compiler or when integration with Objective-C +is disabled, dispatch objects are +.Em not +automatically retained and released when captured by a block. Therefore, when a +dispatch object is captured by a block that will be executed asynchronously, +the object must be manually retained and released: +.Pp +.Bd -literal -offset indent +dispatch_retain(object); +dispatch_async(queue, ^{ + do_something_with_object(object); + dispatch_release(object); +}); +.Ed .Sh SUSPENSION 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 -respectively. +respectively. Other dispatch objects do not support suspension. +.Pp The dispatch framework always checks the suspension status before executing a block, but such changes never affect a block during execution (non-preemptive). Therefore the suspension of an object is asynchronous, unless it is performed @@ -79,8 +151,8 @@ such that the dispatch object is fully resumed when the last reference is 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 pointer may be retrieved and updated with +Dispatch objects support supplemental context pointers. The value of the +context pointer may be retrieved and updated with .Fn dispatch_get_context and .Fn dispatch_set_context @@ -93,13 +165,12 @@ 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_async 3 , .Xr dispatch_group_create 3 , .Xr dispatch_queue_create 3 , .Xr dispatch_semaphore_create 3 , -.Xr dispatch_source_create 3 , -.Xr dispatch_set_target_queue 3 +.Xr dispatch_set_target_queue 3 , +.Xr dispatch_source_cancel 3 , +.Xr dispatch_source_create 3 diff --git a/man/dispatch_queue_create.3 b/man/dispatch_queue_create.3 index 9b3e6a9..b657abf 100644 --- a/man/dispatch_queue_create.3 +++ b/man/dispatch_queue_create.3 @@ -153,12 +153,10 @@ called from the main thread before .Fn dispatch_main is called, then the result of .Fn dispatch_get_main_queue -is returned. The result of -.Fo dispatch_get_global_queue -.Fa DISPATCH_QUEUE_PRIORITY_DEFAULT -.Fa 0 -.Fc -will be returned in all other cases. +is returned. In all other cases, the default target queue will be returned. See +the +.Sx CAVEATS +section below. .Pp The .Fn dispatch_main @@ -226,6 +224,15 @@ configuration. 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 . +.Pp +The result of +.Fn dispatch_get_main_queue +may or may not equal the result of +.Fn dispatch_get_current_queue +when called on the main thread. Comparing the two is not a valid way to test +whether code is executing on the main thread. Foundation/AppKit programs should +use [NSThread isMainThread]. POSIX programs may use +.Xr pthread_main_np 3 . .Sh COMPATIBILITY Cocoa applications need not call .Fn dispatch_main . @@ -240,8 +247,7 @@ exceptions generated by higher level languages such as Objective-C or C++. Applications .Em MUST catch all exceptions before returning from a block submitted to a dispatch -queue; otherwise the internal data structures of the dispatch framework will be -left in an inconsistent state. +queue; otherwise the process will be terminated with an uncaught exception. .Pp The dispatch framework manages the relationship between dispatch queues and threads of execution. As a result, applications diff --git a/man/dispatch_semaphore_create.3 b/man/dispatch_semaphore_create.3 index 096e0e3..896412b 100644 --- a/man/dispatch_semaphore_create.3 +++ b/man/dispatch_semaphore_create.3 @@ -101,27 +101,6 @@ and 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. -What follows is a saturating binary semaphore: -.Bd -literal -void -saturating_semaphore_signal(dispatch_semaphore_t dsema, int *sent) -{ - if (__sync_bool_compare_and_swap(sent, 0, 1)) { - dispatch_semaphore_signal(dsema); - } -} - -void -saturating_semaphore_wait(dispatch_semaphore_t dsema, int *sent) -{ - *sent = 0; - dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); -} -.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 1d774a9..89e7d51 100644 --- a/man/dispatch_source_create.3 +++ b/man/dispatch_source_create.3 @@ -25,6 +25,16 @@ .Fa "void (*function)(void *)" .Fc .Ft void +.Fo dispatch_source_set_registration_handler +.Fa "dispatch_source_t source" +.Fa "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_source_set_registration_handler_f +.Fa "dispatch_source_t source" +.Fa "void (*function)(void *)" +.Fc +.Ft void .Fo dispatch_source_set_cancel_handler .Fa "dispatch_source_t source" .Fa "void (^block)(void)" @@ -90,7 +100,8 @@ 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, +been configured by setting an event handler, cancellation handler, registration +handler, context, etc., the source must be activated by a call to .Fn dispatch_resume before any events will be delivered. @@ -167,7 +178,11 @@ In order to receive events from the dispatch source, an event handler should be specified via .Fn dispatch_source_set_event_handler . The event handler block is submitted to the source's target queue when the state -of the underlying system handle changes, or when an event occurs. +of the underlying system handle changes, or when an event occurs. If a source +is resumed with no event handler block set, events will be quietly ignored. +If the event handler block is changed while the source is suspended, or from a +block running on a serial queue that is the source's target queue, then the next +event handler invocation will use the new block. .Pp Dispatch sources may be suspended or resumed independently of their target queues using @@ -189,12 +204,39 @@ on the .Fa handler block. .Pp +To unset the event handler, call +.Fn dispatch_source_set_event_handler_f +and pass NULL as +.Fa function . +This unsets the event handler regardless of whether the handler +was a function pointer or a block. Registration and cancellation handlers +(see below) may be unset in the same way, but as noted below, a cancellation +handler may be required. +.Sh REGISTRATION +When +.Fn dispatch_resume +is called on a suspended or newly created source, there may be a brief delay +before the source is ready to receive events from the underlying system handle. +During this delay, the event handler will not be invoked, and events will be +missed. +.Pp +Once the dispatch source is registered with the underlying system and is ready +to process all events its optional registration handler will be submitted to +its target queue. This registration handler may be specified via +.Fn dispatch_source_set_registration_handler . +.Pp +The event handler will not be called until the registration handler finishes. +If the source is canceled (see below) before it is registered, +its registration handler will not be called. +.Pp .Sh CANCELLATION The .Fn dispatch_source_cancel function asynchronously cancels the dispatch source, preventing any further invocation of its event handler block. Cancellation does not interrupt a -currently executing handler block (non-preemptive). +currently executing handler block (non-preemptive). If a source is canceled +before the first time it is resumed, its event handler will never be called. +(In this case, note that the source must be resumed before it can be released.) .Pp The .Fn dispatch_source_testcancel diff --git a/man/dispatch_time.3 b/man/dispatch_time.3 index 6d18873..cb65dc5 100644 --- a/man/dispatch_time.3 +++ b/man/dispatch_time.3 @@ -8,7 +8,7 @@ .Nd Calculate temporal milestones .Sh SYNOPSIS .Fd #include -.Vt static const dispatch_time_t DISPATCH_TIME_NOW = 0 ; +.Vt static const dispatch_time_t DISPATCH_TIME_NOW = 0ull ; .Vt static const dispatch_time_t DISPATCH_TIME_FOREVER = ~0ull ; .Ft dispatch_time_t .Fo dispatch_time diff --git a/os/object.h b/os/object.h new file mode 100644 index 0000000..7d1e5ce --- /dev/null +++ b/os/object.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011-2012 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 __OS_OBJECT__ +#define __OS_OBJECT__ + +#ifdef __APPLE__ +#include +#endif + +/*! + * @header + * + * @preprocinfo + * By default, libSystem objects such as GCD and XPC objects are declared as + * Objective-C types when building with an Objective-C compiler. This allows + * them to participate in ARC, in RR management by the Blocks runtime and in + * leaks checking by the static analyzer, and enables them to be added to Cocoa + * collections. + * + * NOTE: this requires explicit cancellation of dispatch sources and xpc + * connections whose handler blocks capture the source/connection object, + * resp. ensuring that such captures do not form retain cycles (e.g. by + * declaring the source as __weak). + * + * To opt-out of this default behavior, add -DOS_OBJECT_USE_OBJC=0 to your + * compiler flags. + * + * This mode requires a platform with the modern Objective-C runtime, the + * Objective-C GC compiler option to be disabled, and at least a Mac OS X 10.8 + * deployment target. + */ + +#ifndef OS_OBJECT_HAVE_OBJC_SUPPORT +#if defined(__OBJC2__) && !defined(__OBJC_GC__) && ( \ + __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_8 || \ + __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_6_0) +#define OS_OBJECT_HAVE_OBJC_SUPPORT 1 +#else +#define OS_OBJECT_HAVE_OBJC_SUPPORT 0 +#endif +#endif + +#if OS_OBJECT_HAVE_OBJC_SUPPORT +#ifndef OS_OBJECT_USE_OBJC +#define OS_OBJECT_USE_OBJC 1 +#endif +#elif defined(OS_OBJECT_USE_OBJC) && OS_OBJECT_USE_OBJC +/* Unsupported platform for OS_OBJECT_USE_OBJC=1 */ +#undef OS_OBJECT_USE_OBJC +#define OS_OBJECT_USE_OBJC 0 +#else +#define OS_OBJECT_USE_OBJC 0 +#endif + +#if OS_OBJECT_USE_OBJC +#import +#define OS_OBJECT_CLASS(name) OS_##name +#define OS_OBJECT_DECL(name, ...) \ + @protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \ + @end \ + typedef NSObject *name##_t +#define OS_OBJECT_DECL_SUBCLASS(name, super) \ + OS_OBJECT_DECL(name, ) +#if defined(__has_attribute) && __has_attribute(ns_returns_retained) +#define OS_OBJECT_RETURNS_RETAINED __attribute__((__ns_returns_retained__)) +#else +#define OS_OBJECT_RETURNS_RETAINED +#endif +#if defined(__has_feature) && __has_feature(objc_arc) +#define OS_OBJECT_BRIDGE __bridge +#else +#define OS_OBJECT_BRIDGE +#endif +#ifndef OS_OBJECT_USE_OBJC_RETAIN_RELEASE +#if defined(__has_feature) && __has_feature(objc_arc) || \ + defined(__clang_analyzer__) +#define OS_OBJECT_USE_OBJC_RETAIN_RELEASE 1 +#else +#define OS_OBJECT_USE_OBJC_RETAIN_RELEASE 0 +#endif +#endif +#else +/*! @parseOnly */ +#define OS_OBJECT_RETURNS_RETAINED +/*! @parseOnly */ +#define OS_OBJECT_BRIDGE +#define OS_OBJECT_USE_OBJC_RETAIN_RELEASE 0 +#endif + +#endif diff --git a/os/object_private.h b/os/object_private.h new file mode 100644 index 0000000..235e0d3 --- /dev/null +++ b/os/object_private.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2011-2012 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 __OS_OBJECT_PRIVATE__ +#define __OS_OBJECT_PRIVATE__ + +#include +#include +#include + +#ifndef __OSX_AVAILABLE_STARTING +#define __OSX_AVAILABLE_STARTING(x, y) +#endif + +#if __GNUC__ +#define OS_OBJECT_NOTHROW __attribute__((__nothrow__)) +#define OS_OBJECT_NONNULL __attribute__((__nonnull__)) +#define OS_OBJECT_WARN_RESULT __attribute__((__warn_unused_result__)) +#define OS_OBJECT_MALLOC __attribute__((__malloc__)) +#define OS_OBJECT_EXPORT extern __attribute__((visibility("default"))) +#else +/*! @parseOnly */ +#define OS_OBJECT_NOTHROW +/*! @parseOnly */ +#define OS_OBJECT_NONNULL +/*! @parseOnly */ +#define OS_OBJECT_WARN_RESULT +/*! @parseOnly */ +#define OS_OBJECT_MALLOC +#define OS_OBJECT_EXPORT extern +#endif + +#if OS_OBJECT_USE_OBJC && defined(__has_feature) && __has_feature(objc_arc) +#define _OS_OBJECT_OBJC_ARC 1 +#else +#define _OS_OBJECT_OBJC_ARC 0 +#endif + +#define _OS_OBJECT_GLOBAL_REFCNT INT_MAX + +#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \ + isa; /* must be pointer-sized */ \ + int volatile ref_cnt; \ + int volatile xref_cnt + +#if OS_OBJECT_HAVE_OBJC_SUPPORT +// Must match size of compiler-generated OBJC_CLASS structure rdar://10640168 +#define _OS_OBJECT_CLASS_HEADER() \ + void *_os_obj_objc_class_t[5] +#else +#define _OS_OBJECT_CLASS_HEADER() \ + void (*_os_obj_xref_dispose)(_os_object_t); \ + void (*_os_obj_dispose)(_os_object_t) +#endif + +#define OS_OBJECT_CLASS(name) OS_##name + +#if OS_OBJECT_USE_OBJC +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0) +OS_OBJECT_EXPORT +@interface OS_OBJECT_CLASS(object) : NSObject +- (void)_xref_dispose; +- (void)_dispose; +@end +typedef OS_OBJECT_CLASS(object) *_os_object_t; +#define _OS_OBJECT_DECL_SUBCLASS_INTERFACE(name, super) \ + @interface OS_OBJECT_CLASS(name) : OS_OBJECT_CLASS(super) \ + \ + @end +#else +typedef struct _os_object_s *_os_object_t; +#endif + +__BEGIN_DECLS + +#if !_OS_OBJECT_OBJC_ARC + +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0) +OS_OBJECT_EXPORT OS_OBJECT_MALLOC OS_OBJECT_WARN_RESULT OS_OBJECT_NOTHROW +_os_object_t +_os_object_alloc(const void *cls, size_t size); + +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0) +OS_OBJECT_EXPORT OS_OBJECT_NONNULL OS_OBJECT_NOTHROW +void _os_object_dealloc(_os_object_t object); + +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0) +OS_OBJECT_EXPORT OS_OBJECT_NONNULL OS_OBJECT_NOTHROW +_os_object_t +_os_object_retain(_os_object_t object); + +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0) +OS_OBJECT_EXPORT OS_OBJECT_NONNULL OS_OBJECT_NOTHROW +void +_os_object_release(_os_object_t object); + +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0) +OS_OBJECT_EXPORT OS_OBJECT_NONNULL OS_OBJECT_NOTHROW +_os_object_t +_os_object_retain_internal(_os_object_t object); + +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0) +OS_OBJECT_EXPORT OS_OBJECT_NONNULL OS_OBJECT_NOTHROW +void +_os_object_release_internal(_os_object_t object); + +#endif // !_OS_OBJECT_OBJC_ARC + +__END_DECLS + +#endif diff --git a/private/benchmark.h b/private/benchmark.h index df42a8a..c6edfe6 100644 --- a/private/benchmark.h +++ b/private/benchmark.h @@ -28,7 +28,7 @@ #define __DISPATCH_BENCHMARK__ #ifndef __DISPATCH_INDIRECT__ -#error "Please #include instead of this file directly." +#error "Please #include instead of this file directly." #include // for HeaderDoc #endif diff --git a/private/data_private.h b/private/data_private.h new file mode 100644 index 0000000..6562b37 --- /dev/null +++ b/private/data_private.h @@ -0,0 +1,178 @@ +/* + * 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_DATA_PRIVATE__ +#define __DISPATCH_DATA_PRIVATE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +__BEGIN_DECLS + +#ifdef __BLOCKS__ + +/*! + * @const DISPATCH_DATA_DESTRUCTOR_NONE + * @discussion The destructor for dispatch data objects that require no + * management. This can be used to allow a data object to efficiently + * encapsulate data that should not be copied or freed by the system. + */ +#define DISPATCH_DATA_DESTRUCTOR_NONE (_dispatch_data_destructor_none) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT const dispatch_block_t _dispatch_data_destructor_none; + +/*! + * @const DISPATCH_DATA_DESTRUCTOR_VM_DEALLOCATE + * @discussion The destructor for dispatch data objects that have been created + * from buffers that require deallocation using vm_deallocate. + */ +#define DISPATCH_DATA_DESTRUCTOR_VM_DEALLOCATE \ + (_dispatch_data_destructor_vm_deallocate) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT const dispatch_block_t _dispatch_data_destructor_vm_deallocate; + +/*! + * @typedef dispatch_data_format_type_t + * + * @abstract + * Data formats are used to specify the input and output types of data supplied + * to dispatch_data_create_transform. + */ +typedef const struct dispatch_data_format_type_s *dispatch_data_format_type_t; + +/*! + * @const DISPATCH_DATA_FORMAT_TYPE_NONE + * @discussion A data format denoting that the given input or output format is, + * or should be, comprised of raw data bytes with no given encoding. + */ +#define DISPATCH_DATA_FORMAT_TYPE_NONE (&_dispatch_data_format_type_none) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT +const struct dispatch_data_format_type_s _dispatch_data_format_type_none; + +/*! + * @const DISPATCH_DATA_FORMAT_TYPE_BASE32 + * @discussion A data format denoting that the given input or output format is, + * or should be, encoded in Base32 (RFC 4648) format. On input, this format will + * skip whitespace characters. Cannot be used in conjunction with UTF format + * types. + */ +#define DISPATCH_DATA_FORMAT_TYPE_BASE32 (&_dispatch_data_format_type_base32) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT +const struct dispatch_data_format_type_s _dispatch_data_format_type_base32; + +/*! + * @const DISPATCH_DATA_FORMAT_TYPE_BASE64 + * @discussion A data format denoting that the given input or output format is, + * or should be, encoded in Base64 (RFC 4648) format. On input, this format will + * skip whitespace characters. Cannot be used in conjunction with UTF format + * types. + */ +#define DISPATCH_DATA_FORMAT_TYPE_BASE64 (&_dispatch_data_format_type_base64) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT +const struct dispatch_data_format_type_s _dispatch_data_format_type_base64; + +/*! + * @const DISPATCH_DATA_FORMAT_TYPE_UTF8 + * @discussion A data format denoting that the given input or output format is, + * or should be, encoded in UTF-8 format. Is only valid when used in conjunction + * with other UTF format types. + */ +#define DISPATCH_DATA_FORMAT_TYPE_UTF8 (&_dispatch_data_format_type_utf8) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT +const struct dispatch_data_format_type_s _dispatch_data_format_type_utf8; + +/*! + * @const DISPATCH_DATA_FORMAT_TYPE_UTF16LE + * @discussion A data format denoting that the given input or output format is, + * or should be, encoded in UTF-16LE format. Is only valid when used in + * conjunction with other UTF format types. + */ +#define DISPATCH_DATA_FORMAT_TYPE_UTF16LE (&_dispatch_data_format_type_utf16le) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT +const struct dispatch_data_format_type_s _dispatch_data_format_type_utf16le; + +/*! + * @const DISPATCH_DATA_FORMAT_TYPE_UTF16BE + * @discussion A data format denoting that the given input or output format is, + * or should be, encoded in UTF-16BE format. Is only valid when used in + * conjunction with other UTF format types. + */ +#define DISPATCH_DATA_FORMAT_TYPE_UTF16BE (&_dispatch_data_format_type_utf16be) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT +const struct dispatch_data_format_type_s _dispatch_data_format_type_utf16be; + +/*! + * @const DISPATCH_DATA_FORMAT_TYPE_UTFANY + * @discussion A data format denoting that dispatch_data_create_transform should + * attempt to automatically detect the input type based on the presence of a + * byte order mark character at the beginning of the data. In the absence of a + * BOM, the data will be assumed to be in UTF-8 format. Only valid as an input + * format. + */ +#define DISPATCH_DATA_FORMAT_TYPE_UTF_ANY (&_dispatch_data_format_type_utf_any) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT +const struct dispatch_data_format_type_s _dispatch_data_format_type_utf_any; + +/*! + * @function dispatch_data_create_transform + * Returns a new dispatch data object after transforming the given data object + * from the supplied format, into the given output format. + * + * @param data + * The data object representing the region(s) of memory to transform. + * @param input_type + * Flags specifying the input format of the source dispatch_data_t + * + * @param output_type + * Flags specifying the expected output format of the resulting transfomation. + * + * @result + * A newly created dispatch data object, dispatch_data_empty if no has been + * produced, or NULL if an error occurred. + */ + +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_RETURNS_RETAINED +DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_data_t +dispatch_data_create_with_transform(dispatch_data_t data, + dispatch_data_format_type_t input_type, + dispatch_data_format_type_t output_type); + +#endif /* __BLOCKS__ */ + +__END_DECLS + +#endif // __DISPATCH_DATA_PRIVATE__ diff --git a/private/dispatch.h b/private/dispatch.h new file mode 100644 index 0000000..3f1f374 --- /dev/null +++ b/private/dispatch.h @@ -0,0 +1,39 @@ +/* + * 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_PRIVATE_LEGACY__ +#define __DISPATCH_PRIVATE_LEGACY__ + +#define DISPATCH_NO_LEGACY 1 +#ifdef DISPATCH_LEGACY // +#error "Dispatch legacy API unavailable." +#endif + +#ifndef __DISPATCH_BUILDING_DISPATCH__ +#include_next +#endif + +#endif // __DISPATCH_PRIVATE_LEGACY__ diff --git a/private/private.h b/private/private.h index 9bb0e01..08a14ce 100644 --- a/private/private.h +++ b/private/private.h @@ -44,18 +44,8 @@ #endif #include -#define DISPATCH_NO_LEGACY 1 -#ifdef DISPATCH_LEGACY // -#error "Dispatch legacy API unavailable." -#endif - #ifndef __DISPATCH_BUILDING_DISPATCH__ -#include_next - -// Workaround -#ifndef __DISPATCH_PUBLIC__ -#include "/usr/include/dispatch/dispatch.h" -#endif +#include #ifndef __DISPATCH_INDIRECT__ #define __DISPATCH_INDIRECT__ @@ -64,13 +54,16 @@ #include #include #include +#include #undef __DISPATCH_INDIRECT__ #endif /* !__DISPATCH_BUILDING_DISPATCH__ */ -/* LEGACY: Use DISPATCH_API_VERSION */ -#define LIBDISPATCH_VERSION DISPATCH_API_VERSION +// Check that public and private dispatch headers match +#if DISPATCH_API_VERSION != 20111201 // Keep in sync with +#error "Dispatch header mismatch between /usr/include and /usr/local/include" +#endif __BEGIN_DECLS @@ -112,6 +105,10 @@ __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0) DISPATCH_EXPORT void (*_dispatch_end_NSAutoReleasePool)(void *); +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NOTHROW +bool _dispatch_is_multithreaded(void); + #define _dispatch_time_after_nsec(t) \ dispatch_time(DISPATCH_TIME_NOW, (t)) #define _dispatch_time_after_usec(t) \ diff --git a/private/queue_private.h b/private/queue_private.h index 5ec36d0..bdfb5b8 100644 --- a/private/queue_private.h +++ b/private/queue_private.h @@ -28,7 +28,7 @@ #define __DISPATCH_QUEUE_PRIVATE__ #ifndef __DISPATCH_INDIRECT__ -#error "Please #include instead of this file directly." +#error "Please #include instead of this file directly." #include // for HeaderDoc #endif @@ -125,6 +125,21 @@ DISPATCH_EXPORT const struct dispatch_queue_offsets_s { const uint16_t dqo_running_size; } dispatch_queue_offsets; +/*! + * @function dispatch_flush_continuation_cache + * + * @abstract + * Flushes the current thread's cache of continuation objects, if any. + * + * @discussion + * Warning: this function is subject to change in a future release. + * Please contact the GCD team before using it in your code. + */ + +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_NA) +DISPATCH_EXPORT DISPATCH_NOTHROW +void +dispatch_flush_continuation_cache(void); __END_DECLS diff --git a/private/source_private.h b/private/source_private.h index 576f64a..8de7308 100644 --- a/private/source_private.h +++ b/private/source_private.h @@ -28,7 +28,7 @@ #define __DISPATCH_SOURCE_PRIVATE__ #ifndef __DISPATCH_INDIRECT__ -#error "Please #include instead of this file directly." +#error "Please #include instead of this file directly." #include // for HeaderDoc #endif @@ -51,6 +51,57 @@ DISPATCH_EXPORT const struct dispatch_source_type_s _dispatch_source_type_vfs; __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_3) DISPATCH_EXPORT const struct dispatch_source_type_s _dispatch_source_type_vm; +/*! + * @const DISPATCH_SOURCE_TYPE_SOCK + * @discussion A dispatch source that monitors events on socket state changes. + */ +#define DISPATCH_SOURCE_TYPE_SOCK (&_dispatch_source_type_sock) +__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) +DISPATCH_EXPORT const struct dispatch_source_type_s _dispatch_source_type_sock; + +/*! + * @enum dispatch_source_sock_flags_t + * + * @constant DISPATCH_SOCK_CONNRESET + * Received RST + * + * @constant DISPATCH_SOCK_READCLOSED + * Read side is shutdown + * + * @constant DISPATCH_SOCK_WRITECLOSED + * Write side is shutdown + * + * @constant DISPATCH_SOCK_TIMEOUT + * Timeout: rexmt, keep-alive or persist + * + * @constant DISPATCH_SOCK_NOSRCADDR + * Source address not available + * + * @constant DISPATCH_SOCK_IFDENIED + * Interface denied connection + * + * @constant DISPATCH_SOCK_SUSPEND + * Output queue suspended + * + * @constant DISPATCH_SOCK_RESUME + * Output queue resumed + * + * @constant DISPATCH_SOCK_KEEPALIVE + * TCP Keepalive received + * + */ +enum { + DISPATCH_SOCK_CONNRESET = 0x00000001, + DISPATCH_SOCK_READCLOSED = 0x00000002, + DISPATCH_SOCK_WRITECLOSED = 0x00000004, + DISPATCH_SOCK_TIMEOUT = 0x00000008, + DISPATCH_SOCK_NOSRCADDR = 0x00000010, + DISPATCH_SOCK_IFDENIED = 0x00000020, + DISPATCH_SOCK_SUSPEND = 0x00000040, + DISPATCH_SOCK_RESUME = 0x00000080, + DISPATCH_SOCK_KEEPALIVE = 0x00000100, +}; + /*! * @enum dispatch_source_vfs_flags_t * diff --git a/src/Makefile.am b/src/Makefile.am index 20b2baa..1af748c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,8 +35,7 @@ libdispatch_la_SOURCES= \ 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@ +INCLUDES=-I$(top_builddir) -I$(top_srcdir) -I$(top_srcdir)/private libdispatch_la_CFLAGS=-Wall $(VISIBILITY_FLAGS) $(OMIT_LEAF_FP_FLAGS) libdispatch_la_CFLAGS+=$(MARCH_FLAGS) $(CBLOCKS_FLAGS) $(KQUEUE_CFLAGS) @@ -48,6 +47,7 @@ libdispatch_la_LDFLAGS+=-Wl,-compatibility_version,1 -Wl,-current_version,$(VERS endif CLEANFILES= +DISTCLEANFILES=System if USE_MIG BUILT_SOURCES= \ @@ -63,11 +63,3 @@ CLEANFILES+=$(BUILT_SOURCES) $(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 9a63439..1a77114 100644 --- a/src/apply.c +++ b/src/apply.c @@ -17,35 +17,18 @@ * * @APPLE_APACHE_LICENSE_HEADER_END@ */ -#include "internal.h" -// We'd use __attribute__((aligned(x))), but it does not atually increase the -// alignment of stack variables. All we really need is the stack usage of the -// 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 -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_thread_semaphore_t da_sema; - dispatch_queue_t da_queue; - long _da_pad1[DISPATCH_CACHELINE_SIZE / sizeof(long)]; -}; +#include "internal.h" DISPATCH_ALWAYS_INLINE static inline void _dispatch_apply_invoke(void *ctxt) { - struct dispatch_apply_s *da = ctxt; + dispatch_apply_t da = ctxt; size_t const iter = da->da_iterations; typeof(da->da_func) const func = da->da_func; void *const da_ctxt = da->da_ctxt; - size_t idx; + size_t idx, done = 0; _dispatch_workitem_dec(); // this unit executes many items @@ -55,13 +38,21 @@ _dispatch_apply_invoke(void *ctxt) while (fastpath((idx = dispatch_atomic_inc2o(da, da_index) - 1) < iter)) { _dispatch_client_callout2(da_ctxt, idx, func); _dispatch_workitem_inc(); + done++; } _dispatch_thread_setspecific(dispatch_apply_key, NULL); dispatch_atomic_release_barrier(); - if (dispatch_atomic_dec2o(da, da_thr_cnt) == 0) { + + // The thread that finished the last workitem wakes up the (possibly waiting) + // thread that called dispatch_apply. They could be one and the same. + if (done && (dispatch_atomic_add2o(da, da_done, done) == iter)) { _dispatch_thread_semaphore_signal(da->da_sema); } + + if (dispatch_atomic_dec2o(da, da_thr_cnt) == 0) { + _dispatch_continuation_free((dispatch_continuation_t)da); + } } DISPATCH_NOINLINE @@ -74,7 +65,7 @@ _dispatch_apply2(void *ctxt) static void _dispatch_apply3(void *ctxt) { - struct dispatch_apply_s *da = ctxt; + dispatch_apply_t da = ctxt; dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); _dispatch_thread_setspecific(dispatch_queue_key, da->da_queue); @@ -85,7 +76,7 @@ _dispatch_apply3(void *ctxt) static void _dispatch_apply_serial(void *ctxt) { - struct dispatch_apply_s *da = ctxt; + dispatch_apply_t da = ctxt; size_t idx = 0; _dispatch_workitem_dec(); // this unit executes many items @@ -93,44 +84,57 @@ _dispatch_apply_serial(void *ctxt) _dispatch_client_callout2(da->da_ctxt, idx, da->da_func); _dispatch_workitem_inc(); } while (++idx < da->da_iterations); + + _dispatch_continuation_free((dispatch_continuation_t)da); } -// 256 threads should be good enough for the short to mid term -#define DISPATCH_APPLY_MAX_CPUS 256 +// 64 threads should be good enough for the short to mid term +#define DISPATCH_APPLY_MAX_CPUS 64 DISPATCH_ALWAYS_INLINE static inline void -_dispatch_apply_f2(dispatch_queue_t dq, struct dispatch_apply_s *da, +_dispatch_apply_f2(dispatch_queue_t dq, dispatch_apply_t da, dispatch_function_t func) { - 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; + uint32_t i = 0; + dispatch_continuation_t head = NULL, tail = NULL; + + // The current thread does not need a continuation + uint32_t continuation_cnt = da->da_thr_cnt - 1; + + dispatch_assert(continuation_cnt); + + for (i = 0; i < continuation_cnt; i++) { + dispatch_continuation_t next = _dispatch_continuation_alloc(); + next->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; + next->dc_func = func; + next->dc_ctxt = da; + + next->do_next = head; + head = next; + + if (!tail) { + tail = next; + } } - da->da_sema = _dispatch_get_thread_semaphore(); + _dispatch_thread_semaphore_t sema = _dispatch_get_thread_semaphore(); + da->da_sema = sema; - _dispatch_queue_push_list(dq, (void *)&da_dc[0], - (void *)&da_dc[da->da_thr_cnt - 2]); + _dispatch_queue_push_list(dq, head, tail, continuation_cnt); // 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); + _dispatch_thread_semaphore_wait(sema); + _dispatch_put_thread_semaphore(sema); + } static void _dispatch_apply_redirect(void *ctxt) { - struct dispatch_apply_s *da = ctxt; + dispatch_apply_t da = ctxt; uint32_t da_width = 2 * (da->da_thr_cnt - 1); dispatch_queue_t dq = da->da_queue, rq = dq, tq; @@ -165,40 +169,42 @@ void dispatch_apply_f(size_t iterations, dispatch_queue_t dq, void *ctxt, void (*func)(void *, size_t)) { - struct dispatch_apply_s da; - - 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; - } if (slowpath(iterations == 0)) { return; } - if (iterations < da.da_thr_cnt) { - da.da_thr_cnt = (uint32_t)iterations; + + dispatch_apply_t da = (typeof(da))_dispatch_continuation_alloc(); + + 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_done = 0; + da->da_queue = NULL; + + if (da->da_thr_cnt > DISPATCH_APPLY_MAX_CPUS) { + da->da_thr_cnt = DISPATCH_APPLY_MAX_CPUS; + } + if (iterations < da->da_thr_cnt) { + da->da_thr_cnt = (uint32_t)iterations; } - if (slowpath(dq->dq_width <= 2) || slowpath(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); + return dispatch_sync_f(dq, da, _dispatch_apply_serial); } 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); + return dispatch_sync_f(dq, da, _dispatch_apply_serial); } else { - da.da_queue = dq; - return dispatch_sync_f(dq, &da, _dispatch_apply_redirect); + 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_apply_f2(dq, da, _dispatch_apply2); _dispatch_thread_setspecific(dispatch_queue_key, old_dq); } diff --git a/src/benchmark.c b/src/benchmark.c index 246affa..f340b44 100644 --- a/src/benchmark.c +++ b/src/benchmark.c @@ -20,7 +20,6 @@ #include "internal.h" - struct __dispatch_benchmark_data_s { #if HAVE_MACH_ABSOLUTE_TIME mach_timebase_info_data_t tbi; diff --git a/src/data.c b/src/data.c index e125656..8048964 100644 --- a/src/data.c +++ b/src/data.c @@ -34,41 +34,66 @@ #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 +#if DISPATCH_USE_RESOLVERS && !defined(DISPATCH_RESOLVED_VARIANT) +#error Resolved variant required for movable +#endif 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, +const dispatch_block_t _dispatch_data_destructor_free = ^{ + DISPATCH_CRASH("free destructor called"); +}; + +const dispatch_block_t _dispatch_data_destructor_none = ^{ + DISPATCH_CRASH("none destructor called"); +}; + +const dispatch_block_t _dispatch_data_destructor_vm_deallocate = ^{ + DISPATCH_CRASH("vmdeallocate destructor called"); +}; + +struct dispatch_data_s _dispatch_data_empty = { + .do_vtable = DISPATCH_VTABLE(data), + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_next = DISPATCH_OBJECT_LISTLESS, }; 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)); + dispatch_data_t data = _dispatch_alloc(DISPATCH_VTABLE(data), + 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; } +static void +_dispatch_data_destroy_buffer(const void* buffer, size_t size, + dispatch_queue_t queue, dispatch_block_t destructor) +{ + if (destructor == DISPATCH_DATA_DESTRUCTOR_FREE) { + free((void*)buffer); + } else if (destructor == DISPATCH_DATA_DESTRUCTOR_NONE) { + // do nothing + } else if (destructor == DISPATCH_DATA_DESTRUCTOR_VM_DEALLOCATE) { + vm_deallocate(mach_task_self(), (vm_address_t)buffer, size); + } else { + if (!queue) { + queue = dispatch_get_global_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + } + dispatch_async_f(queue, destructor, _dispatch_call_block_and_release); + } +} + dispatch_data_t dispatch_data_create(const void* buffer, size_t size, dispatch_queue_t queue, dispatch_block_t destructor) @@ -78,11 +103,9 @@ dispatch_data_create(const void* buffer, size_t size, dispatch_queue_t queue, // 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); + if (destructor) { + _dispatch_data_destroy_buffer(buffer, size, queue, + _dispatch_Block_copy(destructor)); } return dispatch_data_empty; } @@ -92,7 +115,6 @@ dispatch_data_create(const void* buffer, size_t size, dispatch_queue_t queue, 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. @@ -102,10 +124,9 @@ dispatch_data_create(const void* buffer, size_t size, dispatch_queue_t queue, return NULL; } buffer = memcpy(data_buf, buffer, size); + data->destructor = DISPATCH_DATA_DESTRUCTOR_FREE; } else { - if (destructor != DISPATCH_DATA_DESTRUCTOR_FREE) { - data->destructor = Block_copy(destructor); - } + data->destructor = _dispatch_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 @@ -121,11 +142,11 @@ dispatch_data_create(const void* buffer, size_t size, dispatch_queue_t queue, return data; } -static void +void _dispatch_data_dispose(dispatch_data_t dd) { dispatch_block_t destructor = dd->destructor; - if (destructor == DISPATCH_DATA_DESTRUCTOR_DEFAULT) { + if (destructor == NULL) { size_t i; for (i = 0; i < dd->num_records; ++i) { _dispatch_data_release(dd->records[i].data_object); @@ -136,16 +157,13 @@ _dispatch_data_dispose(dispatch_data_t dd) (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_data_destroy_buffer(dd->records[0].data_object, + dd->records[0].length, dd->do_targetq, destructor); } - _dispatch_dispose(dd); } -static size_t +size_t _dispatch_data_debug(dispatch_data_t dd, char* buf, size_t bufsiz) { size_t offset = 0; diff --git a/src/data_internal.h b/src/data_internal.h index 314efa7..2dec5f0 100644 --- a/src/data_internal.h +++ b/src/data_internal.h @@ -32,20 +32,15 @@ #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; +DISPATCH_CLASS_DECL(data); struct dispatch_data_s { - DISPATCH_STRUCT_HEADER(dispatch_data_s, dispatch_data_vtable_s); + DISPATCH_STRUCT_HEADER(data); #if DISPATCH_DATA_MOVABLE unsigned int locked; #endif @@ -55,4 +50,17 @@ struct dispatch_data_s { range_record records[]; }; +typedef dispatch_data_t (*dispatch_transform_t)(dispatch_data_t data); + +struct dispatch_data_format_type_s { + uint64_t type; + uint64_t input_mask; + uint64_t output_mask; + dispatch_transform_t decode; + dispatch_transform_t encode; +}; + +void _dispatch_data_dispose(dispatch_data_t data); +size_t _dispatch_data_debug(dispatch_data_t data, char* buf, size_t bufsiz); + #endif // __DISPATCH_DATA_INTERNAL__ diff --git a/src/init.c b/src/init.c index d72219c..8f1456e 100644 --- a/src/init.c +++ b/src/init.c @@ -70,19 +70,34 @@ dummy_function_r0(void) #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; +void (*dispatch_begin_thread_4GC)(void); +void (*dispatch_end_thread_4GC)(void); +void (*dispatch_no_worker_threads_4GC)(void); +void *(*_dispatch_begin_NSAutoReleasePool)(void); +void (*_dispatch_end_NSAutoReleasePool)(void *); #endif +#if !DISPATCH_USE_DIRECT_TSD +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; +#if DISPATCH_PERF_MON +pthread_key_t dispatch_bcounter_key; +#endif +#endif // !DISPATCH_USE_DIRECT_TSD + struct _dispatch_hw_config_s _dispatch_hw_config; bool _dispatch_safe_fork = true; +DISPATCH_NOINLINE +bool +_dispatch_is_multithreaded(void) +{ + return !_dispatch_safe_fork; +} + const struct dispatch_queue_offsets_s dispatch_queue_offsets = { .dqo_version = 3, .dqo_label = offsetof(struct dispatch_queue_s, dq_label), @@ -101,8 +116,8 @@ const struct dispatch_queue_offsets_s dispatch_queue_offsets = { // renaming this symbol DISPATCH_CACHELINE_ALIGN struct dispatch_queue_s _dispatch_main_q = { + .do_vtable = DISPATCH_VTABLE(queue), #if !DISPATCH_USE_RESOLVERS - .do_vtable = &_dispatch_queue_vtable, .do_targetq = &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY], #endif @@ -115,38 +130,137 @@ struct dispatch_queue_s _dispatch_main_q = { .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_vtable = DISPATCH_VTABLE(queue_attr), .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, -}; +#pragma mark - +#pragma mark dispatch_vtables + +DISPATCH_VTABLE_INSTANCE(semaphore, + .do_type = DISPATCH_SEMAPHORE_TYPE, + .do_kind = "semaphore", + .do_dispose = _dispatch_semaphore_dispose, + .do_debug = _dispatch_semaphore_debug, +); + +DISPATCH_VTABLE_INSTANCE(group, + .do_type = DISPATCH_GROUP_TYPE, + .do_kind = "group", + .do_dispose = _dispatch_semaphore_dispose, + .do_debug = _dispatch_semaphore_debug, +); + +DISPATCH_VTABLE_INSTANCE(queue, + .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, +); + +DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_root, queue, + .do_type = DISPATCH_QUEUE_GLOBAL_TYPE, + .do_kind = "global-queue", + .do_debug = dispatch_queue_debug, + .do_probe = _dispatch_queue_probe_root, +); + +DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_mgr, queue, + .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, +); + +DISPATCH_VTABLE_INSTANCE(queue_specific_queue, + .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, +); + +DISPATCH_VTABLE_INSTANCE(queue_attr, + .do_type = DISPATCH_QUEUE_ATTR_TYPE, + .do_kind = "queue-attr", +); + +DISPATCH_VTABLE_INSTANCE(source, + .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_debug, +); + +DISPATCH_VTABLE_INSTANCE(data, + .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, +); + +DISPATCH_VTABLE_INSTANCE(io, + .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, +); + +DISPATCH_VTABLE_INSTANCE(operation, + .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, +); + +DISPATCH_VTABLE_INSTANCE(disk, + .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, +); -const dispatch_block_t _dispatch_data_destructor_free = ^{ - DISPATCH_CRASH("free destructor called"); -}; +void +_dispatch_vtable_init(void) +{ +#if USE_OBJC + // ObjC classes and dispatch vtables are co-located via linker order and + // alias files, verify correct layout during initialization rdar://10640168 + #define DISPATCH_OBJC_CLASS(name) \ + DISPATCH_CONCAT(OBJC_CLASS_$_,DISPATCH_CLASS(name)) + extern void *DISPATCH_OBJC_CLASS(semaphore); + dispatch_assert((char*)DISPATCH_VTABLE(semaphore) - + (char*)&DISPATCH_OBJC_CLASS(semaphore) == 0); + dispatch_assert((char*)&DISPATCH_CONCAT(_,DISPATCH_CLASS(semaphore_vtable)) + - (char*)&DISPATCH_OBJC_CLASS(semaphore) == + sizeof(_os_object_class_s)); +#endif +} #pragma mark - -#pragma mark dispatch_log +#pragma mark dispatch_bug static char _dispatch_build[16]; static void -_dispatch_bug_init(void *context DISPATCH_UNUSED) +_dispatch_build_init(void *context DISPATCH_UNUSED) { #ifdef __APPLE__ int mib[] = { CTL_KERN, KERN_OSVERSION }; @@ -161,31 +275,36 @@ _dispatch_bug_init(void *context DISPATCH_UNUSED) #endif } +#define _dispatch_bug_log(msg, ...) do { \ + static void *last_seen; \ + void *ra = __builtin_return_address(0); \ + if (last_seen != ra) { \ + last_seen = ra; \ + _dispatch_log((msg), ##__VA_ARGS__); \ + } \ +} while(0) + 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); - } + + dispatch_once_f(&pred, NULL, _dispatch_build_init); + _dispatch_bug_log("BUG in libdispatch: %s - %lu - 0x%lx", + _dispatch_build, (unsigned long)line, val); +} + +void +_dispatch_bug_client(const char* msg) +{ + _dispatch_bug_log("BUG in libdispatch client: %s", msg); } 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); - } + _dispatch_bug_log("BUG in libdispatch client: %s %s - 0x%x", msg, + mach_error_string(kr), kr); } void @@ -195,18 +314,12 @@ _dispatch_abort(size_t line, long val) abort(); } -void -_dispatch_log(const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - _dispatch_logv(msg, ap); - va_end(ap); -} +#pragma mark - +#pragma mark dispatch_log static FILE *dispatch_logfile; static bool dispatch_log_disabled; +static dispatch_once_t _dispatch_logv_pred; static void _dispatch_logv_init(void *context DISPATCH_UNUSED) @@ -249,38 +362,48 @@ _dispatch_logv_init(void *context DISPATCH_UNUSED) } } -void -_dispatch_logv(const char *msg, va_list ap) +DISPATCH_NOINLINE +static void +_dispatch_logv_file(const char *msg, va_list ap) { - static dispatch_once_t pred; - dispatch_once_f(&pred, NULL, _dispatch_logv_init); + char *buf; + size_t len; + + len = vasprintf(&buf, msg, ap); + buf[len++] = '\n'; + fwrite(buf, 1, len, dispatch_logfile); + fflush(dispatch_logfile); + free(buf); +} +static inline void +_dispatch_logv(const char *msg, va_list ap) +{ + dispatch_once_f(&_dispatch_logv_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; + return _dispatch_logv_file(msg, ap); } vsyslog(LOG_NOTICE, msg, ap); } -#pragma mark - -#pragma mark dispatch_debug - +DISPATCH_NOINLINE void -dispatch_debug(dispatch_object_t dou, const char *msg, ...) +_dispatch_log(const char *msg, ...) { va_list ap; va_start(ap, msg); - dispatch_debugv(dou._do, msg, ap); + _dispatch_logv(msg, ap); va_end(ap); } +#pragma mark - +#pragma mark dispatch_debug + +DISPATCH_NOINLINE void dispatch_debugv(dispatch_object_t dou, const char *msg, va_list ap) { @@ -297,6 +420,17 @@ dispatch_debugv(dispatch_object_t dou, const char *msg, va_list ap) _dispatch_logv(buf, ap); } +DISPATCH_NOINLINE +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); +} + #pragma mark - #pragma mark dispatch_block_t @@ -308,10 +442,13 @@ _dispatch_Block_copy(dispatch_block_t db) { dispatch_block_t rval; - while (!(rval = Block_copy(db))) { - sleep(1); + if (fastpath(db)) { + while (!fastpath(rval = Block_copy(db))) { + sleep(1); + } + return rval; } - return rval; + DISPATCH_CLIENT_CRASH("NULL was passed where a block should have been"); } void @@ -327,26 +464,116 @@ _dispatch_call_block_and_release(void *block) #pragma mark - #pragma mark dispatch_client_callout -#if DISPATCH_USE_CLIENT_CALLOUT +// Abort on uncaught exceptions thrown from client callouts rdar://8577499 +#if DISPATCH_USE_CLIENT_CALLOUT && (__arm__ || !USE_OBJC) +// On platforms with SjLj exceptions, avoid the SjLj overhead on every callout +// by clearing the unwinder's TSD pointer to the handler stack around callouts -#undef _dispatch_client_callout -#undef _dispatch_client_callout2 +#define _dispatch_get_tsd_base() +#define _dispatch_get_unwind_tsd() (NULL) +#define _dispatch_set_unwind_tsd(u) do {(void)(u);} while (0) +#define _dispatch_free_unwind_tsd() +#undef _dispatch_client_callout DISPATCH_NOINLINE void _dispatch_client_callout(void *ctxt, dispatch_function_t f) { - return f(ctxt); + _dispatch_get_tsd_base(); + void *u = _dispatch_get_unwind_tsd(); + _dispatch_set_unwind_tsd(NULL); + f(ctxt); + _dispatch_free_unwind_tsd(); + _dispatch_set_unwind_tsd(u); } +#undef _dispatch_client_callout2 DISPATCH_NOINLINE void _dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) { - return f(ctxt, i); + _dispatch_get_tsd_base(); + void *u = _dispatch_get_unwind_tsd(); + _dispatch_set_unwind_tsd(NULL); + f(ctxt, i); + _dispatch_free_unwind_tsd(); + _dispatch_set_unwind_tsd(u); } -#endif +#endif // DISPATCH_USE_CLIENT_CALLOUT + +#pragma mark - +#pragma mark _os_object_t no_objc + +#if !USE_OBJC + +static const _os_object_class_s _os_object_class; + +void +_os_object_init(void) +{ + return; +} + +_os_object_t +_os_object_alloc(const void *cls, size_t size) +{ + _os_object_t obj; + dispatch_assert(size >= sizeof(struct _os_object_s)); + if (!cls) cls = &_os_object_class; + while (!fastpath(obj = calloc(1u, size))) { + sleep(1); // Temporary resource shortage + } + obj->os_obj_isa = cls; + return obj; +} + +void +_os_object_dealloc(_os_object_t obj) +{ + *((void *volatile*)&obj->os_obj_isa) = (void *)0x200; + return free(obj); +} + +void +_os_object_xref_dispose(_os_object_t obj) +{ + if (fastpath(obj->os_obj_isa->_os_obj_xref_dispose)) { + return obj->os_obj_isa->_os_obj_xref_dispose(obj); + } + return _os_object_release_internal(obj); +} + +void +_os_object_dispose(_os_object_t obj) +{ + if (fastpath(obj->os_obj_isa->_os_obj_dispose)) { + return obj->os_obj_isa->_os_obj_dispose(obj); + } + return _os_object_dealloc(obj); +} + +#pragma mark - +#pragma mark dispatch_autorelease_pool no_objc + +#if DISPATCH_COCOA_COMPAT + +void *_dispatch_autorelease_pool_push(void) { + void *pool = NULL; + if (_dispatch_begin_NSAutoReleasePool) { + pool = _dispatch_begin_NSAutoReleasePool(); + } + return pool; +} + +void _dispatch_autorelease_pool_pop(void *pool) { + if (_dispatch_end_NSAutoReleasePool) { + _dispatch_end_NSAutoReleasePool(pool); + } +} + +#endif // DISPATCH_COCOA_COMPAT +#endif // !USE_OBJC #pragma mark - #pragma mark dispatch_source_types @@ -552,6 +779,16 @@ const struct dispatch_source_type_s _dispatch_source_type_mach_recv = { .init = dispatch_source_type_mach_recv_init, }; +const struct dispatch_source_type_s _dispatch_source_type_sock = { + .ke = { + .filter = EVFILT_SOCK, + .flags = EV_CLEAR, + }, + .mask = NOTE_CONNRESET | NOTE_READCLOSED | NOTE_WRITECLOSED | + NOTE_TIMEOUT | NOTE_NOSRCADDR | NOTE_IFDENIED | NOTE_SUSPEND | + NOTE_RESUME | NOTE_KEEPALIVE, +}; + #pragma mark - #pragma mark dispatch_mig @@ -618,5 +855,4 @@ _dispatch_mach_notify_send_once(mach_port_t notify DISPATCH_UNUSED) return KERN_SUCCESS; } - #endif // HAVE_MACH diff --git a/src/internal.h b/src/internal.h index 24d3a04..a90f93f 100644 --- a/src/internal.h +++ b/src/internal.h @@ -32,14 +32,38 @@ #define __DISPATCH_BUILDING_DISPATCH__ #define __DISPATCH_INDIRECT__ +#ifdef __APPLE__ +#include +#include +#endif + + +#if USE_OBJC && ((!TARGET_IPHONE_SIMULATOR && defined(__i386__)) || \ + (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED < 1080)) +// Disable Objective-C support on platforms with legacy objc runtime +#undef USE_OBJC +#define USE_OBJC 0 +#endif + +#if USE_OBJC +#define OS_OBJECT_HAVE_OBJC_SUPPORT 1 +#if __OBJC__ +#define OS_OBJECT_USE_OBJC 1 +#else +#define OS_OBJECT_USE_OBJC 0 +#endif // __OBJC__ +#else +#define OS_OBJECT_HAVE_OBJC_SUPPORT 0 +#endif // USE_OBJC #include #include +#include +#include #include #include -#include #include #include #include @@ -47,15 +71,21 @@ #include #include -/* private.h uses #include_next and must be included last to avoid picking - * up installed headers. */ +/* private.h must be included last to avoid picking up installed headers. */ +#include "object_private.h" #include "queue_private.h" #include "source_private.h" +#include "data_private.h" #include "benchmark.h" #include "private.h" /* More #includes at EOF (dependent on the contents of internal.h) ... */ +// Abort on uncaught exceptions thrown from client callouts rdar://8577499 +#if !defined(DISPATCH_USE_CLIENT_CALLOUT) +#define DISPATCH_USE_CLIENT_CALLOUT 1 +#endif + /* The "_debug" library build */ #ifndef DISPATCH_DEBUG #define DISPATCH_DEBUG 0 @@ -65,10 +95,6 @@ #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 @@ -154,6 +180,8 @@ #else #define DISPATCH_ALWAYS_INLINE_NDEBUG __attribute__((__always_inline__)) #endif +#define DISPATCH_CONCAT(x,y) DISPATCH_CONCAT1(x,y) +#define DISPATCH_CONCAT1(x,y) x ## y // workaround 6368156 #ifdef NSEC_PER_SEC @@ -176,13 +204,13 @@ DISPATCH_NOINLINE void _dispatch_bug(size_t line, long val); DISPATCH_NOINLINE +void _dispatch_bug_client(const char* msg); +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))) +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 @@ -325,6 +353,7 @@ void _dispatch_call_block_and_release(void *block); void dummy_function(void); long dummy_function_r0(void); +void _dispatch_vtable_init(void); void _dispatch_source_drain_kevent(struct kevent *); @@ -333,8 +362,6 @@ 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); -bool _dispatch_source_testcancel(dispatch_source_t); - uint64_t _dispatch_timeout(dispatch_time_t when); extern bool _dispatch_safe_fork; @@ -347,21 +374,29 @@ extern struct _dispatch_hw_config_s { /* #includes dependent on internal.h */ #include "shims.h" -#include "object_internal.h" -#include "queue_internal.h" -#include "semaphore_internal.h" -#include "source_internal.h" -#include "data_internal.h" -#include "io_internal.h" -#include "trace.h" // SnowLeopard and iOS Simulator fallbacks #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 +#ifndef WORKQ_BG_PRIOQUEUE +#define WORKQ_BG_PRIOQUEUE 3 +#endif +#ifndef WORKQ_ADDTHREADS_OPTION_OVERCOMMIT +#define WORKQ_ADDTHREADS_OPTION_OVERCOMMIT 0x00000001 +#endif +#if TARGET_IPHONE_SIMULATOR && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 +#ifndef DISPATCH_NO_BG_PRIORITY +#define DISPATCH_NO_BG_PRIORITY 1 +#endif +#endif +#if TARGET_IPHONE_SIMULATOR && __MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +#ifndef DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK +#define DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK 1 +#endif +#endif +#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +#undef HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP +#define HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP 0 #endif #endif // HAVE_PTHREAD_WORKQUEUES @@ -422,4 +457,18 @@ extern struct _dispatch_hw_config_s { _dispatch_hardware_crash(); \ } while (0) +#define _OS_OBJECT_CLIENT_CRASH(x) do { \ + _dispatch_set_crash_log_message("API MISUSE: " x); \ + _dispatch_hardware_crash(); \ + } while (0) + +/* #includes dependent on internal.h */ +#include "object_internal.h" +#include "semaphore_internal.h" +#include "queue_internal.h" +#include "source_internal.h" +#include "data_internal.h" +#include "io_internal.h" +#include "trace.h" + #endif /* __DISPATCH_INTERNAL__ */ diff --git a/src/io.c b/src/io.c index d01c266..4e36015 100644 --- a/src/io.c +++ b/src/io.c @@ -25,12 +25,10 @@ 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, @@ -52,7 +50,6 @@ static void _dispatch_stream_init(dispatch_fd_entry_t fd_entry, 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, @@ -98,35 +95,7 @@ enum { 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, -}; +#define _dispatch_io_Block_copy(x) ((typeof(x))_dispatch_Block_copy((dispatch_block_t)(x))) #pragma mark - #pragma mark dispatch_io_hashtables @@ -218,11 +187,9 @@ _dispatch_iocntl(uint32_t param, uint64_t value) 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; + dispatch_io_t channel = _dispatch_alloc(DISPATCH_VTABLE(io), + sizeof(struct dispatch_io_s)); 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; @@ -263,7 +230,7 @@ _dispatch_io_init(dispatch_io_t channel, dispatch_fd_entry_t fd_entry, } } -static void +void _dispatch_io_dispose(dispatch_io_t channel) { if (channel->fd_entry && !(channel->atomic_flags & (DIO_CLOSED|DIO_STOPPED))) { @@ -285,7 +252,6 @@ _dispatch_io_dispose(dispatch_io_t channel) if (channel->barrier_group) { dispatch_release(channel->barrier_group); } - _dispatch_dispose(channel); } static int @@ -908,12 +874,10 @@ _dispatch_operation_create(dispatch_op_direction_t direction, }); return NULL; } - dispatch_operation_t op; - op = calloc(1ul, sizeof(struct dispatch_operation_s)); - op->do_vtable = &_dispatch_operation_vtable; + dispatch_operation_t op = _dispatch_alloc(DISPATCH_VTABLE(operation), + sizeof(struct dispatch_operation_s)); op->do_next = DISPATCH_OBJECT_LISTLESS; - op->do_ref_cnt = 1; - op->do_xref_cnt = 0; // operation object is not exposed externally + op->do_xref_cnt = -1; // 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); @@ -921,7 +885,7 @@ _dispatch_operation_create(dispatch_op_direction_t direction, op->direction = direction; op->offset = offset + channel->f_ptr; op->length = length; - op->handler = Block_copy(handler); + op->handler = _dispatch_io_Block_copy(handler); _dispatch_retain(channel); op->channel = channel; op->params = channel->params; @@ -935,7 +899,7 @@ _dispatch_operation_create(dispatch_op_direction_t direction, return op; } -static void +void _dispatch_operation_dispose(dispatch_operation_t op) { // Deliver the data if there's any @@ -964,7 +928,6 @@ _dispatch_operation_dispose(dispatch_operation_t op) dispatch_release(op->op_q); } Block_release(op->handler); - _dispatch_dispose(op); } static void @@ -1436,12 +1399,11 @@ _dispatch_disk_init(dispatch_fd_entry_t fd_entry, dev_t dev) } // 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 = _dispatch_alloc(DISPATCH_VTABLE(disk), + sizeof(struct dispatch_disk_s) + + (pending_reqs_depth * sizeof(dispatch_operation_t))); disk->do_next = DISPATCH_OBJECT_LISTLESS; - disk->do_ref_cnt = 1; - disk->do_xref_cnt = 0; + disk->do_xref_cnt = -1; disk->advise_list_depth = pending_reqs_depth; disk->do_targetq = _dispatch_get_root_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, false); @@ -1456,7 +1418,7 @@ out: TAILQ_INIT(&fd_entry->stream_ops); } -static void +void _dispatch_disk_dispose(dispatch_disk_t disk) { uintptr_t hash = DIO_HASH(disk->dev); @@ -1467,7 +1429,6 @@ _dispatch_disk_dispose(dispatch_disk_t disk) dispatch_assert(!disk->advise_list[i]); } dispatch_release(disk->pick_queue); - free(disk); } #pragma mark - diff --git a/src/io_internal.h b/src/io_internal.h index c43bd75..dbbb6bf 100644 --- a/src/io_internal.h +++ b/src/io_internal.h @@ -76,8 +76,8 @@ typedef unsigned int dispatch_op_flags_t; #define _dispatch_io_debug(msg, fd, args...) #endif -DISPATCH_DECL(dispatch_operation); -DISPATCH_DECL(dispatch_disk); +DISPATCH_DECL_INTERNAL(dispatch_operation); +DISPATCH_DECL_INTERNAL(dispatch_disk); struct dispatch_stream_s { dispatch_queue_t dq; @@ -104,12 +104,9 @@ struct dispatch_stat_s { mode_t mode; }; -struct dispatch_disk_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_disk_s); -}; - +DISPATCH_CLASS_DECL(disk); struct dispatch_disk_s { - DISPATCH_STRUCT_HEADER(dispatch_disk_s, dispatch_disk_vtable_s); + DISPATCH_STRUCT_HEADER(disk); dev_t dev; TAILQ_HEAD(dispatch_disk_operations_s, dispatch_operation_s) operations; dispatch_operation_t cur_rq; @@ -149,12 +146,9 @@ typedef struct dispatch_io_param_s { unsigned long interval_flags; } dispatch_io_param_s; -struct dispatch_operation_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_operation_s); -}; - +DISPATCH_CLASS_DECL(operation); struct dispatch_operation_s { - DISPATCH_STRUCT_HEADER(dispatch_operation_s, dispatch_operation_vtable_s); + DISPATCH_STRUCT_HEADER(operation); dispatch_queue_t op_q; dispatch_op_direction_t direction; // READ OR WRITE dispatch_io_param_s params; @@ -177,12 +171,9 @@ struct dispatch_operation_s { TAILQ_ENTRY(dispatch_operation_s) stream_list; }; -struct dispatch_io_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_io_s); -}; - +DISPATCH_CLASS_DECL(io); struct dispatch_io_s { - DISPATCH_STRUCT_HEADER(dispatch_io_s, dispatch_io_vtable_s); + DISPATCH_STRUCT_HEADER(io); dispatch_queue_t queue, barrier_queue; dispatch_group_t barrier_group; dispatch_io_param_s params; @@ -194,5 +185,8 @@ struct dispatch_io_s { }; void _dispatch_io_set_target_queue(dispatch_io_t channel, dispatch_queue_t dq); +void _dispatch_io_dispose(dispatch_io_t channel); +void _dispatch_operation_dispose(dispatch_operation_t operation); +void _dispatch_disk_dispose(dispatch_disk_t disk); #endif // __DISPATCH_IO_INTERNAL__ diff --git a/src/object.c b/src/object.c index b84979b..7b94c75 100644 --- a/src/object.c +++ b/src/object.c @@ -20,62 +20,164 @@ #include "internal.h" -void -dispatch_retain(dispatch_object_t dou) +#pragma mark - +#pragma mark _os_object_t + +unsigned long +_os_object_retain_count(_os_object_t obj) { - if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { - return; // global object + int xref_cnt = obj->os_obj_xref_cnt; + if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { + return ULONG_MAX; // global object } - if (slowpath((dispatch_atomic_inc2o(dou._do, do_xref_cnt) - 1) == 0)) { - DISPATCH_CLIENT_CRASH("Resurrection of an object"); + return xref_cnt + 1; +} + +_os_object_t +_os_object_retain_internal(_os_object_t obj) +{ + int ref_cnt = obj->os_obj_ref_cnt; + if (slowpath(ref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { + return obj; // global object } + ref_cnt = dispatch_atomic_inc2o(obj, os_obj_ref_cnt); + if (slowpath(ref_cnt <= 0)) { + DISPATCH_CRASH("Resurrection of an object"); + } + return obj; } void -_dispatch_retain(dispatch_object_t dou) +_os_object_release_internal(_os_object_t obj) { - if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + int ref_cnt = obj->os_obj_ref_cnt; + if (slowpath(ref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { return; // global object } - if (slowpath((dispatch_atomic_inc2o(dou._do, do_ref_cnt) - 1) == 0)) { - DISPATCH_CLIENT_CRASH("Resurrection of an object"); + ref_cnt = dispatch_atomic_dec2o(obj, os_obj_ref_cnt); + if (fastpath(ref_cnt >= 0)) { + return; + } + if (slowpath(ref_cnt < -1)) { + DISPATCH_CRASH("Over-release of an object"); + } +#if DISPATCH_DEBUG + if (slowpath(obj->os_obj_xref_cnt >= 0)) { + DISPATCH_CRASH("Release while external references exist"); + } +#endif + return _os_object_dispose(obj); +} + +_os_object_t +_os_object_retain(_os_object_t obj) +{ + int xref_cnt = obj->os_obj_xref_cnt; + if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { + return obj; // global object + } + xref_cnt = dispatch_atomic_inc2o(obj, os_obj_xref_cnt); + if (slowpath(xref_cnt <= 0)) { + _OS_OBJECT_CLIENT_CRASH("Resurrection of an object"); } + return obj; } void -dispatch_release(dispatch_object_t dou) +_os_object_release(_os_object_t obj) { - if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + int xref_cnt = obj->os_obj_xref_cnt; + if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { + return; // global object + } + xref_cnt = dispatch_atomic_dec2o(obj, os_obj_xref_cnt); + if (fastpath(xref_cnt >= 0)) { return; } + if (slowpath(xref_cnt < -1)) { + _OS_OBJECT_CLIENT_CRASH("Over-release of an object"); + } + return _os_object_xref_dispose(obj); +} - unsigned int xref_cnt = dispatch_atomic_dec2o(dou._do, do_xref_cnt) + 1; - if (fastpath(xref_cnt > 1)) { - return; +bool +_os_object_retain_weak(_os_object_t obj) +{ + int xref_cnt = obj->os_obj_xref_cnt; + if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { + return true; // global object } - if (fastpath(xref_cnt == 1)) { - if (dou._do->do_vtable == (void*)&_dispatch_source_kevent_vtable) { - return _dispatch_source_xref_release(dou._ds); - } - if (slowpath(DISPATCH_OBJECT_SUSPENDED(dou._do))) { - // Arguments for and against this assert are within 6705399 - DISPATCH_CLIENT_CRASH("Release of a suspended object"); - } - return _dispatch_release(dou._do); +retry: + if (slowpath(xref_cnt == -1)) { + return false; + } + if (slowpath(xref_cnt < -1)) { + goto overrelease; } - DISPATCH_CLIENT_CRASH("Over-release of an object"); + if (slowpath(!dispatch_atomic_cmpxchg2o(obj, os_obj_xref_cnt, xref_cnt, + xref_cnt + 1))) { + xref_cnt = obj->os_obj_xref_cnt; + goto retry; + } + return true; +overrelease: + _OS_OBJECT_CLIENT_CRASH("Over-release of an object"); +} + +bool +_os_object_allows_weak_reference(_os_object_t obj) +{ + int xref_cnt = obj->os_obj_xref_cnt; + if (slowpath(xref_cnt == -1)) { + return false; + } + if (slowpath(xref_cnt < -1)) { + _OS_OBJECT_CLIENT_CRASH("Over-release of an object"); + } + return true; +} + +#pragma mark - +#pragma mark dispatch_object_t + +void * +_dispatch_alloc(const void *vtable, size_t size) +{ + return _os_object_alloc(vtable, size); } void -_dispatch_dispose(dispatch_object_t dou) +dispatch_retain(dispatch_object_t dou) +{ + (void)_os_object_retain(dou._os_obj); +} + +void +_dispatch_retain(dispatch_object_t dou) +{ + (void)_os_object_retain_internal(dou._os_obj); +} + +void +dispatch_release(dispatch_object_t dou) +{ + _os_object_release(dou._os_obj); +} + +void +_dispatch_release(dispatch_object_t dou) +{ + _os_object_release_internal(dou._os_obj); +} + +static void +_dispatch_dealloc(dispatch_object_t dou) { dispatch_queue_t tq = dou._do->do_targetq; dispatch_function_t func = dou._do->do_finalizer; void *ctxt = dou._do->do_ctxt; - dou._do->do_vtable = (void *)0x200; - - free(dou._do); + _os_object_dealloc(dou._os_obj); if (func && ctxt) { dispatch_async_f(tq, ctxt, func); @@ -84,26 +186,28 @@ _dispatch_dispose(dispatch_object_t dou) } void -_dispatch_release(dispatch_object_t dou) +_dispatch_xref_dispose(dispatch_object_t dou) { - if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { - return; // global object + if (slowpath(DISPATCH_OBJECT_SUSPENDED(dou._do))) { + // Arguments for and against this assert are within 6705399 + DISPATCH_CLIENT_CRASH("Release of a suspended object"); } - - unsigned int ref_cnt = dispatch_atomic_dec2o(dou._do, do_ref_cnt) + 1; - if (fastpath(ref_cnt > 1)) { - return; +#if !USE_OBJC + if (dx_type(dou._do) == DISPATCH_SOURCE_KEVENT_TYPE) { + _dispatch_source_xref_dispose(dou._ds); } - if (fastpath(ref_cnt == 1)) { - if (slowpath(dou._do->do_next != DISPATCH_OBJECT_LISTLESS)) { - DISPATCH_CRASH("release while enqueued"); - } - if (slowpath(dou._do->do_xref_cnt)) { - DISPATCH_CRASH("release while external references exist"); - } - return dx_dispose(dou._do); + return _dispatch_release(dou._os_obj); +#endif +} + +void +_dispatch_dispose(dispatch_object_t dou) +{ + if (slowpath(dou._do->do_next != DISPATCH_OBJECT_LISTLESS)) { + DISPATCH_CRASH("Release while enqueued"); } - DISPATCH_CRASH("over-release"); + dx_dispose(dou._do); + return _dispatch_dealloc(dou); } void * @@ -178,9 +282,8 @@ size_t _dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz) { 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, + "suspend_cnt = 0x%x, locked = %d, ", dou._do->do_xref_cnt + 1, + dou._do->do_ref_cnt + 1, dou._do->do_suspend_cnt / DISPATCH_OBJECT_SUSPEND_INTERVAL, dou._do->do_suspend_cnt & 1); } - diff --git a/src/object.m b/src/object.m new file mode 100644 index 0000000..ea69622 --- /dev/null +++ b/src/object.m @@ -0,0 +1,286 @@ +/* + * 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@ + */ + +#include "internal.h" + +#if USE_OBJC + +#if !__OBJC2__ +#error "Cannot build with legacy ObjC runtime" +#endif +#if _OS_OBJECT_OBJC_ARC +#error "Cannot build with ARC" +#endif + +#include +#include +#include + +#pragma mark - +#pragma mark _os_object_gc + +#if __OBJC_GC__ +#include +#include + +static dispatch_once_t _os_object_gc_pred; +static bool _os_object_have_gc; +static malloc_zone_t *_os_object_gc_zone; + +static void +_os_object_gc_init(void *ctxt DISPATCH_UNUSED) +{ + _os_object_have_gc = objc_collectingEnabled(); + if (slowpath(_os_object_have_gc)) { + _os_object_gc_zone = objc_collectableZone(); + } +} + +static _os_object_t +_os_object_make_uncollectable(_os_object_t obj) +{ + dispatch_once_f(&_os_object_gc_pred, NULL, _os_object_gc_init); + if (slowpath(_os_object_have_gc)) { + auto_zone_retain(_os_object_gc_zone, obj); + } + return obj; +} + +static _os_object_t +_os_object_make_collectable(_os_object_t obj) +{ + dispatch_once_f(&_os_object_gc_pred, NULL, _os_object_gc_init); + if (slowpath(_os_object_have_gc)) { + auto_zone_release(_os_object_gc_zone, obj); + } + return obj; +} +#else +#define _os_object_make_uncollectable(obj) (obj) +#define _os_object_make_collectable(obj) (obj) +#endif // __OBJC_GC__ + +#pragma mark - +#pragma mark _os_object_t + +void +_os_object_init(void) +{ + return _objc_init(); +} + +_os_object_t +_os_object_alloc(const void *_cls, size_t size) +{ + Class cls = _cls; + _os_object_t obj; + dispatch_assert(size >= sizeof(struct _os_object_s)); + size -= sizeof(((struct _os_object_s *)NULL)->os_obj_isa); + if (!cls) cls = [OS_OBJECT_CLASS(object) class]; + while (!fastpath(obj = class_createInstance(cls, size))) { + sleep(1); // Temporary resource shortage + } + return _os_object_make_uncollectable(obj); +} + +void +_os_object_dealloc(_os_object_t obj) +{ + [_os_object_make_collectable(obj) dealloc]; +} + +void +_os_object_xref_dispose(_os_object_t obj) +{ + [obj _xref_dispose]; +} + +void +_os_object_dispose(_os_object_t obj) +{ + [obj _dispose]; +} + +#pragma mark - +#pragma mark _os_object + +@implementation OS_OBJECT_CLASS(object) + +-(id)retain { + return _os_object_retain(self); +} + +-(oneway void)release { + return _os_object_release(self); +} + +-(NSUInteger)retainCount { + return _os_object_retain_count(self); +} + +-(BOOL)retainWeakReference { + return _os_object_retain_weak(self); +} + +-(BOOL)allowsWeakReference { + return _os_object_allows_weak_reference(self); +} + +- (void)_xref_dispose { + return _os_object_release_internal(self); +} + +- (void)_dispose { + return _os_object_dealloc(self); +} + +@end + +#pragma mark - +#pragma mark _dispatch_object + +#include + +// Force non-lazy class realization rdar://10640168 +#define DISPATCH_OBJC_LOAD() + (void)load {} + +@implementation DISPATCH_CLASS(object) + +- (id)init { + self = [super init]; + [self release]; + self = nil; + return self; +} + +- (void)_xref_dispose { + _dispatch_xref_dispose(self); + [super _xref_dispose]; +} + +- (void)_dispose { + return _dispatch_dispose(self); // calls _os_object_dealloc() +} + +- (NSString *)debugDescription { + Class nsstring = objc_lookUpClass("NSString"); + if (!nsstring) return nil; + char buf[4096]; + dx_debug((struct dispatch_object_s *)self, buf, sizeof(buf)); + return [nsstring stringWithFormat: + [nsstring stringWithUTF8String:"<%s: %s>"], + class_getName([self class]), buf]; +} + +@end + +@implementation DISPATCH_CLASS(queue) +DISPATCH_OBJC_LOAD() + +- (NSString *)description { + Class nsstring = objc_lookUpClass("NSString"); + if (!nsstring) return nil; + return [nsstring stringWithFormat: + [nsstring stringWithUTF8String:"<%s: %s[%p]>"], + class_getName([self class]), dispatch_queue_get_label(self), self]; +} + +@end + +@implementation DISPATCH_CLASS(source) +DISPATCH_OBJC_LOAD() + +- (void)_xref_dispose { + _dispatch_source_xref_dispose(self); + [super _xref_dispose]; +} + +@end + +#define DISPATCH_CLASS_IMPL(name) \ + @implementation DISPATCH_CLASS(name) \ + DISPATCH_OBJC_LOAD() \ + @end + +DISPATCH_CLASS_IMPL(semaphore) +DISPATCH_CLASS_IMPL(group) +DISPATCH_CLASS_IMPL(queue_root) +DISPATCH_CLASS_IMPL(queue_mgr) +DISPATCH_CLASS_IMPL(queue_specific_queue) +DISPATCH_CLASS_IMPL(queue_attr) +DISPATCH_CLASS_IMPL(io) +DISPATCH_CLASS_IMPL(operation) +DISPATCH_CLASS_IMPL(disk) +DISPATCH_CLASS_IMPL(data) + +#pragma mark - +#pragma mark dispatch_autorelease_pool + +#if DISPATCH_COCOA_COMPAT + +void * +_dispatch_autorelease_pool_push(void) { + return objc_autoreleasePoolPush(); +} + +void +_dispatch_autorelease_pool_pop(void *context) { + return objc_autoreleasePoolPop(context); +} + +#endif // DISPATCH_COCOA_COMPAT + +#pragma mark - +#pragma mark dispatch_client_callout + +// Abort on uncaught exceptions thrown from client callouts rdar://8577499 +#if DISPATCH_USE_CLIENT_CALLOUT && !__arm__ +// On platforms with zero-cost exceptions, use a compiler-generated catch-all +// exception handler. + +DISPATCH_NORETURN extern void objc_terminate(void); + +#undef _dispatch_client_callout +void +_dispatch_client_callout(void *ctxt, dispatch_function_t f) +{ + @try { + return f(ctxt); + } + @catch (...) { + objc_terminate(); + } +} + +#undef _dispatch_client_callout2 +void +_dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) +{ + @try { + return f(ctxt, i); + } + @catch (...) { + objc_terminate(); + } +} + +#endif // DISPATCH_USE_CLIENT_CALLOUT + +#endif // USE_OBJC diff --git a/src/object_internal.h b/src/object_internal.h index 0627cfd..8bb6733 100644 --- a/src/object_internal.h +++ b/src/object_internal.h @@ -27,45 +27,62 @@ #ifndef __DISPATCH_OBJECT_INTERNAL__ #define __DISPATCH_OBJECT_INTERNAL__ -enum { - _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_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_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_KEVENT_TYPE = 1 | _DISPATCH_SOURCE_TYPE, -}; +#if OS_OBJECT_USE_OBJC +#define DISPATCH_DECL_INTERNAL_SUBCLASS(name, super) \ + OS_OBJECT_DECL_SUBCLASS(name, super) +#define DISPATCH_DECL_INTERNAL(name) \ + DISPATCH_DECL_INTERNAL_SUBCLASS(name, dispatch_object) +#define DISPATCH_DECL_SUBCLASS_INTERFACE(name, super) \ + _OS_OBJECT_DECL_SUBCLASS_INTERFACE(name, super) +#else +#define DISPATCH_DECL_INTERNAL_SUBCLASS(name, super) DISPATCH_DECL(name) +#define DISPATCH_DECL_INTERNAL(name) DISPATCH_DECL(name) +#define DISPATCH_DECL_SUBCLASS_INTERFACE(name, super) +#endif // OS_OBJECT_USE_OBJC + +#if USE_OBJC +#define DISPATCH_CLASS(name) OS_OBJECT_CLASS(dispatch_##name) +// ObjC classes and dispatch vtables are co-located via linker order and alias +// files rdar://10640168 +#define DISPATCH_VTABLE_SUBCLASS_INSTANCE(name, super, ...) \ + __attribute__((section("__DATA,__objc_data"), used)) \ + static const struct { \ + DISPATCH_VTABLE_HEADER(super); \ + } DISPATCH_CONCAT(_,DISPATCH_CLASS(name##_vtable)) = { \ + __VA_ARGS__ \ + } +#else +#define DISPATCH_VTABLE_SUBCLASS_INSTANCE(name, super, ...) \ + const struct dispatch_##super##_vtable_s _dispatch_##name##_vtable = { \ + ._os_obj_xref_dispose = _dispatch_xref_dispose, \ + ._os_obj_dispose = _dispatch_dispose, \ + __VA_ARGS__ \ + } +#endif // USE_OBJC + +#define DISPATCH_SUBCLASS_DECL(name, super) \ + DISPATCH_DECL_SUBCLASS_INTERFACE(dispatch_##name, super) \ + struct dispatch_##name##_s; \ + extern const struct dispatch_##name##_vtable_s { \ + _OS_OBJECT_CLASS_HEADER(); \ + DISPATCH_VTABLE_HEADER(name); \ + } _dispatch_##name##_vtable +#define DISPATCH_CLASS_DECL(name) DISPATCH_SUBCLASS_DECL(name, dispatch_object) +#define DISPATCH_INTERNAL_SUBCLASS_DECL(name, super) \ + DISPATCH_DECL_INTERNAL_SUBCLASS(dispatch_##name, dispatch_##super); \ + DISPATCH_DECL_SUBCLASS_INTERFACE(dispatch_##name, dispatch_##super) \ + extern const struct dispatch_##super##_vtable_s _dispatch_##name##_vtable +#define DISPATCH_VTABLE_INSTANCE(name, ...) \ + DISPATCH_VTABLE_SUBCLASS_INSTANCE(name, name, __VA_ARGS__) +#define DISPATCH_VTABLE(name) &_dispatch_##name##_vtable #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 *); \ - bool (*const do_probe)(struct x *); \ - void (*const do_dispose)(struct x *) + size_t (*const do_debug)(struct dispatch_##x##_s *, char *, size_t); \ + struct dispatch_queue_s *(*const do_invoke)(struct dispatch_##x##_s *); \ + bool (*const do_probe)(struct dispatch_##x##_s *); \ + void (*const do_dispose)(struct dispatch_##x##_s *) #define dx_type(x) (x)->do_vtable->do_type #define dx_kind(x) (x)->do_vtable->do_kind @@ -74,17 +91,18 @@ 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; \ +#define DISPATCH_STRUCT_HEADER(x) \ + _OS_OBJECT_HEADER( \ + const struct dispatch_##x##_vtable_s *do_vtable, \ + do_ref_cnt, \ + do_xref_cnt); \ + struct dispatch_##x##_s *volatile do_next; \ struct dispatch_queue_s *do_targetq; \ void *do_ctxt; \ - void *do_finalizer; + void *do_finalizer; \ + unsigned int do_suspend_cnt; -#define DISPATCH_OBJECT_GLOBAL_REFCNT (~0u) +#define DISPATCH_OBJECT_GLOBAL_REFCNT _OS_OBJECT_GLOBAL_REFCNT // "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 @@ -99,20 +117,72 @@ enum { #define DISPATCH_OBJECT_LISTLESS ((void *)0x89abcdef) #endif -struct dispatch_object_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_object_s); +enum { + _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_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_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 = 1 | _DISPATCH_SEMAPHORE_TYPE, + DISPATCH_GROUP_TYPE = 2 | _DISPATCH_SEMAPHORE_TYPE, + + DISPATCH_SOURCE_KEVENT_TYPE = 1 | _DISPATCH_SOURCE_TYPE, }; +DISPATCH_SUBCLASS_DECL(object, object); struct dispatch_object_s { - DISPATCH_STRUCT_HEADER(dispatch_object_s, dispatch_object_vtable_s); + DISPATCH_STRUCT_HEADER(object); }; size_t _dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz); - +void *_dispatch_alloc(const void *vtable, size_t size); void _dispatch_retain(dispatch_object_t dou); void _dispatch_release(dispatch_object_t dou); +void _dispatch_xref_dispose(dispatch_object_t dou); void _dispatch_dispose(dispatch_object_t dou); -dispatch_queue_t _dispatch_wakeup(dispatch_object_t dou); +#if DISPATCH_COCOA_COMPAT +void *_dispatch_autorelease_pool_push(void); +void _dispatch_autorelease_pool_pop(void *context); +#endif + +typedef struct _os_object_class_s { + _OS_OBJECT_CLASS_HEADER(); +} _os_object_class_s; + +typedef struct _os_object_s { + _OS_OBJECT_HEADER( + const _os_object_class_s *os_obj_isa, + os_obj_ref_cnt, + os_obj_xref_cnt); +} _os_object_s; + +void _os_object_init(void); +unsigned long _os_object_retain_count(_os_object_t obj); +bool _os_object_retain_weak(_os_object_t obj); +bool _os_object_allows_weak_reference(_os_object_t obj); +void _os_object_dispose(_os_object_t obj); +void _os_object_xref_dispose(_os_object_t obj); #endif diff --git a/src/queue.c b/src/queue.c index 595bac5..f01d7f8 100644 --- a/src/queue.c +++ b/src/queue.c @@ -27,22 +27,30 @@ !defined(DISPATCH_ENABLE_THREAD_POOL) #define DISPATCH_ENABLE_THREAD_POOL 1 #endif +#if DISPATCH_ENABLE_THREAD_POOL && !DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK +#define pthread_workqueue_t void* +#endif 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 void _dispatch_queue_wakeup_global2(dispatch_queue_t dq, + unsigned int n); +static inline void _dispatch_queue_wakeup_global(dispatch_queue_t dq); +static _dispatch_thread_semaphore_t _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_USE_LEGACY_WORKQUEUE_FALLBACK +static void _dispatch_worker_thread3(void *context); +#endif +#if HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP +static void _dispatch_worker_thread2(int priority, int options, void *context); +#endif #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); #if DISPATCH_COCOA_COMPAT static unsigned int _dispatch_worker_threads; @@ -50,94 +58,52 @@ 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 dispatch_queue_t _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_vtable = DISPATCH_VTABLE(semaphore), .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_vtable = DISPATCH_VTABLE(semaphore), .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_vtable = DISPATCH_VTABLE(semaphore), .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_vtable = DISPATCH_VTABLE(semaphore), .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_vtable = DISPATCH_VTABLE(semaphore), .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_vtable = DISPATCH_VTABLE(semaphore), .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_vtable = DISPATCH_VTABLE(semaphore), .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_vtable = DISPATCH_VTABLE(semaphore), .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, }, @@ -147,73 +113,114 @@ static struct dispatch_semaphore_s _dispatch_thread_mediator[] = { #define MAX_THREAD_COUNT 255 struct dispatch_root_queue_context_s { + union { + struct { + unsigned int volatile dgq_pending; #if HAVE_PTHREAD_WORKQUEUES - pthread_workqueue_t dgq_kworkqueue; + int dgq_wq_priority, dgq_wq_options; +#if DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK || DISPATCH_ENABLE_THREAD_POOL + pthread_workqueue_t dgq_kworkqueue; #endif - uint32_t dgq_pending; +#endif // HAVE_PTHREAD_WORKQUEUES #if DISPATCH_ENABLE_THREAD_POOL - uint32_t dgq_thread_pool_size; - dispatch_semaphore_t dgq_thread_mediator; + dispatch_semaphore_t dgq_thread_mediator; + uint32_t dgq_thread_pool_size; #endif + }; + char _dgq_pad[DISPATCH_CACHELINE_SIZE]; + }; }; +DISPATCH_CACHELINE_ALIGN static struct dispatch_root_queue_context_s _dispatch_root_queue_contexts[] = { - [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = { + [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = {{{ +#if HAVE_PTHREAD_WORKQUEUES + .dgq_wq_priority = WORKQ_LOW_PRIOQUEUE, + .dgq_wq_options = 0, +#endif #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 - }, - [DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY] = { + }}}, + [DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY] = {{{ +#if HAVE_PTHREAD_WORKQUEUES + .dgq_wq_priority = WORKQ_LOW_PRIOQUEUE, + .dgq_wq_options = WORKQ_ADDTHREADS_OPTION_OVERCOMMIT, +#endif #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 - }, - [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY] = { + }}}, + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY] = {{{ +#if HAVE_PTHREAD_WORKQUEUES + .dgq_wq_priority = WORKQ_DEFAULT_PRIOQUEUE, + .dgq_wq_options = 0, +#endif #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 - }, - [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = { + }}}, + [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = {{{ +#if HAVE_PTHREAD_WORKQUEUES + .dgq_wq_priority = WORKQ_DEFAULT_PRIOQUEUE, + .dgq_wq_options = WORKQ_ADDTHREADS_OPTION_OVERCOMMIT, +#endif #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 - }, - [DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY] = { + }}}, + [DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY] = {{{ +#if HAVE_PTHREAD_WORKQUEUES + .dgq_wq_priority = WORKQ_HIGH_PRIOQUEUE, + .dgq_wq_options = 0, +#endif #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 - }, - [DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = { + }}}, + [DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = {{{ +#if HAVE_PTHREAD_WORKQUEUES + .dgq_wq_priority = WORKQ_HIGH_PRIOQUEUE, + .dgq_wq_options = WORKQ_ADDTHREADS_OPTION_OVERCOMMIT, +#endif #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] = { + }}}, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY] = {{{ +#if HAVE_PTHREAD_WORKQUEUES + .dgq_wq_priority = WORKQ_BG_PRIOQUEUE, + .dgq_wq_options = 0, +#endif #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] = { + }}}, + [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY] = {{{ +#if HAVE_PTHREAD_WORKQUEUES + .dgq_wq_priority = WORKQ_BG_PRIOQUEUE, + .dgq_wq_options = WORKQ_ADDTHREADS_OPTION_OVERCOMMIT, +#endif #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 @@ -222,104 +229,96 @@ static struct dispatch_root_queue_context_s _dispatch_root_queue_contexts[] = { DISPATCH_CACHELINE_ALIGN struct dispatch_queue_s _dispatch_root_queues[] = { [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = { - .do_vtable = &_dispatch_queue_root_vtable, + .do_vtable = DISPATCH_VTABLE(queue_root), .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_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_vtable = DISPATCH_VTABLE(queue_root), .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_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_vtable = DISPATCH_VTABLE(queue_root), .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_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_vtable = DISPATCH_VTABLE(queue_root), .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_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_vtable = DISPATCH_VTABLE(queue_root), .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_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_vtable = DISPATCH_VTABLE(queue_root), .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_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_vtable = DISPATCH_VTABLE(queue_root), .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_vtable = DISPATCH_VTABLE(queue_root), .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, @@ -327,17 +326,41 @@ struct dispatch_queue_s _dispatch_root_queues[] = { }, }; +#if HAVE_PTHREAD_WORKQUEUES +static const dispatch_queue_t _dispatch_wq2root_queues[][2] = { + [WORKQ_LOW_PRIOQUEUE][0] = &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY], + [WORKQ_LOW_PRIOQUEUE][WORKQ_ADDTHREADS_OPTION_OVERCOMMIT] = + &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY], + [WORKQ_DEFAULT_PRIOQUEUE][0] = &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY], + [WORKQ_DEFAULT_PRIOQUEUE][WORKQ_ADDTHREADS_OPTION_OVERCOMMIT] = + &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY], + [WORKQ_HIGH_PRIOQUEUE][0] = &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY], + [WORKQ_HIGH_PRIOQUEUE][WORKQ_ADDTHREADS_OPTION_OVERCOMMIT] = + &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY], + [WORKQ_BG_PRIOQUEUE][0] = &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY], + [WORKQ_BG_PRIOQUEUE][WORKQ_ADDTHREADS_OPTION_OVERCOMMIT] = + &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY], +}; +#endif // HAVE_PTHREAD_WORKQUEUES + // 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_vtable = DISPATCH_VTABLE(queue_mgr), .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_IDX_HIGH_OVERCOMMIT_PRIORITY], - .dq_label = "com.apple.libdispatch-manager", .dq_width = 1, .dq_serialnum = 2, @@ -375,29 +398,62 @@ _dispatch_root_queues_init_workq(void) { bool result = false; #if HAVE_PTHREAD_WORKQUEUES + bool disable_wq = false; #if DISPATCH_ENABLE_THREAD_POOL - if (slowpath(getenv("LIBDISPATCH_DISABLE_KWQ"))) return result; + disable_wq = slowpath(getenv("LIBDISPATCH_DISABLE_KWQ")); #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]; - - 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); + int r; +#if HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP + if (!disable_wq) { + r = pthread_workqueue_setdispatch_np(_dispatch_worker_thread2); +#if !DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK (void)dispatch_assume_zero(r); - result = result || dispatch_assume(pwq); - _dispatch_root_queue_contexts[i].dgq_kworkqueue = pwq; +#endif + result = !r; } - r = pthread_workqueue_attr_destroy_np(&pwq_attr); - (void)dispatch_assume_zero(r); -#endif // HAVE_PTHREAD_WORKQUEUES +#endif // HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP +#if DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK || DISPATCH_ENABLE_THREAD_POOL + if (!result) { +#if DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK + pthread_workqueue_attr_t pwq_attr; + if (!disable_wq) { + r = pthread_workqueue_attr_init_np(&pwq_attr); + (void)dispatch_assume_zero(r); + } +#endif + int i; + for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { + pthread_workqueue_t pwq = NULL; + struct dispatch_root_queue_context_s *qc = + &_dispatch_root_queue_contexts[i]; +#if DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK + if (!disable_wq +#if DISPATCH_NO_BG_PRIORITY + && (qc->dgq_wq_priority != WORKQ_BG_PRIOQUEUE) +#endif + ) { + r = pthread_workqueue_attr_setqueuepriority_np(&pwq_attr, + qc->dgq_wq_priority); + (void)dispatch_assume_zero(r); + r = pthread_workqueue_attr_setovercommit_np(&pwq_attr, + qc->dgq_wq_options & WORKQ_ADDTHREADS_OPTION_OVERCOMMIT); + (void)dispatch_assume_zero(r); + r = pthread_workqueue_create_np(&pwq, &pwq_attr); + (void)dispatch_assume_zero(r); + result = result || dispatch_assume(pwq); + } +#endif + qc->dgq_kworkqueue = pwq ? pwq : (void*)(~0ul); + } +#if DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK + if (!disable_wq) { + r = pthread_workqueue_attr_destroy_np(&pwq_attr); + (void)dispatch_assume_zero(r); + } +#endif + } +#endif // DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK || DISPATCH_ENABLE_THREAD_POOL +#endif // HAVE_PTHREAD_WORKQUEUES return result; } @@ -437,6 +493,7 @@ _dispatch_root_queues_init_thread_pool(void) static void _dispatch_root_queues_init(void *context DISPATCH_UNUSED) { + _dispatch_safe_fork = false; if (!_dispatch_root_queues_init_workq()) { _dispatch_root_queues_init_thread_pool(); } @@ -459,19 +516,24 @@ libdispatch_init(void) dispatch_assert(countof(_dispatch_root_queue_contexts) == DISPATCH_ROOT_QUEUE_COUNT); #if HAVE_PTHREAD_WORKQUEUES - dispatch_assert(countof(_dispatch_root_queue_wq_priorities) == + dispatch_assert(sizeof(_dispatch_wq2root_queues) / + sizeof(_dispatch_wq2root_queues[0][0]) == 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_apply_s) <= + ROUND_UP_TO_CACHELINE_SIZE(sizeof( + struct dispatch_continuation_s))); 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 + dispatch_assert(sizeof(struct dispatch_root_queue_context_s) % + DISPATCH_CACHELINE_SIZE == 0); _dispatch_thread_key_create(&dispatch_queue_key, _dispatch_queue_cleanup); _dispatch_thread_key_create(&dispatch_sema4_key, @@ -484,10 +546,8 @@ libdispatch_init(void) #endif #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 _dispatch_thread_setspecific(dispatch_queue_key, &_dispatch_main_q); @@ -498,6 +558,8 @@ libdispatch_init(void) #endif _dispatch_hw_config_init(); + _dispatch_vtable_init(); + _os_object_init(); } DISPATCH_EXPORT DISPATCH_NOTHROW @@ -550,12 +612,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 - DISPATCH_QUEUE_CACHELINE_PAD + - label_len + 1); - if (slowpath(!dq)) { - return dq; - } + dq = _dispatch_alloc(DISPATCH_VTABLE(queue), + sizeof(struct dispatch_queue_s) - DISPATCH_QUEUE_MIN_LABEL_SIZE - + DISPATCH_QUEUE_CACHELINE_PAD + label_len + 1); _dispatch_queue_init(dq); strcpy(dq->dq_label, label); @@ -592,8 +651,6 @@ _dispatch_queue_dispose(dispatch_queue_t dq) if (dqsq) { _dispatch_release(dqsq); } - - _dispatch_dispose(dq); } const char * @@ -713,8 +770,7 @@ dispatch_set_current_target_queue(dispatch_queue_t dq) #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_STRUCT_HEADER(queue_specific_queue); DISPATCH_QUEUE_HEADER; union { char _dqsq_pad[DISPATCH_QUEUE_MIN_LABEL_SIZE]; @@ -725,24 +781,6 @@ struct dispatch_queue_specific_queue_s { }; }; }; -DISPATCH_DECL(dispatch_queue_specific_queue); - -static void -_dispatch_queue_specific_queue_dispose(dispatch_queue_specific_queue_t dqsq); - -struct dispatch_queue_specific_queue_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_queue_specific_queue_s); -}; - -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; @@ -752,7 +790,7 @@ struct dispatch_queue_specific_s { }; DISPATCH_DECL(dispatch_queue_specific); -static void +void _dispatch_queue_specific_queue_dispose(dispatch_queue_specific_queue_t dqsq) { dispatch_queue_specific_t dqs, tmp; @@ -773,17 +811,18 @@ _dispatch_queue_init_specific(dispatch_queue_t dq) { dispatch_queue_specific_queue_t dqsq; - dqsq = calloc(1ul, sizeof(struct dispatch_queue_specific_queue_s)); + dqsq = _dispatch_alloc(DISPATCH_VTABLE(queue_specific_queue), + 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_xref_cnt = -1; 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))) { + if (slowpath(!dispatch_atomic_cmpxchg2o(dq, dq_specific_q, NULL, + (dispatch_queue_t)dqsq))) { _dispatch_release((dispatch_queue_t)dqsq); } } @@ -980,7 +1019,7 @@ _dispatch_ccache_init(void *context DISPATCH_UNUSED) malloc_set_zone_name(_dispatch_ccache_zone, "DispatchContinuations"); } -static dispatch_continuation_t +dispatch_continuation_t _dispatch_continuation_alloc_from_heap(void) { static dispatch_once_t pred; @@ -988,6 +1027,8 @@ _dispatch_continuation_alloc_from_heap(void) dispatch_once_f(&pred, NULL, _dispatch_ccache_init); + // This is also used for allocating struct dispatch_apply_s. If the + // ROUND_UP behavior is changed, adjust the assert in libdispatch_init while (!(dc = fastpath(malloc_zone_calloc(_dispatch_ccache_zone, 1, ROUND_UP_TO_CACHELINE_SIZE(sizeof(*dc)))))) { sleep(1); @@ -996,18 +1037,6 @@ _dispatch_continuation_alloc_from_heap(void) 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) { @@ -1019,6 +1048,13 @@ _dispatch_force_cache_cleanup(void) } } +// rdar://problem/11500155 +void +dispatch_flush_continuation_cache(void) +{ + _dispatch_force_cache_cleanup(); +} + DISPATCH_NOINLINE static void _dispatch_cache_cleanup(void *value) @@ -1031,16 +1067,6 @@ _dispatch_cache_cleanup(void *value) } } -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) @@ -1081,7 +1107,7 @@ _dispatch_continuation_pop(dispatch_object_t dou) _dispatch_continuation_free(dc); } if ((long)dc->do_vtable & DISPATCH_OBJ_GROUP_BIT) { - dg = dc->dc_group; + dg = dc->dc_data; } else { dg = NULL; } @@ -1144,8 +1170,8 @@ 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; + struct dispatch_continuation_s *other_dc = dc->dc_other; + dispatch_queue_t old_dq, dq = dc->dc_data, rq; old_dq = _dispatch_thread_getspecific(dispatch_queue_key); _dispatch_thread_setspecific(dispatch_queue_key, dq); @@ -1184,16 +1210,13 @@ _dispatch_async_f_redirect(dispatch_queue_t dq, _dispatch_retain(dq); - dc = fastpath(_dispatch_continuation_alloc_cacheonly()); - if (!dc) { - dc = _dispatch_continuation_alloc_from_heap(); - } + dc = _dispatch_continuation_alloc(); 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; + dc->dc_data = dq; + dc->dc_other = other_dc; // Find the queue to redirect to rq = dq->do_targetq; @@ -1316,15 +1339,12 @@ dispatch_group_async_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, _dispatch_retain(dg); dispatch_group_enter(dg); - dc = fastpath(_dispatch_continuation_alloc_cacheonly()); - if (!dc) { - dc = _dispatch_continuation_alloc_from_heap(); - } + dc = _dispatch_continuation_alloc(); dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_GROUP_BIT); dc->dc_func = func; dc->dc_ctxt = ctxt; - dc->dc_group = dg; + dc->dc_data = dg; // No fastpath/slowpath hint because we simply don't know if (dq->dq_width != 1 && dq->do_targetq) { @@ -1389,7 +1409,7 @@ _dispatch_function_recurse(dispatch_queue_t dq, void *ctxt, #pragma mark dispatch_barrier_sync struct dispatch_barrier_sync_slow_s { - DISPATCH_CONTINUATION_HEADER(dispatch_barrier_sync_slow_s); + DISPATCH_CONTINUATION_HEADER(barrier_sync_slow); }; struct dispatch_barrier_sync_slow2_s { @@ -1401,6 +1421,33 @@ struct dispatch_barrier_sync_slow2_s { _dispatch_thread_semaphore_t dbss2_sema; }; +DISPATCH_ALWAYS_INLINE_NDEBUG +static inline _dispatch_thread_semaphore_t +_dispatch_barrier_sync_f_pop(dispatch_queue_t dq, dispatch_object_t dou, + bool lock) +{ + dispatch_continuation_t dc = dou._dc; + + if (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; + } + _dispatch_trace_continuation_pop(dq, dc); + _dispatch_workitem_inc(); + + struct dispatch_barrier_sync_slow_s *dbssp = (void *)dc; + struct dispatch_barrier_sync_slow2_s *dbss2 = dbssp->dc_ctxt; + if (lock) { + (void)dispatch_atomic_add2o(dbss2->dbss2_dq, do_suspend_cnt, + DISPATCH_OBJECT_SUSPEND_INTERVAL); + // rdar://problem/9032024 running lock must be held until sync_f_slow + // returns + (void)dispatch_atomic_add2o(dbss2->dbss2_dq, dq_running, 2); + } + return dbss2->dbss2_sema ? dbss2->dbss2_sema : MACH_PORT_DEAD; +} + static void _dispatch_barrier_sync_f_slow_invoke(void *ctxt) { @@ -1466,12 +1513,9 @@ _dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, _dispatch_function_invoke(dq, ctxt, func); } dispatch_atomic_release_barrier(); - if (fastpath(dq->do_suspend_cnt < 2 * DISPATCH_OBJECT_SUSPEND_INTERVAL)) { + if (fastpath(dq->do_suspend_cnt < 2 * DISPATCH_OBJECT_SUSPEND_INTERVAL) && + dq->dq_running == 2) { // 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) { @@ -1604,7 +1648,7 @@ _dispatch_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { _dispatch_thread_semaphore_t sema = _dispatch_get_thread_semaphore(); struct dispatch_sync_slow_s { - DISPATCH_CONTINUATION_HEADER(dispatch_sync_slow_s); + DISPATCH_CONTINUATION_HEADER(sync_slow); } dss = { .do_vtable = (void*)DISPATCH_OBJ_SYNC_SLOW_BIT, .dc_ctxt = (void*)sema, @@ -1807,8 +1851,8 @@ dispatch_after(dispatch_time_t when, dispatch_queue_t queue, #pragma mark dispatch_wakeup DISPATCH_NOINLINE -void -_dispatch_queue_push_list_slow(dispatch_queue_t dq, +static void +_dispatch_queue_push_list_slow2(dispatch_queue_t dq, struct dispatch_object_s *obj) { // The queue must be retained before dq_items_head is written in order @@ -1823,6 +1867,30 @@ _dispatch_queue_push_list_slow(dispatch_queue_t dq, _dispatch_release(dq); } +DISPATCH_NOINLINE +void +_dispatch_queue_push_list_slow(dispatch_queue_t dq, + struct dispatch_object_s *obj, unsigned int n) +{ + if (dx_type(dq) == DISPATCH_QUEUE_GLOBAL_TYPE) { + dq->dq_items_head = obj; + return _dispatch_queue_wakeup_global2(dq, n); + } + _dispatch_queue_push_list_slow2(dq, obj); +} + +DISPATCH_NOINLINE +void +_dispatch_queue_push_slow(dispatch_queue_t dq, + struct dispatch_object_s *obj) +{ + if (dx_type(dq) == DISPATCH_QUEUE_GLOBAL_TYPE) { + dq->dq_items_head = obj; + return _dispatch_queue_wakeup_global(dq); + } + _dispatch_queue_push_list_slow2(dq, obj); +} + // 6618342 Contact the team that owns the Instrument DTrace probe before // renaming this symbol dispatch_queue_t @@ -1844,11 +1912,12 @@ _dispatch_wakeup(dispatch_object_t dou) DISPATCH_OBJECT_SUSPEND_LOCK)) { #if DISPATCH_COCOA_COMPAT if (dou._dq == &_dispatch_main_q) { - _dispatch_queue_wakeup_main(); + return _dispatch_queue_wakeup_main(); } #endif return NULL; } + dispatch_atomic_acquire_barrier(); _dispatch_retain(dou._do); tq = dou._do->do_targetq; _dispatch_queue_push(tq, dou._do); @@ -1858,14 +1927,16 @@ _dispatch_wakeup(dispatch_object_t dou) #if DISPATCH_COCOA_COMPAT DISPATCH_NOINLINE -void +dispatch_queue_t _dispatch_queue_wakeup_main(void) { kern_return_t kr; dispatch_once_f(&_dispatch_main_q_port_pred, NULL, _dispatch_main_q_port_init); - + if (!main_q_port) { + return NULL; + } kr = _dispatch_send_wakeup_main_thread(main_q_port, 0); switch (kr) { @@ -1877,51 +1948,50 @@ _dispatch_queue_wakeup_main(void) (void)dispatch_assume_zero(kr); break; } - - _dispatch_safe_fork = false; + return NULL; } #endif -static bool -_dispatch_queue_wakeup_global(dispatch_queue_t dq) +DISPATCH_NOINLINE +static void +_dispatch_queue_wakeup_global_slow(dispatch_queue_t dq, unsigned int n) { static dispatch_once_t pred; struct dispatch_root_queue_context_s *qc = dq->do_ctxt; int r; - if (!dq->dq_items_tail) { - return false; - } - - _dispatch_safe_fork = false; - - dispatch_debug_queue(dq, __PRETTY_FUNCTION__); - + dispatch_debug_queue(dq, __func__); dispatch_once_f(&pred, NULL, _dispatch_root_queues_init); #if HAVE_PTHREAD_WORKQUEUES #if DISPATCH_ENABLE_THREAD_POOL - if (qc->dgq_kworkqueue) + if (qc->dgq_kworkqueue != (void*)(~0ul)) #endif { - if (dispatch_atomic_cmpxchg2o(qc, dgq_pending, 0, 1)) { + _dispatch_debug("requesting new worker thread"); +#if DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK + if (qc->dgq_kworkqueue) { 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); - (void)dispatch_assume_zero(r); - } else { - _dispatch_debug("work thread request still pending on global " - "queue: %p", dq); + unsigned int gen_cnt, i = n; + do { + r = pthread_workqueue_additem_np(qc->dgq_kworkqueue, + _dispatch_worker_thread3, dq, &wh, &gen_cnt); + (void)dispatch_assume_zero(r); + } while (--i); + return; } - goto out; +#endif // DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK +#if HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP + r = pthread_workqueue_addthreads_np(qc->dgq_wq_priority, + qc->dgq_wq_options, n); + (void)dispatch_assume_zero(r); +#endif + return; } #endif // HAVE_PTHREAD_WORKQUEUES #if DISPATCH_ENABLE_THREAD_POOL if (dispatch_semaphore_signal(qc->dgq_thread_mediator)) { - goto out; + return; } pthread_t pthr; @@ -1930,7 +2000,7 @@ _dispatch_queue_wakeup_global(dispatch_queue_t dq) t_count = qc->dgq_thread_pool_size; if (!t_count) { _dispatch_debug("The thread pool is full: %p", dq); - goto out; + return; } } while (!dispatch_atomic_cmpxchg2o(qc, dgq_thread_pool_size, t_count, t_count - 1)); @@ -1944,8 +2014,40 @@ _dispatch_queue_wakeup_global(dispatch_queue_t dq) r = pthread_detach(pthr); (void)dispatch_assume_zero(r); #endif // DISPATCH_ENABLE_THREAD_POOL +} -out: +static inline void +_dispatch_queue_wakeup_global2(dispatch_queue_t dq, unsigned int n) +{ + struct dispatch_root_queue_context_s *qc = dq->do_ctxt; + + if (!dq->dq_items_tail) { + return; + } +#if HAVE_PTHREAD_WORKQUEUES + if ( +#if DISPATCH_ENABLE_THREAD_POOL + (qc->dgq_kworkqueue != (void*)(~0ul)) && +#endif + !dispatch_atomic_cmpxchg2o(qc, dgq_pending, 0, n)) { + _dispatch_debug("work thread request still pending on global queue: " + "%p", dq); + return; + } +#endif // HAVE_PTHREAD_WORKQUEUES + return _dispatch_queue_wakeup_global_slow(dq, n); +} + +static inline void +_dispatch_queue_wakeup_global(dispatch_queue_t dq) +{ + return _dispatch_queue_wakeup_global2(dq, 1); +} + +bool +_dispatch_queue_probe_root(dispatch_queue_t dq) +{ + _dispatch_queue_wakeup_global2(dq, 1); return false; } @@ -1962,7 +2064,7 @@ _dispatch_queue_invoke(dispatch_queue_t 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); + _dispatch_thread_semaphore_t sema = _dispatch_queue_drain(dq); if (dq->do_vtable->do_invoke) { // Assume that object invoke checks it is executing on correct queue tq = dx_invoke(dq); @@ -1974,12 +2076,15 @@ _dispatch_queue_invoke(dispatch_queue_t dq) // When the suspend-count lock is dropped, then the check will happen. dispatch_atomic_release_barrier(); (void)dispatch_atomic_dec2o(dq, dq_running); - if (tq) { + if (sema) { + _dispatch_thread_semaphore_signal(sema); + } else if (tq) { return _dispatch_queue_push(tq, dq); } } dq->do_next = DISPATCH_OBJECT_LISTLESS; + dispatch_atomic_release_barrier(); if (!dispatch_atomic_sub2o(dq, do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK)) { if (dq->dq_running == 0) { @@ -1989,12 +2094,13 @@ _dispatch_queue_invoke(dispatch_queue_t dq) _dispatch_release(dq); // added when the queue is put on the list } -static void +static _dispatch_thread_semaphore_t _dispatch_queue_drain(dispatch_queue_t dq) { dispatch_queue_t orig_tq, old_dq; old_dq = _dispatch_thread_getspecific(dispatch_queue_key); struct dispatch_object_s *dc = NULL, *next_dc = NULL; + _dispatch_thread_semaphore_t sema = 0; // Continue draining sources after target queue change rdar://8928171 bool check_tq = (dx_type(dq) != DISPATCH_SOURCE_KEVENT_TYPE); @@ -2002,7 +2108,7 @@ _dispatch_queue_drain(dispatch_queue_t dq) orig_tq = dq->do_targetq; _dispatch_thread_setspecific(dispatch_queue_key, dq); - //dispatch_debug_queue(dq, __PRETTY_FUNCTION__); + //dispatch_debug_queue(dq, __func__); while (dq->dq_items_tail) { while (!(dc = fastpath(dq->dq_items_head))) { @@ -2027,19 +2133,23 @@ _dispatch_queue_drain(dispatch_queue_t dq) 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 (!DISPATCH_OBJ_IS_VTABLE(dc) && - (long)dc->do_vtable & DISPATCH_OBJ_BARRIER_BIT) { - if (dq->dq_running > 1) { - goto out; + if (!fastpath(dq->dq_width == 1)) { + if (!DISPATCH_OBJ_IS_VTABLE(dc) && + (long)dc->do_vtable & DISPATCH_OBJ_BARRIER_BIT) { + if (dq->dq_running > 1) { + goto out; + } + } else { + _dispatch_continuation_redirect(dq, dc); + continue; } - _dispatch_continuation_pop(dc); - _dispatch_workitem_inc(); - } else { - _dispatch_continuation_redirect(dq, dc); } + if ((sema = _dispatch_barrier_sync_f_pop(dq, dc, true))) { + dc = next_dc; + goto out; + } + _dispatch_continuation_pop(dc); + _dispatch_workitem_inc(); } while ((dc = next_dc)); } @@ -2060,6 +2170,7 @@ out: } _dispatch_thread_setspecific(dispatch_queue_key, old_dq); + return sema; } static void @@ -2068,7 +2179,11 @@ _dispatch_queue_serial_drain_till_empty(dispatch_queue_t dq) #if DISPATCH_PERF_MON uint64_t start = _dispatch_absolute_time(); #endif - _dispatch_queue_drain(dq); + _dispatch_thread_semaphore_t sema = _dispatch_queue_drain(dq); + if (sema) { + dispatch_atomic_barrier(); + _dispatch_thread_semaphore_signal(sema); + } #if DISPATCH_PERF_MON _dispatch_queue_merge_stats(start); #endif @@ -2084,7 +2199,7 @@ _dispatch_main_queue_drain(void) return; } struct dispatch_main_queue_drain_marker_s { - DISPATCH_CONTINUATION_HEADER(dispatch_main_queue_drain_marker_s); + DISPATCH_CONTINUATION_HEADER(main_queue_drain_marker); } marker = { .do_vtable = NULL, }; @@ -2140,12 +2255,11 @@ _dispatch_queue_drain_one_barrier_sync(dispatch_queue_t dq) { // rdar://problem/8290662 "lock transfer" struct dispatch_object_s *dc, *next_dc; + _dispatch_thread_semaphore_t sema; // 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)) { + if (slowpath(!dc) || !(sema = _dispatch_barrier_sync_f_pop(dq, dc, false))){ return 0; } // dequeue dc, it is a barrier sync @@ -2158,19 +2272,19 @@ _dispatch_queue_drain_one_barrier_sync(dispatch_queue_t dq) } 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; + return sema; } +#ifndef DISPATCH_HEAD_CONTENTION_SPINS +#define DISPATCH_HEAD_CONTENTION_SPINS 10000 +#endif + static struct dispatch_object_s * _dispatch_queue_concurrent_drain_one(dispatch_queue_t dq) { struct dispatch_object_s *head, *next, *const mediator = (void *)~0ul; +start: // The mediator value acts both as a "lock" and a signal head = dispatch_atomic_xchg2o(dq, dq_items_head, mediator); @@ -2185,7 +2299,13 @@ _dispatch_queue_concurrent_drain_one(dispatch_queue_t dq) if (slowpath(head == mediator)) { // This thread lost the race for ownership of the queue. - // + // Spin for a short while in case many threads have started draining at + // once as part of a dispatch_apply + unsigned int i = DISPATCH_HEAD_CONTENTION_SPINS; + do { + _dispatch_hardware_pause(); + if (dq->dq_items_head != mediator) goto start; + } while (--i); // 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. @@ -2225,22 +2345,18 @@ out: #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) +_dispatch_worker_thread4(dispatch_queue_t dq) { struct dispatch_object_s *item; - dispatch_queue_t dq = context; - struct dispatch_root_queue_context_s *qc = dq->do_ctxt; +#if DISPATCH_DEBUG if (_dispatch_thread_getspecific(dispatch_queue_key)) { DISPATCH_CRASH("Premature thread recycling"); } - +#endif _dispatch_thread_setspecific(dispatch_queue_key, dq); - qc->dgq_pending = 0; #if DISPATCH_COCOA_COMPAT (void)dispatch_atomic_inc(&_dispatch_worker_threads); @@ -2248,8 +2364,8 @@ _dispatch_worker_thread2(void *context) if (dispatch_begin_thread_4GC) { dispatch_begin_thread_4GC(); } - void *pool = _dispatch_begin_NSAutoReleasePool(); -#endif + void *pool = _dispatch_autorelease_pool_push(); +#endif // DISPATCH_COCOA_COMPAT #if DISPATCH_PERF_MON uint64_t start = _dispatch_absolute_time(); @@ -2262,13 +2378,15 @@ _dispatch_worker_thread2(void *context) #endif #if DISPATCH_COCOA_COMPAT - _dispatch_end_NSAutoReleasePool(pool); - dispatch_end_thread_4GC(); + _dispatch_autorelease_pool_pop(pool); + if (dispatch_end_thread_4GC) { + dispatch_end_thread_4GC(); + } if (!dispatch_atomic_dec(&_dispatch_worker_threads) && dispatch_no_worker_threads_4GC) { dispatch_no_worker_threads_4GC(); } -#endif +#endif // DISPATCH_COCOA_COMPAT _dispatch_thread_setspecific(dispatch_queue_key, NULL); @@ -2276,6 +2394,35 @@ _dispatch_worker_thread2(void *context) } +#if DISPATCH_USE_LEGACY_WORKQUEUE_FALLBACK +static void +_dispatch_worker_thread3(void *context) +{ + dispatch_queue_t dq = context; + struct dispatch_root_queue_context_s *qc = dq->do_ctxt; + + (void)dispatch_atomic_dec2o(qc, dgq_pending); + _dispatch_worker_thread4(dq); +} +#endif + +#if HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +static void +_dispatch_worker_thread2(int priority, int options, + void *context DISPATCH_UNUSED) +{ + dispatch_assert(priority >= 0 && priority < WORKQ_NUM_PRIOQUEUE); + dispatch_assert(!(options & ~WORKQ_ADDTHREADS_OPTION_OVERCOMMIT)); + dispatch_queue_t dq = _dispatch_wq2root_queues[priority][options]; + struct dispatch_root_queue_context_s *qc = dq->do_ctxt; + + (void)dispatch_atomic_dec2o(qc, dgq_pending); + _dispatch_worker_thread4(dq); +} +#endif + #if DISPATCH_ENABLE_THREAD_POOL // 6618342 Contact the team that owns the Instrument DTrace probe before // renaming this symbol @@ -2294,7 +2441,7 @@ _dispatch_worker_thread(void *context) (void)dispatch_assume_zero(r); do { - _dispatch_worker_thread2(context); + _dispatch_worker_thread4(dq); // 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); @@ -2348,6 +2495,7 @@ _dispatch_main_q_port_init(void *ctxt DISPATCH_UNUSED) { kern_return_t kr; + _dispatch_safe_fork = false; kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &main_q_port); DISPATCH_VERIFY_MIG(kr); @@ -2358,7 +2506,6 @@ _dispatch_main_q_port_init(void *ctxt DISPATCH_UNUSED) (void)dispatch_assume_zero(kr); _dispatch_program_is_probably_callback_driven = true; - _dispatch_safe_fork = false; } mach_port_t @@ -2438,7 +2585,8 @@ _dispatch_queue_cleanup2(void) { (void)dispatch_atomic_dec(&_dispatch_main_q.dq_running); - if (dispatch_atomic_sub(&_dispatch_main_q.do_suspend_cnt, + dispatch_atomic_release_barrier(); + if (dispatch_atomic_sub2o(&_dispatch_main_q, do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK) == 0) { _dispatch_wakeup(&_dispatch_main_q); } @@ -2447,7 +2595,7 @@ _dispatch_queue_cleanup2(void) // 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_async_f(_dispatch_get_root_queue(0, true), NULL, _dispatch_sig_thread); sleep(1); // workaround 6778970 } @@ -2503,10 +2651,8 @@ _dispatch_get_kq_init(void *context DISPATCH_UNUSED) .flags = EV_ADD|EV_CLEAR, }; - _dispatch_kq = kqueue(); - _dispatch_safe_fork = false; - + _dispatch_kq = kqueue(); if (_dispatch_kq == -1) { DISPATCH_CLIENT_CRASH("kqueue() create failed: " "probably out of file descriptors"); @@ -2533,6 +2679,7 @@ _dispatch_get_kq(void) long _dispatch_update_kq(const struct kevent *kev) { + int rval; struct kevent kev_copy = *kev; // This ensures we don't get a pending kevent back while registering // a new kevent @@ -2564,14 +2711,25 @@ _dispatch_update_kq(const struct kevent *kev) } } - int rval = kevent(_dispatch_get_kq(), &kev_copy, 1, &kev_copy, 1, NULL); +retry: + 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); + int err = errno; + switch (err) { + case EINTR: + goto retry; + case EBADF: + _dispatch_bug_client("Do not close random Unix descriptors"); + break; + default: + (void)dispatch_assume_zero(err); + break; + } //kev_copy.flags |= EV_ERROR; - //kev_copy.data = error; - return errno; + //kev_copy.data = err; + return err; } // The following select workaround only applies to adding kevents @@ -2628,7 +2786,7 @@ _dispatch_update_kq(const struct kevent *kev) return kev_copy.data; } -static bool +bool _dispatch_mgr_wakeup(dispatch_queue_t dq) { static const struct kevent kev = { @@ -2812,7 +2970,7 @@ _dispatch_mgr_invoke(void) } DISPATCH_NORETURN -static dispatch_queue_t +dispatch_queue_t _dispatch_mgr_thread(dispatch_queue_t dq DISPATCH_UNUSED) { // never returns, so burn bridges behind us & clear stack 2k ahead diff --git a/src/queue_internal.h b/src/queue_internal.h index 479ae60..b223cce 100644 --- a/src/queue_internal.h +++ b/src/queue_internal.h @@ -34,12 +34,17 @@ // 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. +// layout until after 'do_next' must align with normal objects. #define DISPATCH_CONTINUATION_HEADER(x) \ - const void *do_vtable; \ - struct x *volatile do_next; \ + _OS_OBJECT_HEADER( \ + const void *do_vtable, \ + do_ref_cnt, \ + do_xref_cnt); \ + struct dispatch_##x##_s *volatile do_next; \ dispatch_function_t dc_func; \ - void *dc_ctxt + void *dc_ctxt; \ + void *dc_data; \ + void *dc_other; #define DISPATCH_OBJ_ASYNC_BIT 0x1 #define DISPATCH_OBJ_BARRIER_BIT 0x2 @@ -49,31 +54,35 @@ #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_CONTINUATION_HEADER(continuation); }; typedef struct dispatch_continuation_s *dispatch_continuation_t; -struct dispatch_queue_attr_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_queue_attr_s); +struct dispatch_apply_s { + size_t da_index; + size_t da_iterations; + void (*da_func)(void *, size_t); + void *da_ctxt; + _dispatch_thread_semaphore_t da_sema; + dispatch_queue_t da_queue; + size_t da_done; + uint32_t da_thr_cnt; }; -struct dispatch_queue_attr_s { - DISPATCH_STRUCT_HEADER(dispatch_queue_attr_s, dispatch_queue_attr_vtable_s); -}; +typedef struct dispatch_apply_s *dispatch_apply_t; -struct dispatch_queue_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_queue_s); +DISPATCH_CLASS_DECL(queue_attr); +struct dispatch_queue_attr_s { + DISPATCH_STRUCT_HEADER(queue_attr); }; #define DISPATCH_QUEUE_MIN_LABEL_SIZE 64 #ifdef __LP64__ -#define DISPATCH_QUEUE_CACHELINE_PAD 32 +#define DISPATCH_QUEUE_CACHELINE_PAD (4*sizeof(void*)) #else -#define DISPATCH_QUEUE_CACHELINE_PAD 8 +#define DISPATCH_QUEUE_CACHELINE_PAD (2*sizeof(void*)) #endif #define DISPATCH_QUEUE_HEADER \ @@ -84,41 +93,35 @@ struct dispatch_queue_vtable_s { unsigned long dq_serialnum; \ dispatch_queue_t dq_specific_q; +DISPATCH_CLASS_DECL(queue); struct dispatch_queue_s { - DISPATCH_STRUCT_HEADER(dispatch_queue_s, dispatch_queue_vtable_s); + DISPATCH_STRUCT_HEADER(queue); DISPATCH_QUEUE_HEADER; char dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE]; // must be last char _dq_pad[DISPATCH_QUEUE_CACHELINE_PAD]; // for static queues only }; +DISPATCH_INTERNAL_SUBCLASS_DECL(queue_root, queue); +DISPATCH_INTERNAL_SUBCLASS_DECL(queue_mgr, queue); + +DISPATCH_DECL_INTERNAL_SUBCLASS(dispatch_queue_specific_queue, dispatch_queue); +DISPATCH_CLASS_DECL(queue_specific_queue); + extern struct dispatch_queue_s _dispatch_mgr_q; void _dispatch_queue_dispose(dispatch_queue_t dq); void _dispatch_queue_invoke(dispatch_queue_t dq); void _dispatch_queue_push_list_slow(dispatch_queue_t dq, + struct dispatch_object_s *obj, unsigned int n); +void _dispatch_queue_push_slow(dispatch_queue_t dq, struct dispatch_object_s *obj); - -DISPATCH_ALWAYS_INLINE -static inline void -_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; - 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 - prev->do_next = head; - } else { - _dispatch_queue_push_list_slow(dq, head); - } -} - -#define _dispatch_queue_push(x, y) _dispatch_queue_push_list((x), (y), (y)) +dispatch_queue_t _dispatch_wakeup(dispatch_object_t dou); +void _dispatch_queue_specific_queue_dispose(dispatch_queue_specific_queue_t + dqsq); +bool _dispatch_queue_probe_root(dispatch_queue_t dq); +bool _dispatch_mgr_wakeup(dispatch_queue_t dq); +DISPATCH_NORETURN +dispatch_queue_t _dispatch_mgr_thread(dispatch_queue_t dq); #if DISPATCH_DEBUG void dispatch_debug_queue(dispatch_queue_t dq, const char* str); @@ -131,13 +134,6 @@ 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); -DISPATCH_ALWAYS_INLINE -static inline dispatch_queue_t -_dispatch_queue_get_current(void) -{ - return _dispatch_thread_getspecific(dispatch_queue_key); -} - #define DISPATCH_QUEUE_PRIORITY_COUNT 4 #define DISPATCH_ROOT_QUEUE_COUNT (DISPATCH_QUEUE_PRIORITY_COUNT * 2) @@ -153,16 +149,67 @@ enum { 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[]; +#if !__OBJC2__ + +DISPATCH_ALWAYS_INLINE +static inline bool +_dispatch_queue_push_list2(dispatch_queue_t dq, struct dispatch_object_s *head, + struct dispatch_object_s *tail) +{ + struct dispatch_object_s *prev; + tail->do_next = NULL; + dispatch_atomic_store_barrier(); + prev = dispatch_atomic_xchg2o(dq, dq_items_tail, tail); + if (fastpath(prev)) { + // if we crash here with a value less than 0x1000, then we are at a + // known bug in client code for example, see _dispatch_queue_dispose + // or _dispatch_atfork_child + prev->do_next = head; + } + return prev; +} + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, + dispatch_object_t _tail, unsigned int n) +{ + struct dispatch_object_s *head = _head._do, *tail = _tail._do; + if (!fastpath(_dispatch_queue_push_list2(dq, head, tail))) { + _dispatch_queue_push_list_slow(dq, head, n); + } +} + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_queue_push(dispatch_queue_t dq, dispatch_object_t _tail) +{ + struct dispatch_object_s *tail = _tail._do; + if (!fastpath(_dispatch_queue_push_list2(dq, tail, tail))) { + _dispatch_queue_push_slow(dq, tail); + } +} + +DISPATCH_ALWAYS_INLINE +static inline dispatch_queue_t +_dispatch_queue_get_current(void) +{ + return _dispatch_thread_getspecific(dispatch_queue_key); +} + DISPATCH_ALWAYS_INLINE DISPATCH_CONST static inline dispatch_queue_t _dispatch_get_root_queue(long priority, bool overcommit) { if (overcommit) switch (priority) { + case DISPATCH_QUEUE_PRIORITY_BACKGROUND: +#if !DISPATCH_NO_BG_PRIORITY + return &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY]; +#endif case DISPATCH_QUEUE_PRIORITY_LOW: return &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY]; @@ -172,20 +219,19 @@ _dispatch_get_root_queue(long priority, bool overcommit) 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_BACKGROUND: +#if !DISPATCH_NO_BG_PRIORITY + return &_dispatch_root_queues[ + DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY]; +#endif 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; } @@ -196,10 +242,7 @@ _dispatch_get_root_queue(long priority, bool overcommit) 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; @@ -207,4 +250,45 @@ _dispatch_queue_init(dispatch_queue_t dq) dq->dq_serialnum = dispatch_atomic_inc(&_dispatch_queue_serial_numbers) - 1; } +dispatch_continuation_t +_dispatch_continuation_alloc_from_heap(void); + +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; +} + +DISPATCH_ALWAYS_INLINE +static inline dispatch_continuation_t +_dispatch_continuation_alloc(void) +{ + dispatch_continuation_t dc; + + dc = fastpath(_dispatch_continuation_alloc_cacheonly()); + if(!dc) { + return _dispatch_continuation_alloc_from_heap(); + } + return 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); +} + +#endif // !__OBJC2__ + #endif diff --git a/src/semaphore.c b/src/semaphore.c index 81a2c41..d3fd431 100644 --- a/src/semaphore.c +++ b/src/semaphore.c @@ -38,24 +38,26 @@ 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 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); -}; +static void +_dispatch_semaphore_init(long value, dispatch_object_t dou) +{ + dispatch_semaphore_t dsema = dou._dsema; -const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable = { - .do_type = DISPATCH_SEMAPHORE_TYPE, - .do_kind = "semaphore", - .do_dispose = _dispatch_semaphore_dispose, - .do_debug = _dispatch_semaphore_debug, -}; + dsema->do_next = DISPATCH_OBJECT_LISTLESS; + 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 +} dispatch_semaphore_t dispatch_semaphore_create(long value) @@ -69,23 +71,9 @@ dispatch_semaphore_create(long value) 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( - 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 - } - + dsema = _dispatch_alloc(DISPATCH_VTABLE(semaphore), + sizeof(struct dispatch_semaphore_s)); + _dispatch_semaphore_init(value, dsema); return dsema; } @@ -99,6 +87,7 @@ _dispatch_semaphore_create_port(semaphore_t *s4) if (*s4) { return; } + _dispatch_safe_fork = false; // lazily allocate the semaphore port @@ -117,14 +106,14 @@ _dispatch_semaphore_create_port(semaphore_t *s4) kr = semaphore_destroy(mach_task_self(), tmp); DISPATCH_SEMAPHORE_VERIFY_KR(kr); } - - _dispatch_safe_fork = false; } #endif -static void -_dispatch_semaphore_dispose(dispatch_semaphore_t dsema) +void +_dispatch_semaphore_dispose(dispatch_object_t dou) { + dispatch_semaphore_t dsema = dou._dsema; + if (dsema->dsema_value < dsema->dsema_orig) { DISPATCH_CLIENT_CRASH( "Semaphore/group object deallocated while in use"); @@ -144,13 +133,13 @@ _dispatch_semaphore_dispose(dispatch_semaphore_t dsema) 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 +_dispatch_semaphore_debug(dispatch_object_t dou, char *buf, size_t bufsiz) { + dispatch_semaphore_t dsema = dou._dsema; + size_t offset = 0; offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dx_kind(dsema), dsema); @@ -199,7 +188,8 @@ dispatch_semaphore_signal(dispatch_semaphore_t dsema) return 0; } if (slowpath(value == LONG_MIN)) { - DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_semaphore_signal()"); + DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_group_leave() or " + "dispatch_semaphore_signal()"); } return _dispatch_semaphore_signal_slow(dsema); } @@ -323,7 +313,10 @@ dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) dispatch_group_t dispatch_group_create(void) { - return (dispatch_group_t)dispatch_semaphore_create(LONG_MAX); + dispatch_group_t dg = _dispatch_alloc(DISPATCH_VTABLE(group), + sizeof(struct dispatch_semaphore_s)); + _dispatch_semaphore_init(LONG_MAX, dg); + return dg; } void @@ -384,12 +377,9 @@ void dispatch_group_leave(dispatch_group_t dg) { dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; - dispatch_atomic_release_barrier(); - long value = dispatch_atomic_inc2o(dsema, dsema_value); - if (slowpath(value == LONG_MIN)) { - DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_group_leave()"); - } - if (slowpath(value == dsema->dsema_orig)) { + + dispatch_semaphore_signal(dsema); + if (dsema->dsema_value == dsema->dsema_orig) { (void)_dispatch_group_wake(dsema); } } @@ -544,7 +534,7 @@ dispatch_group_notify_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, prev->dsn_next = dsn; } else { _dispatch_retain(dg); - (void)dispatch_atomic_xchg2o(dsema, dsema_notify_head, dsn); + dsema->dsema_notify_head = dsn; if (dsema->dsema_value == dsema->dsema_orig) { _dispatch_group_wake(dsema); } @@ -568,6 +558,7 @@ DISPATCH_NOINLINE static _dispatch_thread_semaphore_t _dispatch_thread_semaphore_create(void) { + _dispatch_safe_fork = false; #if USE_MACH_SEM semaphore_t s4; kern_return_t kr; diff --git a/src/semaphore_internal.h b/src/semaphore_internal.h index e5b319e..e27f934 100644 --- a/src/semaphore_internal.h +++ b/src/semaphore_internal.h @@ -27,15 +27,18 @@ #ifndef __DISPATCH_SEMAPHORE_INTERNAL__ #define __DISPATCH_SEMAPHORE_INTERNAL__ +struct dispatch_queue_s; + struct dispatch_sema_notify_s { struct dispatch_sema_notify_s *volatile dsn_next; - dispatch_queue_t dsn_queue; + struct dispatch_queue_s *dsn_queue; void *dsn_ctxt; void (*dsn_func)(void *); }; +DISPATCH_CLASS_DECL(semaphore); struct dispatch_semaphore_s { - DISPATCH_STRUCT_HEADER(dispatch_semaphore_s, dispatch_semaphore_vtable_s); + DISPATCH_STRUCT_HEADER(semaphore); long dsema_value; long dsema_orig; size_t dsema_sent_ksignals; @@ -54,7 +57,11 @@ struct dispatch_semaphore_s { struct dispatch_sema_notify_s *dsema_notify_tail; }; -extern const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable; +DISPATCH_CLASS_DECL(group); + +void _dispatch_semaphore_dispose(dispatch_object_t dou); +size_t _dispatch_semaphore_debug(dispatch_object_t dou, char *buf, + size_t bufsiz); typedef uintptr_t _dispatch_thread_semaphore_t; _dispatch_thread_semaphore_t _dispatch_get_thread_semaphore(void); diff --git a/src/shims/atomic.h b/src/shims/atomic.h index fbc1171..a30c89f 100644 --- a/src/shims/atomic.h +++ b/src/shims/atomic.h @@ -48,8 +48,8 @@ #define dispatch_atomic_release_barrier() #define dispatch_atomic_store_barrier() -#define _dispatch_hardware_pause() asm("") -#define _dispatch_debugger() asm("trap") +#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)) @@ -124,9 +124,9 @@ #endif #endif #undef _dispatch_hardware_pause -#define _dispatch_hardware_pause() asm("pause") +#define _dispatch_hardware_pause() __asm__("pause") #undef _dispatch_debugger -#define _dispatch_debugger() asm("int3") +#define _dispatch_debugger() __asm__("int3") #elif defined(__ppc__) || defined(__ppc64__) diff --git a/src/shims/tsd.h b/src/shims/tsd.h index b8c6640..f300d64 100644 --- a/src/shims/tsd.h +++ b/src/shims/tsd.h @@ -45,7 +45,6 @@ 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 @@ -54,12 +53,12 @@ _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; +extern pthread_key_t dispatch_queue_key; +extern pthread_key_t dispatch_sema4_key; +extern pthread_key_t dispatch_cache_key; +extern pthread_key_t dispatch_io_key; +extern pthread_key_t dispatch_apply_key; +extern pthread_key_t dispatch_bcounter_key; DISPATCH_TSD_INLINE static inline void diff --git a/src/source.c b/src/source.c index 572912b..2b0a9a2 100644 --- a/src/source.c +++ b/src/source.c @@ -25,9 +25,6 @@ #endif #include -static void _dispatch_source_dispose(dispatch_source_t ds); -static dispatch_queue_t _dispatch_source_invoke(dispatch_source_t ds); -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); @@ -43,8 +40,6 @@ 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 @@ -52,15 +47,6 @@ static void _dispatch_kevent_debugger(void *context); #pragma mark - #pragma mark dispatch_source_t -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, @@ -73,13 +59,13 @@ dispatch_source_create(dispatch_source_type_t type, // input validation if (type == NULL || (mask & ~type->mask)) { - goto out_bad; + return NULL; } switch (type->ke.filter) { case EVFILT_SIGNAL: if (handle >= NSIG) { - goto out_bad; + return NULL; } break; case EVFILT_FS: @@ -90,22 +76,14 @@ dispatch_source_create(dispatch_source_type_t type, case DISPATCH_EVFILT_CUSTOM_OR: case DISPATCH_EVFILT_TIMER: if (handle) { - goto out_bad; + return NULL; } 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; @@ -113,12 +91,13 @@ dispatch_source_create(dispatch_source_type_t type, dk->dk_kevent.udata = dk; TAILQ_INIT(&dk->dk_sources); + ds = _dispatch_alloc(DISPATCH_VTABLE(source), + sizeof(struct dispatch_source_s)); // 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_ref_cnt++; // since source is created suspended ds->do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_INTERVAL; @@ -154,7 +133,7 @@ dispatch_source_create(dispatch_source_type_t type, // 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__); + dispatch_debug(ds, "%s", __func__); #endif return ds; @@ -164,7 +143,7 @@ out_bad: return NULL; } -static void +void _dispatch_source_dispose(dispatch_source_t ds) { free(ds->ds_refs); @@ -172,21 +151,16 @@ _dispatch_source_dispose(dispatch_source_t ds) } void -_dispatch_source_xref_release(dispatch_source_t ds) +_dispatch_source_xref_dispose(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, "%s", __FUNCTION__); + dispatch_debug(ds, "%s", __func__); #endif // Right after we set the cancel flag, someone else // could potentially invoke the source, do the cancelation, @@ -251,7 +225,7 @@ _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_assert(dx_type(ds) == DISPATCH_SOURCE_KEVENT_TYPE); dispatch_source_refs_t dr = ds->ds_refs; if (ds->ds_handler_is_block && dr->ds_handler_ctxt) { @@ -276,7 +250,7 @@ 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); + dispatch_assert(dx_type(ds) == DISPATCH_SOURCE_KEVENT_TYPE); dispatch_source_refs_t dr = ds->ds_refs; #ifdef __BLOCKS__ @@ -304,7 +278,7 @@ 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_assert(dx_type(ds) == DISPATCH_SOURCE_KEVENT_TYPE); dispatch_source_refs_t dr = ds->ds_refs; if (ds->ds_cancel_is_block && dr->ds_cancel_handler) { @@ -328,7 +302,7 @@ 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_assert(dx_type(ds) == DISPATCH_SOURCE_KEVENT_TYPE); dispatch_source_refs_t dr = ds->ds_refs; #ifdef __BLOCKS__ @@ -353,7 +327,7 @@ 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_assert(dx_type(ds) == DISPATCH_SOURCE_KEVENT_TYPE); dispatch_source_refs_t dr = ds->ds_refs; if (ds->ds_registration_is_block && dr->ds_registration_handler) { @@ -377,7 +351,7 @@ 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_assert(dx_type(ds) == DISPATCH_SOURCE_KEVENT_TYPE); dispatch_source_refs_t dr = ds->ds_refs; #ifdef __BLOCKS__ @@ -405,7 +379,7 @@ _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)) { + if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == -1)) { // no registration callout if source is canceled rdar://problem/8955246 #ifdef __BLOCKS__ if (ds->ds_registration_is_block) { @@ -473,7 +447,7 @@ _dispatch_source_latch_and_call(dispatch_source_t ds) { unsigned long prev; - if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { + if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == -1)) { return; } dispatch_source_refs_t dr = ds->ds_refs; @@ -508,7 +482,7 @@ _dispatch_source_kevent_resume(dispatch_source_t ds, uint32_t new_flags) } } -static dispatch_queue_t +dispatch_queue_t _dispatch_source_invoke(dispatch_source_t ds) { // This function performs all source actions. Each action is responsible @@ -530,7 +504,7 @@ _dispatch_source_invoke(dispatch_source_t ds) if (dr->ds_registration_handler) { return ds->do_targetq; } - if (slowpath(ds->do_xref_cnt == 0)) { + if (slowpath(ds->do_xref_cnt == -1)) { return &_dispatch_mgr_q; // rdar://problem/9558246 } } else if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) { @@ -544,10 +518,10 @@ _dispatch_source_invoke(dispatch_source_t ds) } // clears ds_registration_handler _dispatch_source_registration_callout(ds); - if (slowpath(ds->do_xref_cnt == 0)) { + if (slowpath(ds->do_xref_cnt == -1)) { return &_dispatch_mgr_q; // rdar://problem/9558246 } - } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { + } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == -1)){ // The source has been cancelled and needs to be uninstalled from the // manager queue. After uninstallation, the cancellation handler needs // to be delivered to the target queue. @@ -587,7 +561,7 @@ _dispatch_source_invoke(dispatch_source_t ds) return NULL; } -static bool +bool _dispatch_source_probe(dispatch_source_t ds) { // This function determines whether the source needs to be invoked. @@ -600,7 +574,7 @@ _dispatch_source_probe(dispatch_source_t ds) } 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)) { + } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == -1)){ // 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. @@ -630,7 +604,7 @@ _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)) { + if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == -1)) { return; } @@ -1846,7 +1820,7 @@ _dispatch_timer_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz) ds_timer(dr).flags); } -static size_t +size_t _dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz) { size_t offset = 0; @@ -1857,13 +1831,6 @@ _dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz) 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; @@ -1940,13 +1907,13 @@ _dispatch_kevent_debugger2(void *context) 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->do_ref_cnt + 1, 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, + "0x%x label: %s\n", dq, dq->do_ref_cnt + 1, dq->do_suspend_cnt, dq->dq_label); } } diff --git a/src/source_internal.h b/src/source_internal.h index a44eef7..c2c706f 100644 --- a/src/source_internal.h +++ b/src/source_internal.h @@ -71,12 +71,6 @@ enum { #define DISPATCH_TIMER_INDEX_MACH 1 #define DISPATCH_TIMER_INDEX_DISARM 2 -struct dispatch_source_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_source_s); -}; - -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_refs_s) dk_sources; @@ -130,8 +124,9 @@ struct dispatch_timer_source_refs_s { #define DSF_CANCELED 1u // cancellation has been requested #define DSF_ARMED 2u // source is armed +DISPATCH_CLASS_DECL(source); struct dispatch_source_s { - DISPATCH_STRUCT_HEADER(dispatch_source_s, dispatch_source_vtable_s); + DISPATCH_STRUCT_HEADER(source); DISPATCH_QUEUE_HEADER; // Instruments always copies DISPATCH_QUEUE_MIN_LABEL_SIZE, which is 64, // so the remainder of the structure must be big enough @@ -159,7 +154,11 @@ struct dispatch_source_s { }; }; -void _dispatch_source_xref_release(dispatch_source_t ds); +void _dispatch_source_xref_dispose(dispatch_source_t ds); void _dispatch_mach_notify_source_init(void *context); +dispatch_queue_t _dispatch_source_invoke(dispatch_source_t ds); +void _dispatch_source_dispose(dispatch_source_t ds); +bool _dispatch_source_probe(dispatch_source_t ds); +size_t _dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz); #endif /* __DISPATCH_SOURCE_INTERNAL__ */ diff --git a/src/trace.h b/src/trace.h index 0d9bc3d..4969cbe 100644 --- a/src/trace.h +++ b/src/trace.h @@ -27,7 +27,7 @@ #ifndef __DISPATCH_TRACE__ #define __DISPATCH_TRACE__ -#if DISPATCH_USE_DTRACE +#if DISPATCH_USE_DTRACE && !__OBJC2__ #include "provider.h" @@ -113,7 +113,7 @@ _dispatch_trace_client_callout_block(dispatch_block_t b) DISPATCH_ALWAYS_INLINE static inline void _dispatch_trace_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, - dispatch_object_t _tail) + dispatch_object_t _tail, unsigned int n) { if (slowpath(DISPATCH_QUEUE_PUSH_ENABLED())) { struct dispatch_object_s *dou = _head._do; @@ -121,17 +121,29 @@ _dispatch_trace_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, _dispatch_trace_continuation(dq, dou, DISPATCH_QUEUE_PUSH); } while (dou != _tail._do && (dou = dou->do_next)); } - _dispatch_queue_push_list(dq, _head, _tail); + _dispatch_queue_push_list(dq, _head, _tail, n); +} + +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_trace_queue_push(dispatch_queue_t dq, dispatch_object_t _tail) +{ + if (slowpath(DISPATCH_QUEUE_PUSH_ENABLED())) { + struct dispatch_object_s *dou = _tail._do; + _dispatch_trace_continuation(dq, dou, DISPATCH_QUEUE_PUSH); + } + _dispatch_queue_push(dq, _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); + _dispatch_queue_push(dq, dou); } #define _dispatch_queue_push_list _dispatch_trace_queue_push_list +#define _dispatch_queue_push _dispatch_trace_queue_push DISPATCH_ALWAYS_INLINE static inline void @@ -145,8 +157,8 @@ _dispatch_trace_continuation_pop(dispatch_queue_t dq, #else #define _dispatch_queue_push_notrace _dispatch_queue_push -#define _dispatch_trace_continuation_pop(dq, dou) +#define _dispatch_trace_continuation_pop(dq, dou) (void)(dq) -#endif // DISPATCH_USE_DTRACE +#endif // DISPATCH_USE_DTRACE && !__OBJC2__ #endif // __DISPATCH_TRACE__ diff --git a/src/transform.c b/src/transform.c new file mode 100644 index 0000000..775ce41 --- /dev/null +++ b/src/transform.c @@ -0,0 +1,1015 @@ +/* + * 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@ + */ + +#include "internal.h" + +#include + +#if defined(__LITTLE_ENDIAN__) +#define DISPATCH_DATA_FORMAT_TYPE_UTF16_HOST DISPATCH_DATA_FORMAT_TYPE_UTF16LE +#define DISPATCH_DATA_FORMAT_TYPE_UTF16_REV DISPATCH_DATA_FORMAT_TYPE_UTF16BE +#elif defined(__BIG_ENDIAN__) +#define DISPATCH_DATA_FORMAT_TYPE_UTF16_HOST DISPATCH_DATA_FORMAT_TYPE_UTF16BE +#define DISPATCH_DATA_FORMAT_TYPE_UTF16_REV DISPATCH_DATA_FORMAT_TYPE_UTF16LE +#endif + +enum { + _DISPATCH_DATA_FORMAT_NONE = 0x1, + _DISPATCH_DATA_FORMAT_UTF8 = 0x2, + _DISPATCH_DATA_FORMAT_UTF16LE = 0x4, + _DISPATCH_DATA_FORMAT_UTF16BE = 0x8, + _DISPATCH_DATA_FORMAT_UTF_ANY = 0x10, + _DISPATCH_DATA_FORMAT_BASE32 = 0x20, + _DISPATCH_DATA_FORMAT_BASE64 = 0x40, +}; + +#pragma mark - +#pragma mark baseXX tables + +static const char base32_encode_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ23456789"; + +static const char base32_decode_table[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, + 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25 +}; +static const ssize_t base32_decode_table_size = sizeof(base32_decode_table) + / sizeof(*base32_decode_table); + +static const char base64_encode_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const char base64_decode_table[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 +}; + +static const ssize_t base64_decode_table_size = sizeof(base64_decode_table) + / sizeof(*base64_decode_table); + +#pragma mark - +#pragma mark dispatch_transform_buffer + +typedef struct dispatch_transform_buffer_s { + dispatch_data_t data; + uint8_t *start; + union { + uint8_t *u8; + uint16_t *u16; + } ptr; + size_t size; +} dispatch_transform_buffer_s; + +static size_t +_dispatch_transform_sizet_mul(size_t a, size_t b) +{ + size_t rv = SIZE_MAX; + if (a == 0 || rv/a >= b) { + rv = a * b; + } + return rv; +} + +#define BUFFER_MALLOC_MAX (100*1024*1024) + +static bool +_dispatch_transform_buffer_new(dispatch_transform_buffer_s *buffer, + size_t required, size_t size) +{ + size_t remaining = buffer->size - (buffer->ptr.u8 - buffer->start); + if (required == 0 || remaining < required) { + if (buffer->start) { + if (buffer->ptr.u8 > buffer->start) { + dispatch_data_t _new = dispatch_data_create(buffer->start, + buffer->ptr.u8 - buffer->start, NULL, + DISPATCH_DATA_DESTRUCTOR_FREE); + dispatch_data_t _concat = dispatch_data_create_concat( + buffer->data, _new); + dispatch_release(_new); + dispatch_release(buffer->data); + buffer->data = _concat; + } else { + free(buffer->start); + } + } + buffer->size = required + size; + buffer->start = NULL; + if (buffer->size > 0) { + if (buffer->size > BUFFER_MALLOC_MAX) { + return false; + } + buffer->start = (uint8_t*)malloc(buffer->size); + if (buffer->start == NULL) { + return false; + } + } + buffer->ptr.u8 = buffer->start; + } + return true; +} + +#pragma mark - +#pragma mark dispatch_transform_helpers + +static dispatch_data_t +_dispatch_data_subrange_map(dispatch_data_t data, const void **ptr, + size_t offset, size_t size) +{ + dispatch_data_t subrange, map = NULL; + + subrange = dispatch_data_create_subrange(data, offset, size); + if (dispatch_data_get_size(subrange) == size) { + map = dispatch_data_create_map(subrange, ptr, NULL); + } + dispatch_release(subrange); + return map; +} + +static dispatch_data_format_type_t +_dispatch_transform_detect_utf(dispatch_data_t data) +{ + const void *p; + dispatch_data_t subrange = _dispatch_data_subrange_map(data, &p, 0, 2); + + if (subrange == NULL) { + return NULL; + } + + const uint16_t ch = *(const uint16_t *)p; + dispatch_data_format_type_t type = DISPATCH_DATA_FORMAT_TYPE_UTF8; + + if (ch == 0xfeff) { + type = DISPATCH_DATA_FORMAT_TYPE_UTF16_HOST; + } else if (ch == 0xfffe) { + type = DISPATCH_DATA_FORMAT_TYPE_UTF16_REV; + } + + dispatch_release(subrange); + + return type; +} + +static uint16_t +_dispatch_transform_swap_to_host(uint16_t x, int32_t byteOrder) +{ + if (byteOrder == OSLittleEndian) { + return OSSwapLittleToHostInt16(x); + } + return OSSwapBigToHostInt16(x); +} + +static uint16_t +_dispatch_transform_swap_from_host(uint16_t x, int32_t byteOrder) +{ + if (byteOrder == OSLittleEndian) { + return OSSwapHostToLittleInt16(x); + } + return OSSwapHostToBigInt16(x); +} + +#pragma mark - +#pragma mark UTF-8 + +static uint8_t +_dispatch_transform_utf8_length(uint8_t byte) +{ + if ((byte & 0x80) == 0) { + return 1; + } else if ((byte & 0xe0) == 0xc0) { + return 2; + } else if ((byte & 0xf0) == 0xe0) { + return 3; + } else if ((byte & 0xf8) == 0xf0) { + return 4; + } + return 0; +} + +static uint32_t +_dispatch_transform_read_utf8_sequence(const uint8_t *bytes) +{ + uint32_t wch = 0; + uint8_t seq_length = _dispatch_transform_utf8_length(*bytes); + + switch (seq_length) { + case 4: + wch |= (*bytes & 0x7); + wch <<= 6; + break; + case 3: + wch |= (*bytes & 0xf); + wch <<= 6; + break; + case 2: + wch |= (*bytes & 0x1f); + wch <<= 6; + break; + case 1: + wch = (*bytes & 0x7f); + break; + default: + // Not a utf-8 sequence + break; + } + + bytes++; + seq_length--; + + while (seq_length > 0) { + wch |= (*bytes & 0x3f); + bytes++; + seq_length--; + + if (seq_length > 0) { + wch <<= 6; + } + } + return wch; +} + +#pragma mark - +#pragma mark UTF-16 + +static dispatch_data_t +_dispatch_transform_to_utf16(dispatch_data_t data, int32_t byteOrder) +{ + __block size_t skip = 0; + + __block dispatch_transform_buffer_s buffer = { + .data = dispatch_data_empty, + }; + + bool success = dispatch_data_apply(data, ^( + DISPATCH_UNUSED dispatch_data_t region, + size_t offset, const void *_buffer, size_t size) { + const uint8_t *src = _buffer; + size_t i; + + if (offset == 0) { + size_t dest_size = 2 + _dispatch_transform_sizet_mul(size, + sizeof(uint16_t)); + if (!_dispatch_transform_buffer_new(&buffer, dest_size, 0)) { + return (bool)false; + } + // Insert BOM + *(buffer.ptr.u16)++ = _dispatch_transform_swap_from_host(0xfeff, + byteOrder); + } + + // Skip is incremented if the previous block read-ahead into our block + if (skip >= size) { + skip -= size; + return (bool)true; + } else if (skip > 0) { + src += skip; + size -= skip; + skip = 0; + } + + for (i = 0; i < size;) { + uint32_t wch = 0; + uint8_t byte_size = _dispatch_transform_utf8_length(*src); + + if (byte_size == 0) { + return (bool)false; + } else if (byte_size + i > size) { + // UTF-8 byte sequence spans over into the next block(s) + const void *p; + dispatch_data_t subrange = _dispatch_data_subrange_map(data, &p, + offset + i, byte_size); + if (subrange == NULL) { + return (bool)false; + } + + wch = _dispatch_transform_read_utf8_sequence(p); + skip += byte_size - (size - i); + src += byte_size; + i = size; + + dispatch_release(subrange); + } else { + wch = _dispatch_transform_read_utf8_sequence(src); + src += byte_size; + i += byte_size; + } + + size_t next = _dispatch_transform_sizet_mul(size - i, sizeof(uint16_t)); + if (wch >= 0xd800 && wch < 0xdfff) { + // Illegal range (surrogate pair) + return (bool)false; + } else if (wch >= 0x10000) { + // Surrogate pair + if (!_dispatch_transform_buffer_new(&buffer, 2 * + sizeof(uint16_t), next)) { + return (bool)false; + } + wch -= 0x10000; + *(buffer.ptr.u16)++ = _dispatch_transform_swap_from_host( + ((wch >> 10) & 0x3ff) + 0xd800, byteOrder); + *(buffer.ptr.u16)++ = _dispatch_transform_swap_from_host( + (wch & 0x3ff) + 0xdc00, byteOrder); + } else { + if (!_dispatch_transform_buffer_new(&buffer, 1 * + sizeof(uint16_t), next)) { + return (bool)false; + } + *(buffer.ptr.u16)++ = _dispatch_transform_swap_from_host( + (wch & 0xffff), byteOrder); + } + } + + (void)_dispatch_transform_buffer_new(&buffer, 0, 0); + + return (bool)true; + }); + + if (!success) { + dispatch_release(buffer.data); + return NULL; + } + + return buffer.data; +} + +static dispatch_data_t +_dispatch_transform_from_utf16(dispatch_data_t data, int32_t byteOrder) +{ + __block size_t skip = 0; + + __block dispatch_transform_buffer_s buffer = { + .data = dispatch_data_empty, + }; + + bool success = dispatch_data_apply(data, ^( + DISPATCH_UNUSED dispatch_data_t region, size_t offset, + const void *_buffer, size_t size) { + const uint16_t *src = _buffer; + + if (offset == 0) { + // Assume first buffer will be mostly single-byte UTF-8 sequences + size_t dest_size = _dispatch_transform_sizet_mul(size, 2) / 3; + if (!_dispatch_transform_buffer_new(&buffer, dest_size, 0)) { + return (bool)false; + } + } + + size_t i = 0, max = size / 2; + + // Skip is incremented if the previous block read-ahead into our block + if (skip >= size) { + skip -= size; + return (bool)true; + } else if (skip > 0) { + src = (uint16_t *)(((uint8_t *)src) + skip); + size -= skip; + max = (size / 2); + skip = 0; + } + + // If the buffer is an odd size, allow read ahead into the next region + if ((size % 2) != 0) { + max += 1; + } + + for (i = 0; i < max; i++) { + uint32_t wch = 0; + uint16_t ch; + + if ((i == (max - 1)) && (max > (size / 2))) { + // Last byte of an odd sized range + const void *p; + dispatch_data_t range = _dispatch_data_subrange_map(data, &p, + offset + (i * 2), 2); + if (range == NULL) { + return (bool)false; + } + ch = _dispatch_transform_swap_to_host(*(uint64_t*)p, byteOrder); + dispatch_release(range); + skip += 1; + } else { + ch = _dispatch_transform_swap_to_host(src[i], byteOrder); + } + + if (ch == 0xfffe && offset == 0 && i == 0) { + // Wrong-endian BOM at beginning of data + return (bool)false; + } else if (ch == 0xfeff && offset == 0 && i == 0) { + // Correct-endian BOM, skip it + continue; + } + + if ((ch >= 0xd800) && (ch <= 0xdbff)) { + // Surrogate pair + wch = ((ch - 0xd800) << 10); + if (++i >= max) { + // Surrogate byte isn't in this block + const void *p; + dispatch_data_t range = _dispatch_data_subrange_map(data, + &p, offset + (i * 2), 2); + if (range == NULL) { + return (bool)false; + } + ch = _dispatch_transform_swap_to_host(*(uint16_t *)p, + byteOrder); + dispatch_release(range); + skip += 2; + } else { + ch = _dispatch_transform_swap_to_host(src[i], byteOrder); + } + if (!((ch >= 0xdc00) && (ch <= 0xdfff))) { + return (bool)false; + } + wch = (wch | (ch & 0x3ff)); + wch += 0x10000; + } else if ((ch >= 0xdc00) && (ch <= 0xdfff)) { + return (bool)false; + } else { + wch = ch; + } + + size_t next = _dispatch_transform_sizet_mul(max - i, 2); + if (wch < 0x80) { + if (!_dispatch_transform_buffer_new(&buffer, 1, next)) { + return (bool)false; + } + *(buffer.ptr.u8)++ = (uint8_t)(wch & 0xff); + } else if (wch < 0x800) { + if (!_dispatch_transform_buffer_new(&buffer, 2, next)) { + return (bool)false; + } + *(buffer.ptr.u8)++ = (uint8_t)(0xc0 | (wch >> 6)); + *(buffer.ptr.u8)++ = (uint8_t)(0x80 | (wch & 0x3f)); + } else if (wch < 0x10000) { + if (!_dispatch_transform_buffer_new(&buffer, 3, next)) { + return (bool)false; + } + *(buffer.ptr.u8)++ = (uint8_t)(0xe0 | (wch >> 12)); + *(buffer.ptr.u8)++ = (uint8_t)(0x80 | ((wch >> 6) & 0x3f)); + *(buffer.ptr.u8)++ = (uint8_t)(0x80 | (wch & 0x3f)); + } else if (wch < 0x200000) { + if (!_dispatch_transform_buffer_new(&buffer, 4, next)) { + return (bool)false; + } + *(buffer.ptr.u8)++ = (uint8_t)(0xf0 | (wch >> 18)); + *(buffer.ptr.u8)++ = (uint8_t)(0x80 | ((wch >> 12) & 0x3f)); + *(buffer.ptr.u8)++ = (uint8_t)(0x80 | ((wch >> 6) & 0x3f)); + *(buffer.ptr.u8)++ = (uint8_t)(0x80 | (wch & 0x3f)); + } + } + + (void)_dispatch_transform_buffer_new(&buffer, 0, 0); + + return (bool)true; + }); + + if (!success) { + dispatch_release(buffer.data); + return NULL; + } + + return buffer.data; +} + +static dispatch_data_t +_dispatch_transform_from_utf16le(dispatch_data_t data) +{ + return _dispatch_transform_from_utf16(data, OSLittleEndian); +} + +static dispatch_data_t +_dispatch_transform_from_utf16be(dispatch_data_t data) +{ + return _dispatch_transform_from_utf16(data, OSBigEndian); +} + +static dispatch_data_t +_dispatch_transform_to_utf16le(dispatch_data_t data) +{ + return _dispatch_transform_to_utf16(data, OSLittleEndian); +} + +static dispatch_data_t +_dispatch_transform_to_utf16be(dispatch_data_t data) +{ + return _dispatch_transform_to_utf16(data, OSBigEndian); +} + +#pragma mark - +#pragma mark base32 + +static dispatch_data_t +_dispatch_transform_from_base32(dispatch_data_t data) +{ + __block uint64_t x = 0, count = 0, pad = 0; + + __block dispatch_data_t rv = dispatch_data_empty; + + bool success = dispatch_data_apply(data, ^( + DISPATCH_UNUSED dispatch_data_t region, + DISPATCH_UNUSED size_t offset, const void *buffer, size_t size) { + size_t i, dest_size = (size * 5) / 8; + + uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(char)); + uint8_t *ptr = dest; + if (dest == NULL) { + return (bool)false; + } + + const uint8_t *bytes = buffer; + + for (i = 0; i < size; i++) { + if (bytes[i] == '\n' || bytes[i] == '\t' || bytes[i] == ' ') { + continue; + } + + ssize_t index = bytes[i]; + if (index >= base32_decode_table_size || + base32_decode_table[index] == -1) { + free(dest); + return (bool)false; + } + count++; + + char value = base32_decode_table[index]; + if (value == -2) { + value = 0; + pad++; + } + + x <<= 5; + x += value; + + if ((count & 0x7) == 0) { + *ptr++ = (x >> 32) & 0xff; + *ptr++ = (x >> 24) & 0xff; + *ptr++ = (x >> 16) & 0xff; + *ptr++ = (x >> 8) & 0xff; + *ptr++ = x & 0xff; + } + } + + size_t final = (ptr - dest); + switch (pad) { + case 1: + final -= 1; + break; + case 3: + final -= 2; + break; + case 4: + final -= 3; + break; + case 6: + final -= 4; + break; + } + + dispatch_data_t val = dispatch_data_create(dest, final, NULL, + DISPATCH_DATA_DESTRUCTOR_FREE); + dispatch_data_t concat = dispatch_data_create_concat(rv, val); + + dispatch_release(val); + dispatch_release(rv); + rv = concat; + + return (bool)true; + }); + + if (!success) { + dispatch_release(rv); + return NULL; + } + + return rv; +} + +static dispatch_data_t +_dispatch_transform_to_base32(dispatch_data_t data) +{ + size_t total = dispatch_data_get_size(data); + __block size_t count = 0; + + size_t dest_size = ((total + 4) * 8) / 5; + dest_size -= dest_size % 8; + + uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(uint8_t)); + if (dest == NULL) { + return NULL; + } + + __block uint8_t *ptr = dest; + + /* + 0 1 2 3 4 + 8-bit bytes: xxxxxxxx yyyyyyyy zzzzzzzz xxxxxxxx yyyyyyyy + 5-bit chunks: aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh + */ + + bool success = dispatch_data_apply(data, ^( + DISPATCH_UNUSED dispatch_data_t region, + size_t offset, const void *buffer, size_t size) { + const uint8_t *bytes = buffer; + size_t i; + + for (i = 0; i < size; i++, count++) { + uint8_t curr = bytes[i], last = 0; + + if ((count % 5) != 0) { + if (i == 0) { + const void *p; + dispatch_data_t subrange = _dispatch_data_subrange_map(data, + &p, offset - 1, 1); + if (subrange == NULL) { + return (bool)false; + } + last = *(uint8_t*)p; + dispatch_release(subrange); + } else { + last = bytes[i - 1]; + } + } + + switch (count % 5) { + case 0: + // a + *ptr++ = base32_encode_table[(curr >> 3) & 0x1f]; + break; + case 1: + // b + c + *ptr++ = base32_encode_table[((last << 2)|(curr >> 6)) & 0x1f]; + *ptr++ = base32_encode_table[(curr >> 1) & 0x1f]; + break; + case 2: + // d + *ptr++ = base32_encode_table[((last << 4)|(curr >> 4)) & 0x1f]; + break; + case 3: + // e + f + *ptr++ = base32_encode_table[((last << 1)|(curr >> 7)) & 0x1f]; + *ptr++ = base32_encode_table[(curr >> 2) & 0x1f]; + break; + case 4: + // g + h + *ptr++ = base32_encode_table[((last << 3)|(curr >> 5)) & 0x1f]; + *ptr++ = base32_encode_table[curr & 0x1f]; + break; + } + } + + // Last region, insert padding bytes, if needed + if (offset + size == total) { + switch (count % 5) { + case 0: + break; + case 1: + // b[4:2] + *ptr++ = base32_encode_table[(bytes[size-1] << 2) & 0x1c]; + break; + case 2: + // d[4] + *ptr++ = base32_encode_table[(bytes[size-1] << 4) & 0x10]; + break; + case 3: + // e[4:1] + *ptr++ = base32_encode_table[(bytes[size-1] << 1) & 0x1e]; + break; + case 4: + // g[4:3] + *ptr++ = base32_encode_table[bytes[size-1] & 0x18]; + break; + } + switch (count % 5) { + case 0: + break; + case 1: + *ptr++ = '='; // c + *ptr++ = '='; // d + case 2: + *ptr++ = '='; // e + case 3: + *ptr++ = '='; // f + *ptr++ = '='; // g + case 4: + *ptr++ = '='; // h + break; + } + } + + return (bool)true; + }); + + if (!success) { + free(dest); + return NULL; + } + return dispatch_data_create(dest, dest_size, NULL, + DISPATCH_DATA_DESTRUCTOR_FREE); +} + +#pragma mark - +#pragma mark base64 + +static dispatch_data_t +_dispatch_transform_from_base64(dispatch_data_t data) +{ + __block uint64_t x = 0, count = 0; + __block size_t pad = 0; + + __block dispatch_data_t rv = dispatch_data_empty; + + bool success = dispatch_data_apply(data, ^( + DISPATCH_UNUSED dispatch_data_t region, + DISPATCH_UNUSED size_t offset, const void *buffer, size_t size) { + size_t i, dest_size = (size * 3) / 4; + + uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(uint8_t)); + uint8_t *ptr = dest; + if (dest == NULL) { + return (bool)false; + } + + const uint8_t *bytes = buffer; + + for (i = 0; i < size; i++) { + if (bytes[i] == '\n' || bytes[i] == '\t' || bytes[i] == ' ') { + continue; + } + + ssize_t index = bytes[i]; + if (index >= base64_decode_table_size || + base64_decode_table[index] == -1) { + free(dest); + return (bool)false; + } + count++; + + char value = base64_decode_table[index]; + if (value == -2) { + value = 0; + pad++; + } + + x <<= 6; + x += value; + + if ((count & 0x3) == 0) { + *ptr++ = (x >> 16) & 0xff; + *ptr++ = (x >> 8) & 0xff; + *ptr++ = x & 0xff; + } + } + + size_t final = (ptr - dest); + if (pad > 0) { + // 2 bytes of pad means only had one char in final group + final -= pad; + } + + dispatch_data_t val = dispatch_data_create(dest, final, NULL, + DISPATCH_DATA_DESTRUCTOR_FREE); + dispatch_data_t concat = dispatch_data_create_concat(rv, val); + + dispatch_release(val); + dispatch_release(rv); + rv = concat; + + return (bool)true; + }); + + if (!success) { + dispatch_release(rv); + return NULL; + } + + return rv; +} + +static dispatch_data_t +_dispatch_transform_to_base64(dispatch_data_t data) +{ + // RFC 4648 states that we should not linebreak + // http://tools.ietf.org/html/rfc4648 + size_t total = dispatch_data_get_size(data); + __block size_t count = 0; + + size_t dest_size = ((total + 2) * 4) / 3; + dest_size -= dest_size % 4; + + uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(uint8_t)); + if (dest == NULL) { + return NULL; + } + + __block uint8_t *ptr = dest; + + /* + * 3 8-bit bytes: xxxxxxxx yyyyyyyy zzzzzzzz + * 4 6-bit chunks: aaaaaabb bbbbcccc ccdddddd + */ + + bool success = dispatch_data_apply(data, ^( + DISPATCH_UNUSED dispatch_data_t region, + size_t offset, const void *buffer, size_t size) { + const uint8_t *bytes = buffer; + size_t i; + + for (i = 0; i < size; i++, count++) { + uint8_t curr = bytes[i], last = 0; + + if ((count % 3) != 0) { + if (i == 0) { + const void *p; + dispatch_data_t subrange = _dispatch_data_subrange_map(data, + &p, offset - 1, 1); + if (subrange == NULL) { + return (bool)false; + } + last = *(uint8_t*)p; + dispatch_release(subrange); + } else { + last = bytes[i - 1]; + } + } + + switch (count % 3) { + case 0: + *ptr++ = base64_encode_table[(curr >> 2) & 0x3f]; + break; + case 1: + *ptr++ = base64_encode_table[((last << 4)|(curr >> 4)) & 0x3f]; + break; + case 2: + *ptr++ = base64_encode_table[((last << 2)|(curr >> 6)) & 0x3f]; + *ptr++ = base64_encode_table[(curr & 0x3f)]; + break; + } + } + + // Last region, insert padding bytes, if needed + if (offset + size == total) { + switch (count % 3) { + case 0: + break; + case 1: + *ptr++ = base64_encode_table[(bytes[size-1] << 4) & 0x30]; + *ptr++ = '='; + *ptr++ = '='; + break; + case 2: + *ptr++ = base64_encode_table[(bytes[size-1] << 2) & 0x3c]; + *ptr++ = '='; + break; + } + } + + return (bool)true; + }); + + if (!success) { + free(dest); + return NULL; + } + return dispatch_data_create(dest, dest_size, NULL, + DISPATCH_DATA_DESTRUCTOR_FREE); +} + +#pragma mark - +#pragma mark dispatch_data_transform + +dispatch_data_t +dispatch_data_create_with_transform(dispatch_data_t data, + dispatch_data_format_type_t input, dispatch_data_format_type_t output) +{ + if (input->type == _DISPATCH_DATA_FORMAT_UTF_ANY) { + input = _dispatch_transform_detect_utf(data); + } + + if ((input->type & ~output->input_mask) != 0) { + return NULL; + } + + if ((output->type & ~input->output_mask) != 0) { + return NULL; + } + + if (dispatch_data_get_size(data) == 0) { + return data; + } + + dispatch_data_t temp1; + if (input->decode) { + temp1 = input->decode(data); + } else { + dispatch_retain(data); + temp1 = data; + } + + if (!temp1) { + return NULL; + } + + dispatch_data_t temp2; + if (output->encode) { + temp2 = output->encode(temp1); + } else { + dispatch_retain(temp1); + temp2 = temp1; + } + + dispatch_release(temp1); + return temp2; +} + +const struct dispatch_data_format_type_s _dispatch_data_format_type_none = { + .type = _DISPATCH_DATA_FORMAT_NONE, + .input_mask = ~0, + .output_mask = ~0, + .decode = NULL, + .encode = NULL, +}; + +const struct dispatch_data_format_type_s _dispatch_data_format_type_base32 = { + .type = _DISPATCH_DATA_FORMAT_BASE32, + .input_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 | + _DISPATCH_DATA_FORMAT_BASE64), + .output_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 | + _DISPATCH_DATA_FORMAT_BASE64), + .decode = _dispatch_transform_from_base32, + .encode = _dispatch_transform_to_base32, +}; + +const struct dispatch_data_format_type_s _dispatch_data_format_type_base64 = { + .type = _DISPATCH_DATA_FORMAT_BASE64, + .input_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 | + _DISPATCH_DATA_FORMAT_BASE64), + .output_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 | + _DISPATCH_DATA_FORMAT_BASE64), + .decode = _dispatch_transform_from_base64, + .encode = _dispatch_transform_to_base64, +}; + +const struct dispatch_data_format_type_s _dispatch_data_format_type_utf16le = { + .type = _DISPATCH_DATA_FORMAT_UTF16LE, + .input_mask = (_DISPATCH_DATA_FORMAT_UTF8 | _DISPATCH_DATA_FORMAT_UTF16BE | + _DISPATCH_DATA_FORMAT_UTF16LE), + .output_mask = (_DISPATCH_DATA_FORMAT_UTF8 | _DISPATCH_DATA_FORMAT_UTF16BE | + _DISPATCH_DATA_FORMAT_UTF16LE), + .decode = _dispatch_transform_from_utf16le, + .encode = _dispatch_transform_to_utf16le, +}; + +const struct dispatch_data_format_type_s _dispatch_data_format_type_utf16be = { + .type = _DISPATCH_DATA_FORMAT_UTF16BE, + .input_mask = (_DISPATCH_DATA_FORMAT_UTF8 | _DISPATCH_DATA_FORMAT_UTF16BE | + _DISPATCH_DATA_FORMAT_UTF16LE), + .output_mask = (_DISPATCH_DATA_FORMAT_UTF8 | _DISPATCH_DATA_FORMAT_UTF16BE | + _DISPATCH_DATA_FORMAT_UTF16LE), + .decode = _dispatch_transform_from_utf16be, + .encode = _dispatch_transform_to_utf16be, +}; + +const struct dispatch_data_format_type_s _dispatch_data_format_type_utf8 = { + .type = _DISPATCH_DATA_FORMAT_UTF8, + .input_mask = (_DISPATCH_DATA_FORMAT_UTF8 | _DISPATCH_DATA_FORMAT_UTF16BE | + _DISPATCH_DATA_FORMAT_UTF16LE), + .output_mask = (_DISPATCH_DATA_FORMAT_UTF8 | _DISPATCH_DATA_FORMAT_UTF16BE | + _DISPATCH_DATA_FORMAT_UTF16LE), + .decode = NULL, + .encode = NULL, +}; + +const struct dispatch_data_format_type_s _dispatch_data_format_type_utf_any = { + .type = _DISPATCH_DATA_FORMAT_UTF_ANY, + .input_mask = 0, + .output_mask = 0, + .decode = NULL, + .encode = NULL, +}; diff --git a/xcodeconfig/libdispatch.aliases b/xcodeconfig/libdispatch.aliases new file mode 100644 index 0000000..aae0bcc --- /dev/null +++ b/xcodeconfig/libdispatch.aliases @@ -0,0 +1,12 @@ +_OBJC_CLASS_$_OS_dispatch_semaphore __dispatch_semaphore_vtable +_OBJC_CLASS_$_OS_dispatch_group __dispatch_group_vtable +_OBJC_CLASS_$_OS_dispatch_queue __dispatch_queue_vtable +_OBJC_CLASS_$_OS_dispatch_queue_root __dispatch_queue_root_vtable +_OBJC_CLASS_$_OS_dispatch_queue_mgr __dispatch_queue_mgr_vtable +_OBJC_CLASS_$_OS_dispatch_queue_specific_queue __dispatch_queue_specific_queue_vtable +_OBJC_CLASS_$_OS_dispatch_queue_attr __dispatch_queue_attr_vtable +_OBJC_CLASS_$_OS_dispatch_source __dispatch_source_vtable +_OBJC_CLASS_$_OS_dispatch_data __dispatch_data_vtable +_OBJC_CLASS_$_OS_dispatch_io __dispatch_io_vtable +_OBJC_CLASS_$_OS_dispatch_operation __dispatch_operation_vtable +_OBJC_CLASS_$_OS_dispatch_disk __dispatch_disk_vtable diff --git a/xcodeconfig/libdispatch.order b/xcodeconfig/libdispatch.order new file mode 100644 index 0000000..64787b7 --- /dev/null +++ b/xcodeconfig/libdispatch.order @@ -0,0 +1,40 @@ +_OBJC_CLASS_$_OS_object +_OBJC_METACLASS_$_OS_object +_OBJC_CLASS_$_OS_dispatch_object +_OBJC_METACLASS_$_OS_dispatch_object +_OBJC_CLASS_$_OS_dispatch_semaphore +__OS_dispatch_semaphore_vtable +_OBJC_METACLASS_$_OS_dispatch_semaphore +_OBJC_CLASS_$_OS_dispatch_group +__OS_dispatch_group_vtable +_OBJC_METACLASS_$_OS_dispatch_group +_OBJC_CLASS_$_OS_dispatch_queue +__OS_dispatch_queue_vtable +_OBJC_METACLASS_$_OS_dispatch_queue +_OBJC_CLASS_$_OS_dispatch_queue_root +__OS_dispatch_queue_root_vtable +_OBJC_METACLASS_$_OS_dispatch_queue_root +_OBJC_CLASS_$_OS_dispatch_queue_mgr +__OS_dispatch_queue_mgr_vtable +_OBJC_METACLASS_$_OS_dispatch_queue_mgr +_OBJC_CLASS_$_OS_dispatch_queue_specific_queue +__OS_dispatch_queue_specific_queue_vtable +_OBJC_METACLASS_$_OS_dispatch_queue_specific_queue +_OBJC_CLASS_$_OS_dispatch_queue_attr +__OS_dispatch_queue_attr_vtable +_OBJC_METACLASS_$_OS_dispatch_queue_attr +_OBJC_CLASS_$_OS_dispatch_source +__OS_dispatch_source_vtable +_OBJC_METACLASS_$_OS_dispatch_source +_OBJC_CLASS_$_OS_dispatch_data +__OS_dispatch_data_vtable +_OBJC_METACLASS_$_OS_dispatch_data +_OBJC_CLASS_$_OS_dispatch_io +__OS_dispatch_io_vtable +_OBJC_METACLASS_$_OS_dispatch_io +_OBJC_CLASS_$_OS_dispatch_operation +__OS_dispatch_operation_vtable +_OBJC_METACLASS_$_OS_dispatch_operation +_OBJC_CLASS_$_OS_dispatch_disk +__OS_dispatch_disk_vtable +_OBJC_METACLASS_$_OS_dispatch_disk diff --git a/xcodeconfig/libdispatch.unexport b/xcodeconfig/libdispatch.unexport new file mode 100644 index 0000000..035bd9c --- /dev/null +++ b/xcodeconfig/libdispatch.unexport @@ -0,0 +1,12 @@ +__dispatch_semaphore_vtable +__dispatch_group_vtable +__dispatch_queue_vtable +__dispatch_queue_root_vtable +__dispatch_queue_mgr_vtable +__dispatch_queue_specific_queue_vtable +__dispatch_queue_attr_vtable +__dispatch_source_vtable +__dispatch_data_vtable +__dispatch_io_vtable +__dispatch_operation_vtable +__dispatch_disk_vtable diff --git a/xcodeconfig/libdispatch.xcconfig b/xcodeconfig/libdispatch.xcconfig index e7d44f4..e651bfc 100644 --- a/xcodeconfig/libdispatch.xcconfig +++ b/xcodeconfig/libdispatch.xcconfig @@ -22,7 +22,6 @@ 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 @@ -31,6 +30,10 @@ 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 +OS_PUBLIC_HEADERS_FOLDER_PATH = /usr/include/os +OS_PUBLIC_HEADERS_FOLDER_PATH[sdk=iphonesimulator*] = $(SDKROOT)/usr/include/os +OS_PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/os +OS_PRIVATE_HEADERS_FOLDER_PATH[sdk=iphonesimulator*] = $(SDKROOT)/usr/local/include/os HEADER_SEARCH_PATHS = $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders $(PROJECT_DIR) INSTALLHDRS_SCRIPT_PHASE = YES ALWAYS_SEARCH_USER_PATHS = NO @@ -39,10 +42,7 @@ 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_OBJC_GC[sdk=macosx*] = supported GCC_ENABLE_PASCAL_STRINGS = NO GCC_WARN_SHADOW = YES GCC_WARN_64_TO_32_BIT_CONVERSION = YES @@ -54,14 +54,19 @@ 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 = -fdiagnostics-show-option -fverbose-asm +OTHER_CFLAGS[arch=i386][sdk=macosx*] = $(OTHER_CFLAGS) -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-exceptions +OTHER_CFLAGS_normal = -momit-leaf-frame-pointer +OTHER_CFLAGS_normal[arch=armv6][sdk=macosx*] = +OTHER_CFLAGS_profile = $(OTHER_CFLAGS_normal) -DDISPATCH_PROFILE=1 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) +OBJC_LDFLAGS = -Wl,-upward-lobjc -Wl,-order_file,$(SRCROOT)/xcodeconfig/libdispatch.order -Wl,-alias_list,$(SRCROOT)/xcodeconfig/libdispatch.aliases -Wl,-unexported_symbols_list,$(SRCROOT)/xcodeconfig/libdispatch.unexport +OBJC_LDFLAGS[sdk=macosx*] = $(OBJC_LDFLAGS) -Wl,-upward-lauto +OBJC_LDFLAGS[arch=i386][sdk=macosx*] = +OBJC_EXCLUDED_SOURCE_FILE_NAMES_i386_macosx = object.m +OTHER_LDFLAGS = $(OTHER_LDFLAGS) $(UMBRELLA_LDFLAGS) $(CR_LDFLAGS) $(OBJC_LDFLAGS) diff --git a/xcodescripts/symlink-headers.sh b/xcodescripts/install-headers.sh similarity index 61% rename from xcodescripts/symlink-headers.sh rename to xcodescripts/install-headers.sh index a062a6f..cb5e804 100755 --- a/xcodescripts/symlink-headers.sh +++ b/xcodescripts/install-headers.sh @@ -1,6 +1,6 @@ #!/bin/bash -e # -# Copyright (c) 2010-2011 Apple Inc. All rights reserved. +# Copyright (c) 2012 Apple Inc. All rights reserved. # # @APPLE_APACHE_LICENSE_HEADER_START@ # @@ -19,11 +19,11 @@ # @APPLE_APACHE_LICENSE_HEADER_END@ # -if [ "$DEPLOYMENT_LOCATION" != YES ]; then - DSTROOT="$CONFIGURATION_BUILD_DIR" - [ -L "$DSTROOT$PRIVATE_HEADERS_FOLDER_PATH"/private.h ] && exit +if [ "${DEPLOYMENT_LOCATION}" != YES ]; then + DSTROOT="${CONFIGURATION_BUILD_DIR}" 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 +mkdir -p "${DSTROOT}${OS_PUBLIC_HEADERS_FOLDER_PATH}" || true +mkdir -p "${DSTROOT}${OS_PRIVATE_HEADERS_FOLDER_PATH}" || true +cp -X "${SCRIPT_INPUT_FILE_1}" "${DSTROOT}${OS_PUBLIC_HEADERS_FOLDER_PATH}" +cp -X "${SCRIPT_INPUT_FILE_2}" "${DSTROOT}${OS_PRIVATE_HEADERS_FOLDER_PATH}" diff --git a/xcodescripts/install-manpages.sh b/xcodescripts/install-manpages.sh index 2d88a26..2ea1f68 100755 --- a/xcodescripts/install-manpages.sh +++ b/xcodescripts/install-manpages.sh @@ -80,6 +80,7 @@ for m in dispatch_get_current_queue dispatch_main dispatch_get_main_queue \ done for m in dispatch_source_set_event_handler dispatch_source_set_event_handler_f \ + dispatch_source_set_registration_handler dispatch_source_set_registration_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 \ @@ -98,7 +99,7 @@ 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 + dispatch_io_close dispatch_io_barrier; do ln -f dispatch_io_create.3 ${m}.3 done -- 2.45.2