]> git.saurik.com Git - apple/libdispatch.git/blob - src/object.m
953cb0bd61125bbe60f420956a04e9aa6f6950fb
[apple/libdispatch.git] / src / object.m
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
42 static bool _os_object_have_gc;
43 static malloc_zone_t *_os_object_gc_zone;
44
45 static 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
55 static _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
64 static _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
73 DISPATCH_NOINLINE
74 static 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
83 DISPATCH_NOINLINE
84 static 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
93 DISPATCH_NOINLINE
94 static 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
104 DISPATCH_NOINLINE
105 static 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
127 static 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
138 static void*
139 _os_objc_destructInstance(id obj)
140 {
141 // noop if only Libystem is loaded
142 return obj;
143 }
144
145 void
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
175 void
176 _os_object_dealloc(_os_object_t obj)
177 {
178 [_os_object_make_collectable(obj) dealloc];
179 }
180
181 void
182 _os_object_xref_dispose(_os_object_t obj)
183 {
184 [obj _xref_dispose];
185 }
186
187 void
188 _os_object_dispose(_os_object_t obj)
189 {
190 [obj _dispose];
191 }
192
193 #undef os_retain
194 void*
195 os_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
202 void
203 os_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
249 id
250 _dispatch_objc_alloc(Class cls, size_t size)
251 {
252 return _os_objc_alloc(cls, size);
253 }
254
255 void
256 _dispatch_objc_retain(dispatch_object_t dou)
257 {
258 return (void)os_retain(dou);
259 }
260
261 void
262 _dispatch_objc_release(dispatch_object_t dou)
263 {
264 return os_release(dou);
265 }
266
267 void
268 _dispatch_objc_set_context(dispatch_object_t dou, void *context)
269 {
270 return [dou _setContext:context];
271 }
272
273 void *
274 _dispatch_objc_get_context(dispatch_object_t dou)
275 {
276 return [dou _getContext];
277 }
278
279 void
280 _dispatch_objc_set_finalizer_f(dispatch_object_t dou,
281 dispatch_function_t finalizer)
282 {
283 return [dou _setFinalizer:finalizer];
284 }
285
286 void
287 _dispatch_objc_set_target_queue(dispatch_object_t dou, dispatch_queue_t queue)
288 {
289 return [dou _setTargetQueue:queue];
290 }
291
292 void
293 _dispatch_objc_suspend(dispatch_object_t dou)
294 {
295 return [dou _suspend];
296 }
297
298 void
299 _dispatch_objc_resume(dispatch_object_t dou)
300 {
301 return [dou _resume];
302 }
303
304 size_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)
358 DISPATCH_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)
371 DISPATCH_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)
381 DISPATCH_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
395 DISPATCH_CLASS_IMPL(semaphore)
396 DISPATCH_CLASS_IMPL(group)
397 DISPATCH_CLASS_IMPL(queue_root)
398 DISPATCH_CLASS_IMPL(queue_mgr)
399 DISPATCH_CLASS_IMPL(queue_specific_queue)
400 DISPATCH_CLASS_IMPL(queue_attr)
401 DISPATCH_CLASS_IMPL(mach)
402 DISPATCH_CLASS_IMPL(mach_msg)
403 DISPATCH_CLASS_IMPL(io)
404 DISPATCH_CLASS_IMPL(operation)
405 DISPATCH_CLASS_IMPL(disk)
406
407 @implementation OS_OBJECT_CLASS(voucher)
408 DISPATCH_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)
439 DISPATCH_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
464 void *
465 _dispatch_autorelease_pool_push(void) {
466 return objc_autoreleasePoolPush();
467 }
468
469 void
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
484 DISPATCH_NORETURN extern void objc_terminate(void);
485
486 #undef _dispatch_client_callout
487 void
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
499 void
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
511 bool
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
524 void
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.
544 extern void DISPATCH_BLOCK_SPECIAL_INVOKE(void *)
545 asm("____dispatch_block_create_block_invoke");
546 void (*_dispatch_block_special_invoke)(void*) = DISPATCH_BLOCK_SPECIAL_INVOKE;
547
548 dispatch_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