diff --git a/Test/AssociatedObject.m b/Test/AssociatedObject.m index cd9464e..e41da6f 100644 --- a/Test/AssociatedObject.m +++ b/Test/AssociatedObject.m @@ -19,8 +19,21 @@ int main(void) @autoreleasepool { Associated *object = [Associated new]; Test *holder = [[Test new] autorelease]; - objc_setAssociatedObject(object, &objc_setAssociatedObjectKey, holder, OBJC_ASSOCIATION_RETAIN); + objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, object, OBJC_ASSOCIATION_RETAIN); [object release]; - } + assert(!deallocCalled); + } + // dealloc should be called when holder is released during pool drain + assert(deallocCalled); + + deallocCalled = NO; + + Associated *object = [Associated new]; + Test *holder = [Test new]; + objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, object, OBJC_ASSOCIATION_RETAIN); + [object release]; // commuted into associated object storage + objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, nil, OBJC_ASSOCIATION_ASSIGN); + [holder release]; + assert(deallocCalled); } diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index a1332d7..4165d29 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -33,6 +33,7 @@ set(TESTS MethodArguments.m zeroSizedIVar.m exchange.m + hash_table_delete.c setSuperclass.m ) diff --git a/Test/hash_table_delete.c b/Test/hash_table_delete.c new file mode 100644 index 0000000..ad7af89 --- /dev/null +++ b/Test/hash_table_delete.c @@ -0,0 +1,64 @@ +#include +#include + +struct test_struct { + uintptr_t key; +}; + +struct test_struct null_placeholder = {0}; + +static int test_compare(const void *key, const struct test_struct test) { + return (uintptr_t)key == test.key; +} + +// force hash collisions +static uint32_t test_key_hash(const void *ptr) { + return ((uint32_t)(uintptr_t)ptr)>>2; +} + +static uint32_t test_value_hash(const struct test_struct test) { + return test.key>>2; +} + +static int test_is_null(const struct test_struct test) { + return test.key == 0; +} + +#define MAP_TABLE_NAME test +#define MAP_TABLE_COMPARE_FUNCTION test_compare +#define MAP_TABLE_VALUE_TYPE struct test_struct +#define MAP_TABLE_VALUE_NULL test_is_null +#define MAP_TABLE_HASH_KEY test_key_hash +#define MAP_TABLE_HASH_VALUE test_value_hash +#define MAP_TABLE_VALUE_PLACEHOLDER null_placeholder +#define MAP_TABLE_ACCESS_BY_REFERENCE 1 +#define MAP_TABLE_SINGLE_THREAD 1 +#define MAP_TABLE_NO_LOCK 1 + +#include "../hash_table.h" + +int main(int argc, char *argv[]) +{ + test_table *testTable; + test_initialize(&testTable, 128); + + struct test_struct one, two, three; + one.key = 1; + two.key = 2; + three.key = 3; + + test_insert(testTable, one); + test_insert(testTable, two); + test_insert(testTable, three); + + test_remove(testTable, (void*)2); + test_remove(testTable, (void*)1); + + struct test_struct *pthree = test_table_get(testTable, (void*)3); + if (!pthree) { + fprintf(stderr, "failed to find value (key=3) inserted into hash table\n"); + return 1; + } + + return 0; +} diff --git a/associate.m b/associate.m index 37b9c26..d3ab8a9 100644 --- a/associate.m +++ b/associate.m @@ -166,12 +166,17 @@ static void setReference(struct reference_list *list, lock = lock_for_pointer(r); lock_spinlock(lock); } - r->policy = policy; - id old = r->object; - r->object = obj; - if (OBJC_ASSOCIATION_ASSIGN != r->policy) + @try { - objc_release(old); + if (OBJC_ASSOCIATION_ASSIGN != r->policy) + { + objc_release(r->object); + } + } + @finally + { + r->policy = policy; + r->object = obj; } if (needLock) { diff --git a/class_table.c b/class_table.c index fa9a876..ec81255 100644 --- a/class_table.c +++ b/class_table.c @@ -258,10 +258,10 @@ PRIVATE BOOL objc_resolve_class(Class cls) PRIVATE void objc_resolve_class_links(void) { LOCK_RUNTIME_FOR_SCOPE(); - Class class = unresolved_class_list; BOOL resolvedClass; do { + Class class = unresolved_class_list; resolvedClass = NO; while ((Nil != class)) { diff --git a/hash_table.h b/hash_table.h index aaae772..efa5cf6 100644 --- a/hash_table.h +++ b/hash_table.h @@ -392,6 +392,22 @@ static void PREFIX(_remove)(PREFIX(_table) *table, void *key) MAP_LOCK(); PREFIX(_table_cell) cell = PREFIX(_table_get_cell)(table, key); if (NULL == cell) { return; } + + uint32_t hash = MAP_TABLE_HASH_KEY(key); + PREFIX(_table_cell) baseCell = PREFIX(_table_lookup)(table, hash); + if (baseCell && baseCell <= cell && cell - baseCell <= 32) + { + uint32_t jump = 1 << (cell - baseCell - 1); + if ((baseCell->secondMaps & jump)) + { + // If we are removing a cell stored adjacent to its base due to hash + // collision, we have to clear the base cell's neighbor bit. + // Otherwise, a later remove can move the new placeholder value to the head + // which will cause further chained lookups to fail. + baseCell->secondMaps &= ~jump; + } + } + // If the cell contains a value, set it to the placeholder and shuffle up // everything if (0 == cell->secondMaps) diff --git a/objc/encoding.h b/objc/encoding.h index 4c4ff53..ceccd59 100644 --- a/objc/encoding.h +++ b/objc/encoding.h @@ -5,6 +5,10 @@ #ifndef __LIBOBJC_ENCODING_H_INCLUDED__ #define __LIBOBJC_ENCODING_H_INCLUDED__ +#ifdef __cplusplus +extern "C" { +#endif + const char *objc_skip_type_qualifiers (const char *type); const char *objc_skip_typespec(const char *type); @@ -71,4 +75,8 @@ void objc_layout_structure_get_info (struct objc_struct_layout *layout, #define _F_ONEWAY 0x10 #define _F_GCINVISIBLE 0x20 +#ifdef __cplusplus +} +#endif + #endif // __LIBOBJC_ENCODING_H_INCLUDED__ diff --git a/objc/hooks.h b/objc/hooks.h index 42c67ed..8b7e045 100644 --- a/objc/hooks.h +++ b/objc/hooks.h @@ -2,6 +2,10 @@ #pragma clang system_header #endif +#ifdef __cplusplus +extern "C" { +#endif + /** * This file includes all of the hooks that can be used to alter the behaviour * of the runtime. @@ -102,4 +106,6 @@ typedef IMP (*objc_tracing_hook)(id, SEL, IMP, int, void*); */ int objc_registerTracingHook(SEL, objc_tracing_hook); - +#ifdef __cplusplus +} +#endif diff --git a/objc/objc-arc.h b/objc/objc-arc.h index 32ecb7a..14f7d00 100644 --- a/objc/objc-arc.h +++ b/objc/objc-arc.h @@ -4,6 +4,11 @@ #ifndef __OBJC_ARC_INCLUDED__ #define __OBJC_ARC_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + /** * Autoreleases the argument. Equivalent to [obj autorelease]. */ @@ -138,5 +143,10 @@ unsigned long objc_arc_autorelease_count_np(void); * this thread. */ unsigned long objc_arc_autorelease_count_for_object_np(id); + +#ifdef __cplusplus +} +#endif + #endif // __OBJC_ARC_INCLUDED__ diff --git a/objc/runtime.h b/objc/runtime.h index 3092f72..12b49d5 100644 --- a/objc/runtime.h +++ b/objc/runtime.h @@ -198,7 +198,11 @@ typedef struct #ifdef __GNUC # define _OBJC_NULL_PTR __null #elif defined(__cplusplus) -# define _OBJC_NULL_PTR 0 +# if __has_feature(cxx_nullptr) +# define _OBJC_NULL_PTR nullptr +# else +# define _OBJC_NULL_PTR 0 +# endif #else # define _OBJC_NULL_PTR ((void*)0) #endif diff --git a/visibility.h b/visibility.h index 86c379b..d98d052 100644 --- a/visibility.h +++ b/visibility.h @@ -17,7 +17,7 @@ # define ASSERT(x) assert(x) #else # define UNREACHABLE(x) __builtin_unreachable() -# define ASSERT(x) do { if (x) __builtin_unreachable(); } while(0) +# define ASSERT(x) do { if (!(x)) __builtin_unreachable(); } while(0) #endif #define LIKELY(x) __builtin_expect(x, 1)