]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-externalref.mm
objc4-532.2.tar.gz
[apple/objc4.git] / runtime / objc-externalref.mm
1 /*
2 * Copyright (c) 2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <malloc/malloc.h>
25 #include <assert.h>
26 #include "runtime.h"
27 #include "objc-os.h"
28 #include "objc-private.h"
29 #include "message.h"
30 #if SUPPORT_GC
31 #include "auto_zone.h"
32 #endif
33
34 enum {
35 // external references to data segment objects all use this type
36 OBJC_XREF_TYPE_STATIC = 3,
37
38 OBJC_XREF_TYPE_MASK = 3
39 };
40
41 // Macros to encode/decode reference values and types.
42 #define encode_pointer_and_type(pointer, type) (~((uintptr_t)(pointer) | type))
43 #define decode_pointer(encoded) ((id)((~(encoded)) & (~OBJC_XREF_TYPE_MASK)))
44 #define decode_type(encoded) ((~(encoded)) & OBJC_XREF_TYPE_MASK)
45 #define encode_index_and_type(index, type) (~((index<<3) | type))
46 #define decode_index(encoded) ((~encoded)>>3)
47
48 #if SUPPORT_GC
49
50 typedef struct {
51 objc_xref_type_t _type; // type of list.
52 dispatch_queue_t _synchronizer; // a reader/write lock
53 __strong void **_buffer; // a retained all pointers block
54 size_t _size; // number of pointers that fit in _list (buffer size)
55 size_t _count; // count of pointers in _list (in use count)
56 size_t _search; // lowest index in list which *might* be unused
57 } external_ref_list;
58
59 static external_ref_list _xref_lists[2];
60
61 #define is_strong(list) (list->_type == OBJC_XREF_STRONG)
62 #define is_weak(list) (list->_type == OBJC_XREF_WEAK)
63
64 inline static size_t _index_for_type(objc_xref_type_t ref_type) {
65 assert(ref_type == OBJC_XREF_STRONG || ref_type == OBJC_XREF_WEAK);
66 return (ref_type - 1);
67 }
68
69 static void _initialize_gc() {
70 static dispatch_once_t init_guard;
71 dispatch_once(&init_guard, ^{
72 external_ref_list *_strong_list = &_xref_lists[_index_for_type(OBJC_XREF_STRONG)];
73 _strong_list->_type = OBJC_XREF_STRONG;
74 _strong_list->_synchronizer = dispatch_queue_create("OBJC_XREF_STRONG synchronizer", DISPATCH_QUEUE_CONCURRENT);
75
76 external_ref_list *_weak_list = &_xref_lists[_index_for_type(OBJC_XREF_WEAK)];
77 _weak_list->_type = OBJC_XREF_WEAK;
78 _weak_list->_synchronizer = dispatch_queue_create("OBJC_XREF_WEAK synchronizer", DISPATCH_QUEUE_CONCURRENT);
79 });
80 }
81
82 #define EMPTY_SLOT ((void*)0x1)
83
84 // grow the buffer by one page
85 static bool _grow_list(external_ref_list *list) {
86 auto_memory_type_t memory_type = (is_strong(list) ? AUTO_MEMORY_ALL_POINTERS : AUTO_MEMORY_ALL_WEAK_POINTERS);
87 size_t new_size = list->_size + PAGE_SIZE / sizeof(void *);
88 // auto_realloc() has been enhanced to handle strong and weak memory.
89 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));
90 if (!new_list) _objc_fatal("unable to allocate, size = %ld\n", new_size);
91
92 list->_search = list->_size;
93 // Fill the newly allocated space with empty slot tokens.
94 for (size_t index = list->_size; index < new_size; ++index)
95 new_list[index] = EMPTY_SLOT;
96 list->_size = new_size;
97 auto_zone_root_write_barrier(gc_zone, &list->_buffer, new_list);
98 return true;
99 }
100
101
102 // find an unused slot in the list, growing the list if necessary
103 static size_t _find_unused_index(external_ref_list *list) {
104 size_t index;
105 if (list->_size == list->_count) {
106 _grow_list(list);
107 }
108 // find the lowest unused index in _list
109 index = list->_search;
110 while (list->_buffer[index] != EMPTY_SLOT)
111 index++;
112 // mark the slot as no longer empty, good form for weak slots.
113 list->_buffer[index] = NULL;
114 return index;
115 }
116
117
118 // return the strong or weak list
119 inline static external_ref_list *_list_for_type(objc_xref_type_t ref_type) {
120 return &_xref_lists[_index_for_type(ref_type)];
121 }
122
123
124 // create a GC external reference
125 OBJC_EXTERN
126 objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_type_t ref_type) {
127 _initialize_gc();
128 __block size_t index;
129 objc_xref_t xref;
130
131 if (auto_zone_is_valid_pointer(gc_zone, obj)) {
132 external_ref_list *list = _list_for_type(ref_type);
133
134 // writer lock
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);
139 } else {
140 auto_assign_weak_reference(gc_zone, obj, (const void **)&list->_buffer[index], NULL);
141 }
142 list->_count++;
143 });
144 xref = encode_index_and_type(index, ref_type);
145 } else {
146 // data segment object
147 xref = encode_pointer_and_type(obj, OBJC_XREF_TYPE_STATIC);
148 }
149 return xref;
150 }
151
152 OBJC_EXTERN
153 id _object_readExternalReference_gc(objc_xref_t ref) {
154 _initialize_gc();
155 __block id result;
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);
160
161 dispatch_sync(list->_synchronizer, ^{
162 if (index >= list->_size) {
163 _objc_fatal("attempted to resolve invalid external reference\n");
164 }
165 if (ref_type == OBJC_XREF_STRONG)
166 result = (id)list->_buffer[index];
167 else
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");
171 });
172 } else {
173 // data segment object
174 result = decode_pointer(ref);
175 }
176 return result;
177 }
178
179 OBJC_EXTERN
180 void _object_removeExternalReference_gc(objc_xref_t ref) {
181 _initialize_gc();
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);
186
187 dispatch_barrier_sync(list->_synchronizer, ^{
188 if (index >= list->_size) {
189 _objc_fatal("attempted to destroy invalid external reference\n");
190 }
191 id old_value;
192 if (ref_type == OBJC_XREF_STRONG) {
193 old_value = (id)list->_buffer[index];
194 } else {
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);
197 }
198 list->_buffer[index] = EMPTY_SLOT;
199 if (old_value == (id)EMPTY_SLOT)
200 _objc_fatal("attempted to destroy unallocated external reference\n");
201 list->_count--;
202 if (list->_search > index)
203 list->_search = index;
204 });
205 } else {
206 // nothing for data segment object
207 }
208 }
209
210 // SUPPORT_GC
211 #endif
212
213 OBJC_EXTERN
214 objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_type_t ref_type) {
215 switch (ref_type) {
216 case OBJC_XREF_STRONG:
217 ((id(*)(id, SEL))objc_msgSend)(obj, SEL_retain);
218 break;
219 case OBJC_XREF_WEAK:
220 break;
221 default:
222 _objc_fatal("invalid external reference type: %d", (int)ref_type);
223 break;
224 }
225 return encode_pointer_and_type(obj, ref_type);
226 }
227
228 OBJC_EXTERN
229 id _object_readExternalReference_rr(objc_xref_t ref) {
230 id obj = decode_pointer(ref);
231 return obj;
232 }
233
234 OBJC_EXTERN
235 void _object_removeExternalReference_rr(objc_xref_t ref) {
236 id obj = decode_pointer(ref);
237 objc_xref_type_t ref_type = decode_type(ref);
238 switch (ref_type) {
239 case OBJC_XREF_STRONG:
240 ((void(*)(id, SEL))objc_msgSend)(obj, SEL_release);
241 break;
242 case OBJC_XREF_WEAK:
243 break;
244 default:
245 _objc_fatal("invalid external reference type: %d", (int)ref_type);
246 break;
247 }
248 }
249
250 objc_xref_t _object_addExternalReference(id obj, objc_xref_t type) {
251 #if SUPPORT_GC
252 if (UseGC)
253 return _object_addExternalReference_gc(obj, type);
254 else
255 #endif
256 return _object_addExternalReference_rr(obj, type);
257 }
258
259 id _object_readExternalReference(objc_xref_t ref) {
260 #if SUPPORT_GC
261 if (UseGC)
262 return _object_readExternalReference_gc(ref);
263 else
264 #endif
265 return _object_readExternalReference_rr(ref);
266 }
267
268 void _object_removeExternalReference(objc_xref_t ref) {
269 #if SUPPORT_GC
270 if (UseGC)
271 _object_removeExternalReference_gc(ref);
272 else
273 #endif
274 _object_removeExternalReference_rr(ref);
275 }
276
277 uintptr_t _object_getExternalHash(id object) {
278 #if SUPPORT_GC
279 if (UseCompaction)
280 return auto_zone_get_associative_hash(gc_zone, object);
281 else
282 #endif
283 return (uintptr_t)object;
284 }