]> git.saurik.com Git - apple/objc4.git/blobdiff - runtime/objc-externalref.m
objc4-493.9.tar.gz
[apple/objc4.git] / runtime / objc-externalref.m
diff --git a/runtime/objc-externalref.m b/runtime/objc-externalref.m
new file mode 100644 (file)
index 0000000..121faad
--- /dev/null
@@ -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 <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 = %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;
+}