blob: efee829471e9827563eeb1383f7b8ff41c9ce565 [file] [log] [blame]
/*
* Copyright (c) 2011-2014 Apple Inc. All rights reserved.
*
* @APPLE_APACHE_LICENSE_HEADER_START@
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @APPLE_APACHE_LICENSE_HEADER_END@
*/
#include "internal.h"
#if USE_OBJC
#if _OS_OBJECT_OBJC_ARC
#error "Cannot build with ARC"
#endif
#if defined(__OBJC_GC__)
#error Objective C GC isn't supported anymore
#endif
#if __has_include(<objc/objc-internal.h>)
#include <objc/objc-internal.h>
#else
extern id _Nullable objc_retain(id _Nullable obj) __asm__("_objc_retain");
extern void objc_release(id _Nullable obj) __asm__("_objc_release");
extern void _objc_init(void);
extern void _objc_atfork_prepare(void);
extern void _objc_atfork_parent(void);
extern void _objc_atfork_child(void);
#endif // __has_include(<objc/objc-internal.h>)
#include <objc/objc-exception.h>
#include <Foundation/NSString.h>
// NOTE: this file must not contain any atomic operations
#pragma mark -
#pragma mark _os_object_t
static inline id
_os_objc_alloc(Class cls, size_t size)
{
id obj;
size -= sizeof(((struct _os_object_s *)NULL)->os_obj_isa);
while (!fastpath(obj = class_createInstance(cls, size))) {
_dispatch_temporary_resource_shortage();
}
return obj;
}
static void*
_os_objc_destructInstance(id obj)
{
// noop if only Libystem is loaded
return obj;
}
#if DISPATCH_COCOA_COMPAT
static bool _os_object_debug_missing_pools;
#endif
void
_os_object_init(void)
{
_objc_init();
Block_callbacks_RR callbacks = {
sizeof(Block_callbacks_RR),
(void (*)(const void *))&objc_retain,
(void (*)(const void *))&objc_release,
(void (*)(const void *))&_os_objc_destructInstance
};
_Block_use_RR2(&callbacks);
#if DISPATCH_COCOA_COMPAT
const char *v = getenv("OBJC_DEBUG_MISSING_POOLS");
_os_object_debug_missing_pools = v && !strcmp(v, "YES");
#endif
}
_os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
dispatch_assert(size >= sizeof(struct _os_object_s));
return _os_objc_alloc(cls, size);
}
_os_object_t
_os_object_alloc(const void *_cls, size_t size)
{
dispatch_assert(size >= sizeof(struct _os_object_s));
Class cls = _cls ? [(id)_cls class] : [OS_OBJECT_CLASS(object) class];
return _os_objc_alloc(cls, size);
}
void
_os_object_dealloc(_os_object_t obj)
{
[obj dealloc];
}
void
_os_object_xref_dispose(_os_object_t obj)
{
struct _os_object_s *o = (struct _os_object_s *)obj;
_os_object_xrefcnt_dispose_barrier(o);
[obj _xref_dispose];
}
void
_os_object_dispose(_os_object_t obj)
{
struct _os_object_s *o = (struct _os_object_s *)obj;
_os_object_refcnt_dispose_barrier(o);
[obj _dispose];
}
#undef os_retain
void*
os_retain(void *obj)
{
return objc_retain(obj);
}
#undef os_release
void
os_release(void *obj)
{
return objc_release(obj);
}
void
_os_object_atfork_prepare(void)
{
return _objc_atfork_prepare();
}
void
_os_object_atfork_parent(void)
{
return _objc_atfork_parent();
}
void
_os_object_atfork_child(void)
{
return _objc_atfork_child();
}
#pragma mark -
#pragma mark _os_object
@implementation OS_OBJECT_CLASS(object)
DISPATCH_UNAVAILABLE_INIT()
-(id)retain {
return _os_object_retain(self);
}
-(oneway void)release {
return _os_object_release(self);
}
-(NSUInteger)retainCount {
return _os_object_retain_count(self);
}
-(BOOL)retainWeakReference {
return _os_object_retain_weak(self);
}
-(BOOL)allowsWeakReference {
return _os_object_allows_weak_reference(self);
}
- (void)_xref_dispose {
return _os_object_release_internal(self);
}
- (void)_dispose {
return _os_object_dealloc(self);
}
@end
#pragma mark -
#pragma mark _dispatch_objc
#if OS_OBJECT_HAVE_OBJC2
id
_dispatch_objc_alloc(Class cls, size_t size)
{
return _os_objc_alloc(cls, size);
}
void
_dispatch_objc_retain(dispatch_object_t dou)
{
return (void)os_retain(dou);
}
void
_dispatch_objc_release(dispatch_object_t dou)
{
return os_release(dou);
}
void
_dispatch_objc_set_context(dispatch_object_t dou, void *context)
{
return [dou _setContext:context];
}
void *
_dispatch_objc_get_context(dispatch_object_t dou)
{
return [dou _getContext];
}
void
_dispatch_objc_set_finalizer_f(dispatch_object_t dou,
dispatch_function_t finalizer)
{
return [dou _setFinalizer:finalizer];
}
void
_dispatch_objc_set_target_queue(dispatch_object_t dou, dispatch_queue_t queue)
{
return [dou _setTargetQueue:queue];
}
void
_dispatch_objc_suspend(dispatch_object_t dou)
{
return [dou _suspend];
}
void
_dispatch_objc_resume(dispatch_object_t dou)
{
return [dou _resume];
}
void
_dispatch_objc_activate(dispatch_object_t dou)
{
return [dou _activate];
}
size_t
_dispatch_objc_debug(dispatch_object_t dou, char* buf, size_t bufsiz)
{
NSUInteger offset = 0;
NSString *desc = [dou debugDescription];
[desc getBytes:buf maxLength:bufsiz-1 usedLength:&offset
encoding:NSUTF8StringEncoding options:(NSStringEncodingConversionOptions)0
range:NSMakeRange(0, [desc length]) remainingRange:NULL];
if (offset) buf[offset] = 0;
return offset;
}
#endif
#pragma mark -
#pragma mark _dispatch_object
// Force non-lazy class realization rdar://10640168
#define DISPATCH_OBJC_LOAD() + (void)load {}
@implementation DISPATCH_CLASS(object)
DISPATCH_UNAVAILABLE_INIT()
- (void)_dispose {
return _dispatch_dispose(self); // calls _os_object_dealloc()
}
- (NSString *)debugDescription {
Class nsstring = objc_lookUpClass("NSString");
if (!nsstring) return nil;
char buf[2048];
struct dispatch_object_s *obj = (struct dispatch_object_s *)self;
if (dx_vtable(obj)->do_debug) {
dx_debug(obj, buf, sizeof(buf));
} else {
strlcpy(buf, dx_kind(obj), sizeof(buf));
}
NSString *format = [nsstring stringWithUTF8String:"<%s: %s>"];
if (!format) return nil;
return [nsstring stringWithFormat:format, class_getName([self class]), buf];
}
- (void)dealloc DISPATCH_NORETURN {
DISPATCH_INTERNAL_CRASH(0, "Calling dealloc on a dispatch object");
[super dealloc]; // make clang happy
}
@end
@implementation DISPATCH_CLASS(queue)
DISPATCH_OBJC_LOAD()
DISPATCH_UNAVAILABLE_INIT()
- (NSString *)description {
Class nsstring = objc_lookUpClass("NSString");
if (!nsstring) return nil;
NSString *format = [nsstring stringWithUTF8String:"<%s: %s>"];
if (!format) return nil;
return [nsstring stringWithFormat:format, class_getName([self class]),
dispatch_queue_get_label(self), self];
}
- (void)_xref_dispose {
_dispatch_queue_xref_dispose((struct dispatch_queue_s *)self);
[super _xref_dispose];
}
@end
@implementation DISPATCH_CLASS(source)
DISPATCH_OBJC_LOAD()
DISPATCH_UNAVAILABLE_INIT()
- (void)_xref_dispose {
_dispatch_queue_xref_dispose((struct dispatch_queue_s *)self);
_dispatch_source_xref_dispose(self);
[super _xref_dispose];
}
@end
@implementation DISPATCH_CLASS(mach)
DISPATCH_OBJC_LOAD()
DISPATCH_UNAVAILABLE_INIT()
- (void)_xref_dispose {
_dispatch_queue_xref_dispose((struct dispatch_queue_s *)self);
_dispatch_mach_xref_dispose((struct dispatch_mach_s *)self);
[super _xref_dispose];
}
@end
@implementation DISPATCH_CLASS(queue_runloop)
DISPATCH_OBJC_LOAD()
DISPATCH_UNAVAILABLE_INIT()
- (void)_xref_dispose {
_dispatch_queue_xref_dispose((struct dispatch_queue_s *)self);
_dispatch_runloop_queue_xref_dispose(self);
[super _xref_dispose];
}
@end
#define DISPATCH_CLASS_IMPL(name) \
@implementation DISPATCH_CLASS(name) \
DISPATCH_OBJC_LOAD() \
DISPATCH_UNAVAILABLE_INIT() \
@end
#if !DISPATCH_DATA_IS_BRIDGED_TO_NSDATA
DISPATCH_CLASS_IMPL(data)
#endif
DISPATCH_CLASS_IMPL(semaphore)
DISPATCH_CLASS_IMPL(group)
DISPATCH_CLASS_IMPL(queue_serial)
DISPATCH_CLASS_IMPL(queue_concurrent)
DISPATCH_CLASS_IMPL(queue_main)
DISPATCH_CLASS_IMPL(queue_root)
DISPATCH_CLASS_IMPL(queue_mgr)
DISPATCH_CLASS_IMPL(queue_specific_queue)
DISPATCH_CLASS_IMPL(queue_attr)
DISPATCH_CLASS_IMPL(mach_msg)
DISPATCH_CLASS_IMPL(io)
DISPATCH_CLASS_IMPL(operation)
DISPATCH_CLASS_IMPL(disk)
@implementation OS_OBJECT_CLASS(voucher)
DISPATCH_UNAVAILABLE_INIT()
DISPATCH_OBJC_LOAD()
-(id)retain {
return (id)_voucher_retain_inline((struct voucher_s *)self);
}
-(oneway void)release {
return _voucher_release_inline((struct voucher_s *)self);
}
- (void)_xref_dispose {
return _voucher_xref_dispose(self); // calls _os_object_release_internal()
}
- (void)_dispose {
return _voucher_dispose(self); // calls _os_object_dealloc()
}
- (NSString *)debugDescription {
Class nsstring = objc_lookUpClass("NSString");
if (!nsstring) return nil;
char buf[2048];
_voucher_debug(self, buf, sizeof(buf));
NSString *format = [nsstring stringWithUTF8String:"<%s: %s>"];
if (!format) return nil;
return [nsstring stringWithFormat:format, class_getName([self class]), buf];
}
@end
#if VOUCHER_ENABLE_RECIPE_OBJECTS
@implementation OS_OBJECT_CLASS(voucher_recipe)
DISPATCH_UNAVAILABLE_INIT()
DISPATCH_OBJC_LOAD()
- (void)_dispose {
}
- (NSString *)debugDescription {
return nil; // TODO: voucher_recipe debugDescription
}
@end
#endif
#pragma mark -
#pragma mark dispatch_last_resort_autorelease_pool
#if DISPATCH_COCOA_COMPAT
void
_dispatch_last_resort_autorelease_pool_push(dispatch_invoke_context_t dic)
{
if (!slowpath(_os_object_debug_missing_pools)) {
dic->dic_autorelease_pool = _dispatch_autorelease_pool_push();
}
}
void
_dispatch_last_resort_autorelease_pool_pop(dispatch_invoke_context_t dic)
{
if (!slowpath(_os_object_debug_missing_pools)) {
_dispatch_autorelease_pool_pop(dic->dic_autorelease_pool);
dic->dic_autorelease_pool = NULL;
}
}
#endif // DISPATCH_COCOA_COMPAT
#pragma mark -
#pragma mark dispatch_client_callout
// Abort on uncaught exceptions thrown from client callouts rdar://8577499
#if DISPATCH_USE_CLIENT_CALLOUT && !__USING_SJLJ_EXCEPTIONS__ && \
OS_OBJECT_HAVE_OBJC2
// On platforms with zero-cost exceptions, use a compiler-generated catch-all
// exception handler.
DISPATCH_NORETURN extern void objc_terminate(void);
#undef _dispatch_client_callout
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt);
}
@catch (...) {
objc_terminate();
}
}
#undef _dispatch_client_callout2
void
_dispatch_client_callout2(void *ctxt, size_t i, void (*f)(void *, size_t))
{
@try {
return f(ctxt, i);
}
@catch (...) {
objc_terminate();
}
}
#if HAVE_MACH
#undef _dispatch_client_callout3
void
_dispatch_client_callout3(void *ctxt, dispatch_mach_reason_t reason,
dispatch_mach_msg_t dmsg, dispatch_mach_async_reply_callback_t f)
{
@try {
return f(ctxt, reason, dmsg);
}
@catch (...) {
objc_terminate();
}
}
#undef _dispatch_client_callout4
void
_dispatch_client_callout4(void *ctxt, dispatch_mach_reason_t reason,
dispatch_mach_msg_t dmsg, mach_error_t error,
dispatch_mach_handler_function_t f)
{
@try {
return f(ctxt, reason, dmsg, error);
}
@catch (...) {
objc_terminate();
}
}
#endif // HAVE_MACH
#endif // DISPATCH_USE_CLIENT_CALLOUT
#endif // USE_OBJC