X-Git-Url: https://git.saurik.com/apple/objc4.git/blobdiff_plain/ee974f79090efc52c8d37c74dff5abadabf50296..8972963c21fb120c80c09e06536ba4aa7eb98af3:/runtime/objc-externalref.m diff --git a/runtime/objc-externalref.m b/runtime/objc-externalref.m new file mode 100644 index 0000000..121faad --- /dev/null +++ b/runtime/objc-externalref.m @@ -0,0 +1,278 @@ +/* + * 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 +#include +#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 = %ld\n", new_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: %d", (int)ref_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: %d", (int)ref_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; +}