]> git.saurik.com Git - apple/libdispatch.git/blob - src/object.c
libdispatch-913.1.6.tar.gz
[apple/libdispatch.git] / src / object.c
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
26 unsigned 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
36 DISPATCH_NOINLINE
37 _os_object_t
38 _os_object_retain_internal(_os_object_t obj)
39 {
40 return _os_object_retain_internal_n_inline(obj, 1);
41 }
42
43 DISPATCH_NOINLINE
44 _os_object_t
45 _os_object_retain_internal_n(_os_object_t obj, uint16_t n)
46 {
47 return _os_object_retain_internal_n_inline(obj, n);
48 }
49
50 DISPATCH_NOINLINE
51 void
52 _os_object_release_internal(_os_object_t obj)
53 {
54 return _os_object_release_internal_n_inline(obj, 1);
55 }
56
57 DISPATCH_NOINLINE
58 void
59 _os_object_release_internal_n(_os_object_t obj, uint16_t n)
60 {
61 return _os_object_release_internal_n_inline(obj, n);
62 }
63
64 DISPATCH_NOINLINE
65 _os_object_t
66 _os_object_retain(_os_object_t obj)
67 {
68 int xref_cnt = _os_object_xrefcnt_inc(obj);
69 if (slowpath(xref_cnt <= 0)) {
70 _OS_OBJECT_CLIENT_CRASH("Resurrection of an object");
71 }
72 return obj;
73 }
74
75 DISPATCH_NOINLINE
76 _os_object_t
77 _os_object_retain_with_resurrect(_os_object_t obj)
78 {
79 int xref_cnt = _os_object_xrefcnt_inc(obj);
80 if (slowpath(xref_cnt < 0)) {
81 _OS_OBJECT_CLIENT_CRASH("Resurrection of an over-released object");
82 }
83 if (slowpath(xref_cnt == 0)) {
84 _os_object_retain_internal(obj);
85 }
86 return obj;
87 }
88
89 DISPATCH_NOINLINE
90 void
91 _os_object_release(_os_object_t obj)
92 {
93 int xref_cnt = _os_object_xrefcnt_dec(obj);
94 if (fastpath(xref_cnt >= 0)) {
95 return;
96 }
97 if (slowpath(xref_cnt < -1)) {
98 _OS_OBJECT_CLIENT_CRASH("Over-release of an object");
99 }
100 return _os_object_xref_dispose(obj);
101 }
102
103 bool
104 _os_object_retain_weak(_os_object_t obj)
105 {
106 int xref_cnt, nxref_cnt;
107 os_atomic_rmw_loop2o(obj, os_obj_xref_cnt, xref_cnt, nxref_cnt, relaxed, {
108 if (slowpath(xref_cnt == _OS_OBJECT_GLOBAL_REFCNT)) {
109 os_atomic_rmw_loop_give_up(return true); // global object
110 }
111 if (slowpath(xref_cnt == -1)) {
112 os_atomic_rmw_loop_give_up(return false);
113 }
114 if (slowpath(xref_cnt < -1)) {
115 os_atomic_rmw_loop_give_up(goto overrelease);
116 }
117 nxref_cnt = xref_cnt + 1;
118 });
119 return true;
120 overrelease:
121 _OS_OBJECT_CLIENT_CRASH("Over-release of an object");
122 }
123
124 bool
125 _os_object_allows_weak_reference(_os_object_t obj)
126 {
127 int xref_cnt = obj->os_obj_xref_cnt;
128 if (slowpath(xref_cnt == -1)) {
129 return false;
130 }
131 if (slowpath(xref_cnt < -1)) {
132 _OS_OBJECT_CLIENT_CRASH("Over-release of an object");
133 }
134 return true;
135 }
136
137 #pragma mark -
138 #pragma mark dispatch_object_t
139
140 void *
141 _dispatch_object_alloc(const void *vtable, size_t size)
142 {
143 #if OS_OBJECT_HAVE_OBJC1
144 const struct dispatch_object_vtable_s *_vtable = vtable;
145 dispatch_object_t dou;
146 dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
147 dou._do->do_vtable = vtable;
148 return dou._do;
149 #else
150 return _os_object_alloc_realized(vtable, size);
151 #endif
152 }
153
154 void
155 _dispatch_object_finalize(dispatch_object_t dou)
156 {
157 #if USE_OBJC
158 objc_destructInstance((id)dou._do);
159 #else
160 (void)dou;
161 #endif
162 }
163
164 void
165 _dispatch_object_dealloc(dispatch_object_t dou)
166 {
167 // so that ddt doesn't pick up bad objects when malloc reuses this memory
168 dou._os_obj->os_obj_isa = NULL;
169 #if OS_OBJECT_HAVE_OBJC1
170 dou._do->do_vtable = NULL;
171 #endif
172 free(dou._os_obj);
173 }
174
175 void
176 dispatch_retain(dispatch_object_t dou)
177 {
178 DISPATCH_OBJECT_TFB(_dispatch_objc_retain, dou);
179 (void)_os_object_retain(dou._os_obj);
180 }
181
182 void
183 dispatch_release(dispatch_object_t dou)
184 {
185 DISPATCH_OBJECT_TFB(_dispatch_objc_release, dou);
186 _os_object_release(dou._os_obj);
187 }
188
189 #if !USE_OBJC
190 void
191 _dispatch_xref_dispose(dispatch_object_t dou)
192 {
193 unsigned long metatype = dx_metatype(dou._do);
194 if (metatype == _DISPATCH_QUEUE_TYPE || metatype == _DISPATCH_SOURCE_TYPE) {
195 _dispatch_queue_xref_dispose(dou._dq);
196 }
197 if (dx_type(dou._do) == DISPATCH_SOURCE_KEVENT_TYPE) {
198 _dispatch_source_xref_dispose(dou._ds);
199 #if HAVE_MACH
200 } else if (dx_type(dou._do) == DISPATCH_MACH_CHANNEL_TYPE) {
201 _dispatch_mach_xref_dispose(dou._dm);
202 #endif
203 } else if (dx_type(dou._do) == DISPATCH_QUEUE_RUNLOOP_TYPE) {
204 _dispatch_runloop_queue_xref_dispose(dou._dq);
205 }
206 return _dispatch_release_tailcall(dou._os_obj);
207 }
208 #endif
209
210 void
211 _dispatch_dispose(dispatch_object_t dou)
212 {
213 dispatch_queue_t tq = dou._do->do_targetq;
214 dispatch_function_t func = dou._do->do_finalizer;
215 void *ctxt = dou._do->do_ctxt;
216 bool allow_free = true;
217
218 if (slowpath(dou._do->do_next != DISPATCH_OBJECT_LISTLESS)) {
219 DISPATCH_INTERNAL_CRASH(dou._do->do_next, "Release while enqueued");
220 }
221
222 dx_dispose(dou._do, &allow_free);
223
224 // Past this point, the only thing left of the object is its memory
225 if (likely(allow_free)) {
226 _dispatch_object_finalize(dou);
227 _dispatch_object_dealloc(dou);
228 }
229 if (func && ctxt) {
230 dispatch_async_f(tq, ctxt, func);
231 }
232 if (tq) _dispatch_release_tailcall(tq);
233 }
234
235 void *
236 dispatch_get_context(dispatch_object_t dou)
237 {
238 DISPATCH_OBJECT_TFB(_dispatch_objc_get_context, dou);
239 if (unlikely(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT ||
240 dx_hastypeflag(dou._do, QUEUE_ROOT) ||
241 dx_hastypeflag(dou._do, QUEUE_BASE))) {
242 return NULL;
243 }
244 return dou._do->do_ctxt;
245 }
246
247 void
248 dispatch_set_context(dispatch_object_t dou, void *context)
249 {
250 DISPATCH_OBJECT_TFB(_dispatch_objc_set_context, dou, context);
251 if (unlikely(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT ||
252 dx_hastypeflag(dou._do, QUEUE_ROOT) ||
253 dx_hastypeflag(dou._do, QUEUE_BASE))) {
254 return;
255 }
256 dou._do->do_ctxt = context;
257 }
258
259 void
260 dispatch_set_finalizer_f(dispatch_object_t dou, dispatch_function_t finalizer)
261 {
262 DISPATCH_OBJECT_TFB(_dispatch_objc_set_finalizer_f, dou, finalizer);
263 if (unlikely(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT ||
264 dx_hastypeflag(dou._do, QUEUE_ROOT) ||
265 dx_hastypeflag(dou._do, QUEUE_BASE))) {
266 return;
267 }
268 dou._do->do_finalizer = finalizer;
269 }
270
271 void
272 dispatch_set_target_queue(dispatch_object_t dou, dispatch_queue_t tq)
273 {
274 DISPATCH_OBJECT_TFB(_dispatch_objc_set_target_queue, dou, tq);
275 if (dx_vtable(dou._do)->do_set_targetq) {
276 dx_vtable(dou._do)->do_set_targetq(dou._do, tq);
277 } else if (likely(dou._do->do_ref_cnt != DISPATCH_OBJECT_GLOBAL_REFCNT &&
278 !dx_hastypeflag(dou._do, QUEUE_ROOT) &&
279 !dx_hastypeflag(dou._do, QUEUE_BASE))) {
280 if (slowpath(!tq)) {
281 tq = _dispatch_get_root_queue(DISPATCH_QOS_DEFAULT, false);
282 }
283 _dispatch_object_set_target_queue_inline(dou._do, tq);
284 }
285 }
286
287 void
288 dispatch_activate(dispatch_object_t dou)
289 {
290 DISPATCH_OBJECT_TFB(_dispatch_objc_activate, dou);
291 if (dx_vtable(dou._do)->do_resume) {
292 dx_vtable(dou._do)->do_resume(dou._do, true);
293 }
294 }
295
296 void
297 dispatch_suspend(dispatch_object_t dou)
298 {
299 DISPATCH_OBJECT_TFB(_dispatch_objc_suspend, dou);
300 if (dx_vtable(dou._do)->do_suspend) {
301 dx_vtable(dou._do)->do_suspend(dou._do);
302 }
303 }
304
305 void
306 dispatch_resume(dispatch_object_t dou)
307 {
308 DISPATCH_OBJECT_TFB(_dispatch_objc_resume, dou);
309 // the do_suspend below is not a typo. Having a do_resume but no do_suspend
310 // allows for objects to support activate, but have no-ops suspend/resume
311 if (dx_vtable(dou._do)->do_suspend) {
312 dx_vtable(dou._do)->do_resume(dou._do, false);
313 }
314 }
315
316 size_t
317 _dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz)
318 {
319 return dsnprintf(buf, bufsiz, "xref = %d, ref = %d, ",
320 dou._do->do_xref_cnt + 1, dou._do->do_ref_cnt + 1);
321 }