]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-externalref.mm
objc4-680.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 "objc-private.h"
25
26 #include <malloc/malloc.h>
27 #include <assert.h>
28 #include "runtime.h"
29 #include "objc-os.h"
30 #include "message.h"
31 #if SUPPORT_GC
32 #include "auto_zone.h"
33 #endif
34
35 enum {
36 // external references to data segment objects all use this type
37 OBJC_XREF_TYPE_STATIC = 3,
38
39 OBJC_XREF_TYPE_MASK = 3
40 };
41
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)
48
49 #if SUPPORT_GC
50
51 typedef struct {
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
58 } external_ref_list;
59
60 static external_ref_list _xref_lists[2];
61
62 #define is_strong(list) (list->_type == OBJC_XREF_STRONG)
63 #define is_weak(list) (list->_type == OBJC_XREF_WEAK)
64
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);
68 }
69
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);
76
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);
80 });
81 }
82
83 #define EMPTY_SLOT ((void*)0x1)
84
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);
92
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);
99 return true;
100 }
101
102
103 // find an unused slot in the list, growing the list if necessary
104 static size_t _find_unused_index(external_ref_list *list) {
105 size_t index;
106 if (list->_size == list->_count) {
107 _grow_list(list);
108 }
109 // find the lowest unused index in _list
110 index = list->_search;
111 while (list->_buffer[index] != EMPTY_SLOT)
112 index++;
113 // mark the slot as no longer empty, good form for weak slots.
114 list->_buffer[index] = NULL;
115 return index;
116 }
117
118
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)];
122 }
123
124
125 // create a GC external reference
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
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
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
211 // SUPPORT_GC
212 #endif
213
214
215 objc_xref_t _object_addExternalReference_non_gc(id obj, objc_xref_type_t ref_type) {
216 switch (ref_type) {
217 case OBJC_XREF_STRONG:
218 ((id(*)(id, SEL))objc_msgSend)(obj, SEL_retain);
219 break;
220 case OBJC_XREF_WEAK:
221 break;
222 default:
223 _objc_fatal("invalid external reference type: %d", (int)ref_type);
224 break;
225 }
226 return encode_pointer_and_type(obj, ref_type);
227 }
228
229
230 id _object_readExternalReference_non_gc(objc_xref_t ref) {
231 id obj = decode_pointer(ref);
232 return obj;
233 }
234
235
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);
239 switch (ref_type) {
240 case OBJC_XREF_STRONG:
241 ((void(*)(id, SEL))objc_msgSend)(obj, SEL_release);
242 break;
243 case OBJC_XREF_WEAK:
244 break;
245 default:
246 _objc_fatal("invalid external reference type: %d", (int)ref_type);
247 break;
248 }
249 }
250
251
252 uintptr_t _object_getExternalHash(id object) {
253 return (uintptr_t)object;
254 }
255
256
257 #if SUPPORT_GC
258
259 // These functions are resolver functions in objc-auto.mm.
260
261 #else
262
263 objc_xref_t
264 _object_addExternalReference(id obj, objc_xref_t type)
265 {
266 return _object_addExternalReference_non_gc(obj, type);
267 }
268
269
270 id
271 _object_readExternalReference(objc_xref_t ref)
272 {
273 return _object_readExternalReference_non_gc(ref);
274 }
275
276
277 void
278 _object_removeExternalReference(objc_xref_t ref)
279 {
280 _object_removeExternalReference_non_gc(ref);
281 }
282
283 #endif