objc-externalref.m [plain text]
/*
* Copyright (c) 2010 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <malloc/malloc.h>
#include <assert.h>
#include "runtime.h"
#include "objc-os.h"
#include "objc-private.h"
#include "message.h"
#if SUPPORT_GC
#include "auto_zone.h"
#endif
enum {
// external references to data segment objects all use this type
OBJC_XREF_TYPE_STATIC = 3,
OBJC_XREF_TYPE_MASK = 3
};
// Macros to encode/decode reference values and types.
#define encode_pointer_and_type(pointer, type) (~((uintptr_t)(pointer) | type))
#define decode_pointer(encoded) ((id)((~(encoded)) & (~OBJC_XREF_TYPE_MASK)))
#define decode_type(encoded) ((~(encoded)) & OBJC_XREF_TYPE_MASK)
#define encode_index_and_type(index, type) (~((index<<3) | type))
#define decode_index(encoded) ((~encoded)>>3)
#if SUPPORT_GC
typedef struct {
objc_xref_type_t _type; // type of list.
dispatch_queue_t _synchronizer; // a reader/write lock
__strong void **_buffer; // a retained all pointers block
size_t _size; // number of pointers that fit in _list (buffer size)
size_t _count; // count of pointers in _list (in use count)
size_t _search; // lowest index in list which *might* be unused
} external_ref_list;
static external_ref_list _xref_lists[2];
#define is_strong(list) (list->_type == OBJC_XREF_STRONG)
#define is_weak(list) (list->_type == OBJC_XREF_WEAK)
inline static size_t _index_for_type(objc_xref_type_t ref_type) {
assert(ref_type == OBJC_XREF_STRONG || ref_type == OBJC_XREF_WEAK);
return (ref_type - 1);
}
static void _initialize_gc() {
static dispatch_once_t init_guard;
dispatch_once(&init_guard, ^{
external_ref_list *_strong_list = &_xref_lists[_index_for_type(OBJC_XREF_STRONG)];
_strong_list->_type = OBJC_XREF_STRONG;
_strong_list->_synchronizer = dispatch_queue_create("OBJC_XREF_STRONG synchronizer", DISPATCH_QUEUE_CONCURRENT);
external_ref_list *_weak_list = &_xref_lists[_index_for_type(OBJC_XREF_WEAK)];
_weak_list->_type = OBJC_XREF_WEAK;
_weak_list->_synchronizer = dispatch_queue_create("OBJC_XREF_WEAK synchronizer", DISPATCH_QUEUE_CONCURRENT);
});
}
#define EMPTY_SLOT ((void*)0x1)
// grow the buffer by one page
static bool _grow_list(external_ref_list *list) {
auto_memory_type_t memory_type = (is_strong(list) ? AUTO_MEMORY_ALL_POINTERS : AUTO_MEMORY_ALL_WEAK_POINTERS);
size_t new_size = list->_size + PAGE_SIZE / sizeof(void *);
// auto_realloc() has been enhanced to handle strong and weak memory.
void **new_list = (void **)(list->_buffer ? malloc_zone_realloc(gc_zone, list->_buffer, new_size * sizeof(void *)) : auto_zone_allocate_object(gc_zone, new_size * sizeof(void *), memory_type, false, false));
if (!new_list) _objc_fatal("unable to allocate, size =
list->_search = list->_size;
// Fill the newly allocated space with empty slot tokens.
for (size_t index = list->_size; index < new_size; ++index)
new_list[index] = EMPTY_SLOT;
list->_size = new_size;
auto_zone_root_write_barrier(gc_zone, &list->_buffer, new_list);
return true;
}
// find an unused slot in the list, growing the list if necessary
static size_t _find_unused_index(external_ref_list *list) {
size_t index;
if (list->_size == list->_count) {
_grow_list(list);
}
// find the lowest unused index in _list
index = list->_search;
while (list->_buffer[index] != EMPTY_SLOT)
index++;
// mark the slot as no longer empty, good form for weak slots.
list->_buffer[index] = NULL;
return index;
}
// return the strong or weak list
inline static external_ref_list *_list_for_type(objc_xref_type_t ref_type) {
return &_xref_lists[_index_for_type(ref_type)];
}
// create a GC external reference
PRIVATE_EXTERN objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_type_t ref_type) {
_initialize_gc();
__block size_t index;
objc_xref_t xref;
if (auto_zone_is_valid_pointer(gc_zone, obj)) {
external_ref_list *list = _list_for_type(ref_type);
// writer lock
dispatch_barrier_sync(list->_synchronizer, (dispatch_block_t)^{
index = _find_unused_index(list);
if (ref_type == OBJC_XREF_STRONG) {
auto_zone_set_write_barrier(gc_zone, &list->_buffer[index], obj);
} else {
auto_assign_weak_reference(gc_zone, obj, (const void **)&list->_buffer[index], NULL);
}
list->_count++;
});
xref = encode_index_and_type(index, ref_type);
} else {
// data segment object
xref = encode_pointer_and_type(obj, OBJC_XREF_TYPE_STATIC);
}
return xref;
}
PRIVATE_EXTERN id _object_readExternalReference_gc(objc_xref_t ref) {
_initialize_gc();
__block id result;
objc_xref_type_t ref_type = decode_type(ref);
if (ref_type != OBJC_XREF_TYPE_STATIC) {
size_t index = decode_index(ref);
external_ref_list *list = _list_for_type(ref_type);
dispatch_sync(list->_synchronizer, ^{
if (index >= list->_size) {
_objc_fatal("attempted to resolve invalid external reference\n");
}
if (ref_type == OBJC_XREF_STRONG)
result = (id)list->_buffer[index];
else
result = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
if (result == (id)EMPTY_SLOT)
_objc_fatal("attempted to resolve unallocated external reference\n");
});
} else {
// data segment object
result = decode_pointer(ref);
}
return result;
}
PRIVATE_EXTERN void _object_removeExternalReference_gc(objc_xref_t ref) {
_initialize_gc();
objc_xref_type_t ref_type = decode_type(ref);
if (ref_type != OBJC_XREF_TYPE_STATIC) {
size_t index = decode_index(ref);
external_ref_list *list = _list_for_type(ref_type);
dispatch_barrier_sync(list->_synchronizer, ^{
if (index >= list->_size) {
_objc_fatal("attempted to destroy invalid external reference\n");
}
id old_value;
if (ref_type == OBJC_XREF_STRONG) {
old_value = (id)list->_buffer[index];
} else {
old_value = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
auto_assign_weak_reference(gc_zone, NULL, (const void **)&list->_buffer[index], NULL);
}
list->_buffer[index] = EMPTY_SLOT;
if (old_value == (id)EMPTY_SLOT)
_objc_fatal("attempted to destroy unallocated external reference\n");
list->_count--;
if (list->_search > index)
list->_search = index;
});
} else {
// nothing for data segment object
}
}
// SUPPORT_GC
#endif
PRIVATE_EXTERN objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_type_t ref_type) {
switch (ref_type) {
case OBJC_XREF_STRONG:
objc_msgSend(obj, SEL_retain);
break;
case OBJC_XREF_WEAK:
break;
default:
_objc_fatal("invalid external reference type: break;
}
return encode_pointer_and_type(obj, ref_type);
}
PRIVATE_EXTERN id _object_readExternalReference_rr(objc_xref_t ref) {
id obj = decode_pointer(ref);
return obj;
}
PRIVATE_EXTERN void _object_removeExternalReference_rr(objc_xref_t ref) {
id obj = decode_pointer(ref);
objc_xref_type_t ref_type = decode_type(ref);
switch (ref_type) {
case OBJC_XREF_STRONG:
objc_msgSend(obj, SEL_release);
break;
case OBJC_XREF_WEAK:
break;
default:
_objc_fatal("invalid external reference type: break;
}
}
objc_xref_t _object_addExternalReference(id obj, objc_xref_t type) {
#if SUPPORT_GC
if (UseGC)
return _object_addExternalReference_gc(obj, type);
else
#endif
return _object_addExternalReference_rr(obj, type);
}
id _object_readExternalReference(objc_xref_t ref) {
#if SUPPORT_GC
if (UseGC)
return _object_readExternalReference_gc(ref);
else
#endif
return _object_readExternalReference_rr(ref);
}
void _object_removeExternalReference(objc_xref_t ref) {
#if SUPPORT_GC
if (UseGC)
_object_removeExternalReference_gc(ref);
else
#endif
_object_removeExternalReference_rr(ref);
}
uintptr_t _object_getExternalHash(id object) {
#if SUPPORT_GC
if (UseCompaction)
return auto_zone_get_associative_hash(gc_zone, object);
else
#endif
return (uintptr_t)object;
}