From f36c17b3659cfff8fd6a131bac3d0a4d9665cff5 Mon Sep 17 00:00:00 2001 From: Niels Grewe Date: Fri, 24 May 2024 23:29:28 +0200 Subject: [PATCH] feat(blocks runtime): Enable using libBlocksRuntime instead of the embedded runtime --- .cirrus.yml | 2 +- .github/workflows/main.yml | 18 ++++++++-- CMakeLists.txt | 52 ++++++++++++++++++++++------ INSTALL | 14 ++++++++ NSBlocks.m | 29 ++++++++++++++-- arc.mm | 71 ++++++++++++++++++++++++++++++-------- blocks_runtime.m | 35 +++++++++++++++---- blocks_runtime_np.m | 57 ++++++++++++++++++++++++++++++ class.h | 5 +++ loader.c | 2 ++ objc/blocks_private.h | 24 +++++++++++++ 11 files changed, 271 insertions(+), 38 deletions(-) create mode 100644 blocks_runtime_np.m diff --git a/.cirrus.yml b/.cirrus.yml index 49a022c7..4a43645e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,7 +1,7 @@ libcxxrt_freebsd_task: matrix: - freebsd_instance: - image_family: freebsd-13-2 + image_family: freebsd-13-3 - freebsd_instance: image_family: freebsd-15-0-snap - freebsd_instance: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 66d1a2d4..413f72b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,6 +20,7 @@ jobs: # Build each combination of OS and release/debug variants os: [ "ubuntu-22.04", "ubuntu-20.04" ] build-type: [ Release, Debug ] + blocks-runtime: [ "EMBEDDED", "swift-5.10-RELEASE" ] cxxlib: [ "libc++", "libstdc++" ] llvm-version: [10, 11, 12, 13, 14, 15] # Don't bother testing the LLVM versions that aren't in the default image for the different platforms @@ -41,7 +42,7 @@ jobs: # Don't abort runners if a single one fails fail-fast: false runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} ${{ matrix.build-type }} LLVM-${{ matrix.llvm-version }} ${{ matrix.cxxlib }} + name: ${{ matrix.os }} ${{ matrix.build-type }} LLVM-${{ matrix.llvm-version }} ${{ matrix.cxxlib }} BlocksRuntime-${{ matrix.blocks-runtime }} steps: - uses: actions/checkout@v3 - name: Install dependencies @@ -53,11 +54,24 @@ jobs: sudo apt install libc++-${{matrix.llvm-version}}-dev libc++abi-${{matrix.llvm-version}}-dev sudo apt install libunwind-${{matrix.llvm-version}}-dev || true fi + if [ "${{ matrix.blocks-runtime }}" != "EMBEDDED" ]; then + git clone --depth 1 --branch "${{ matrix.blocks-runtime }}" https://github.com/apple/swift-corelibs-libdispatch.git ${{github.workspace}}/swift-corelibs-libdispatch + cmake -B ${{github.workspace}}/swift-corelibs-libdispatch/build -G Ninja -DINSTALL_PRIVATE_HEADERS=ON -DCMAKE_C_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm-version}} -S ${{github.workspace}}/swift-corelibs-libdispatch + pushd ${{github.workspace}}/swift-corelibs-libdispatch/build + ninja + sudo ninja install + popd + fi - name: Configure CMake run: | export LDFLAGS=-L/usr/lib/llvm-${{ matrix.llvm-version }}/lib/ + if [ "${{ matrix.blocks-runtime }}" != "EMBEDDED" ]; then + export EMBEDDED_BLOCKS_RUNTIME=OFF + else + export EMBEDDED_BLOCKS_RUNTIME=ON + fi ls -lahR /usr/lib/llvm-${{ matrix.llvm-version }}/lib/ - cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build-type}} -G Ninja -DTESTS=ON -DCMAKE_C_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_OBJC_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_ASM_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm-version}} -DCMAKE_OBJCXX_COMPILER=clang++-${{matrix.llvm-version}} -DCMAKE_CXX_FLAGS="-stdlib=${{matrix.cxxlib}}" + cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build-type}} -G Ninja -DTESTS=ON -DEMBEDDED_BLOCKS_RUNTIME=$EMBEDDED_BLOCKS_RUNTIME -DCMAKE_C_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_OBJC_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_ASM_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm-version}} -DCMAKE_OBJCXX_COMPILER=clang++-${{matrix.llvm-version}} -DCMAKE_CXX_FLAGS="-stdlib=${{matrix.cxxlib}}" # Build with a nice ninja status line - name: Build working-directory: ${{github.workspace}}/build diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a148058..0242d976 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ endif () INCLUDE (CheckCXXSourceCompiles) INCLUDE (FetchContent) +INCLUDE (CheckSymbolExists) set(libobjc_VERSION 4.6) @@ -58,7 +59,7 @@ set(libobjc_OBJC_SRCS NSBlocks.m Protocol2.m associate.m - blocks_runtime.m + blocks_runtime_np.m properties.m) set(libobjc_C_SRCS alias_table.c @@ -83,8 +84,6 @@ set(libobjc_HDRS objc/Availability.h objc/Object.h objc/Protocol.h - objc/blocks_private.h - objc/blocks_runtime.h objc/capabilities.h objc/developer.h objc/encoding.h @@ -101,10 +100,7 @@ set(libobjc_HDRS objc/runtime-deprecated.h objc/runtime.h objc/slot.h) -set(libBlocksRuntime_COMPATIBILITY_HDRS - Block.h - Block_private.h - ) + set(libobjc_CXX_SRCS selector_table.cc ) @@ -143,7 +139,7 @@ option(DEBUG_ARC_COMPAT "Log warnings for classes that don't hit ARC fast paths" OFF) option(ENABLE_OBJCXX "Enable support for Objective-C++" ON) option(TESTS "Enable building the tests") - +option(EMBEDDED_BLOCKS_RUNTIME "Include an embedded blocks runtime, rather than relying on libBlocksRuntime to supply it" ON) # For release builds, we disable spamming the terminal with warnings about # selector type mismatches @@ -230,6 +226,31 @@ else () find_library(M_LIBRARY m) endif () +if (EMBEDDED_BLOCKS_RUNTIME) + set(libBlocksRuntime_COMPATIBILITY_HDRS + Block.h + Block_private.h + ) + list(APPEND libobjc_OBJC_SRCS blocks_runtime.m) + list(APPEND libobjc_HDRS objc/blocks_private.h) + list(APPEND libobjc_HDRS objc/blocks_runtime.h) + add_definitions(-DEMBEDDED_BLOCKS_RUNTIME) +else () + find_library(BLOCKS_RUNTIME_LIBRARY BlocksRuntime) + if (BLOCKS_RUNTIME_LIBRARY) + set(CMAKE_REQUIRED_LIBRARIES ${BLOCKS_RUNTIME_LIBRARY}) + check_symbol_exists(_Block_use_RR2 "Block_private.h" HAVE_BLOCK_USE_RR2) + if (HAVE_BLOCK_USE_RR2) + add_definitions(-DHAVE_BLOCK_USE_RR2) + else () + message(FATAL_ERROR "libBlocksRuntime does not contain _Block_use_RR2(). Enable EMBEDDED_BLOCKS_RUNTIME to use the built-in blocks runtime.") + endif () + unset(CMAKE_REQUIRED_LIBRARIES) + else () + message(FATAL_ERROR "libBlocksRuntime not found. Enable EMBEDDED_BLOCKS_RUNTIME to use the built-in blocks runtime.") + endif () +endif () + add_library(objc SHARED ${libobjc_C_SRCS} ${libobjc_ASM_SRCS} ${libobjc_OBJC_SRCS} ${libobjc_OBJCXX_SRCS} ${libobjc_ASM_OBJS}) target_compile_options(objc PRIVATE "$<$,$>:-Wno-deprecated-objc-isa-usage;-Wno-objc-root-class;-fobjc-runtime=gnustep-2.0>$<$:-Xclang;-fexceptions>") @@ -268,6 +289,10 @@ if (M_LIBRARY) target_link_libraries(objc PUBLIC ${M_LIBRARY}) endif () +if (BLOCKS_RUNTIME_LIBRARY) + target_link_libraries(objc PUBLIC ${BLOCKS_RUNTIME_LIBRARY}) +endif () + # Make weak symbols work on OS X if (APPLE) set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS @@ -340,8 +365,12 @@ install(TARGETS ${INSTALL_TARGETS} install(FILES ${libobjc_HDRS} DESTINATION "${HEADER_INSTALL_PATH}/${INCLUDE_DIRECTORY}") -install(FILES ${libBlocksRuntime_COMPATIBILITY_HDRS} - DESTINATION "${HEADER_INSTALL_PATH}") + +if (EMBEDDED_BLOCKS_RUNTIME) + install(FILES ${libBlocksRuntime_COMPATIBILITY_HDRS} + DESTINATION "${HEADER_INSTALL_PATH}") +endif () + set(CPACK_GENERATOR TGZ CACHE STRING "Installer types to generate. Sensible options include TGZ, RPM and DEB") @@ -386,6 +415,9 @@ set(PC_LIBS_PRIVATE ${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES}) if (M_LIBRARY) list(APPEND PC_LIBS_PRIVATE ${M_LIBRARY}) endif () +if (BLOCKS_RUNTIME_LIBRARY) + list(APPEND PC_LIBS_PRIVATE ${BLOCKS_RUNTIME_LIBRARY}) +endif () list(REMOVE_DUPLICATES PC_LIBS_PRIVATE) string(REPLACE ";" " -l" PC_LIBS_PRIVATE "${PC_LIBS_PRIVATE}") set(PC_LIBS_PRIVATE "Libs.private: -l${PC_LIBS_PRIVATE}") diff --git a/INSTALL b/INSTALL index a91268fa..ff438157 100644 --- a/INSTALL +++ b/INSTALL @@ -79,6 +79,20 @@ this configuration, we provide a separate libobjcxx.so, which avoids the need for the Objective-C runtime to depend on the STL implementation just to be able to interoperate with C++ exceptions. +Blocks Runtime Integration +-------------------------- + +libobjc2 ships with a runtime for the blocks C extension (i.e. closures/lambdas) and +will install compatibility headers for the libBlocksRuntime library that ships with +LLVM's compiler-rt or Swift's libdispatch. Alternatively, libobjc2 can be built without +the embedded blocks runtime and utilise the one from libdispatch instead. This can be +enabled by adding `-DEMBEDDED_BLOCKS_RUNTIME=OFF` to the `cmake` command. It's required +that your version of libBlocksRuntime provides the `Blocks_private.h` header. +(enabled with `-DINSTALL_PRIVATE_HEADERS=ON` when building libdispatch from source) + +Regardless of the chosen blocks runtime implementation, blocks will be fully integrated +into the Objective-C runtime. + Installation Location --------------------- diff --git a/NSBlocks.m b/NSBlocks.m index 31bf3d11..49eee9d9 100644 --- a/NSBlocks.m +++ b/NSBlocks.m @@ -6,13 +6,23 @@ #include "dtable.h" #include -OBJC_PUBLIC struct objc_class _NSConcreteGlobalBlock; -OBJC_PUBLIC struct objc_class _NSConcreteStackBlock; -OBJC_PUBLIC struct objc_class _NSConcreteMallocBlock; +#ifdef EMBEDDED_BLOCKS_RUNTIME +#define BLOCK_STORAGE OBJC_PUBLIC +#else +#define BLOCK_STORAGE extern +#endif + +BLOCK_STORAGE struct objc_class _NSConcreteGlobalBlock; +BLOCK_STORAGE struct objc_class _NSConcreteStackBlock; +BLOCK_STORAGE struct objc_class _NSConcreteMallocBlock; +BLOCK_STORAGE struct objc_class _NSConcreteAutoBlock; +BLOCK_STORAGE struct objc_class _NSConcreteFinalizingBlock; static struct objc_class _NSConcreteGlobalBlockMeta; static struct objc_class _NSConcreteStackBlockMeta; static struct objc_class _NSConcreteMallocBlockMeta; +static struct objc_class _NSConcreteAutoBlockMeta; +static struct objc_class _NSConcreteFinalizingBlockMeta; static struct objc_class _NSBlock; static struct objc_class _NSBlockMeta; @@ -31,6 +41,7 @@ static void createNSBlockSubclass(Class superclass, Class newClass, newClass->super_class = superclass; newClass->name = name; newClass->dtable = uninstalled_dtable; + newClass->info = objc_class_flag_is_block; LOCK_RUNTIME_FOR_SCOPE(); objc_load_class(newClass); @@ -49,8 +60,20 @@ BOOL objc_create_block_classes_as_subclasses_of(Class super) NEW_CLASS(&_NSBlock, _NSConcreteStackBlock); NEW_CLASS(&_NSBlock, _NSConcreteGlobalBlock); NEW_CLASS(&_NSBlock, _NSConcreteMallocBlock); + NEW_CLASS(&_NSBlock, _NSConcreteAutoBlock); + NEW_CLASS(&_NSBlock, _NSConcreteFinalizingBlock); // Global blocks never need refcount manipulation. objc_set_class_flag(&_NSConcreteGlobalBlock, objc_class_flag_permanent_instances); return YES; } + +PRIVATE void init_early_blocks(void) +{ + if (_NSBlock.super_class != NULL) { return; } + _NSConcreteStackBlock.info = objc_class_flag_is_block; + _NSConcreteGlobalBlock.info = objc_class_flag_is_block | objc_class_flag_permanent_instances; + _NSConcreteMallocBlock.info = objc_class_flag_is_block; + _NSConcreteAutoBlock.info = objc_class_flag_is_block; + _NSConcreteFinalizingBlock.info = objc_class_flag_is_block; +} \ No newline at end of file diff --git a/arc.mm b/arc.mm index d31ec5a3..ee3efbfe 100644 --- a/arc.mm +++ b/arc.mm @@ -10,14 +10,19 @@ #include #import "lock.h" #import "objc/runtime.h" +#ifdef EMBEDDED_BLOCKS_RUNTIME +#import "objc/blocks_private.h" #import "objc/blocks_runtime.h" +#else +#include +#include +#endif #import "nsobject.h" #import "class.h" #import "selector.h" #import "visibility.h" #import "objc/hooks.h" #import "objc/objc-arc.h" -#import "objc/blocks_runtime.h" #include "objc/message.h" /** @@ -75,12 +80,16 @@ static inline arc_tls_key_t arc_tls_key_create(arc_cleanup_function_t cleanupFun arc_tls_key_t ARCThreadKey; #endif +#ifndef HAVE_BLOCK_USE_RR2 extern "C" { extern struct objc_class _NSConcreteMallocBlock; extern struct objc_class _NSConcreteStackBlock; extern struct objc_class _NSConcreteGlobalBlock; + extern struct objc_class _NSConcreteAutoBlock; + extern struct objc_class _NSConcreteFinalizingBlock; } +#endif @interface NSAutoreleasePool + (Class)class; @@ -315,8 +324,7 @@ static inline id retain(id obj, BOOL isWeak) { if (isPersistentObject(obj)) { return obj; } Class cls = obj->isa; - if ((Class)&_NSConcreteMallocBlock == cls || - (Class)&_NSConcreteStackBlock == cls) + if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block))) { return Block_copy(obj); } @@ -376,15 +384,15 @@ static inline void release(id obj) { if (isPersistentObject(obj)) { return; } Class cls = obj->isa; - if (cls == static_cast(&_NSConcreteMallocBlock)) + if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block))) { + if (cls == static_cast(&_NSConcreteStackBlock)) + { + return; + } _Block_release(obj); return; } - if (cls == static_cast(&_NSConcreteStackBlock)) - { - return; - } if (objc_test_class_flag(cls, objc_class_flag_fast_arc)) { objc_release_fast_np(obj); @@ -702,12 +710,24 @@ void deallocate(T* p, std::size_t) } +#ifdef HAVE_BLOCK_USE_RR2 +static const struct Block_callbacks_RR blocks_runtime_callbacks = { + sizeof(Block_callbacks_RR), + (void (*)(const void*))objc_retain, + (void (*)(const void*))objc_release, + (void (*)(const void*))objc_delete_weak_refs + }; +#endif + PRIVATE extern "C" void init_arc(void) { INIT_LOCK(weakRefLock); #ifdef arc_tls_store ARCThreadKey = arc_tls_key_create((arc_cleanup_function_t)cleanupPools); #endif +#ifdef HAVE_BLOCK_USE_RR2 + _Block_use_RR2(&blocks_runtime_callbacks); +#endif } /** @@ -843,9 +863,18 @@ static BOOL setObjectHasWeakRefs(id obj) *addr = obj; return obj; } - // If the object is being deallocated return nil. - if (object_getRetainCount_np(obj) == 0) + Class cls = classForObject(obj); + if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block))) { + // Check whether the block is being deallocated and return nil if so + if (_Block_isDeallocating(obj)) { + *addr = nil; + return nil; + } + } + else if (object_getRetainCount_np(obj) == 0) + { + // If the object is being deallocated return nil. *addr = nil; return nil; } @@ -916,19 +945,31 @@ static BOOL setObjectHasWeakRefs(id obj) return nil; } Class cls = classForObject(obj); - if (static_cast(&_NSConcreteMallocBlock) == cls) + if (objc_test_class_flag(cls, objc_class_flag_permanent_instances)) { - obj = static_cast(block_load_weak(obj)); + return obj; } - else if (objc_test_class_flag(cls, objc_class_flag_permanent_instances)) + else if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block))) { - return obj; + obj = static_cast(block_load_weak(obj)); + if (obj == nil) + { + return nil; + } + // This is a defeasible retain operation that protects against another thread concurrently + // starting to deallocate the block. + if (_Block_tryRetain(obj)) + { + return obj; + } + return nil; + } else if (!objc_test_class_flag(cls, objc_class_flag_fast_arc)) { obj = _objc_weak_load(obj); } - // block_load_weak() or _objc_weak_load() can return nil + // _objc_weak_load() can return nil if (obj == nil) { return nil; } return retain(obj, YES); } diff --git a/blocks_runtime.m b/blocks_runtime.m index bf61bdbf..91ab85d9 100644 --- a/blocks_runtime.m +++ b/blocks_runtime.m @@ -24,6 +24,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ #import "objc/blocks_runtime.h" +#include "objc/blocks_private.h" #import "objc/runtime.h" #import "objc/objc-arc.h" #include "blocks_runtime.h" @@ -60,10 +61,6 @@ OBJC_PUBLIC bool _Block_has_signature(void *b) } return block->descriptor->encoding; } -OBJC_PUBLIC const char *block_getType_np(const void *b) -{ - return _Block_signature((void*)b); -} static int increment24(int *ref) { @@ -296,8 +293,32 @@ OBJC_PUBLIC void _Block_release(const void *src) } } -PRIVATE void* block_load_weak(void *block) +OBJC_PUBLIC bool _Block_isDeallocating(const void* arg) { - struct Block_layout *self = block; - return (self->reserved) > 0 ? block : 0; + struct Block_layout *block = (struct Block_layout*)arg; + int *refCountPtr = &((struct Block_layout*)arg)->reserved; + int refCount = __sync_fetch_and_add(refCountPtr, 0); + return refCount == 0; } + +OBJC_PUBLIC bool _Block_tryRetain(const void* arg) +{ + /* This is used by the weak reference management in ARC. The implementation + * follows the reasoning of `retain_fast()` in arc.mm: We want to abandon the + * retain operation if another thread has started deallocating the object between + * loading the weak pointer and executing the retain operation. + */ + struct Block_layout *block = (struct Block_layout*)arg; + int *refCountPtr = &block->reserved; + int refCountVal = __sync_fetch_and_add(refCountPtr, 0); + int newVal = refCountVal; + do { + refCountVal = newVal; + if (refCountVal <= 0) + { + return false; + } + newVal = __sync_val_compare_and_swap(refCountPtr, refCountVal, newVal + 1); + } while (newVal != refCountVal); + return true; +} \ No newline at end of file diff --git a/blocks_runtime_np.m b/blocks_runtime_np.m new file mode 100644 index 00000000..789539bc --- /dev/null +++ b/blocks_runtime_np.m @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2009 Remy Demarest + * Portions Copyright (c) 2009 David Chisnall + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifdef EMBEDDED_BLOCKS_RUNTIME +#import "objc/blocks_runtime.h" +#include "blocks_runtime.h" +#else +#import +#import +#endif +#include "visibility.h" + + +OBJC_PUBLIC const char *block_getType_np(const void *b) +{ + return _Block_signature((void*)b); +} + +/** + * Returns the block pointer, or NULL if the block is already + * being deallocated. The implementation does not employ atomic + * operations, so this function must only be called by the ARC + * subsystem after obtaining the weak-reference lock. + */ +PRIVATE void* block_load_weak(void *block) +{ + struct Block_layout *self = block; + #ifdef EMBEDDED_BLOCKS_RUNTIME + return (self->reserved) > 0 ? block : 0; + #else + return (self->flags) & BLOCK_REFCOUNT_MASK ? block : 0; + #endif +} diff --git a/class.h b/class.h index 7a5b2404..0910d0b7 100644 --- a/class.h +++ b/class.h @@ -366,6 +366,11 @@ enum objc_class_flags * On a class, guarantees that `+init` is trivial. */ objc_class_flag_fast_alloc_init = (1<<15), + /** + * The class is a block class. Reference count management must be done by + * the underlying blocks runtime. + */ + objc_class_flag_is_block = (1 << 16), }; /** diff --git a/loader.c b/loader.c index 00464921..a49100a9 100644 --- a/loader.c +++ b/loader.c @@ -27,6 +27,7 @@ void init_gc(void); void init_protocol_table(void); void init_selector_tables(void); void init_trampolines(void); +void init_early_blocks(void); void objc_send_load_message(Class class); void log_selector_memory_usage(void); @@ -76,6 +77,7 @@ static void init_runtime(void) init_protocol_table(); init_class_tables(); init_alias_table(); + init_early_blocks(); init_arc(); init_trampolines(); first_run = NO; diff --git a/objc/blocks_private.h b/objc/blocks_private.h index 60a1d590..8fd8ab8d 100644 --- a/objc/blocks_private.h +++ b/objc/blocks_private.h @@ -3,7 +3,14 @@ #if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) #pragma clang system_header #endif +#ifdef __cplusplus +#define BLOCKS_EXPORT extern "C" +#else +#define BLOCKS_EXPORT extern +#endif +#include +#include "Availability.h" /* * This header file exposes some implementation details of the blocks runtime @@ -38,6 +45,23 @@ struct Block_descriptor const char *encoding; }; +/** + * Checks whether the block is currently being deallocated. + * + * Used by ARC weak reference management. Only call this after the weak + * reference lock is acquired. + */ +OBJC_PUBLIC BLOCKS_EXPORT bool _Block_isDeallocating(const void *aBlock); +/** + * Atomically increments the reference count of the block. + * Returns true if the block was retained, and false if it is already + * being deallocated. + * + * Used by ARC weak reference management. Only call this after the weak + * reference lock is acquired. + */ +OBJC_PUBLIC BLOCKS_EXPORT bool _Block_tryRetain(const void *aBlock); + // Helper structure struct Block_layout {