feat(blocks runtime): Enable using libBlocksRuntime instead of the embedded runtime

main
Niels Grewe 2 years ago committed by David Chisnall
parent dc031d2741
commit 2855d17714

@ -1,7 +1,7 @@
libcxxrt_freebsd_task: libcxxrt_freebsd_task:
matrix: matrix:
- freebsd_instance: - freebsd_instance:
image_family: freebsd-13-2 image_family: freebsd-13-3
- freebsd_instance: - freebsd_instance:
image_family: freebsd-15-0-snap image_family: freebsd-15-0-snap
- freebsd_instance: - freebsd_instance:

@ -20,6 +20,7 @@ jobs:
# Build each combination of OS and release/debug variants # Build each combination of OS and release/debug variants
os: [ "ubuntu-22.04", "ubuntu-20.04" ] os: [ "ubuntu-22.04", "ubuntu-20.04" ]
build-type: [ Release, Debug ] build-type: [ Release, Debug ]
blocks-runtime: [ "EMBEDDED", "swift-5.10-RELEASE" ]
cxxlib: [ "libc++", "libstdc++" ] cxxlib: [ "libc++", "libstdc++" ]
llvm-version: [10, 11, 12, 13, 14, 15] 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 # 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 # Don't abort runners if a single one fails
fail-fast: false fail-fast: false
runs-on: ${{ matrix.os }} 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: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install dependencies - 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 libc++-${{matrix.llvm-version}}-dev libc++abi-${{matrix.llvm-version}}-dev
sudo apt install libunwind-${{matrix.llvm-version}}-dev || true sudo apt install libunwind-${{matrix.llvm-version}}-dev || true
fi 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 - name: Configure CMake
run: | run: |
export LDFLAGS=-L/usr/lib/llvm-${{ matrix.llvm-version }}/lib/ 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/ 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 # Build with a nice ninja status line
- name: Build - name: Build
working-directory: ${{github.workspace}}/build working-directory: ${{github.workspace}}/build

