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