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