@ -28,6 +28,7 @@ endif ()
INCLUDE (CheckCXXSourceCompiles) INCLUDE (CheckCXXSourceCompiles)
INCLUDE (FetchContent) INCLUDE (FetchContent)
INCLUDE (CheckSymbolExists)
set(libobjc_VERSION 4.6) set(libobjc_VERSION 4.6)
@ -58,7 +59,7 @@ set(libobjc_OBJC_SRCS
NSBlocks.m NSBlocks.m
Protocol2.m Protocol2.m
associate.m associate.m
blocks_runtime.m blocks_runtime_np.m
properties.m) properties.m)
set(libobjc_C_SRCS set(libobjc_C_SRCS
alias_table.c alias_table.c
@ -83,8 +84,6 @@ set(libobjc_HDRS
objc/Availability.h objc/Availability.h
objc/Object.h objc/Object.h
objc/Protocol.h objc/Protocol.h
objc/blocks_private.h
objc/blocks_runtime.h
objc/capabilities.h objc/capabilities.h
objc/developer.h objc/developer.h
objc/encoding.h objc/encoding.h
@ -101,10 +100,7 @@ set(libobjc_HDRS
objc/runtime-deprecated.h objc/runtime-deprecated.h
objc/runtime.h objc/runtime.h
objc/slot.h) objc/slot.h)
set(libBlocksRuntime_COMPATIBILITY_HDRS
Block.h
Block_private.h
)
set(libobjc_CXX_SRCS set(libobjc_CXX_SRCS
selector_table.cc selector_table.cc
) )
@ -143,7 +139,7 @@ option(DEBUG_ARC_COMPAT
"Log warnings for classes that don't hit ARC fast paths" OFF) "Log warnings for classes that don't hit ARC fast paths" OFF)
option(ENABLE_OBJCXX "Enable support for Objective-C++" ON) option(ENABLE_OBJCXX "Enable support for Objective-C++" ON)
option(TESTS "Enable building the tests") 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 # For release builds, we disable spamming the terminal with warnings about
# selector type mismatches # selector type mismatches
@ -230,6 +226,31 @@ else ()
find_library(M_LIBRARY m) find_library(M_LIBRARY m)
endif () 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}) add_library(objc SHARED ${libobjc_C_SRCS} ${libobjc_ASM_SRCS} ${libobjc_OBJC_SRCS} ${libobjc_OBJCXX_SRCS} ${libobjc_ASM_OBJS})
target_compile_options(objc PRIVATE "$<$<OR:$<COMPILE_LANGUAGE:OBJC>,$<COMPILE_LANGUAGE:OBJCXX>>:-Wno-deprecated-objc-isa-usage;-Wno-objc-root-class;-fobjc-runtime=gnustep-2.0>$<$<COMPILE_LANGUAGE:C>:-Xclang;-fexceptions>") target_compile_options(objc PRIVATE "$<$<OR:$<COMPILE_LANGUAGE:OBJC>,$<COMPILE_LANGUAGE:OBJCXX>>:-Wno-deprecated-objc-isa-usage;-Wno-objc-root-class;-fobjc-runtime=gnustep-2.0>$<$<COMPILE_LANGUAGE:C>:-Xclang;-fexceptions>")
@ -268,6 +289,10 @@ if (M_LIBRARY)
target_link_libraries(objc PUBLIC ${M_LIBRARY}) target_link_libraries(objc PUBLIC ${M_LIBRARY})
endif () endif ()
if (BLOCKS_RUNTIME_LIBRARY)
target_link_libraries(objc PUBLIC ${BLOCKS_RUNTIME_LIBRARY})
endif ()
# Make weak symbols work on OS X # Make weak symbols work on OS X
if (APPLE) if (APPLE)
set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS
@ -340,8 +365,12 @@ install(TARGETS ${INSTALL_TARGETS}
install(FILES ${libobjc_HDRS} install(FILES ${libobjc_HDRS}
DESTINATION "${HEADER_INSTALL_PATH}/${INCLUDE_DIRECTORY}") 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 set(CPACK_GENERATOR TGZ CACHE STRING
"Installer types to generate. Sensible options include TGZ, RPM and DEB") "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) if (M_LIBRARY)
list(APPEND PC_LIBS_PRIVATE ${M_LIBRARY}) list(APPEND PC_LIBS_PRIVATE ${M_LIBRARY})
endif () endif ()
if (BLOCKS_RUNTIME_LIBRARY)
list(APPEND PC_LIBS_PRIVATE ${BLOCKS_RUNTIME_LIBRARY})
endif ()
list(REMOVE_DUPLICATES PC_LIBS_PRIVATE) list(REMOVE_DUPLICATES PC_LIBS_PRIVATE)
string(REPLACE ";" " -l" PC_LIBS_PRIVATE "${PC_LIBS_PRIVATE}") string(REPLACE ";" " -l" PC_LIBS_PRIVATE "${PC_LIBS_PRIVATE}")
set(PC_LIBS_PRIVATE "Libs.private: -l${PC_LIBS_PRIVATE}") set(PC_LIBS_PRIVATE "Libs.private: -l${PC_LIBS_PRIVATE}")

@ -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 for the Objective-C runtime to depend on the STL implementation just to be able
to interoperate with C++ exceptions. 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 Installation Location
--------------------- ---------------------

@ -6,13 +6,23 @@
#include "dtable.h" #include "dtable.h"
#include <assert.h> #include <assert.h>
OBJC_PUBLIC struct objc_class _NSConcreteGlobalBlock; #ifdef EMBEDDED_BLOCKS_RUNTIME
OBJC_PUBLIC struct objc_class _NSConcreteStackBlock; #define BLOCK_STORAGE OBJC_PUBLIC
OBJC_PUBLIC struct objc_class _NSConcreteMallocBlock; #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 _NSConcreteGlobalBlockMeta;
static struct objc_class _NSConcreteStackBlockMeta; static struct objc_class _NSConcreteStackBlockMeta;
static struct objc_class _NSConcreteMallocBlockMeta; static struct objc_class _NSConcreteMallocBlockMeta;
static struct objc_class _NSConcreteAutoBlockMeta;
static struct objc_class _NSConcreteFinalizingBlockMeta;
static struct objc_class _NSBlock; static struct objc_class _NSBlock;
static struct objc_class _NSBlockMeta; static struct objc_class _NSBlockMeta;
@ -31,6 +41,7 @@ static void createNSBlockSubclass(Class superclass, Class newClass,
newClass->super_class = superclass; newClass->super_class = superclass;
newClass->name = name; newClass->name = name;
newClass->dtable = uninstalled_dtable; newClass->dtable = uninstalled_dtable;
newClass->info = objc_class_flag_is_block;
LOCK_RUNTIME_FOR_SCOPE(); LOCK_RUNTIME_FOR_SCOPE();
objc_load_class(newClass); 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, _NSConcreteStackBlock);
NEW_CLASS(&_NSBlock, _NSConcreteGlobalBlock); NEW_CLASS(&_NSBlock, _NSConcreteGlobalBlock);
NEW_CLASS(&_NSBlock, _NSConcreteMallocBlock); NEW_CLASS(&_NSBlock, _NSConcreteMallocBlock);
NEW_CLASS(&_NSBlock, _NSConcreteAutoBlock);
NEW_CLASS(&_NSBlock, _NSConcreteFinalizingBlock);
// Global blocks never need refcount manipulation. // Global blocks never need refcount manipulation.
objc_set_class_flag(&_NSConcreteGlobalBlock, objc_set_class_flag(&_NSConcreteGlobalBlock,
objc_class_flag_permanent_instances); objc_class_flag_permanent_instances);
return YES; 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;
}

@ -10,14 +10,19 @@
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
#import "lock.h" #import "lock.h"
#import "objc/runtime.h" #import "objc/runtime.h"
#ifdef EMBEDDED_BLOCKS_RUNTIME
#import "objc/blocks_private.h"
#import "objc/blocks_runtime.h" #import "objc/blocks_runtime.h"
#else
#include <Block.h>
#include <Block_private.h>
#endif
#import "nsobject.h" #import "nsobject.h"
#import "class.h" #import "class.h"
#import "selector.h" #import "selector.h"
#import "visibility.h" #import "visibility.h"
#import "objc/hooks.h" #import "objc/hooks.h"
#import "objc/objc-arc.h" #import "objc/objc-arc.h"
#import "objc/blocks_runtime.h"
#include "objc/message.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; arc_tls_key_t ARCThreadKey;
#endif #endif
#ifndef HAVE_BLOCK_USE_RR2
extern "C" extern "C"
{ {
extern struct objc_class _NSConcreteMallocBlock; extern struct objc_class _NSConcreteMallocBlock;
extern struct objc_class _NSConcreteStackBlock; extern struct objc_class _NSConcreteStackBlock;
extern struct objc_class _NSConcreteGlobalBlock; extern struct objc_class _NSConcreteGlobalBlock;
extern struct objc_class _NSConcreteAutoBlock;
extern struct objc_class _NSConcreteFinalizingBlock;
} }
#endif
@interface NSAutoreleasePool @interface NSAutoreleasePool
+ (Class)class; + (Class)class;
@ -315,8 +324,7 @@ static inline id retain(id obj, BOOL isWeak)
{ {
if (isPersistentObject(obj)) { return obj; } if (isPersistentObject(obj)) { return obj; }
Class cls = obj->isa; Class cls = obj->isa;
if ((Class)&_NSConcreteMallocBlock == cls || if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block)))
(Class)&_NSConcreteStackBlock == cls)
{ {
return Block_copy(obj); return Block_copy(obj);
} }
@ -376,15 +384,15 @@ static inline void release(id obj)
{ {
if (isPersistentObject(obj)) { return; } if (isPersistentObject(obj)) { return; }
Class cls = obj->isa; Class cls = obj->isa;
if (cls == static_cast<Class>(&_NSConcreteMallocBlock)) if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block)))
{ {
if (cls == static_cast<void*>(&_NSConcreteStackBlock))
{
return;
}
_Block_release(obj); _Block_release(obj);
return; return;
} }
if (cls == static_cast<Class>(&_NSConcreteStackBlock))
{
return;
}
if (objc_test_class_flag(cls, objc_class_flag_fast_arc)) if (objc_test_class_flag(cls, objc_class_flag_fast_arc))
{ {
objc_release_fast_np(obj); objc_release_fast_np(obj);
@ -702,12 +710,24 @@ mutex_t weakRefLock;
} }
#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) PRIVATE extern "C" void init_arc(void)
{ {
INIT_LOCK(weakRefLock); INIT_LOCK(weakRefLock);
#ifdef arc_tls_store #ifdef arc_tls_store
ARCThreadKey = arc_tls_key_create((arc_cleanup_function_t)cleanupPools); ARCThreadKey = arc_tls_key_create((arc_cleanup_function_t)cleanupPools);
#endif #endif
#ifdef HAVE_BLOCK_USE_RR2
_Block_use_RR2(&blocks_runtime_callbacks);
#endif
} }
/** /**
@ -843,9 +863,18 @@ extern "C" OBJC_PUBLIC id objc_storeWeak(id *addr, id obj)
*addr = obj; *addr = obj;
return obj; return obj;
} }
// If the object is being deallocated return nil. Class cls = classForObject(obj);
if (object_getRetainCount_np(obj) == 0) 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; *addr = nil;
return nil; return nil;
} }
@ -916,19 +945,31 @@ extern "C" OBJC_PUBLIC id objc_loadWeakRetained(id* addr)
return nil; return nil;
} }
Class cls = classForObject(obj); Class cls = classForObject(obj);
if (static_cast<Class>(&_NSConcreteMallocBlock) == cls) if (objc_test_class_flag(cls, objc_class_flag_permanent_instances))
{ {
obj = static_cast<id>(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<id>(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)) else if (!objc_test_class_flag(cls, objc_class_flag_fast_arc))
{ {
obj = _objc_weak_load(obj); 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; } if (obj == nil) { return nil; }
return retain(obj, YES); return retain(obj, YES);
} }

@ -24,6 +24,7 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
#import "objc/blocks_runtime.h" #import "objc/blocks_runtime.h"
#include "objc/blocks_private.h"
#import "objc/runtime.h" #import "objc/runtime.h"
#import "objc/objc-arc.h" #import "objc/objc-arc.h"
#include "blocks_runtime.h" #include "blocks_runtime.h"
@ -60,10 +61,6 @@ OBJC_PUBLIC const char * _Block_signature(void *b)
} }
return block->descriptor->encoding; return block->descriptor->encoding;
} }
OBJC_PUBLIC const char *block_getType_np(const void *b)
{
return _Block_signature((void*)b);
}
static int increment24(int *ref) 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; struct Block_layout *block = (struct Block_layout*)arg;
return (self->reserved) > 0 ? block : 0; 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;
}

@ -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 <Block.h>
#import <Block_private.h>
#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
}

@ -366,6 +366,11 @@ enum objc_class_flags
* On a class, guarantees that `+init` is trivial. * On a class, guarantees that `+init` is trivial.
*/ */
objc_class_flag_fast_alloc_init = (1<<15), 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),
}; };
/** /**

@ -27,6 +27,7 @@ void init_gc(void);
void init_protocol_table(void); void init_protocol_table(void);
void init_selector_tables(void); void init_selector_tables(void);
void init_trampolines(void); void init_trampolines(void);
void init_early_blocks(void);
void objc_send_load_message(Class class); void objc_send_load_message(Class class);
void log_selector_memory_usage(void); void log_selector_memory_usage(void);
@ -76,6 +77,7 @@ static void init_runtime(void)
init_protocol_table(); init_protocol_table();
init_class_tables(); init_class_tables();
init_alias_table(); init_alias_table();
init_early_blocks();
init_arc(); init_arc();
init_trampolines(); init_trampolines();
first_run = NO; first_run = NO;

@ -3,7 +3,14 @@
#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) #if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__)
#pragma clang system_header #pragma clang system_header
#endif #endif
#ifdef __cplusplus
#define BLOCKS_EXPORT extern "C"
#else
#define BLOCKS_EXPORT extern
#endif
#include <stdbool.h>
#include "Availability.h"
/* /*
* This header file exposes some implementation details of the blocks runtime * This header file exposes some implementation details of the blocks runtime
@ -38,6 +45,23 @@ struct Block_descriptor
const char *encoding; 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 // Helper structure
struct Block_layout struct Block_layout
{ {

Loading…
Cancel
Save