+/*
+ * 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;
+}