]> git.saurik.com Git - apple/cf.git/blob - CFRuntime.c
CF-476.19.tar.gz
[apple/cf.git] / CFRuntime.c
1 /*
2 * Copyright (c) 2008 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 /* CFRuntime.c
24 Copyright 1999-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
28 #define ENABLE_ZOMBIES 1
29
30 #include "CFRuntime.h"
31 #include "CFInternal.h"
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <dlfcn.h>
36 #include <mach-o/dyld.h>
37 #include <monitor.h>
38 #include <crt_externs.h>
39 #include <unistd.h>
40 #include "auto_stubs.h"
41
42 #define __CFRecordAllocationEvent(a, b, c, d, e) ((void)0)
43
44 enum {
45 // retain/release recording constants -- must match values
46 // used by OA for now; probably will change in the future
47 __kCFRetainEvent = 28,
48 __kCFReleaseEvent = 29
49 };
50
51 #include <malloc/malloc.h>
52
53 extern void __HALT(void);
54
55 static CFTypeID __kCFNotATypeTypeID = _kCFRuntimeNotATypeID;
56
57 #if !defined (__cplusplus)
58 static const CFRuntimeClass __CFNotATypeClass = {
59 0,
60 "Not A Type",
61 (void *)__HALT,
62 (void *)__HALT,
63 (void *)__HALT,
64 (void *)__HALT,
65 (void *)__HALT,
66 (void *)__HALT,
67 (void *)__HALT
68 };
69
70 static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID;
71
72 static const CFRuntimeClass __CFTypeClass = {
73 0,
74 "CFType",
75 (void *)__HALT,
76 (void *)__HALT,
77 (void *)__HALT,
78 (void *)__HALT,
79 (void *)__HALT,
80 (void *)__HALT,
81 (void *)__HALT
82 };
83 #else
84 void SIG1(CFTypeRef){__HALT();};;
85 CFTypeRef SIG2(CFAllocatorRef,CFTypeRef){__HALT();return NULL;};
86 Boolean SIG3(CFTypeRef,CFTypeRef){__HALT();return FALSE;};
87 CFHashCode SIG4(CFTypeRef){__HALT(); return 0;};
88 CFStringRef SIG5(CFTypeRef,CFDictionaryRef){__HALT();return NULL;};
89 CFStringRef SIG6(CFTypeRef){__HALT();return NULL;};
90
91 static const CFRuntimeClass __CFNotATypeClass = {
92 0,
93 "Not A Type",
94 SIG1,
95 SIG2,
96 SIG1,
97 SIG3,
98 SIG4,
99 SIG5,
100 SIG6
101 };
102
103 static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID;
104
105 static const CFRuntimeClass __CFTypeClass = {
106 0,
107 "CFType",
108 SIG1,
109 SIG2,
110 SIG1,
111 SIG3,
112 SIG4,
113 SIG5,
114 SIG6
115 };
116 #endif //__cplusplus
117
118 static CFRuntimeClass ** __CFRuntimeClassTable = NULL;
119 int32_t __CFRuntimeClassTableSize = 0;
120 static int32_t __CFRuntimeClassTableCount = 0;
121
122 __private_extern__ void * (*__CFSendObjCMsg)(const void *, SEL, ...) = NULL;
123
124 __private_extern__ malloc_zone_t *__CFCollectableZone = NULL;
125
126 bool (*__CFObjCIsCollectable)(void *) = NULL;
127
128 static const void* objc_AssignIvar_none(const void *value, const void *base, const void **slot) { return (*slot = value); }
129 const void* (*__CFObjCAssignIvar)(const void *value, const void *base, const void **slot) = objc_AssignIvar_none;
130
131 static const void* objc_StrongAssign_none(const void *value, const void **slot) { return (*slot = value); }
132 const void* (*__CFObjCStrongAssign)(const void *value, const void **slot) = objc_StrongAssign_none;
133
134 void* (*__CFObjCMemmoveCollectable)(void *dst, const void *, size_t) = memmove;
135
136 // GC: to be moved to objc if necessary.
137 static void objc_WriteBarrierRange_none(void *ptr, size_t size) {}
138 static void objc_WriteBarrierRange_auto(void *ptr, size_t size) { auto_zone_write_barrier_range(__CFCollectableZone, ptr, size); }
139 void (*__CFObjCWriteBarrierRange)(void *, size_t) = objc_WriteBarrierRange_none;
140
141 // Compiler uses this symbol name; must match compiler built-in decl
142 #if __LP64__
143 int __CFConstantStringClassReference[24] = {0};
144 #else
145 int __CFConstantStringClassReference[12] = {0};
146 #endif
147
148 // #warning the whole business of reallocating the ClassTables is not thread-safe, because access to those values is not protected
149
150 CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls) {
151 // version field must be 0
152 // className must be pure ASCII string, non-null
153 if (__CFMaxRuntimeTypes <= __CFRuntimeClassTableCount) {
154 CFLog(kCFLogLevelWarning, CFSTR("*** CoreFoundation class table full; registration failing for class '%s'. Program will crash soon."), cls->className);
155 return _kCFRuntimeNotATypeID;
156 }
157 if (__CFRuntimeClassTableSize <= __CFRuntimeClassTableCount) {
158 int32_t old_size = __CFRuntimeClassTableSize;
159 int32_t new_size = __CFRuntimeClassTableSize * 4;
160
161 void *new_table1 = calloc(new_size, sizeof(CFRuntimeClass *));
162 memmove(new_table1, __CFRuntimeClassTable, old_size * sizeof(CFRuntimeClass *));
163 __CFRuntimeClassTable = (CFRuntimeClass**)new_table1;
164 __CFRuntimeClassTableSize = new_size;
165 // The old value of __CFRuntimeClassTable is intentionally leaked
166 // for thread-safety reasons:
167 // other threads might have loaded the value of that, in functions here
168 // in this file executing in other threads, and may attempt to use it after
169 // this thread gets done reallocating here, so freeing is unsafe. We
170 // don't want to pay the expense of locking around all uses of these variables.
171 // The old value of __CFRuntimeObjCClassTable is intentionally leaked
172 // for thread-safety reasons:
173 // other threads might have loaded the value of that, since it is
174 // accessible via CFBridgingPriv.h, and may attempt to use it after
175 // this thread gets done reallocating here, so freeing is unsafe.
176 }
177 __CFRuntimeClassTable[__CFRuntimeClassTableCount++] = (CFRuntimeClass *)cls;
178 return __CFRuntimeClassTableCount - 1;
179 }
180
181 void _CFRuntimeBridgeClasses(CFTypeID cf_typeID, const char *objc_classname) {
182 return;
183 }
184
185 const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID) {
186 return __CFRuntimeClassTable[typeID];
187 }
188
189 void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID) {
190 __CFRuntimeClassTable[typeID] = NULL;
191 }
192
193
194 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
195
196 /* CFZombieLevel levels:
197 * bit 0: scribble deallocated CF object memory
198 * bit 1: do not scribble on CFRuntimeBase header (when bit 0)
199 * bit 4: do not free CF objects
200 * bit 7: use 3rd-order byte as scribble byte for dealloc (otherwise 0xFC)
201 */
202
203 static uint32_t __CFZombieLevel = 0x0;
204 static uint8_t __CFZombieEnabled = 0;
205 static uint8_t __CFDeallocateZombies = 0;
206 static void *_original_objc_dealloc = 0;
207
208 #endif /* DEBUG */
209
210 // XXX_PCB: use the class version field as a bitmask, to allow classes to opt-in for GC scanning.
211
212 #define CF_GET_COLLECTABLE_MEMORY_TYPE(x) (0)
213
214 CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, CFIndex extraBytes, unsigned char *category) {
215 CFRuntimeBase *memory;
216 Boolean usesSystemDefaultAllocator;
217 CFIndex size;
218
219 CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__);
220
221 if (NULL == __CFRuntimeClassTable[typeID]) {
222 return NULL;
223 }
224 allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
225 usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault);
226 size = sizeof(CFRuntimeBase) + extraBytes + (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef));
227 size = (size + 0xF) & ~0xF; // CF objects are multiples of 16 in size
228 // CFType version 0 objects are unscanned by default since they don't have write-barriers and hard retain their innards
229 // CFType version 1 objects are scanned and use hand coded write-barriers to store collectable storage within
230 memory = (CFRuntimeBase *)CFAllocatorAllocate(allocator, size, CF_GET_COLLECTABLE_MEMORY_TYPE(__CFRuntimeClassTable[typeID]));
231 if (NULL == memory) {
232 return NULL;
233 }
234 memset(memory, 0, malloc_size(memory));
235 if (__CFOASafe && category) {
236 __CFSetLastAllocationEventName(memory, (char *)category);
237 } else if (__CFOASafe) {
238 __CFSetLastAllocationEventName(memory, (char *)__CFRuntimeClassTable[typeID]->className);
239 }
240 if (!usesSystemDefaultAllocator) {
241 // add space to hold allocator ref for non-standard allocators.
242 // (this screws up 8 byte alignment but seems to work)
243 *(CFAllocatorRef *)((char *)memory) = (CFAllocatorRef)CFRetain(allocator);
244 memory = (CFRuntimeBase *)((char *)memory + sizeof(CFAllocatorRef));
245 }
246 memory->_cfisa = __CFISAForTypeID(typeID);
247 #if __LP64__
248 *(uint32_t *)(memory->_cfinfo) = (uint32_t)((0 << 24) + ((typeID & 0xFFFF) << 8) + (usesSystemDefaultAllocator ? 0x80 : 0x00));
249 memory->_rc = 1;
250 #else
251 *(uint32_t *)(memory->_cfinfo) = (uint32_t)((1 << 24) + ((typeID & 0xFFFF) << 8) + (usesSystemDefaultAllocator ? 0x80 : 0x00));
252 #endif
253 if (NULL != __CFRuntimeClassTable[typeID]->init) {
254 (__CFRuntimeClassTable[typeID]->init)(memory);
255 }
256 return memory;
257 }
258
259 void _CFRuntimeInitStaticInstance(void *ptr, CFTypeID typeID) {
260 CFRuntimeBase *memory = (CFRuntimeBase *)ptr;
261 CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__);
262 if (NULL == __CFRuntimeClassTable[typeID]) {
263 return;
264 }
265 memory->_cfisa = __CFISAForTypeID(typeID);
266 *(uint32_t *)(memory->_cfinfo) = (uint32_t)((0 << 24) + ((typeID & 0xFFFF) << 8) + 0x80);
267 #if __LP64__
268 memory->_rc = 0;
269 #endif
270 if (NULL != __CFRuntimeClassTable[typeID]->init) {
271 (__CFRuntimeClassTable[typeID]->init)(memory);
272 }
273 }
274
275 void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID) {
276 *(uint16_t *)(((CFRuntimeBase *)cf)->_cfinfo + 1) = (uint16_t)(typeID & 0xFFFF);
277 }
278
279 __private_extern__ Boolean __CFRuntimeIsFreedObject(id anObject) {
280 if (!anObject) return false;
281 static Class freedClass = Nil;
282 if (!freedClass) freedClass = _objc_getFreedObjectClass();
283 Class cls = object_getClass(anObject);
284 if (cls == freedClass) return true;
285 // in 64-bit, a future class has nil isa, and calling class_getName() on
286 // such will crash so we do this test; zombie classes are not future classes
287 if (objc_getClass((id)cls) == nil) return false;
288 const char *cname = class_getName(cls);
289 if (cname && 0 == strncmp(cname, "_NSZombie_", 10)) return true;
290 return false;
291 }
292
293 CFTypeID __CFGenericTypeID(const void *cf) {
294 return (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF;
295 }
296
297 CF_INLINE CFTypeID __CFGenericTypeID_inline(const void *cf) {
298 return (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF;
299 }
300
301 CFTypeID CFTypeGetTypeID(void) {
302 return __kCFTypeTypeID;
303 }
304
305 __private_extern__ void __CFGenericValidateType_(CFTypeRef cf, CFTypeID type, const char *func) {
306 if (cf && CF_IS_OBJC(type, cf)) return;
307 CFAssert2((cf != NULL) && (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]) && (__kCFNotATypeTypeID != __CFGenericTypeID_inline(cf)) && (__kCFTypeTypeID != __CFGenericTypeID_inline(cf)), __kCFLogAssertion, "%s(): pointer %p is not a CF object", func, cf); \
308 CFAssert3(__CFGenericTypeID_inline(cf) == type, __kCFLogAssertion, "%s(): pointer %p is not a %s", func, cf, __CFRuntimeClassTable[type]->className); \
309 }
310
311 #define __CFGenericAssertIsCF(cf) \
312 CFAssert2(cf != NULL && (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]) && (__kCFNotATypeTypeID != __CFGenericTypeID_inline(cf)) && (__kCFTypeTypeID != __CFGenericTypeID_inline(cf)), __kCFLogAssertion, "%s(): pointer %p is not a CF object", __PRETTY_FUNCTION__, cf);
313
314 #define CFTYPE_IS_OBJC(obj) (false)
315 #define CFTYPE_OBJC_FUNCDISPATCH0(rettype, obj, sel) do {} while (0)
316 #define CFTYPE_OBJC_FUNCDISPATCH1(rettype, obj, sel, a1) do {} while (0)
317
318 CFTypeID CFGetTypeID(CFTypeRef cf) {
319 #if defined(DEBUG)
320 if (NULL == cf) HALT;
321 #endif
322 CFTYPE_OBJC_FUNCDISPATCH0(CFTypeID, cf, "_cfTypeID");
323 __CFGenericAssertIsCF(cf);
324 return __CFGenericTypeID_inline(cf);
325 }
326
327 CFStringRef CFCopyTypeIDDescription(CFTypeID type) {
328 CFAssert2((NULL != __CFRuntimeClassTable[type]) && __kCFNotATypeTypeID != type && __kCFTypeTypeID != type, __kCFLogAssertion, "%s(): type %d is not a CF type ID", __PRETTY_FUNCTION__, type);
329 return CFStringCreateWithCString(kCFAllocatorSystemDefault, __CFRuntimeClassTable[type]->className, kCFStringEncodingASCII);
330 }
331
332 #define DISGUISE(object) ((void *)(((uintptr_t)object) + 1))
333 #define UNDISGUISE(disguised) ((id)(((uintptr_t)disguised) - 1))
334
335 // Bit 31 (highest bit) in second word of cf instance indicates external ref count
336
337 CF_EXPORT void _CFRelease(CFTypeRef cf);
338 CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf);
339 CF_EXPORT CFHashCode _CFHash(CFTypeRef cf);
340
341 CFTypeRef CFRetain(CFTypeRef cf) {
342 if (CF_IS_COLLECTABLE(cf)) {
343 // always honor CFRetain's with a GC-visible retain.
344 auto_zone_retain(__CFCollectableZone, (void*)cf);
345 return cf;
346 }
347 CFTYPE_OBJC_FUNCDISPATCH0(CFTypeRef, cf, "retain");
348 if (cf) __CFGenericAssertIsCF(cf);
349 return _CFRetain(cf);
350 }
351
352 __private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf);
353
354 void CFRelease(CFTypeRef cf) {
355 #if !defined(__WIN32__)
356 if (CF_IS_COLLECTABLE(cf)) {
357 // release the GC-visible reference.
358 if (auto_zone_release(__CFCollectableZone, (void*)cf) == 0 && !CFTYPE_IS_OBJC(cf)) {
359 CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)];
360 if (cfClass->version & _kCFRuntimeResourcefulObject) {
361 if (cfClass->reclaim) cfClass->reclaim(cf);
362 }
363 }
364 return;
365 }
366 #endif
367 CFTYPE_OBJC_FUNCDISPATCH0(void, cf, "release");
368 if (cf) __CFGenericAssertIsCF(cf);
369 _CFRelease(cf);
370 }
371
372
373 __private_extern__ const void *__CFStringCollectionCopy(CFAllocatorRef allocator, const void *ptr) {
374 CFStringRef theString = (CFStringRef)ptr;
375 CFStringRef result = CFStringCreateCopy(allocator, theString);
376 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
377 result = (CFStringRef)CFMakeCollectable(result);
378 }
379 return (const void *)result;
380 }
381
382 extern void CFCollection_non_gc_storage_error(void);
383
384 __private_extern__ const void *__CFTypeCollectionRetain(CFAllocatorRef allocator, const void *ptr) {
385 CFTypeRef cf = (CFTypeRef)ptr;
386 // only collections allocated in the GC zone can opt-out of reference counting.
387 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
388 if (CFTYPE_IS_OBJC(cf)) return cf; // do nothing for OBJC objects.
389 if (auto_zone_is_valid_pointer(__CFCollectableZone, ptr)) {
390 CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)];
391 if (cfClass->version & _kCFRuntimeResourcefulObject) {
392 // GC: If this a CF object in the GC heap that is marked resourceful, then
393 // it must be retained keep it alive in a CF collection.
394 // We're basically inlining CFRetain() here, to avoid an extra heap membership test.
395 auto_zone_retain(__CFCollectableZone, (void*)cf);
396 }
397 else
398 ; // don't retain normal CF objects
399 return cf;
400 } else {
401 // support constant CFTypeRef objects.
402 #if __LP64__
403 uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc;
404 #else
405 uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS];
406 #endif
407 if (lowBits == 0) return cf;
408 // complain about non-GC objects in GC containers.
409 CFLog(kCFLogLevelWarning, CFSTR("storing a non-GC object %p in a GC collection, break on CFCollection_non_gc_storage_error to debug."), cf);
410 CFCollection_non_gc_storage_error();
411 // XXX should halt, except Patrick is using this somewhere.
412 // HALT;
413 }
414 }
415 return CFRetain(cf);
416 }
417
418
419 __private_extern__ void __CFTypeCollectionRelease(CFAllocatorRef allocator, const void *ptr) {
420 CFTypeRef cf = (CFTypeRef)ptr;
421 // only collections allocated in the GC zone can opt-out of reference counting.
422 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
423 if (CFTYPE_IS_OBJC(cf)) return; // do nothing for OBJC objects.
424 if (auto_zone_is_valid_pointer(__CFCollectableZone, cf)) {
425 #if !defined(__WIN32__)
426 // GC: If this a CF object in the GC heap that is marked uncollectable, then
427 // must balance the retain done in __CFTypeCollectionRetain().
428 // We're basically inlining CFRelease() here, to avoid an extra heap membership test.
429 CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)];
430 if (cfClass->version & _kCFRuntimeResourcefulObject && auto_zone_release(__CFCollectableZone, (void*)cf) == 0) {
431 // ResourceFull objects trigger 'reclaim' on transition to zero
432 if (cfClass->reclaim) cfClass->reclaim(cf);
433 }
434 else // avoid releasing normal CF objects. Like other collections, for example
435 ;
436 return;
437 #endif
438 } else {
439 // support constant CFTypeRef objects.
440 #if __LP64__
441 uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc;
442 #else
443 uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS];
444 #endif
445 if (lowBits == 0) return;
446 }
447 }
448 CFRelease(cf);
449 }
450
451 #if !__LP64__
452 static CFSpinLock_t __CFRuntimeExternRefCountTableLock = CFSpinLockInit;
453 static CFMutableBagRef __CFRuntimeExternRefCountTable = NULL;
454 #endif
455
456 static uint64_t __CFGetFullRetainCount(CFTypeRef cf) {
457 #if __LP64__
458 uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc;
459 if (0 == lowBits) {
460 return (uint64_t)0x0fffffffffffffffULL;
461 }
462 return lowBits;
463 #else
464 uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS];
465 if (0 == lowBits) {
466 return (uint64_t)0x0fffffffffffffffULL;
467 }
468 uint64_t highBits = 0;
469 if ((lowBits & 0x80) != 0) {
470 __CFSpinLock(&__CFRuntimeExternRefCountTableLock);
471 highBits = (uint64_t)CFBagGetCountOfValue(__CFRuntimeExternRefCountTable, DISGUISE(cf));
472 __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock);
473 }
474 uint64_t compositeRC = (lowBits & 0x7f) + (highBits << 6);
475 return compositeRC;
476 #endif
477 }
478
479 CFTypeRef _CFRetainGC(CFTypeRef cf) {
480 #if defined(DEBUG)
481 if (CF_USING_COLLECTABLE_MEMORY && !CF_IS_COLLECTABLE(cf)) {
482 fprintf(stderr, "non-auto object %p passed to _CFRetainGC.\n", cf);
483 HALT;
484 }
485 #endif
486 return CF_USING_COLLECTABLE_MEMORY ? cf : CFRetain(cf);
487 }
488
489 void _CFReleaseGC(CFTypeRef cf) {
490 #if defined(DEBUG)
491 if (CF_USING_COLLECTABLE_MEMORY && !CF_IS_COLLECTABLE(cf)) {
492 fprintf(stderr, "non-auto object %p passed to _CFReleaseGC.\n", cf);
493 HALT;
494 }
495 #endif
496 if (!CF_USING_COLLECTABLE_MEMORY) CFRelease(cf);
497 }
498
499 CFIndex CFGetRetainCount(CFTypeRef cf) {
500 if (NULL == cf) return 0;
501 if (CF_IS_COLLECTABLE(cf)) {
502 return auto_zone_retain_count(__CFCollectableZone, cf);
503 }
504 CFTYPE_OBJC_FUNCDISPATCH0(CFIndex, cf, "retainCount");
505 __CFGenericAssertIsCF(cf);
506 uint64_t rc = __CFGetFullRetainCount(cf);
507 return (rc < (uint64_t)LONG_MAX) ? (CFIndex)rc : (CFIndex)LONG_MAX;
508 }
509
510 CFTypeRef CFMakeCollectable(CFTypeRef cf) {
511 if (NULL == cf) return NULL;
512 if (CF_IS_COLLECTABLE(cf)) {
513 #if defined(DEBUG)
514 CFAllocatorRef allocator = CFGetAllocator(cf);
515 if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
516 CFLog(kCFLogLevelWarning, CFSTR("object %p with non-GC allocator %p passed to CFMakeCollectable."), cf, allocator);
517 HALT;
518 }
519 #endif
520 if (!CFTYPE_IS_OBJC(cf)) {
521 CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)];
522 if (cfClass->version & (_kCFRuntimeResourcefulObject)) {
523 // don't allow the collector to manage uncollectable objects.
524 CFLog(kCFLogLevelWarning, CFSTR("uncollectable object %p passed to CFMakeCollectable."), cf);
525 HALT;
526 }
527 }
528 if (auto_zone_retain_count(__CFCollectableZone, cf) == 0) {
529 CFLog(kCFLogLevelWarning, CFSTR("object %p with 0 retain-count passed to CFMakeCollectable."), cf);
530 return cf;
531 }
532 auto_zone_release(__CFCollectableZone, (void *)cf);
533 }
534 return cf;
535 }
536
537 Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2) {
538 #if defined(DEBUG)
539 if (NULL == cf1) HALT;
540 if (NULL == cf2) HALT;
541 #endif
542 if (cf1 == cf2) return true;
543 CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf1, "isEqual:", cf2);
544 CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf2, "isEqual:", cf1);
545 __CFGenericAssertIsCF(cf1);
546 __CFGenericAssertIsCF(cf2);
547 if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false;
548 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) {
549 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2);
550 }
551 return false;
552 }
553
554 CFHashCode CFHash(CFTypeRef cf) {
555 CFTYPE_OBJC_FUNCDISPATCH0(CFHashCode, cf, "hash");
556 __CFGenericAssertIsCF(cf);
557 return _CFHash(cf);
558 }
559
560 // definition: produces a normally non-NULL debugging description of the object
561 CFStringRef CFCopyDescription(CFTypeRef cf) {
562 #if defined(DEBUG)
563 if (NULL == cf) HALT;
564 #endif
565 __CFGenericAssertIsCF(cf);
566 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc) {
567 CFStringRef result;
568 result = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc(cf);
569 if (NULL != result) return result;
570 }
571 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<%s %p [%p]>"), __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->className, cf, CFGetAllocator(cf));
572 }
573
574 // Definition: if type produces a formatting description, return that string, otherwise NULL
575 __private_extern__ CFStringRef __CFCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
576 #if defined(DEBUG)
577 if (NULL == cf) HALT;
578 #endif
579 __CFGenericAssertIsCF(cf);
580 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyFormattingDesc) {
581 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyFormattingDesc(cf, formatOptions);
582 }
583 return NULL;
584 }
585
586 extern CFAllocatorRef __CFAllocatorGetAllocator(CFTypeRef);
587
588 CFAllocatorRef CFGetAllocator(CFTypeRef cf) {
589 if (NULL == cf) return kCFAllocatorSystemDefault;
590 if (__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf)) {
591 return __CFAllocatorGetAllocator(cf);
592 }
593 return __CFGetAllocator(cf);
594 }
595
596 extern void __CFBaseInitialize(void);
597 extern void __CFNullInitialize(void);
598 extern void __CFAllocatorInitialize(void);
599 extern void __CFStringInitialize(void);
600 extern void __CFArrayInitialize(void);
601 extern void __CFBagInitialize(void);
602 extern void __CFBooleanInitialize(void);
603 extern void __CFCharacterSetInitialize(void);
604 extern void __CFDataInitialize(void);
605 extern void __CFDateInitialize(void);
606 extern void __CFDictionaryInitialize(void);
607 extern void __CFNumberInitialize(void);
608 extern void __CFSetInitialize(void);
609 extern void __CFStorageInitialize(void);
610 extern void __CFErrorInitialize(void);
611 extern void __CFTimeZoneInitialize(void);
612 extern void __CFTreeInitialize(void);
613 extern void __CFURLInitialize(void);
614 #if DEPLOYMENT_TARGET_MACOSX
615 extern void __CFMachPortInitialize(void);
616 #endif
617 #if DEPLOYMENT_TARGET_MACOSX
618 extern void __CFMessagePortInitialize(void);
619 #endif
620 #if DEPLOYMENT_TARGET_MACOSX || defined(__WIN32__)
621 extern void __CFRunLoopInitialize(void);
622 extern void __CFRunLoopObserverInitialize(void);
623 extern void __CFRunLoopSourceInitialize(void);
624 extern void __CFRunLoopTimerInitialize(void);
625 extern void __CFSocketInitialize(void);
626 #endif
627 extern void __CFBundleInitialize(void);
628 extern void __CFPlugInInitialize(void);
629 extern void __CFPlugInInstanceInitialize(void);
630 extern void __CFUUIDInitialize(void);
631 extern void __CFBinaryHeapInitialize(void);
632 extern void __CFBitVectorInitialize(void);
633 extern void __CFStreamInitialize(void);
634
635 static void __exceptionInit(void) {}
636 static void __collatorInit(void) {}
637 static void __forwarding_prep_0___(void) {}
638 static void __forwarding_prep_1___(void) {}
639 static void __NSFastEnumerationMutationHandler(id obj) {}
640 const void *__CFArgStuff = NULL;
641 __private_extern__ void *__CFAppleLanguages = NULL;
642
643 bool kCFUseCollectableAllocator = false;
644
645
646 #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
647 static void __CFInitialize(void) __attribute__ ((constructor));
648 static
649 #endif
650 void __CFInitialize(void) {
651 static int __done = 0;
652
653 if (!__done) {
654 __done = 1;
655
656 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
657 const char *value = getenv("NSZombieEnabled");
658 if (value && (*value == 'Y' || *value == 'y')) __CFZombieEnabled = 0xff;
659 value = getenv("NSDeallocateZombies");
660 if (value && (*value == 'Y' || *value == 'y')) __CFDeallocateZombies = 0xff;
661
662 value = getenv("CFZombieLevel");
663 if (NULL != value) {
664 __CFZombieLevel = (uint32_t)strtoul_l(value, NULL, 0, NULL);
665 }
666 if (0x0 == __CFZombieLevel) __CFZombieLevel = 0x0000FC00; // default
667 #endif
668
669 __CFRuntimeClassTableSize = 1024;
670 __CFRuntimeClassTable = (CFRuntimeClass **)calloc(__CFRuntimeClassTableSize, sizeof(CFRuntimeClass *));
671 __CFBaseInitialize();
672
673 /* Here so that two runtime classes get indices 0, 1. */
674 __kCFNotATypeTypeID = _CFRuntimeRegisterClass(&__CFNotATypeClass);
675 __kCFTypeTypeID = _CFRuntimeRegisterClass(&__CFTypeClass);
676
677 /* Here so that __kCFAllocatorTypeID gets index 2. */
678 __CFAllocatorInitialize();
679
680 #if DEPLOYMENT_TARGET_MACOSX
681 {
682 CFIndex idx, cnt;
683 char **args = *_NSGetArgv();
684 cnt = *_NSGetArgc();
685 for (idx = 1; idx < cnt - 1; idx++) {
686 if (NULL == args[idx]) continue;
687 if (0 == strcmp(args[idx], "-AppleLanguages") && args[idx + 1]) {
688 CFIndex length = strlen(args[idx + 1]);
689 __CFAppleLanguages = malloc(length + 1);
690 memmove(__CFAppleLanguages, args[idx + 1], length + 1);
691 break;
692 }
693 }
694 }
695 #endif
696
697
698 /* CFBag needs to be up before CFString. */
699 __CFBagInitialize();
700
701 #if !__LP64__
702 // Creating this lazily in CFRetain causes recursive call to CFRetain
703 __CFRuntimeExternRefCountTable = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
704 #endif
705
706 /*** _CFRuntimeCreateInstance() can finally be called generally after this line. ***/
707
708 __CFRuntimeClassTableCount = 7;
709 __CFStringInitialize(); // CFString's TypeID must be 0x7, now and forever
710 __CFRuntimeClassTableCount = 16;
711 __CFDictionaryInitialize();
712 __CFArrayInitialize();
713 __CFDataInitialize();
714 __CFSetInitialize();
715 __CFNullInitialize(); // See above for hard-coding of this position
716 __CFBooleanInitialize(); // See above for hard-coding of this position
717 __CFNumberInitialize(); // See above for hard-coding of this position
718
719
720 __CFDateInitialize(); // just initializes the time goo
721 // _CFRuntimeBridgeClasses(CFDateGetTypeID(), objc_lookUpClass("NSCFDate") ? "NSCFDate" : "__NSCFDate");
722 __CFTimeZoneInitialize();
723 // _CFRuntimeBridgeClasses(CFTimeZoneGetTypeID(), "NSCFTimeZone");
724 __CFBinaryHeapInitialize();
725 __CFBitVectorInitialize();
726 __CFCharacterSetInitialize();
727 __CFStorageInitialize();
728 __CFErrorInitialize();
729 __CFTreeInitialize();
730 __CFURLInitialize();
731 __CFBundleInitialize();
732 #if DEPLOYMENT_TARGET_MACOSX
733 __CFPlugInInitialize();
734 __CFPlugInInstanceInitialize();
735 #endif //__MACH__
736 __CFUUIDInitialize();
737 #if DEPLOYMENT_TARGET_MACOSX
738 __CFMessagePortInitialize();
739 #endif
740 #if DEPLOYMENT_TARGET_MACOSX
741 __CFMachPortInitialize();
742 #endif
743 __CFStreamInitialize();
744 __CFPreferencesDomainInitialize();
745 #if DEPLOYMENT_TARGET_MACOSX || defined(__WIN32__)
746 __CFRunLoopInitialize();
747 __CFRunLoopObserverInitialize();
748 __CFRunLoopSourceInitialize();
749 __CFRunLoopTimerInitialize();
750 __CFSocketInitialize();
751 #endif
752
753
754 #if DEPLOYMENT_TARGET_MACOSX
755 {
756 CFIndex idx, cnt;
757 char **args;
758 args = *_NSGetArgv();
759 cnt = *_NSGetArgc();
760 CFIndex count;
761 CFStringRef *list, buffer[256];
762 list = (cnt <= 256) ? buffer : malloc(cnt * sizeof(CFStringRef));
763 for (idx = 0, count = 0; idx < cnt; idx++) {
764 if (NULL == args[idx]) continue;
765 list[count] = CFStringCreateWithCString(kCFAllocatorSystemDefault, args[idx], kCFStringEncodingUTF8);
766 if (NULL == list[count]) {
767 list[count] = CFStringCreateWithCString(kCFAllocatorSystemDefault, args[idx], kCFStringEncodingISOLatin1);
768 // We CANNOT use the string SystemEncoding here;
769 // Do not argue: it is not initialized yet, but these
770 // arguments MUST be initialized before it is.
771 // We should just ignore the argument if the UTF-8
772 // conversion fails, but out of charity we try once
773 // more with ISO Latin1, a standard unix encoding.
774 }
775 if (NULL != list[count]) count++;
776 }
777 __CFArgStuff = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)list, count, &kCFTypeArrayCallBacks);
778 }
779 #endif
780 _CFProcessPath(); // cache this early
781
782 if (__CFRuntimeClassTableCount < 256) __CFRuntimeClassTableCount = 256;
783
784 #if defined(DEBUG) && !defined(__WIN32__)
785 CFLog(kCFLogLevelWarning, CFSTR("Assertions enabled"));
786 #endif
787 }
788 }
789
790 //#if defined(__WIN32__)
791
792 #ifdef _BUILD_NET_FOUNDATION_
793 #ifdef __cplusplus
794 extern "C"{
795 #endif //C++
796 extern void _CFFTPCleanup(void);
797 extern void _CFHTTPMessageCleanup(void);
798 extern void _CFHTTPStreamCleanup(void);
799 #ifdef __cplusplus
800 }
801 #endif //C++
802
803 #endif //_BUILD_NET_FOUNDATION_
804
805 #if 0
806
807 /* We have to call __CFInitialize when library is attached to the process.
808 * (Sergey Zubarev)
809 */
810 #if defined(_BUILD_NET_FOUNDATION_)
811 extern "C" {
812 BOOL WINAPI CoreFoundationDllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved );
813 }
814
815 BOOL WINAPI CoreFoundationDllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) {
816 #else
817 BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) {
818 #endif
819 static CFBundleRef cfBundle = NULL;
820 if (dwReason == DLL_PROCESS_ATTACH) {
821 __CFInitialize();
822 cfBundle = RegisterCoreFoundationBundle();
823 } else if (dwReason == DLL_PROCESS_DETACH) {
824 if (cfBundle) CFRelease(cfBundle);
825 #if !0
826 __CFStringCleanup();
827 __CFSocketCleanup();
828 #endif
829 __CFUniCharCleanup();
830 __CFStreamCleanup();
831 __CFBaseCleanup();
832 } else if (dwReason == DLL_THREAD_DETACH) {
833 __CFFinalizeThreadData(NULL);
834 }
835 return TRUE;
836 }
837
838 #endif
839
840 // Functions that avoid ObC dispatch and CF type validation, for use by NSNotifyingCFArray, etc.
841 // Hopefully all of this will just go away. 3321464. M.P. To Do - 7/9/03
842
843 Boolean _CFEqual(CFTypeRef cf1, CFTypeRef cf2) {
844 if (cf1 == cf2) return true;
845 if (NULL == cf1) return false;
846 if (NULL == cf2) return false;
847 __CFGenericAssertIsCF(cf1);
848 __CFGenericAssertIsCF(cf2);
849 if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false;
850 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) {
851 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2);
852 }
853 return false;
854 }
855
856 CFIndex _CFGetRetainCount(CFTypeRef cf) {
857 if (NULL == cf) return 0;
858 if (CF_IS_COLLECTABLE(cf)) {
859 return auto_zone_retain_count(__CFCollectableZone, cf);
860 }
861 uint64_t rc = __CFGetFullRetainCount(cf);
862 return (rc < (uint64_t)LONG_MAX) ? (CFIndex)rc : (CFIndex)LONG_MAX;
863 }
864
865 CFHashCode _CFHash(CFTypeRef cf) {
866 if (NULL == cf) return 0;
867 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash) {
868 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash(cf);
869 }
870 return (CFHashCode)cf;
871 }
872
873 #if 0 || 0
874 static inline bool myOSAtomicCompareAndSwap32Barrier(int32_t oldValue, int32_t newValue, volatile int32_t *theValue) {
875 int32_t actualOldValue = InterlockedCompareExchange((volatile LONG *)theValue, newValue, oldValue);
876 return actualOldValue == oldValue ? true : false;
877 }
878 #else
879 static bool (*myOSAtomicCompareAndSwap32Barrier)(int32_t __oldValue, int32_t __newValue, volatile int32_t *__theValue) = OSAtomicCompareAndSwap32Barrier;
880 #endif
881
882 CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf) {
883 if (NULL == cf) return NULL;
884 #if __LP64__
885 uint32_t lowBits;
886 do {
887 lowBits = ((CFRuntimeBase *)cf)->_rc;
888 if (0 == lowBits) return cf; // Constant CFTypeRef
889 } while (!myOSAtomicCompareAndSwap32Barrier(lowBits, lowBits + 1, (int32_t *)&((CFRuntimeBase *)cf)->_rc));
890 #else
891 #define RC_START 24
892 #define RC_END 31
893 volatile UInt32 *infoLocation = (UInt32 *)&(((CFRuntimeBase *)cf)->_cfinfo);
894 CFIndex rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START);
895 if (__builtin_expect(0 == rcLowBits, 0)) return cf; // Constant CFTypeRef
896 bool success = 0;
897 do {
898 UInt32 initialCheckInfo = *infoLocation;
899 UInt32 prospectiveNewInfo = initialCheckInfo; // don't want compiler to generate prospectiveNewInfo = *infoLocation. This is why infoLocation is declared as a pointer to volatile memory.
900 prospectiveNewInfo += (1 << RC_START);
901 rcLowBits = __CFBitfieldGetValue(prospectiveNewInfo, RC_END, RC_START);
902 if (__builtin_expect((rcLowBits & 0x7f) == 0, 0)) {
903 /* Roll over another bit to the external ref count
904 Real ref count = low 7 bits of info[CF_RC_BITS] + external ref count << 6
905 Bit 8 of low bits indicates that external ref count is in use.
906 External ref count is shifted by 6 rather than 7 so that we can set the low
907 bits to to 1100 0000 rather than 1000 0000.
908 This prevents needing to access the external ref count for successive retains and releases
909 when the composite retain count is right around a multiple of 1 << 7.
910 */
911 prospectiveNewInfo = initialCheckInfo;
912 __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, ((1 << 7) | (1 << 6)));
913 __CFSpinLock(&__CFRuntimeExternRefCountTableLock);
914 success = myOSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
915 if (__builtin_expect(success, 1)) {
916 CFBagAddValue(__CFRuntimeExternRefCountTable, DISGUISE(cf));
917 }
918 __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock);
919 } else {
920 success = myOSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
921 }
922 } while (__builtin_expect(!success, 0));
923 #endif
924 if (__builtin_expect(__CFOASafe, 0)) {
925 __CFRecordAllocationEvent(__kCFRetainEvent, (void *)cf, 0, _CFGetRetainCount(cf), NULL);
926 }
927 return cf;
928 }
929
930 CF_EXPORT void _CFRelease(CFTypeRef cf) {
931 Boolean isAllocator = false;
932 #if __LP64__
933 uint32_t lowBits;
934 do {
935 lowBits = ((CFRuntimeBase *)cf)->_rc;
936 if (0 == lowBits) return; // Constant CFTypeRef
937 if (1 == lowBits) {
938 // CANNOT WRITE ANY NEW VALUE INTO [CF_RC_BITS] UNTIL AFTER FINALIZATION
939 CFTypeID typeID = __CFGenericTypeID_inline(cf);
940 isAllocator = (__kCFAllocatorTypeID_CONST == typeID);
941 CFRuntimeClass *cfClass = __CFRuntimeClassTable[typeID];
942 if (cfClass->version & _kCFRuntimeResourcefulObject && cfClass->reclaim != NULL) {
943 cfClass->reclaim(cf);
944 }
945 void (*func)(CFTypeRef) = __CFRuntimeClassTable[typeID]->finalize;
946 if (NULL != func) {
947 func(cf);
948 }
949 // We recheck lowBits to see if the object has been retained again during
950 // the finalization process. This allows for the finalizer to resurrect,
951 // but the main point is to allow finalizers to be able to manage the
952 // removal of objects from uniquing caches, which may race with other threads
953 // which are allocating (looking up and finding) objects from those caches,
954 // which (that thread) would be the thing doing the extra retain in that case.
955 if (isAllocator || myOSAtomicCompareAndSwap32Barrier(1, 0, (int32_t *)&((CFRuntimeBase *)cf)->_rc)) {
956 goto really_free;
957 }
958 }
959 } while (!myOSAtomicCompareAndSwap32Barrier(lowBits, lowBits - 1, (int32_t *)&((CFRuntimeBase *)cf)->_rc));
960 #else
961 volatile UInt32 *infoLocation = (UInt32 *)&(((CFRuntimeBase *)cf)->_cfinfo);
962 CFIndex rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START);
963 if (__builtin_expect(0 == rcLowBits, 0)) return; // Constant CFTypeRef
964 bool success = 0;
965 do {
966 UInt32 initialCheckInfo = *infoLocation;
967 rcLowBits = __CFBitfieldGetValue(initialCheckInfo, RC_END, RC_START);
968 if (__builtin_expect(1 == rcLowBits, 0)) {
969 // we think cf should be deallocated
970 if (__builtin_expect(__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf), 0)) {
971 if (__builtin_expect(__CFOASafe, 0)) __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL);
972 __CFAllocatorDeallocate((void *)cf);
973 success = 1;
974 } else {
975 // CANNOT WRITE ANY NEW VALUE INTO [CF_RC_BITS] UNTIL AFTER FINALIZATION
976 CFTypeID typeID = __CFGenericTypeID_inline(cf);
977 CFRuntimeClass *cfClass = __CFRuntimeClassTable[typeID];
978 if (cfClass->version & _kCFRuntimeResourcefulObject && cfClass->reclaim != NULL) {
979 cfClass->reclaim(cf);
980 }
981 if (NULL != __CFRuntimeClassTable[typeID]->finalize) {
982 __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->finalize(cf);
983 }
984 // We recheck rcLowBits to see if the object has been retained again during
985 // the finalization process. This allows for the finalizer to resurrect,
986 // but the main point is to allow finalizers to be able to manage the
987 // removal of objects from uniquing caches, which may race with other threads
988 // which are allocating (looking up and finding) objects from those caches,
989 // which (that thread) would be the thing doing the extra retain in that case.
990 rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START);
991 success = (1 == rcLowBits);
992 if (__builtin_expect(success, 1)) {
993 goto really_free;
994 }
995 }
996 } else {
997 // not yet junk
998 UInt32 prospectiveNewInfo = initialCheckInfo; // don't want compiler to generate prospectiveNewInfo = *infoLocation. This is why infoLocation is declared as a pointer to volatile memory.
999 if (__builtin_expect((1 << 7) == rcLowBits, 0)) {
1000 // Time to remove a bit from the external ref count
1001 __CFSpinLock(&__CFRuntimeExternRefCountTableLock);
1002 CFIndex rcHighBitsCnt = CFBagGetCountOfValue(__CFRuntimeExternRefCountTable, DISGUISE(cf));
1003 if (1 == rcHighBitsCnt) {
1004 __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, (1 << 6) - 1);
1005 } else {
1006 __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, ((1 << 6) | (1 << 7)) - 1);
1007 }
1008 success = myOSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
1009 if (__builtin_expect(success, 1)) {
1010 CFBagRemoveValue(__CFRuntimeExternRefCountTable, DISGUISE(cf));
1011 }
1012 __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock);
1013 } else {
1014 prospectiveNewInfo -= (1 << RC_START);
1015 success = myOSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
1016 }
1017 }
1018 } while (__builtin_expect(!success, 0));
1019
1020 #endif
1021 if (__builtin_expect(__CFOASafe, 0)) {
1022 __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, _CFGetRetainCount(cf), NULL);
1023 }
1024 return;
1025
1026 really_free:;
1027 if (__builtin_expect(__CFOASafe, 0)) {
1028 // do not use _CFGetRetainCount() because cf has been freed if it was an allocator
1029 __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL);
1030 }
1031 // cannot zombify allocators, which get deallocated by __CFAllocatorDeallocate (finalize)
1032 if (!isAllocator) {
1033 CFAllocatorRef allocator;
1034 Boolean usesSystemDefaultAllocator;
1035
1036 if (__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 7, 7)) {
1037 allocator = kCFAllocatorSystemDefault;
1038 } else {
1039 allocator = CFGetAllocator(cf);
1040 }
1041 usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault);
1042
1043 if (__CFZombieLevel & (1 << 0)) {
1044 uint8_t *ptr = (uint8_t *)cf - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef));
1045 size_t size = malloc_size(ptr);
1046 uint8_t byte = 0xFC;
1047 if (__CFZombieLevel & (1 << 1)) {
1048 ptr = (uint8_t *)cf + sizeof(CFRuntimeBase);
1049 size = size - sizeof(CFRuntimeBase) - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef));
1050 }
1051 if (__CFZombieLevel & (1 << 7)) {
1052 byte = (__CFZombieLevel >> 8) & 0xFF;
1053 }
1054 memset(ptr, byte, size);
1055 }
1056 if (!(__CFZombieLevel & (1 << 4))) {
1057 CFAllocatorDeallocate(allocator, (uint8_t *)cf - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)));
1058 }
1059
1060 if (kCFAllocatorSystemDefault != allocator) {
1061 CFRelease(allocator);
1062 }
1063 }
1064 }
1065
1066 #undef __kCFAllocatorTypeID_CONST
1067 #undef __CFGenericAssertIsCF
1068