blob: 261e1996dc8d2ac2f84cd721012afdbbeb5cf1f5 [file] [log] [blame]
/*
* Copyright (c) 2008-2013 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"
#pragma mark -
#pragma mark _os_object_t
unsigned long
_os_object_retain_count(_os_object_t obj)
{
int xref_cnt = obj->os_obj_xref_cnt;
if (unlikely(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) {
return ULONG_MAX; // global object
}
return (unsigned long)(xref_cnt + 1);
}
DISPATCH_NOINLINE
_os_object_t
_os_object_retain_internal(_os_object_t obj)
{
return _os_object_retain_internal_n_inline(obj, 1);
}
DISPATCH_NOINLINE
_os_object_t
_os_object_retain_internal_n(_os_object_t obj, uint16_t n)
{
return _os_object_retain_internal_n_inline(obj, n);
}
DISPATCH_NOINLINE
void
_os_object_release_internal(_os_object_t obj)
{
return _os_object_release_internal_n_inline(obj, 1);
}
DISPATCH_NOINLINE
void
_os_object_release_internal_n(_os_object_t obj, uint16_t n)
{
return _os_object_release_internal_n_inline(obj, n);
}
DISPATCH_NOINLINE
_os_object_t
_os_object_retain(_os_object_t obj)
{
int xref_cnt = _os_object_xrefcnt_inc_orig(obj);
if (unlikely(xref_cnt < 0)) {
_OS_OBJECT_CLIENT_CRASH("Resurrection of an object");
}
return obj;
}
DISPATCH_NOINLINE
_os_object_t
_os_object_retain_with_resurrect(_os_object_t obj)
{
int xref_cnt = _os_object_xrefcnt_inc_orig(obj) + 1;
if (unlikely(xref_cnt < 0)) {
_OS_OBJECT_CLIENT_CRASH("Resurrection of an over-released object");
}
if (unlikely(xref_cnt == 0)) {
_os_object_retain_internal(obj);
}
return obj;
}
DISPATCH_NOINLINE
void
_os_object_release(_os_object_t obj)
{
int xref_cnt = _os_object_xrefcnt_dec(obj);
if (likely(xref_cnt >= 0)) {
return;
}
if (unlikely(xref_cnt < -1)) {
_OS_OBJECT_CLIENT_CRASH("Over-release of an object");
}
return _os_object_xref_dispose(obj);
}
bool
_os_object_retain_weak(_os_object_t obj)
{
int xref_cnt, nxref_cnt;
os_atomic_rmw_loop2o(obj, os_obj_xref_cnt, xref_cnt, nxref_cnt, relaxed, {
if (unlikely(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) {
os_atomic_rmw_loop_give_up(return true); // global object
}
if (unlikely(xref_cnt == -1)) {
os_atomic_rmw_loop_give_up(return false);
}
if (unlikely(xref_cnt < -1)) {
os_atomic_rmw_loop_give_up(goto overrelease);
}
nxref_cnt = xref_cnt + 1;
});
return true;
overrelease:
_OS_OBJECT_CLIENT_CRASH("Over-release of an object");
}
bool
_os_object_allows_weak_reference(_os_object_t obj)
{
int xref_cnt = obj->os_obj_xref_cnt;
if (unlikely(xref_cnt == -1)) {
return false;
}
if (unlikely(xref_cnt < -1)) {
_OS_OBJECT_CLIENT_CRASH("Over-release of an object");
}
return true;
}
#pragma mark -
#pragma mark dispatch_object_t
void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
#if OS_OBJECT_HAVE_OBJC1
const struct dispatch_object_vtable_s *_vtable = vtable;
dispatch_object_t dou;
dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
dou._do->do_vtable = vtable;
return dou._do;
#else
return _os_object_alloc_realized(vtable, size);
#endif
}
void
_dispatch_object_finalize(dispatch_object_t dou)
{
#if USE_OBJC
objc_destructInstance((id)dou._do);
#else
(void)dou;
#endif
}
void
_dispatch_object_dealloc(dispatch_object_t dou)
{
// so that ddt doesn't pick up bad objects when malloc reuses this memory
dou._os_obj->os_obj_isa = NULL;
#if OS_OBJECT_HAVE_OBJC1
dou._do->do_vtable = NULL;
#endif
free(dou._os_obj);
}
void
dispatch_retain(dispatch_object_t dou)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_retain, dou);
(void)_os_object_retain(dou._os_obj);
}
void
dispatch_release(dispatch_object_t dou)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_release, dou);
_os_object_release(dou._os_obj);
}
#if !USE_OBJC
void
_dispatch_xref_dispose(dispatch_object_t dou)
{
if (dx_cluster(dou._do) == _DISPATCH_QUEUE_CLUSTER) {
_dispatch_queue_xref_dispose(dou._dq);
}
switch (dx_type(dou._do)) {
case DISPATCH_SOURCE_KEVENT_TYPE:
_dispatch_source_xref_dispose(dou._ds);
break;
#if HAVE_MACH
case DISPATCH_MACH_CHANNEL_TYPE:
_dispatch_mach_xref_dispose(dou._dm);
break;
#endif
case DISPATCH_QUEUE_RUNLOOP_TYPE:
_dispatch_runloop_queue_xref_dispose(dou._dl);
break;
}
return _dispatch_release_tailcall(dou._os_obj);
}
#endif
void
_dispatch_dispose(dispatch_object_t dou)
{
dispatch_queue_t tq = dou._do->do_targetq;
dispatch_function_t func = _dispatch_object_finalizer(dou);
void *ctxt = dou._do->do_ctxt;
bool allow_free = true;
if (unlikely(dou._do->do_next != DISPATCH_OBJECT_LISTLESS)) {
DISPATCH_INTERNAL_CRASH(dou._do->do_next, "Release while enqueued");
}
if (unlikely(tq && tq->dq_serialnum == DISPATCH_QUEUE_SERIAL_NUMBER_WLF)) {
// the workloop fallback global queue is never serviced, so redirect
// the finalizer onto a global queue
tq = _dispatch_get_root_queue(DISPATCH_QOS_DEFAULT, false)->_as_dq;
}
dx_dispose(dou._do, &allow_free);
// Past this point, the only thing left of the object is its memory
if (likely(allow_free)) {
_dispatch_object_finalize(dou);
_dispatch_object_dealloc(dou);
}
if (func && ctxt) {
dispatch_async_f(tq, ctxt, func);
}
if (tq) _dispatch_release_tailcall(tq);
}
void *
dispatch_get_context(dispatch_object_t dou)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_get_context, dou);
if (unlikely(dx_hastypeflag(dou._do, NO_CONTEXT))) {
return NULL;
}
return dou._do->do_ctxt;
}
void
dispatch_set_context(dispatch_object_t dou, void *context)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_set_context, dou, context);
if (unlikely(dx_hastypeflag(dou._do, NO_CONTEXT))) {
return;
}
dou._do->do_ctxt = context;
}
void
dispatch_set_finalizer_f(dispatch_object_t dou, dispatch_function_t finalizer)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_set_finalizer_f, dou, finalizer);
if (unlikely(dx_hastypeflag(dou._do, NO_CONTEXT))) {
return;
}
_dispatch_object_set_finalizer(dou, finalizer);
}
void
dispatch_set_target_queue(dispatch_object_t dou, dispatch_queue_t tq)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_set_target_queue, dou, tq);
if (unlikely(_dispatch_object_is_global(dou) ||
_dispatch_object_is_root_or_base_queue(dou))) {
return;
}
if (dx_cluster(dou._do) == _DISPATCH_QUEUE_CLUSTER) {
return _dispatch_lane_set_target_queue(dou._dl, tq);
}
if (dx_type(dou._do) == DISPATCH_IO_TYPE) {
// <rdar://problem/34417216> FIXME: dispatch IO should be a "source"
return _dispatch_io_set_target_queue(dou._dchannel, tq);
}
if (tq == DISPATCH_TARGET_QUEUE_DEFAULT) {
tq = _dispatch_get_default_queue(false);
}
_dispatch_object_set_target_queue_inline(dou._do, tq);
}
void
dispatch_activate(dispatch_object_t dou)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_activate, dou);
if (unlikely(_dispatch_object_is_global(dou))) {
return;
}
if (dx_metatype(dou._do) == _DISPATCH_WORKLOOP_TYPE) {
return _dispatch_workloop_activate(dou._dwl);
}
if (dx_cluster(dou._do) == _DISPATCH_QUEUE_CLUSTER) {
return _dispatch_lane_resume(dou._dl, true);
}
}
void
dispatch_suspend(dispatch_object_t dou)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_suspend, dou);
if (unlikely(_dispatch_object_is_global(dou) ||
_dispatch_object_is_root_or_base_queue(dou))) {
return;
}
if (dx_cluster(dou._do) == _DISPATCH_QUEUE_CLUSTER) {
return _dispatch_lane_suspend(dou._dl);
}
}
void
dispatch_resume(dispatch_object_t dou)
{
DISPATCH_OBJECT_TFB(_dispatch_objc_resume, dou);
if (unlikely(_dispatch_object_is_global(dou) ||
_dispatch_object_is_root_or_base_queue(dou))) {
return;
}
if (dx_cluster(dou._do) == _DISPATCH_QUEUE_CLUSTER) {
_dispatch_lane_resume(dou._dl, false);
}
}
size_t
_dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz)
{
return dsnprintf(buf, bufsiz, "xref = %d, ref = %d, ",
dou._do->do_xref_cnt + 1, dou._do->do_ref_cnt + 1);
}