1/* 2 * Copyright (c) 2011-2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_APACHE_LICENSE_HEADER_START@ 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * @APPLE_APACHE_LICENSE_HEADER_END@ 19 */ 20 21#include "internal.h" 22 23#if USE_OBJC 24 25#if !__OBJC2__ 26#error "Cannot build with legacy ObjC runtime" 27#endif 28#if _OS_OBJECT_OBJC_ARC 29#error "Cannot build with ARC" 30#endif 31 32#include <objc/objc-internal.h> 33#include <objc/objc-exception.h> 34 35#pragma mark - 36#pragma mark _os_object_gc 37 38#if __OBJC_GC__ 39#include <objc/objc-auto.h> 40#include <auto_zone.h> 41 42static bool _os_object_have_gc; 43static malloc_zone_t *_os_object_gc_zone; 44 45static void 46_os_object_gc_init(void) 47{ 48 _os_object_have_gc = objc_collectingEnabled(); 49 if (slowpath(_os_object_have_gc)) { 50 _os_object_gc_zone = objc_collectableZone(); 51 (void)[OS_OBJECT_CLASS(object) class]; // OS_object class realization 52 } 53} 54 55static _os_object_t 56_os_object_make_uncollectable(_os_object_t obj) 57{ 58 if (slowpath(_os_object_have_gc)) { 59 auto_zone_retain(_os_object_gc_zone, obj); 60 } 61 return obj; 62} 63 64static _os_object_t 65_os_object_make_collectable(_os_object_t obj) 66{ 67 if (slowpath(_os_object_have_gc)) { 68 auto_zone_release(_os_object_gc_zone, obj); 69 } 70 return obj; 71} 72 73DISPATCH_NOINLINE 74static id 75_os_objc_gc_retain(id obj) 76{ 77 if (fastpath(obj)) { 78 auto_zone_retain(_os_object_gc_zone, obj); 79 } 80 return obj; 81} 82 83DISPATCH_NOINLINE 84static void 85_os_objc_gc_release(id obj) 86{ 87 if (fastpath(obj)) { 88 (void)auto_zone_release(_os_object_gc_zone, obj); 89 } 90 asm(""); // prevent tailcall 91} 92 93DISPATCH_NOINLINE 94static id 95_os_object_gc_retain(id obj) 96{ 97 if ([obj isKindOfClass:OS_OBJECT_OBJC_CLASS(object)]) { 98 return _os_object_retain(obj); 99 } else { 100 return _os_objc_gc_retain(obj); 101 } 102} 103 104DISPATCH_NOINLINE 105static void 106_os_object_gc_release(id obj) 107{ 108 if ([obj isKindOfClass:OS_OBJECT_OBJC_CLASS(object)]) { 109 return _os_object_release(obj); 110 } else { 111 return _os_objc_gc_release(obj); 112 } 113} 114 115#else // __OBJC_GC__ 116#define _os_object_gc_init() 117#define _os_object_make_uncollectable(obj) (obj) 118#define _os_object_make_collectable(obj) (obj) 119#define _os_object_have_gc 0 120#define _os_object_gc_retain(obj) (obj) 121#define _os_object_gc_release(obj) 122#endif // __OBJC_GC__ 123 124#pragma mark - 125#pragma mark _os_object_t 126 127static inline id 128_os_objc_alloc(Class cls, size_t size) 129{ 130 id obj; 131 size -= sizeof(((struct _os_object_s *)NULL)->os_obj_isa); 132 while (!fastpath(obj = class_createInstance(cls, size))) { 133 _dispatch_temporary_resource_shortage(); 134 } 135 return obj; 136} 137 138static void* 139_os_objc_destructInstance(id obj) 140{ 141 // noop if only Libystem is loaded 142 return obj; 143} 144 145void 146_os_object_init(void) 147{ 148 _objc_init(); 149 _os_object_gc_init(); 150 if (slowpath(_os_object_have_gc)) return; 151 Block_callbacks_RR callbacks = { 152 sizeof(Block_callbacks_RR), 153 (void (*)(const void *))&objc_retain, 154 (void (*)(const void *))&objc_release, 155 (void (*)(const void *))&_os_objc_destructInstance 156 }; 157 _Block_use_RR2(&callbacks); 158} 159 160_os_object_t 161_os_object_alloc_realized(const void *cls, size_t size) 162{ 163 dispatch_assert(size >= sizeof(struct _os_object_s)); 164 return _os_object_make_uncollectable(_os_objc_alloc(cls, size)); 165} 166 167_os_object_t 168_os_object_alloc(const void *_cls, size_t size) 169{ 170 dispatch_assert(size >= sizeof(struct _os_object_s)); 171 Class cls = _cls ? [(id)_cls class] : [OS_OBJECT_CLASS(object) class]; 172 return _os_object_make_uncollectable(_os_objc_alloc(cls, size)); 173} 174 175void 176_os_object_dealloc(_os_object_t obj) 177{ 178 [_os_object_make_collectable(obj) dealloc]; 179} 180 181void 182_os_object_xref_dispose(_os_object_t obj) 183{ 184 [obj _xref_dispose]; 185} 186 187void 188_os_object_dispose(_os_object_t obj) 189{ 190 [obj _dispose]; 191} 192 193#undef os_retain 194void* 195os_retain(void *obj) 196{ 197 if (slowpath(_os_object_have_gc)) return _os_object_gc_retain(obj); 198 return objc_retain(obj); 199} 200 201#undef os_release 202void 203os_release(void *obj) 204{ 205 if (slowpath(_os_object_have_gc)) return _os_object_gc_release(obj); 206 return objc_release(obj); 207} 208 209#pragma mark - 210#pragma mark _os_object 211 212@implementation OS_OBJECT_CLASS(object) 213 214-(id)retain { 215 return _os_object_retain(self); 216} 217 218-(oneway void)release { 219 return _os_object_release(self); 220} 221 222-(NSUInteger)retainCount { 223 return _os_object_retain_count(self); 224} 225 226-(BOOL)retainWeakReference { 227 return _os_object_retain_weak(self); 228} 229 230-(BOOL)allowsWeakReference { 231 return _os_object_allows_weak_reference(self); 232} 233 234- (void)_xref_dispose { 235 return _os_object_release_internal(self); 236} 237 238- (void)_dispose { 239 return _os_object_dealloc(self); 240} 241 242@end 243 244#pragma mark - 245#pragma mark _dispatch_objc 246 247#include <Foundation/NSString.h> 248 249id 250_dispatch_objc_alloc(Class cls, size_t size) 251{ 252 return _os_objc_alloc(cls, size); 253} 254 255void 256_dispatch_objc_retain(dispatch_object_t dou) 257{ 258 return (void)os_retain(dou); 259} 260 261void 262_dispatch_objc_release(dispatch_object_t dou) 263{ 264 return os_release(dou); 265} 266 267void 268_dispatch_objc_set_context(dispatch_object_t dou, void *context) 269{ 270 return [dou _setContext:context]; 271} 272 273void * 274_dispatch_objc_get_context(dispatch_object_t dou) 275{ 276 return [dou _getContext]; 277} 278 279void 280_dispatch_objc_set_finalizer_f(dispatch_object_t dou, 281 dispatch_function_t finalizer) 282{ 283 return [dou _setFinalizer:finalizer]; 284} 285 286void 287_dispatch_objc_set_target_queue(dispatch_object_t dou, dispatch_queue_t queue) 288{ 289 return [dou _setTargetQueue:queue]; 290} 291 292void 293_dispatch_objc_suspend(dispatch_object_t dou) 294{ 295 return [dou _suspend]; 296} 297 298void 299_dispatch_objc_resume(dispatch_object_t dou) 300{ 301 return [dou _resume]; 302} 303 304size_t 305_dispatch_objc_debug(dispatch_object_t dou, char* buf, size_t bufsiz) 306{ 307 NSUInteger offset = 0; 308 NSString *desc = [dou debugDescription]; 309 [desc getBytes:buf maxLength:bufsiz-1 usedLength:&offset 310 encoding:NSUTF8StringEncoding options:0 311 range:NSMakeRange(0, [desc length]) remainingRange:NULL]; 312 if (offset) buf[offset] = 0; 313 return offset; 314} 315 316#pragma mark - 317#pragma mark _dispatch_object 318 319// Force non-lazy class realization rdar://10640168 320#define DISPATCH_OBJC_LOAD() + (void)load {} 321 322@implementation DISPATCH_CLASS(object) 323 324- (id)init { 325 self = [super init]; 326 [self release]; 327 self = nil; 328 return self; 329} 330 331- (void)_xref_dispose { 332 _dispatch_xref_dispose(self); 333 [super _xref_dispose]; 334} 335 336- (void)_dispose { 337 return _dispatch_dispose(self); // calls _os_object_dealloc() 338} 339 340- (NSString *)debugDescription { 341 Class nsstring = objc_lookUpClass("NSString"); 342 if (!nsstring) return nil; 343 char buf[2048]; 344 struct dispatch_object_s *obj = (struct dispatch_object_s *)self; 345 if (obj->do_vtable->do_debug) { 346 dx_debug(obj, buf, sizeof(buf)); 347 } else { 348 strlcpy(buf, dx_kind(obj), sizeof(buf)); 349 } 350 return [nsstring stringWithFormat: 351 [nsstring stringWithUTF8String:"<%s: %s>"], 352 class_getName([self class]), buf]; 353} 354 355@end 356 357@implementation DISPATCH_CLASS(queue) 358DISPATCH_OBJC_LOAD() 359 360- (NSString *)description { 361 Class nsstring = objc_lookUpClass("NSString"); 362 if (!nsstring) return nil; 363 return [nsstring stringWithFormat: 364 [nsstring stringWithUTF8String:"<%s: %s[%p]>"], 365 class_getName([self class]), dispatch_queue_get_label(self), self]; 366} 367 368@end 369 370@implementation DISPATCH_CLASS(source) 371DISPATCH_OBJC_LOAD() 372 373- (void)_xref_dispose { 374 _dispatch_source_xref_dispose(self); 375 [super _xref_dispose]; 376} 377 378@end 379 380@implementation DISPATCH_CLASS(queue_runloop) 381DISPATCH_OBJC_LOAD() 382 383- (void)_xref_dispose { 384 _dispatch_runloop_queue_xref_dispose(self); 385 [super _xref_dispose]; 386} 387 388@end 389 390#define DISPATCH_CLASS_IMPL(name) \ 391 @implementation DISPATCH_CLASS(name) \ 392 DISPATCH_OBJC_LOAD() \ 393 @end 394 395DISPATCH_CLASS_IMPL(semaphore) 396DISPATCH_CLASS_IMPL(group) 397DISPATCH_CLASS_IMPL(queue_root) 398DISPATCH_CLASS_IMPL(queue_mgr) 399DISPATCH_CLASS_IMPL(queue_specific_queue) 400DISPATCH_CLASS_IMPL(queue_attr) 401DISPATCH_CLASS_IMPL(mach) 402DISPATCH_CLASS_IMPL(mach_msg) 403DISPATCH_CLASS_IMPL(io) 404DISPATCH_CLASS_IMPL(operation) 405DISPATCH_CLASS_IMPL(disk) 406 407@implementation OS_OBJECT_CLASS(voucher) 408DISPATCH_OBJC_LOAD() 409 410- (id)init { 411 self = [super init]; 412 [self release]; 413 self = nil; 414 return self; 415} 416 417- (void)_xref_dispose { 418 return _voucher_xref_dispose(self); // calls _os_object_release_internal() 419} 420 421- (void)_dispose { 422 return _voucher_dispose(self); // calls _os_object_dealloc() 423} 424 425- (NSString *)debugDescription { 426 Class nsstring = objc_lookUpClass("NSString"); 427 if (!nsstring) return nil; 428 char buf[2048]; 429 _voucher_debug(self, buf, sizeof(buf)); 430 return [nsstring stringWithFormat: 431 [nsstring stringWithUTF8String:"<%s: %s>"], 432 class_getName([self class]), buf]; 433} 434 435@end 436 437#if VOUCHER_ENABLE_RECIPE_OBJECTS 438@implementation OS_OBJECT_CLASS(voucher_recipe) 439DISPATCH_OBJC_LOAD() 440 441- (id)init { 442 self = [super init]; 443 [self release]; 444 self = nil; 445 return self; 446} 447 448- (void)_dispose { 449 450} 451 452- (NSString *)debugDescription { 453 return nil; // TODO: voucher_recipe debugDescription 454} 455 456@end 457#endif 458 459#pragma mark - 460#pragma mark dispatch_autorelease_pool 461 462#if DISPATCH_COCOA_COMPAT 463 464void * 465_dispatch_autorelease_pool_push(void) { 466 return objc_autoreleasePoolPush(); 467} 468 469void 470_dispatch_autorelease_pool_pop(void *context) { 471 return objc_autoreleasePoolPop(context); 472} 473 474#endif // DISPATCH_COCOA_COMPAT 475 476#pragma mark - 477#pragma mark dispatch_client_callout 478 479// Abort on uncaught exceptions thrown from client callouts rdar://8577499 480#if DISPATCH_USE_CLIENT_CALLOUT && !__USING_SJLJ_EXCEPTIONS__ 481// On platforms with zero-cost exceptions, use a compiler-generated catch-all 482// exception handler. 483 484DISPATCH_NORETURN extern void objc_terminate(void); 485 486#undef _dispatch_client_callout 487void 488_dispatch_client_callout(void *ctxt, dispatch_function_t f) 489{ 490 @try { 491 return f(ctxt); 492 } 493 @catch (...) { 494 objc_terminate(); 495 } 496} 497 498#undef _dispatch_client_callout2 499void 500_dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t)) 501{ 502 @try { 503 return f(ctxt, i); 504 } 505 @catch (...) { 506 objc_terminate(); 507 } 508} 509 510#undef _dispatch_client_callout3 511bool 512_dispatch_client_callout3(void *ctxt, dispatch_data_t region, size_t offset, 513 const void *buffer, size_t size, dispatch_data_applier_function_t f) 514{ 515 @try { 516 return f(ctxt, region, offset, buffer, size); 517 } 518 @catch (...) { 519 objc_terminate(); 520 } 521} 522 523#undef _dispatch_client_callout4 524void 525_dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason, 526 dispatch_mach_msg_t dmsg, mach_error_t error, 527 dispatch_mach_handler_function_t f) 528{ 529 @try { 530 return f(ctxt, reason, dmsg, error); 531 } 532 @catch (...) { 533 objc_terminate(); 534 } 535} 536 537#endif // DISPATCH_USE_CLIENT_CALLOUT 538 539#pragma mark - 540#pragma mark _dispatch_block_create 541 542// The compiler hides the name of the function it generates, and changes it if 543// we try to reference it directly, but the linker still sees it. 544extern void DISPATCH_BLOCK_SPECIAL_INVOKE(void *) 545 asm("____dispatch_block_create_block_invoke"); 546void (*_dispatch_block_special_invoke)(void*) = DISPATCH_BLOCK_SPECIAL_INVOKE; 547 548dispatch_block_t 549_dispatch_block_create(dispatch_block_flags_t flags, voucher_t voucher, 550 pthread_priority_t pri, dispatch_block_t block) 551{ 552 dispatch_block_t copy_block = _dispatch_Block_copy(block); // 17094902 553 struct dispatch_block_private_data_s dbpds = 554 DISPATCH_BLOCK_PRIVATE_DATA_INITIALIZER(flags, voucher, pri, copy_block); 555 dispatch_block_t new_block = _dispatch_Block_copy(^{ 556 // Capture object references, which retains copy_block and voucher. 557 // All retained objects must be captured by the *block*. We 558 // cannot borrow any references, because the block might be 559 // called zero or several times, so Block_release() is the 560 // only place that can release retained objects. 561 (void)copy_block; 562 (void)voucher; 563 _dispatch_block_invoke(&dbpds); 564 }); 565 Block_release(copy_block); 566 return new_block; 567} 568 569#endif // USE_OBJC 570