1/* 2 * Copyright (c) 2008-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#pragma mark - 24#pragma mark _os_object_t 25 26unsigned long 27_os_object_retain_count(_os_object_t obj) 28{ 29 int xref_cnt = obj->os_obj_xref_cnt; 30 if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { 31 return ULONG_MAX; // global object 32 } 33 return (unsigned long)(xref_cnt + 1); 34} 35 36DISPATCH_NOINLINE 37_os_object_t 38_os_object_retain_internal(_os_object_t obj) 39{ 40 int ref_cnt = obj->os_obj_ref_cnt; 41 if (slowpath(ref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { 42 return obj; // global object 43 } 44 ref_cnt = dispatch_atomic_inc2o(obj, os_obj_ref_cnt, relaxed); 45 if (slowpath(ref_cnt <= 0)) { 46 DISPATCH_CRASH("Resurrection of an object"); 47 } 48 return obj; 49} 50 51DISPATCH_NOINLINE 52void 53_os_object_release_internal(_os_object_t obj) 54{ 55 int ref_cnt = obj->os_obj_ref_cnt; 56 if (slowpath(ref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { 57 return; // global object 58 } 59 ref_cnt = dispatch_atomic_dec2o(obj, os_obj_ref_cnt, relaxed); 60 if (fastpath(ref_cnt >= 0)) { 61 return; 62 } 63 if (slowpath(ref_cnt < -1)) { 64 DISPATCH_CRASH("Over-release of an object"); 65 } 66#if DISPATCH_DEBUG 67 if (slowpath(obj->os_obj_xref_cnt >= 0)) { 68 DISPATCH_CRASH("Release while external references exist"); 69 } 70#endif 71 return _os_object_dispose(obj); 72} 73 74DISPATCH_NOINLINE 75_os_object_t 76_os_object_retain(_os_object_t obj) 77{ 78 int xref_cnt = obj->os_obj_xref_cnt; 79 if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { 80 return obj; // global object 81 } 82 xref_cnt = dispatch_atomic_inc2o(obj, os_obj_xref_cnt, relaxed); 83 if (slowpath(xref_cnt <= 0)) { 84 _OS_OBJECT_CLIENT_CRASH("Resurrection of an object"); 85 } 86 return obj; 87} 88 89DISPATCH_NOINLINE 90void 91_os_object_release(_os_object_t obj) 92{ 93 int xref_cnt = obj->os_obj_xref_cnt; 94 if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { 95 return; // global object 96 } 97 xref_cnt = dispatch_atomic_dec2o(obj, os_obj_xref_cnt, relaxed); 98 if (fastpath(xref_cnt >= 0)) { 99 return; 100 } 101 if (slowpath(xref_cnt < -1)) { 102 _OS_OBJECT_CLIENT_CRASH("Over-release of an object"); 103 } 104 return _os_object_xref_dispose(obj); 105} 106 107bool 108_os_object_retain_weak(_os_object_t obj) 109{ 110 int xref_cnt = obj->os_obj_xref_cnt; 111 if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) { 112 return true; // global object 113 } 114retry: 115 if (slowpath(xref_cnt == -1)) { 116 return false; 117 } 118 if (slowpath(xref_cnt < -1)) { 119 goto overrelease; 120 } 121 if (slowpath(!dispatch_atomic_cmpxchgvw2o(obj, os_obj_xref_cnt, xref_cnt, 122 xref_cnt + 1, &xref_cnt, relaxed))) { 123 goto retry; 124 } 125 return true; 126overrelease: 127 _OS_OBJECT_CLIENT_CRASH("Over-release of an object"); 128} 129 130bool 131_os_object_allows_weak_reference(_os_object_t obj) 132{ 133 int xref_cnt = obj->os_obj_xref_cnt; 134 if (slowpath(xref_cnt == -1)) { 135 return false; 136 } 137 if (slowpath(xref_cnt < -1)) { 138 _OS_OBJECT_CLIENT_CRASH("Over-release of an object"); 139 } 140 return true; 141} 142 143#pragma mark - 144#pragma mark dispatch_object_t 145 146void * 147_dispatch_alloc(const void *vtable, size_t size) 148{ 149 return _os_object_alloc_realized(vtable, size); 150} 151 152void 153dispatch_retain(dispatch_object_t dou) 154{ 155 DISPATCH_OBJECT_TFB(_dispatch_objc_retain, dou); 156 (void)_os_object_retain(dou._os_obj); 157} 158 159void 160_dispatch_retain(dispatch_object_t dou) 161{ 162 (void)_os_object_retain_internal(dou._os_obj); 163} 164 165void 166dispatch_release(dispatch_object_t dou) 167{ 168 DISPATCH_OBJECT_TFB(_dispatch_objc_release, dou); 169 _os_object_release(dou._os_obj); 170} 171 172void 173_dispatch_release(dispatch_object_t dou) 174{ 175 _os_object_release_internal(dou._os_obj); 176} 177 178static void 179_dispatch_dealloc(dispatch_object_t dou) 180{ 181 dispatch_queue_t tq = dou._do->do_targetq; 182 dispatch_function_t func = dou._do->do_finalizer; 183 void *ctxt = dou._do->do_ctxt; 184 185 _os_object_dealloc(dou._os_obj); 186 187 if (func && ctxt) { 188 dispatch_async_f(tq, ctxt, func); 189 } 190 _dispatch_release(tq); 191} 192 193void 194_dispatch_xref_dispose(dispatch_object_t dou) 195{ 196 if (slowpath(DISPATCH_OBJECT_SUSPENDED(dou._do))) { 197 // Arguments for and against this assert are within 6705399 198 DISPATCH_CLIENT_CRASH("Release of a suspended object"); 199 } 200#if !USE_OBJC 201 if (dx_type(dou._do) == DISPATCH_SOURCE_KEVENT_TYPE) { 202 _dispatch_source_xref_dispose(dou._ds); 203 } else if (dou._dq->do_vtable == DISPATCH_VTABLE(queue_runloop)) { 204 _dispatch_runloop_queue_xref_dispose(dou._dq); 205 } 206 return _dispatch_release(dou._os_obj); 207#endif 208} 209 210void 211_dispatch_dispose(dispatch_object_t dou) 212{ 213 if (slowpath(dou._do->do_next != DISPATCH_OBJECT_LISTLESS)) { 214 DISPATCH_CRASH("Release while enqueued"); 215 } 216 dx_dispose(dou._do); 217 return _dispatch_dealloc(dou); 218} 219 220void * 221dispatch_get_context(dispatch_object_t dou) 222{ 223 DISPATCH_OBJECT_TFB(_dispatch_objc_get_context, dou); 224 if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) || 225 slowpath(dx_type(dou._do) == DISPATCH_QUEUE_ROOT_TYPE)) { 226 return NULL; 227 } 228 return dou._do->do_ctxt; 229} 230 231void 232dispatch_set_context(dispatch_object_t dou, void *context) 233{ 234 DISPATCH_OBJECT_TFB(_dispatch_objc_set_context, dou, context); 235 if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) || 236 slowpath(dx_type(dou._do) == DISPATCH_QUEUE_ROOT_TYPE)) { 237 return; 238 } 239 dou._do->do_ctxt = context; 240} 241 242void 243dispatch_set_finalizer_f(dispatch_object_t dou, dispatch_function_t finalizer) 244{ 245 DISPATCH_OBJECT_TFB(_dispatch_objc_set_finalizer_f, dou, finalizer); 246 if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) || 247 slowpath(dx_type(dou._do) == DISPATCH_QUEUE_ROOT_TYPE)) { 248 return; 249 } 250 dou._do->do_finalizer = finalizer; 251} 252 253void 254dispatch_suspend(dispatch_object_t dou) 255{ 256 DISPATCH_OBJECT_TFB(_dispatch_objc_suspend, dou); 257 if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) || 258 slowpath(dx_type(dou._do) == DISPATCH_QUEUE_ROOT_TYPE)) { 259 return; 260 } 261 // rdar://8181908 explains why we need to do an internal retain at every 262 // suspension. 263 (void)dispatch_atomic_add2o(dou._do, do_suspend_cnt, 264 DISPATCH_OBJECT_SUSPEND_INTERVAL, relaxed); 265 _dispatch_retain(dou._do); 266} 267 268DISPATCH_NOINLINE 269static void 270_dispatch_resume_slow(dispatch_object_t dou) 271{ 272 _dispatch_wakeup(dou._do); 273 // Balancing the retain() done in suspend() for rdar://8181908 274 _dispatch_release(dou._do); 275} 276 277void 278dispatch_resume(dispatch_object_t dou) 279{ 280 DISPATCH_OBJECT_TFB(_dispatch_objc_resume, dou); 281 // Global objects cannot be suspended or resumed. This also has the 282 // side effect of saturating the suspend count of an object and 283 // guarding against resuming due to overflow. 284 if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) || 285 slowpath(dx_type(dou._do) == DISPATCH_QUEUE_ROOT_TYPE)) { 286 return; 287 } 288 // Check the previous value of the suspend count. If the previous 289 // value was a single suspend interval, the object should be resumed. 290 // If the previous value was less than the suspend interval, the object 291 // has been over-resumed. 292 unsigned int suspend_cnt = dispatch_atomic_sub_orig2o(dou._do, 293 do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_INTERVAL, relaxed); 294 if (fastpath(suspend_cnt > DISPATCH_OBJECT_SUSPEND_INTERVAL)) { 295 // Balancing the retain() done in suspend() for rdar://8181908 296 return _dispatch_release(dou._do); 297 } 298 if (fastpath(suspend_cnt == DISPATCH_OBJECT_SUSPEND_INTERVAL)) { 299 return _dispatch_resume_slow(dou); 300 } 301 DISPATCH_CLIENT_CRASH("Over-resume of an object"); 302} 303 304size_t 305_dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz) 306{ 307 return dsnprintf(buf, bufsiz, "xrefcnt = 0x%x, refcnt = 0x%x, " 308 "suspend_cnt = 0x%x, locked = %d, ", dou._do->do_xref_cnt + 1, 309 dou._do->do_ref_cnt + 1, 310 dou._do->do_suspend_cnt / DISPATCH_OBJECT_SUSPEND_INTERVAL, 311 dou._do->do_suspend_cnt & 1); 312} 313