1/*
2 * Copyright (c) 2011-2013 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	}
52}
53
54static _os_object_t
55_os_object_make_uncollectable(_os_object_t obj)
56{
57	if (slowpath(_os_object_have_gc)) {
58		auto_zone_retain(_os_object_gc_zone, obj);
59	}
60	return obj;
61}
62
63static _os_object_t
64_os_object_make_collectable(_os_object_t obj)
65{
66	if (slowpath(_os_object_have_gc)) {
67		auto_zone_release(_os_object_gc_zone, obj);
68	}
69	return obj;
70}
71
72#define _os_objc_gc_retain(obj) \
73	if (slowpath(_os_object_have_gc)) { \
74		return auto_zone_retain(_os_object_gc_zone, obj); \
75	}
76
77#define _os_objc_gc_release(obj) \
78	if (slowpath(_os_object_have_gc)) { \
79		return (void)auto_zone_release(_os_object_gc_zone, obj); \
80	}
81
82#else // __OBJC_GC__
83#define _os_object_gc_init()
84#define _os_object_make_uncollectable(obj) (obj)
85#define _os_object_make_collectable(obj) (obj)
86#define _os_objc_gc_retain(obj)
87#define _os_objc_gc_release(obj)
88#endif // __OBJC_GC__
89
90#pragma mark -
91#pragma mark _os_object_t
92
93static inline id
94_os_objc_alloc(Class cls, size_t size)
95{
96	id obj;
97	size -= sizeof(((struct _os_object_s *)NULL)->os_obj_isa);
98	while (!fastpath(obj = class_createInstance(cls, size))) {
99		_dispatch_temporary_resource_shortage();
100	}
101	return obj;
102}
103
104void
105_os_object_init(void)
106{
107	_objc_init();
108	_os_object_gc_init();
109}
110
111_os_object_t
112_os_object_alloc_realized(const void *cls, size_t size)
113{
114	dispatch_assert(size >= sizeof(struct _os_object_s));
115	return _os_object_make_uncollectable(_os_objc_alloc(cls, size));
116}
117
118_os_object_t
119_os_object_alloc(const void *_cls, size_t size)
120{
121	dispatch_assert(size >= sizeof(struct _os_object_s));
122	Class cls = _cls ? [(id)_cls class] : [OS_OBJECT_CLASS(object) class];
123	return _os_object_make_uncollectable(_os_objc_alloc(cls, size));
124}
125
126void
127_os_object_dealloc(_os_object_t obj)
128{
129	[_os_object_make_collectable(obj) dealloc];
130}
131
132void
133_os_object_xref_dispose(_os_object_t obj)
134{
135	[obj _xref_dispose];
136}
137
138void
139_os_object_dispose(_os_object_t obj)
140{
141	[obj _dispose];
142}
143
144#pragma mark -
145#pragma mark _os_object
146
147@implementation OS_OBJECT_CLASS(object)
148
149-(id)retain {
150	return _os_object_retain(self);
151}
152
153-(oneway void)release {
154	return _os_object_release(self);
155}
156
157-(NSUInteger)retainCount {
158	return _os_object_retain_count(self);
159}
160
161-(BOOL)retainWeakReference {
162	return _os_object_retain_weak(self);
163}
164
165-(BOOL)allowsWeakReference {
166	return _os_object_allows_weak_reference(self);
167}
168
169- (void)_xref_dispose {
170	return _os_object_release_internal(self);
171}
172
173- (void)_dispose {
174	return _os_object_dealloc(self);
175}
176
177@end
178
179#pragma mark -
180#pragma mark _dispatch_objc
181
182#include <Foundation/NSString.h>
183
184id
185_dispatch_objc_alloc(Class cls, size_t size)
186{
187	return _os_objc_alloc(cls, size);
188}
189
190void
191_dispatch_objc_retain(dispatch_object_t dou)
192{
193	_os_objc_gc_retain(dou);
194	return (void)[dou retain];
195}
196
197void
198_dispatch_objc_release(dispatch_object_t dou)
199{
200	_os_objc_gc_release(dou);
201	return [dou release];
202}
203
204void
205_dispatch_objc_set_context(dispatch_object_t dou, void *context)
206{
207	return [dou _setContext:context];
208}
209
210void *
211_dispatch_objc_get_context(dispatch_object_t dou)
212{
213	return [dou _getContext];
214}
215
216void
217_dispatch_objc_set_finalizer_f(dispatch_object_t dou,
218		dispatch_function_t finalizer)
219{
220	return [dou _setFinalizer:finalizer];
221}
222
223void
224_dispatch_objc_set_target_queue(dispatch_object_t dou, dispatch_queue_t queue)
225{
226	return [dou _setTargetQueue:queue];
227}
228
229void
230_dispatch_objc_suspend(dispatch_object_t dou)
231{
232	return [dou _suspend];
233}
234
235void
236_dispatch_objc_resume(dispatch_object_t dou)
237{
238	return [dou _resume];
239}
240
241size_t
242_dispatch_objc_debug(dispatch_object_t dou, char* buf, size_t bufsiz)
243{
244	NSUInteger offset = 0;
245	NSString *desc = [dou debugDescription];
246	[desc getBytes:buf maxLength:bufsiz-1 usedLength:&offset
247			encoding:NSUTF8StringEncoding options:0
248			range:NSMakeRange(0, [desc length]) remainingRange:NULL];
249	if (offset) buf[offset] = 0;
250	return offset;
251}
252
253#pragma mark -
254#pragma mark _dispatch_object
255
256// Force non-lazy class realization rdar://10640168
257#define DISPATCH_OBJC_LOAD() + (void)load {}
258
259@implementation DISPATCH_CLASS(object)
260
261- (id)init {
262	self = [super init];
263	[self release];
264	self = nil;
265	return self;
266}
267
268- (void)_xref_dispose {
269	_dispatch_xref_dispose(self);
270	[super _xref_dispose];
271}
272
273- (void)_dispose {
274	return _dispatch_dispose(self); // calls _os_object_dealloc()
275}
276
277- (NSString *)debugDescription {
278	Class nsstring = objc_lookUpClass("NSString");
279	if (!nsstring) return nil;
280	char buf[2048];
281	struct dispatch_object_s *obj = (struct dispatch_object_s *)self;
282	if (obj->do_vtable->do_debug) {
283		dx_debug(obj, buf, sizeof(buf));
284	} else {
285		strlcpy(buf, dx_kind(obj), sizeof(buf));
286	}
287	return [nsstring stringWithFormat:
288			[nsstring stringWithUTF8String:"<%s: %s>"],
289			class_getName([self class]), buf];
290}
291
292@end
293
294@implementation DISPATCH_CLASS(queue)
295DISPATCH_OBJC_LOAD()
296
297- (NSString *)description {
298	Class nsstring = objc_lookUpClass("NSString");
299	if (!nsstring) return nil;
300	return [nsstring stringWithFormat:
301			[nsstring stringWithUTF8String:"<%s: %s[%p]>"],
302			class_getName([self class]), dispatch_queue_get_label(self), self];
303}
304
305@end
306
307@implementation DISPATCH_CLASS(source)
308DISPATCH_OBJC_LOAD()
309
310- (void)_xref_dispose {
311	_dispatch_source_xref_dispose(self);
312	[super _xref_dispose];
313}
314
315@end
316
317@implementation DISPATCH_CLASS(queue_runloop)
318DISPATCH_OBJC_LOAD()
319
320- (void)_xref_dispose {
321	_dispatch_runloop_queue_xref_dispose(self);
322	[super _xref_dispose];
323}
324
325@end
326
327#define DISPATCH_CLASS_IMPL(name) \
328		@implementation DISPATCH_CLASS(name) \
329		DISPATCH_OBJC_LOAD() \
330		@end
331
332DISPATCH_CLASS_IMPL(semaphore)
333DISPATCH_CLASS_IMPL(group)
334DISPATCH_CLASS_IMPL(queue_root)
335DISPATCH_CLASS_IMPL(queue_mgr)
336DISPATCH_CLASS_IMPL(queue_specific_queue)
337DISPATCH_CLASS_IMPL(queue_attr)
338DISPATCH_CLASS_IMPL(mach)
339DISPATCH_CLASS_IMPL(mach_msg)
340DISPATCH_CLASS_IMPL(io)
341DISPATCH_CLASS_IMPL(operation)
342DISPATCH_CLASS_IMPL(disk)
343
344#pragma mark -
345#pragma mark dispatch_autorelease_pool
346
347#if DISPATCH_COCOA_COMPAT
348
349void *
350_dispatch_autorelease_pool_push(void) {
351	return objc_autoreleasePoolPush();
352}
353
354void
355_dispatch_autorelease_pool_pop(void *context) {
356	return objc_autoreleasePoolPop(context);
357}
358
359#endif // DISPATCH_COCOA_COMPAT
360
361#pragma mark -
362#pragma mark dispatch_client_callout
363
364// Abort on uncaught exceptions thrown from client callouts rdar://8577499
365#if DISPATCH_USE_CLIENT_CALLOUT && !__arm__
366// On platforms with zero-cost exceptions, use a compiler-generated catch-all
367// exception handler.
368
369DISPATCH_NORETURN extern void objc_terminate(void);
370
371#undef _dispatch_client_callout
372void
373_dispatch_client_callout(void *ctxt, dispatch_function_t f)
374{
375	@try {
376		return f(ctxt);
377	}
378	@catch (...) {
379		objc_terminate();
380	}
381}
382
383#undef _dispatch_client_callout2
384void
385_dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t))
386{
387	@try {
388		return f(ctxt, i);
389	}
390	@catch (...) {
391		objc_terminate();
392	}
393}
394
395#undef _dispatch_client_callout3
396bool
397_dispatch_client_callout3(void *ctxt, dispatch_data_t region, size_t offset,
398		const void *buffer, size_t size, dispatch_data_applier_function_t f)
399{
400	@try {
401		return f(ctxt, region, offset, buffer, size);
402	}
403	@catch (...) {
404		objc_terminate();
405	}
406}
407
408#undef _dispatch_client_callout4
409void
410_dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason,
411		dispatch_mach_msg_t dmsg, mach_error_t error,
412		dispatch_mach_handler_function_t f)
413{
414	@try {
415		return f(ctxt, reason, dmsg, error);
416	}
417	@catch (...) {
418		objc_terminate();
419	}
420}
421
422#endif // DISPATCH_USE_CLIENT_CALLOUT
423
424#endif // USE_OBJC
425