2 * Copyright (c) 2010 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "objc-private.h"
26 #include <malloc/malloc.h>
32 #include "auto_zone.h"
36 // external references to data segment objects all use this type
37 OBJC_XREF_TYPE_STATIC = 3,
39 OBJC_XREF_TYPE_MASK = 3
42 // Macros to encode/decode reference values and types.
43 #define encode_pointer_and_type(pointer, type) (~((uintptr_t)(pointer) | type))
44 #define decode_pointer(encoded) ((id)((~(encoded)) & (~OBJC_XREF_TYPE_MASK)))
45 #define decode_type(encoded) ((~(encoded)) & OBJC_XREF_TYPE_MASK)
46 #define encode_index_and_type(index, type) (~((index<<3) | type))
47 #define decode_index(encoded) ((~encoded)>>3)
52 objc_xref_type_t _type; // type of list.
53 dispatch_queue_t _synchronizer; // a reader/write lock
54 __strong void **_buffer; // a retained all pointers block
55 size_t _size; // number of pointers that fit in _list (buffer size)
56 size_t _count; // count of pointers in _list (in use count)
57 size_t _search; // lowest index in list which *might* be unused
60 static external_ref_list _xref_lists[2];
62 #define is_strong(list) (list->_type == OBJC_XREF_STRONG)
63 #define is_weak(list) (list->_type == OBJC_XREF_WEAK)
65 inline static size_t _index_for_type(objc_xref_type_t ref_type) {
66 assert(ref_type == OBJC_XREF_STRONG || ref_type == OBJC_XREF_WEAK);
67 return (ref_type - 1);
70 static void _initialize_gc() {
71 static dispatch_once_t init_guard;
72 dispatch_once(&init_guard, ^{
73 external_ref_list *_strong_list = &_xref_lists[_index_for_type(OBJC_XREF_STRONG)];
74 _strong_list->_type = OBJC_XREF_STRONG;
75 _strong_list->_synchronizer = dispatch_queue_create("OBJC_XREF_STRONG synchronizer", DISPATCH_QUEUE_CONCURRENT);
77 external_ref_list *_weak_list = &_xref_lists[_index_for_type(OBJC_XREF_WEAK)];
78 _weak_list->_type = OBJC_XREF_WEAK;
79 _weak_list->_synchronizer = dispatch_queue_create("OBJC_XREF_WEAK synchronizer", DISPATCH_QUEUE_CONCURRENT);
83 #define EMPTY_SLOT ((void*)0x1)
85 // grow the buffer by one page
86 static bool _grow_list(external_ref_list *list) {
87 auto_memory_type_t memory_type = (is_strong(list) ? AUTO_MEMORY_ALL_POINTERS : AUTO_MEMORY_ALL_WEAK_POINTERS);
88 size_t new_size = list->_size + PAGE_MAX_SIZE / sizeof(void *);
89 // auto_realloc() has been enhanced to handle strong and weak memory.
90 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));
91 if (!new_list) _objc_fatal("unable to allocate, size = %ld\n", new_size);
93 list->_search = list->_size;
94 // Fill the newly allocated space with empty slot tokens.
95 for (size_t index = list->_size; index < new_size; ++index)
96 new_list[index] = EMPTY_SLOT;
97 list->_size = new_size;
98 auto_zone_root_write_barrier(gc_zone, &list->_buffer, new_list);
103 // find an unused slot in the list, growing the list if necessary
104 static size_t _find_unused_index(external_ref_list *list) {
106 if (list->_size == list->_count) {
109 // find the lowest unused index in _list
110 index = list->_search;
111 while (list->_buffer[index] != EMPTY_SLOT)
113 // mark the slot as no longer empty, good form for weak slots.
114 list->_buffer[index] = NULL;
119 // return the strong or weak list
120 inline static external_ref_list *_list_for_type(objc_xref_type_t ref_type) {
121 return &_xref_lists[_index_for_type(ref_type)];
125 // create a GC external reference
126 objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_type_t ref_type) {
128 __block size_t index;
131 if (auto_zone_is_valid_pointer(gc_zone, obj)) {
132 external_ref_list *list = _list_for_type(ref_type);
135 dispatch_barrier_sync(list->_synchronizer, (dispatch_block_t)^{
136 index = _find_unused_index(list);
137 if (ref_type == OBJC_XREF_STRONG) {
138 auto_zone_set_write_barrier(gc_zone, &list->_buffer[index], obj);
140 auto_assign_weak_reference(gc_zone, obj, (const void **)&list->_buffer[index], NULL);
144 xref = encode_index_and_type(index, ref_type);
146 // data segment object
147 xref = encode_pointer_and_type(obj, OBJC_XREF_TYPE_STATIC);
153 id _object_readExternalReference_gc(objc_xref_t ref) {
156 objc_xref_type_t ref_type = decode_type(ref);
157 if (ref_type != OBJC_XREF_TYPE_STATIC) {
158 size_t index = decode_index(ref);
159 external_ref_list *list = _list_for_type(ref_type);
161 dispatch_sync(list->_synchronizer, ^{
162 if (index >= list->_size) {
163 _objc_fatal("attempted to resolve invalid external reference\n");
165 if (ref_type == OBJC_XREF_STRONG)
166 result = (id)list->_buffer[index];
168 result = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
169 if (result == (id)EMPTY_SLOT)
170 _objc_fatal("attempted to resolve unallocated external reference\n");
173 // data segment object
174 result = decode_pointer(ref);
180 void _object_removeExternalReference_gc(objc_xref_t ref) {
182 objc_xref_type_t ref_type = decode_type(ref);
183 if (ref_type != OBJC_XREF_TYPE_STATIC) {
184 size_t index = decode_index(ref);
185 external_ref_list *list = _list_for_type(ref_type);
187 dispatch_barrier_sync(list->_synchronizer, ^{
188 if (index >= list->_size) {
189 _objc_fatal("attempted to destroy invalid external reference\n");
192 if (ref_type == OBJC_XREF_STRONG) {
193 old_value = (id)list->_buffer[index];
195 old_value = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
196 auto_assign_weak_reference(gc_zone, NULL, (const void **)&list->_buffer[index], NULL);
198 list->_buffer[index] = EMPTY_SLOT;
199 if (old_value == (id)EMPTY_SLOT)
200 _objc_fatal("attempted to destroy unallocated external reference\n");
202 if (list->_search > index)
203 list->_search = index;
206 // nothing for data segment object
215 objc_xref_t _object_addExternalReference_non_gc(id obj, objc_xref_type_t ref_type) {
217 case OBJC_XREF_STRONG:
218 ((id(*)(id, SEL))objc_msgSend)(obj, SEL_retain);
223 _objc_fatal("invalid external reference type: %d", (int)ref_type);
226 return encode_pointer_and_type(obj, ref_type);
230 id _object_readExternalReference_non_gc(objc_xref_t ref) {
231 id obj = decode_pointer(ref);
236 void _object_removeExternalReference_non_gc(objc_xref_t ref) {
237 id obj = decode_pointer(ref);
238 objc_xref_type_t ref_type = decode_type(ref);
240 case OBJC_XREF_STRONG:
241 ((void(*)(id, SEL))objc_msgSend)(obj, SEL_release);
246 _objc_fatal("invalid external reference type: %d", (int)ref_type);
252 uintptr_t _object_getExternalHash(id object) {
253 return (uintptr_t)object;
259 // These functions are resolver functions in objc-auto.mm.
264 _object_addExternalReference(id obj, objc_xref_t type)
266 return _object_addExternalReference_non_gc(obj, type);
271 _object_readExternalReference(objc_xref_t ref)
273 return _object_readExternalReference_non_gc(ref);
278 _object_removeExternalReference(objc_xref_t ref)
280 _object_removeExternalReference_non_gc(ref);