Skip to content

Commit

Permalink
Implemented support for __weak with ARC.
Browse files Browse the repository at this point in the history
ARC functions are all now exposed in a header, but not yet documented.  See the ARC ABI spec for now:

http://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime
  • Loading branch information
theraven committed Jun 29, 2011
1 parent 065531b commit bc87ed2
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 4 deletions.
1 change: 1 addition & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ ${LIBOBJC}_HEADER_FILES = \
slot.h\
objc.h\
objc-api.h\
objc-arc.h\
objc-auto.h\
toydispatch.h
endif
Expand Down
182 changes: 182 additions & 0 deletions arc.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#import "objc/blocks_runtime.h"
#import "nsobject.h"
#import "selector.h"
#import "visibility.h"
#import "objc/hooks.h"
#import "objc/objc-arc.h"

static Class AutoreleasePool;
static IMP NewAutoreleasePool;
Expand Down Expand Up @@ -80,3 +83,182 @@ id objc_storeStrong(id *object, id value)
[oldValue release];
return value;
}

////////////////////////////////////////////////////////////////////////////////
// Weak references
////////////////////////////////////////////////////////////////////////////////

typedef struct objc_weak_ref
{
id obj;
id *ref[4];
struct objc_weak_ref *next;
} WeakRef;


static int weak_ref_compare(const id obj, const WeakRef weak_ref)
{
return obj == weak_ref.obj;
}

static uint32_t ptr_hash(const void *ptr)
{
// Bit-rotate right 4, since the lowest few bits in an object pointer will
// always be 0, which is not so useful for a hash value
return ((uintptr_t)ptr >> 4) | ((uintptr_t)ptr << ((sizeof(id) * 8) - 4));
}
static int weak_ref_hash(const WeakRef weak_ref)
{
return ptr_hash(weak_ref.obj);
}
static int weak_ref_is_null(const WeakRef weak_ref)
{
return weak_ref.obj == NULL;
}
const static WeakRef NullWeakRef;
#define MAP_TABLE_NAME weak_ref
#define MAP_TABLE_COMPARE_FUNCTION weak_ref_compare
#define MAP_TABLE_HASH_KEY ptr_hash
#define MAP_TABLE_HASH_VALUE weak_ref_hash
#define MAP_TABLE_HASH_VALUE weak_ref_hash
#define MAP_TABLE_VALUE_TYPE struct objc_weak_ref
#define MAP_TABLE_VALUE_NULL weak_ref_is_null
#define MAP_TABLE_VALUE_PLACEHOLDER NullWeakRef
#define MAP_TABLE_ACCESS_BY_REFERENCE 1
#define MAP_TABLE_SINGLE_THREAD 1
#define MAP_TABLE_NO_LOCK 1

#include "hash_table.h"

static weak_ref_table *weakRefs;
mutex_t weakRefLock;

PRIVATE void init_arc(void)
{
weak_ref_initialize(&weakRefs, 128);
INIT_LOCK(weakRefLock);
}

id objc_storeWeak(id *addr, id obj)
{
id old = *addr;
LOCK_FOR_SCOPE(&weakRefLock);
WeakRef *oldRef = weak_ref_table_get(weakRefs, old);
while (NULL != oldRef)
{
for (int i=0 ; i<4 ; i++)
{
if (oldRef->ref[i] == addr)
{
oldRef->ref[i] = 0;
oldRef = 0;
break;
}
}
}
if (nil == obj)
{
*addr = obj;
return nil;
}
obj = _objc_weak_load(obj);
if (nil != obj)
{
WeakRef *ref = weak_ref_table_get(weakRefs, obj);
while (NULL != ref)
{
for (int i=0 ; i<4 ; i++)
{
if (0 == ref->ref[i])
{
ref->ref[i] = addr;
return obj;
}
}
if (ref->next == NULL)
{
break;
}
}
if (NULL != ref)
{
ref->next = calloc(sizeof(WeakRef), 1);
ref->next->ref[0] = addr;
}
else
{
WeakRef newRef = {0};
newRef.obj = obj;
newRef.ref[0] = addr;
weak_ref_insert(weakRefs, newRef);
}
}
return obj;
}

static void zeroRefs(WeakRef *ref, BOOL shouldFree)
{
if (NULL != ref->next)
{
zeroRefs(ref->next, YES);
}
for (int i=0 ; i<4 ; i++)
{
if (0 != ref->ref[i])
{
*ref->ref[i] = 0;
}
}
if (shouldFree)
{
free(ref);
}
else
{
memset(ref, 0, sizeof(WeakRef));
}
}

