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