void objc_delete_weak_refs(id obj)
{
LOCK_FOR_SCOPE(&weakRefLock);
WeakRef *oldRef = weak_ref_table_get(weakRefs, obj);
if (0 != oldRef)
{
zeroRefs(oldRef, NO);
}
}

id objc_loadWeakRetained(id* obj)
{
LOCK_FOR_SCOPE(&weakRefLock);
return objc_retain(*obj);
}

id objc_loadWeak(id* object)
{
return objc_autorelease(objc_loadWeakRetained(object));
}

void objc_copyWeak(id *dest, id *src)
{
objc_release(objc_initWeak(dest, objc_loadWeakRetained(src)));
}

void objc_moveWeak(id *dest, id *src)
{
// FIXME: src can be zero'd here, removing the relationship between the
// object and the pointer, which can be cheaper.
objc_moveWeak(dest, src);
}

void objc_destroyWeak(id* obj)
{
objc_storeWeak(obj, nil);
}

id objc_initWeak(id *object, id value)
{
*object = nil;
return objc_storeWeak(object, value);
}
1 change: 1 addition & 0 deletions caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ static const int32_t caps =
(1<<OBJC_CAP_NONFRAGILE_IVARS) |
(1<<OBJC_DEVELOPER_MODE) |
(1<<OBJC_CAP_REGISTERED_COMPATIBILITY_ALIASES) |
(1<<OBJC_CAP_ARC) |
#ifndef NO_OBJCXX
(1<<OBJC_UNIFIED_EXCEPTION_MODEL) |
#endif
Expand Down
1 change: 1 addition & 0 deletions hash_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>


#ifdef ENABLE_GC
Expand Down
8 changes: 5 additions & 3 deletions loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
PRIVATE mutex_t runtime_mutex;
LEGACY void *__objc_runtime_mutex = &runtime_mutex;

void init_selector_tables(void);
void init_protocol_table(void);
void init_alias_table(void);
void init_arc(void);
void init_class_tables(void);
void init_dispatch_tables(void);
void init_alias_table(void);
void init_gc(void);
void init_protocol_table(void);
void init_selector_tables(void);
void objc_send_load_message(Class class);

/* Number of threads that are alive. */
Expand Down Expand Up @@ -57,6 +58,7 @@ void __objc_exec_class(struct objc_module_abi_8 *module)
init_class_tables();
init_dispatch_tables();
init_alias_table();
init_arc();
first_run = NO;
}

Expand Down
5 changes: 5 additions & 0 deletions objc/capabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ extern "C" {
* alias as the argument.
*/
#define OBJC_CAP_REGISTERED_COMPATIBILITY_ALIASES 10
/**
* The runtime supports automatic reference counting, including support for
* __weak references.
*/
#define OBJC_CAP_ARC 11

/**
* Macro used to require the existence of a specific capability. This creates
Expand Down
8 changes: 8 additions & 0 deletions objc/hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,11 @@ OBJC_HOOK Class (*_objc_class_for_boxing_foreign_exception)(int64_t exceptionCla
*/
extern struct objc_slot* (*_objc_selector_type_mismatch)(Class cls,
SEL selector, struct objc_slot *result);

/**
* Returns the object if it is not currently in the process of being
* deallocated. Returns nil otherwise.
*
* This hook must be set for weak references to work with automatic reference counting.
*/
OBJC_HOOK id (*_objc_weak_load)(id object);
26 changes: 26 additions & 0 deletions objc/objc-arc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
id objc_autorelease(id obj);
id objc_autoreleaseReturnValue(id obj);
id objc_initWeak(id *object, id value);
id objc_loadWeak(id* object);
id objc_loadWeakRetained(id* obj);
id objc_retain(id obj);
id objc_retainAutorelease(id obj);
id objc_retainAutoreleaseReturnValue(id obj);
id objc_retainAutoreleasedReturnValue(id obj);
id objc_retainBlock(id b);
id objc_storeStrong(id *object, id value);
id objc_storeWeak(id *addr, id obj);
void *objc_autoreleasePoolPush(void);
void objc_autoreleasePoolPop(void *pool);
void objc_copyWeak(id *dest, id *src);
void objc_destroyWeak(id* obj);
void objc_moveWeak(id *dest, id *src);
void objc_release(id obj);
/**
* Mark the object as about to begin deallocation. All subsequent reads of
* weak pointers will return 0. This function should be called in -release,
* before calling [self dealloc].
*
* Nonstandard extension.
*/
void objc_delete_weak_refs(id obj);
2 changes: 1 addition & 1 deletion string_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
__attribute__((unused))
static uint32_t string_hash(const char *str)
{
uint32_t hash = 33;
uint32_t hash = 0;
int32_t c;
while ((c = *str++))
{
Expand Down

0 comments on commit bc87ed2

Please sign in to comment.