]>
Commit | Line | Data |
---|---|---|
9ce05555 | 1 | /* |
8ca704e1 | 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. |
9ce05555 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
9ce05555 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
f64f9b69 | 23 | |
9ce05555 | 24 | /* CFMachPort.c |
8ca704e1 | 25 | Copyright (c) 1998-2011, Apple Inc. All rights reserved. |
9ce05555 A |
26 | Responsibility: Christopher Kane |
27 | */ | |
28 | ||
9ce05555 A |
29 | #include <CoreFoundation/CFMachPort.h> |
30 | #include <CoreFoundation/CFRunLoop.h> | |
cf7d2af9 A |
31 | #include <CoreFoundation/CFArray.h> |
32 | #include <dispatch/dispatch.h> | |
9ce05555 | 33 | #include <mach/mach.h> |
bd5b749c | 34 | #include <dlfcn.h> |
cf7d2af9 A |
35 | #include "CFInternal.h" |
36 | ||
37 | #define AVOID_WEAK_COLLECTIONS 1 | |
9ce05555 | 38 | |
7c97c3e0 A |
39 | #if DEPLOYMENT_TARGET_EMBEDDED |
40 | #define AVOID_WEAK_COLLECTIONS 1 | |
41 | #endif | |
42 | ||
cf7d2af9 A |
43 | #if !defined(AVOID_WEAK_COLLECTIONS) |
44 | #import "CFPointerArray.h" | |
45 | #endif | |
46 | ||
47 | DISPATCH_HELPER_FUNCTIONS(port, CFMachPort) | |
48 | ||
49 | ||
50 | enum { | |
51 | kCFMachPortStateReady = 0, | |
52 | kCFMachPortStateInvalidating = 1, | |
53 | kCFMachPortStateInvalid = 2, | |
54 | kCFMachPortStateDeallocating = 3 | |
55 | }; | |
9ce05555 A |
56 | |
57 | struct __CFMachPort { | |
58 | CFRuntimeBase _base; | |
cf7d2af9 A |
59 | int32_t _state; |
60 | mach_port_t _port; /* immutable */ | |
61 | dispatch_source_t _dsrc; | |
62 | dispatch_source_t _dsrc2; | |
63 | dispatch_semaphore_t _dsrc_sem; | |
64 | dispatch_semaphore_t _dsrc2_sem; | |
9ce05555 | 65 | CFMachPortInvalidationCallBack _icallout; |
cf7d2af9 A |
66 | CFRunLoopSourceRef _source; /* immutable, once created */ |
67 | CFMachPortCallBack _callout; /* immutable */ | |
68 | CFMachPortContext _context; /* immutable */ | |
9ce05555 A |
69 | }; |
70 | ||
9ce05555 A |
71 | /* Bit 1 in the base reserved bits is used for has-receive-ref state */ |
72 | /* Bit 2 in the base reserved bits is used for has-send-ref state */ | |
cf7d2af9 | 73 | /* Bit 3 in the base reserved bits is used for has-send-ref2 state */ |
9ce05555 A |
74 | |
75 | CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) { | |
bd5b749c | 76 | return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1); |
9ce05555 A |
77 | } |
78 | ||
79 | CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) { | |
bd5b749c | 80 | __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1, 1); |
9ce05555 A |
81 | } |
82 | ||
83 | CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) { | |
bd5b749c | 84 | return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2); |
9ce05555 A |
85 | } |
86 | ||
87 | CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) { | |
bd5b749c | 88 | __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2, 1); |
9ce05555 A |
89 | } |
90 | ||
cf7d2af9 | 91 | CF_INLINE Boolean __CFMachPortHasSend2(CFMachPortRef mp) { |
bd5b749c | 92 | return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3); |
9ce05555 A |
93 | } |
94 | ||
cf7d2af9 | 95 | CF_INLINE void __CFMachPortSetHasSend2(CFMachPortRef mp) { |
bd5b749c | 96 | __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3, 1); |
9ce05555 A |
97 | } |
98 | ||
cf7d2af9 A |
99 | CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) { |
100 | return kCFMachPortStateReady == mp->_state; | |
9ce05555 A |
101 | } |
102 | ||
9ce05555 | 103 | |
9ce05555 | 104 | void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) { |
9ce05555 A |
105 | } |
106 | ||
107 | static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) { | |
108 | CFMachPortRef mp1 = (CFMachPortRef)cf1; | |
109 | CFMachPortRef mp2 = (CFMachPortRef)cf2; | |
9ce05555 A |
110 | return (mp1->_port == mp2->_port); |
111 | } | |
112 | ||
113 | static CFHashCode __CFMachPortHash(CFTypeRef cf) { | |
114 | CFMachPortRef mp = (CFMachPortRef)cf; | |
9ce05555 A |
115 | return (CFHashCode)mp->_port; |
116 | } | |
117 | ||
118 | static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) { | |
119 | CFMachPortRef mp = (CFMachPortRef)cf; | |
9ce05555 | 120 | CFStringRef contextDesc = NULL; |
9ce05555 | 121 | if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) { |
cf7d2af9 | 122 | contextDesc = mp->_context.copyDescription(mp->_context.info); |
9ce05555 A |
123 | } |
124 | if (NULL == contextDesc) { | |
cf7d2af9 | 125 | contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info); |
9ce05555 | 126 | } |
bd5b749c | 127 | Dl_info info; |
cf7d2af9 | 128 | void *addr = mp->_callout; |
bd5b749c | 129 | const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; |
cf7d2af9 | 130 | CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMachPort %p [%p]>{valid = %s, port = %p, source = %p, callout = %s (%p), context = %@}"), cf, CFGetAllocator(mp), (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, name, addr, contextDesc); |
9ce05555 | 131 | if (NULL != contextDesc) { |
cf7d2af9 | 132 | CFRelease(contextDesc); |
9ce05555 A |
133 | } |
134 | return result; | |
135 | } | |
136 | ||
137 | static void __CFMachPortDeallocate(CFTypeRef cf) { | |
cf7d2af9 | 138 | CHECK_FOR_FORK_RET(); |
9ce05555 | 139 | CFMachPortRef mp = (CFMachPortRef)cf; |
cf7d2af9 A |
140 | |
141 | // CFMachPortRef is invalid before we get here, except under GC | |
142 | __block CFRunLoopSourceRef source = NULL; | |
143 | __block Boolean wasReady = false; | |
144 | void (^block)(void) = ^{ | |
145 | wasReady = (mp->_state == kCFMachPortStateReady); | |
146 | if (wasReady) { | |
147 | mp->_state = kCFMachPortStateInvalidating; | |
148 | OSMemoryBarrier(); | |
149 | if (mp->_dsrc) { | |
150 | dispatch_source_cancel(mp->_dsrc); | |
151 | mp->_dsrc = NULL; | |
152 | } | |
153 | if (mp->_dsrc2) { | |
154 | dispatch_source_cancel(mp->_dsrc2); | |
155 | mp->_dsrc2 = NULL; | |
156 | } | |
157 | source = mp->_source; | |
158 | mp->_source = NULL; | |
159 | } | |
160 | }; | |
161 | if (!__portSyncDispatchIsSafe(__portQueue())) { | |
162 | block(); | |
163 | } else { | |
164 | dispatch_sync(__portQueue(), block); | |
9ce05555 | 165 | } |
cf7d2af9 A |
166 | if (wasReady) { |
167 | CFMachPortInvalidationCallBack cb = mp->_icallout; | |
168 | if (cb) { | |
169 | cb(mp, mp->_context.info); | |
170 | } | |
171 | if (NULL != source) { | |
172 | CFRunLoopSourceInvalidate(source); | |
173 | CFRelease(source); | |
174 | } | |
175 | void *info = mp->_context.info; | |
176 | mp->_context.info = NULL; | |
177 | if (mp->_context.release) { | |
178 | mp->_context.release(info); | |
179 | } | |
180 | mp->_state = kCFMachPortStateInvalid; | |
181 | OSMemoryBarrier(); | |
9ce05555 | 182 | } |
cf7d2af9 A |
183 | mp->_state = kCFMachPortStateDeallocating; |
184 | ||
185 | // hand ownership of the port and semaphores to the block below | |
186 | mach_port_t port = mp->_port; | |
187 | dispatch_semaphore_t sem1 = mp->_dsrc_sem; | |
188 | dispatch_semaphore_t sem2 = mp->_dsrc2_sem; | |
189 | Boolean doSend2 = __CFMachPortHasSend2(mp), doSend = __CFMachPortHasSend(mp), doReceive = __CFMachPortHasReceive(mp); | |
190 | ||
7c97c3e0 | 191 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ |
cf7d2af9 A |
192 | if (sem1) { |
193 | dispatch_semaphore_wait(sem1, DISPATCH_TIME_FOREVER); | |
194 | // immediate release is only safe if dispatch_semaphore_signal() does not touch the semaphore after doing the signal bit | |
195 | dispatch_release(sem1); | |
196 | } | |
197 | if (sem2) { | |
198 | dispatch_semaphore_wait(sem2, DISPATCH_TIME_FOREVER); | |
199 | // immediate release is only safe if dispatch_semaphore_signal() does not touch the semaphore after doing the signal bit | |
200 | dispatch_release(sem2); | |
201 | } | |
202 | ||
203 | // MUST deallocate the send right FIRST if necessary, | |
204 | // then the receive right if necessary. Don't ask me why; | |
205 | // if it's done in the other order the port will leak. | |
206 | if (doSend2) { | |
207 | mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1); | |
208 | } | |
209 | if (doSend) { | |
210 | mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1); | |
211 | } | |
212 | if (doReceive) { | |
213 | mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); | |
214 | } | |
215 | }); | |
9ce05555 A |
216 | } |
217 | ||
cf7d2af9 A |
218 | #if defined(AVOID_WEAK_COLLECTIONS) |
219 | static CFMutableArrayRef __CFAllMachPorts = NULL; | |
220 | #else | |
221 | static __CFPointerArray *__CFAllMachPorts = nil; | |
222 | #endif | |
223 | ||
224 | static Boolean __CFMachPortCheck(mach_port_t port) { | |
225 | mach_port_type_t type = 0; | |
226 | kern_return_t ret = mach_port_type(mach_task_self(), port, &type); | |
8ca704e1 | 227 | return (KERN_SUCCESS != ret || (0 == (type & MACH_PORT_TYPE_PORT_RIGHTS))) ? false : true; |
cf7d2af9 A |
228 | } |
229 | ||
230 | static void __CFMachPortChecker(Boolean fromTimer) { // only call on __portQueue() | |
231 | #if defined(AVOID_WEAK_COLLECTIONS) | |
232 | for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) { | |
233 | CFMachPortRef mp = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx); | |
234 | #else | |
235 | for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) { | |
236 | CFMachPortRef mp = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx]; | |
237 | #endif | |
238 | if (!mp) continue; | |
239 | // second clause cleans no-longer-wanted CFMachPorts out of our strong table | |
240 | if (!__CFMachPortCheck(mp->_port) || (!kCFUseCollectableAllocator && 1 == CFGetRetainCount(mp))) { | |
241 | CFRunLoopSourceRef source = NULL; | |
242 | Boolean wasReady = (mp->_state == kCFMachPortStateReady); | |
243 | if (wasReady) { | |
244 | mp->_state = kCFMachPortStateInvalidating; | |
245 | OSMemoryBarrier(); | |
246 | if (mp->_dsrc) { | |
247 | dispatch_source_cancel(mp->_dsrc); | |
248 | mp->_dsrc = NULL; | |
249 | } | |
250 | if (mp->_dsrc2) { | |
251 | dispatch_source_cancel(mp->_dsrc2); | |
252 | mp->_dsrc2 = NULL; | |
253 | } | |
254 | source = mp->_source; | |
255 | mp->_source = NULL; | |
256 | CFRetain(mp); | |
257 | dispatch_async(dispatch_get_main_queue(), ^{ | |
258 | CFMachPortInvalidationCallBack cb = mp->_icallout; | |
259 | if (cb) { | |
260 | cb(mp, mp->_context.info); | |
261 | } | |
262 | if (NULL != source) { | |
263 | CFRunLoopSourceInvalidate(source); | |
264 | CFRelease(source); | |
265 | } | |
266 | void *info = mp->_context.info; | |
267 | mp->_context.info = NULL; | |
268 | if (mp->_context.release) { | |
269 | mp->_context.release(info); | |
270 | } | |
271 | // For hashing and equality purposes, cannot get rid of _port here | |
272 | mp->_state = kCFMachPortStateInvalid; | |
273 | OSMemoryBarrier(); | |
274 | CFRelease(mp); | |
275 | }); | |
276 | } | |
277 | #if defined(AVOID_WEAK_COLLECTIONS) | |
278 | CFArrayRemoveValueAtIndex(__CFAllMachPorts, idx); | |
279 | #else | |
280 | [__CFAllMachPorts removePointerAtIndex:idx]; | |
281 | #endif | |
282 | idx--; | |
283 | cnt--; | |
284 | } | |
285 | } | |
286 | #if !defined(AVOID_WEAK_COLLECTIONS) | |
287 | [__CFAllMachPorts compact]; | |
288 | #endif | |
289 | }; | |
290 | ||
291 | ||
9ce05555 A |
292 | static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID; |
293 | ||
294 | static const CFRuntimeClass __CFMachPortClass = { | |
295 | 0, | |
296 | "CFMachPort", | |
297 | NULL, // init | |
298 | NULL, // copy | |
299 | __CFMachPortDeallocate, | |
300 | __CFMachPortEqual, | |
301 | __CFMachPortHash, | |
302 | NULL, // | |
303 | __CFMachPortCopyDescription | |
304 | }; | |
305 | ||
306 | __private_extern__ void __CFMachPortInitialize(void) { | |
307 | __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass); | |
9ce05555 A |
308 | } |
309 | ||
310 | CFTypeID CFMachPortGetTypeID(void) { | |
9ce05555 A |
311 | return __kCFMachPortTypeID; |
312 | } | |
313 | ||
9ce05555 A |
314 | /* Note: any receive or send rights that the port contains coming in will |
315 | * not be cleaned up by CFMachPort; it will increment and decrement | |
316 | * references on the port if the kernel ever allows that in the future, | |
317 | * but will not cleanup any references you got when you got the port. */ | |
cf7d2af9 | 318 | CFMachPortRef _CFMachPortCreateWithPort2(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo, Boolean deathWatch) { |
9ce05555 | 319 | if (shouldFreeInfo) *shouldFreeInfo = true; |
cf7d2af9 A |
320 | CHECK_FOR_FORK_RET(NULL); |
321 | ||
322 | mach_port_type_t type = 0; | |
323 | kern_return_t ret = mach_port_type(mach_task_self(), port, &type); | |
8ca704e1 | 324 | if (KERN_SUCCESS != ret || (0 == (type & MACH_PORT_TYPE_PORT_RIGHTS))) { |
cf7d2af9 A |
325 | if (type & ~MACH_PORT_TYPE_DEAD_NAME) { |
326 | CFLog(kCFLogLevelError, CFSTR("*** CFMachPortCreateWithPort(): bad Mach port parameter (0x%lx) or unsupported mysterious kind of Mach port (%d, %ld)"), (unsigned long)port, ret, (unsigned long)type); | |
327 | } | |
328 | return NULL; | |
9ce05555 | 329 | } |
cf7d2af9 A |
330 | |
331 | __block CFMachPortRef mp = NULL; | |
332 | dispatch_sync(__portQueue(), ^{ | |
8ca704e1 A |
333 | static dispatch_source_t timerSource = NULL; |
334 | if (timerSource == NULL) { | |
cf7d2af9 | 335 | uint64_t nanos = 63 * 1000 * 1000 * 1000ULL; |
7c97c3e0 | 336 | uint64_t leeway = 9 * 1000ULL; |
8ca704e1 | 337 | timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, __portQueue()); |
7c97c3e0 A |
338 | dispatch_source_set_timer(timerSource, dispatch_time(DISPATCH_TIME_NOW, nanos), nanos, leeway); |
339 | dispatch_source_set_event_handler(timerSource, ^{ | |
340 | __CFMachPortChecker(true); | |
341 | }); | |
342 | dispatch_resume(timerSource); | |
cf7d2af9 A |
343 | } |
344 | ||
345 | #if defined(AVOID_WEAK_COLLECTIONS) | |
346 | for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) { | |
347 | CFMachPortRef p = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx); | |
348 | if (p && p->_port == port) { | |
349 | CFRetain(p); | |
350 | mp = p; | |
351 | return; | |
352 | } | |
353 | } | |
354 | #else | |
355 | for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) { | |
356 | CFMachPortRef p = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx]; | |
357 | if (p && p->_port == port) { | |
358 | CFRetain(p); | |
359 | mp = p; | |
360 | return; | |
361 | } | |
362 | } | |
363 | #endif | |
364 | ||
365 | CFIndex size = sizeof(struct __CFMachPort) - sizeof(CFRuntimeBase); | |
366 | CFMachPortRef memory = (CFMachPortRef)_CFRuntimeCreateInstance(allocator, CFMachPortGetTypeID(), size, NULL); | |
367 | if (NULL == memory) { | |
368 | return; | |
369 | } | |
370 | memory->_port = port; | |
371 | memory->_dsrc = NULL; | |
372 | memory->_dsrc2 = NULL; | |
373 | memory->_dsrc_sem = NULL; | |
374 | memory->_dsrc2_sem = NULL; | |
375 | memory->_icallout = NULL; | |
376 | memory->_source = NULL; | |
377 | memory->_context.info = NULL; | |
378 | memory->_context.retain = NULL; | |
379 | memory->_context.release = NULL; | |
380 | memory->_context.copyDescription = NULL; | |
381 | memory->_callout = callout; | |
382 | if (NULL != context) { | |
383 | objc_memmove_collectable(&memory->_context, context, sizeof(CFMachPortContext)); | |
384 | memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info; | |
385 | } | |
386 | memory->_state = kCFMachPortStateReady; | |
387 | #if defined(AVOID_WEAK_COLLECTIONS) | |
388 | if (!__CFAllMachPorts) __CFAllMachPorts = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); | |
389 | CFArrayAppendValue(__CFAllMachPorts, memory); | |
390 | #else | |
391 | if (!__CFAllMachPorts) __CFAllMachPorts = [[__CFPointerArray alloc] initWithOptions:(kCFUseCollectableAllocator ? CFPointerFunctionsZeroingWeakMemory : CFPointerFunctionsStrongMemory)]; | |
392 | [__CFAllMachPorts addPointer:memory]; | |
393 | #endif | |
394 | mp = memory; | |
395 | if (shouldFreeInfo) *shouldFreeInfo = false; | |
396 | ||
397 | if (type & MACH_PORT_TYPE_SEND_RIGHTS) { | |
7c97c3e0 A |
398 | dispatch_source_t theSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, port, DISPATCH_MACH_SEND_DEAD, __portQueue()); |
399 | dispatch_source_set_cancel_handler(theSource, ^{ | |
400 | dispatch_release(theSource); | |
401 | }); | |
402 | dispatch_source_set_event_handler(theSource, ^{ | |
403 | __CFMachPortChecker(false); | |
404 | }); | |
405 | memory->_dsrc = theSource; | |
406 | dispatch_resume(theSource); | |
cf7d2af9 A |
407 | } |
408 | if (memory->_dsrc) { | |
409 | dispatch_source_t source = memory->_dsrc; // put these in locals so they are fully copied into the block | |
410 | dispatch_semaphore_t sem = dispatch_semaphore_create(0); | |
411 | memory->_dsrc_sem = sem; | |
412 | dispatch_source_set_cancel_handler(memory->_dsrc, ^{ dispatch_semaphore_signal(sem); dispatch_release(source); }); | |
413 | } | |
414 | if (memory->_dsrc2) { | |
415 | dispatch_source_t source = memory->_dsrc2; | |
416 | dispatch_semaphore_t sem = dispatch_semaphore_create(0); | |
417 | memory->_dsrc2_sem = sem; | |
418 | dispatch_source_set_cancel_handler(memory->_dsrc2, ^{ dispatch_semaphore_signal(sem); dispatch_release(source); }); | |
419 | } | |
420 | }); | |
7c97c3e0 A |
421 | if (mp && !CFMachPortIsValid(mp)) { // must do this outside lock to avoid deadlock |
422 | CFRelease(mp); | |
423 | mp = NULL; | |
424 | } | |
cf7d2af9 A |
425 | return mp; |
426 | } | |
427 | ||
428 | CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) { | |
429 | return _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true); | |
430 | } | |
431 | ||
432 | CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) { | |
433 | if (shouldFreeInfo) *shouldFreeInfo = true; | |
434 | CHECK_FOR_FORK_RET(NULL); | |
435 | mach_port_t port = MACH_PORT_NULL; | |
436 | kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); | |
437 | if (KERN_SUCCESS == ret) { | |
438 | ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); | |
9ce05555 | 439 | } |
cf7d2af9 A |
440 | if (KERN_SUCCESS != ret) { |
441 | if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port); | |
442 | return NULL; | |
9ce05555 | 443 | } |
cf7d2af9 A |
444 | CFMachPortRef result = _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true); |
445 | if (NULL == result) { | |
446 | if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port); | |
447 | return NULL; | |
9ce05555 | 448 | } |
cf7d2af9 A |
449 | __CFMachPortSetHasReceive(result); |
450 | __CFMachPortSetHasSend(result); | |
451 | return result; | |
452 | } | |
453 | ||
454 | void CFMachPortInvalidate(CFMachPortRef mp) { | |
455 | CHECK_FOR_FORK_RET(); | |
456 | CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), void, mp, "invalidate"); | |
457 | __CFGenericValidateType(mp, CFMachPortGetTypeID()); | |
458 | CFRetain(mp); | |
459 | __block CFRunLoopSourceRef source = NULL; | |
460 | __block Boolean wasReady = false; | |
461 | dispatch_sync(__portQueue(), ^{ | |
462 | wasReady = (mp->_state == kCFMachPortStateReady); | |
463 | if (wasReady) { | |
464 | mp->_state = kCFMachPortStateInvalidating; | |
465 | OSMemoryBarrier(); | |
466 | #if defined(AVOID_WEAK_COLLECTIONS) | |
467 | for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) { | |
468 | CFMachPortRef p = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx); | |
469 | if (p == mp) { | |
470 | CFArrayRemoveValueAtIndex(__CFAllMachPorts, idx); | |
471 | break; | |
472 | } | |
473 | } | |
474 | #else | |
475 | for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) { | |
476 | CFMachPortRef p = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx]; | |
477 | if (p == mp) { | |
478 | [__CFAllMachPorts removePointerAtIndex:idx]; | |
479 | break; | |
480 | } | |
481 | } | |
482 | #endif | |
483 | if (mp->_dsrc) { | |
484 | dispatch_source_cancel(mp->_dsrc); | |
485 | mp->_dsrc = NULL; | |
486 | } | |
487 | if (mp->_dsrc2) { | |
488 | dispatch_source_cancel(mp->_dsrc2); | |
489 | mp->_dsrc2 = NULL; | |
490 | } | |
491 | source = mp->_source; | |
492 | mp->_source = NULL; | |
493 | } | |
494 | }); | |
495 | if (wasReady) { | |
496 | CFMachPortInvalidationCallBack cb = mp->_icallout; | |
497 | if (cb) { | |
498 | cb(mp, mp->_context.info); | |
499 | } | |
500 | if (NULL != source) { | |
501 | CFRunLoopSourceInvalidate(source); | |
502 | CFRelease(source); | |
503 | } | |
504 | void *info = mp->_context.info; | |
505 | mp->_context.info = NULL; | |
506 | if (mp->_context.release) { | |
507 | mp->_context.release(info); | |
508 | } | |
509 | // For hashing and equality purposes, cannot get rid of _port here | |
510 | mp->_state = kCFMachPortStateInvalid; | |
511 | OSMemoryBarrier(); | |
9ce05555 | 512 | } |
cf7d2af9 | 513 | CFRelease(mp); |
9ce05555 A |
514 | } |
515 | ||
516 | mach_port_t CFMachPortGetPort(CFMachPortRef mp) { | |
cf7d2af9 A |
517 | CHECK_FOR_FORK_RET(0); |
518 | CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), mach_port_t, mp, "machPort"); | |
519 | __CFGenericValidateType(mp, CFMachPortGetTypeID()); | |
9ce05555 A |
520 | return mp->_port; |
521 | } | |
522 | ||
523 | void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) { | |
cf7d2af9 | 524 | __CFGenericValidateType(mp, CFMachPortGetTypeID()); |
9ce05555 | 525 | CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); |
cf7d2af9 | 526 | objc_memmove_collectable(context, &mp->_context, sizeof(CFMachPortContext)); |
9ce05555 A |
527 | } |
528 | ||
529 | Boolean CFMachPortIsValid(CFMachPortRef mp) { | |
cf7d2af9 A |
530 | CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), Boolean, mp, "isValid"); |
531 | __CFGenericValidateType(mp, CFMachPortGetTypeID()); | |
7c97c3e0 A |
532 | if (!__CFMachPortIsValid(mp)) return false; |
533 | mach_port_type_t type = 0; | |
534 | kern_return_t ret = mach_port_type(mach_task_self(), mp->_port, &type); | |
535 | if (KERN_SUCCESS != ret || (type & ~(MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_SEND_ONCE|MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_DNREQUEST))) { | |
7c97c3e0 A |
536 | return false; |
537 | } | |
538 | return true; | |
9ce05555 A |
539 | } |
540 | ||
541 | CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) { | |
cf7d2af9 | 542 | __CFGenericValidateType(mp, CFMachPortGetTypeID()); |
9ce05555 A |
543 | return mp->_icallout; |
544 | } | |
545 | ||
cf7d2af9 A |
546 | /* After the CFMachPort has started going invalid, or done invalid, you can't change this, and |
547 | we'll only do the callout directly on a transition from NULL to non-NULL. */ | |
9ce05555 | 548 | void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) { |
cf7d2af9 A |
549 | CHECK_FOR_FORK_RET(); |
550 | __CFGenericValidateType(mp, CFMachPortGetTypeID()); | |
551 | if (__CFMachPortIsValid(mp) || !callout) { | |
552 | mp->_icallout = callout; | |
553 | } else if (!mp->_icallout && callout) { | |
554 | callout(mp, mp->_context.info); | |
9ce05555 | 555 | } else { |
cf7d2af9 | 556 | CFLog(kCFLogLevelWarning, CFSTR("CFMachPortSetInvalidationCallBack(): attempt to set invalidation callback (%p) on invalid CFMachPort (%p) thwarted"), callout, mp); |
9ce05555 A |
557 | } |
558 | } | |
559 | ||
cf7d2af9 | 560 | /* Returns the number of messages queued for a receive port. */ |
9ce05555 | 561 | CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) { |
cf7d2af9 A |
562 | CHECK_FOR_FORK_RET(0); |
563 | __CFGenericValidateType(mp, CFMachPortGetTypeID()); | |
9ce05555 A |
564 | mach_port_status_t status; |
565 | mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT; | |
cf7d2af9 | 566 | kern_return_t ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num); |
9ce05555 A |
567 | return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount; |
568 | } | |
569 | ||
9ce05555 | 570 | static mach_port_t __CFMachPortGetPort(void *info) { |
cf7d2af9 | 571 | CFMachPortRef mp = (CFMachPortRef)info; |
9ce05555 A |
572 | return mp->_port; |
573 | } | |
574 | ||
575 | static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { | |
cf7d2af9 A |
576 | CHECK_FOR_FORK_RET(NULL); |
577 | CFMachPortRef mp = (CFMachPortRef)info; | |
578 | __block Boolean isValid = false; | |
8ca704e1 A |
579 | __block void *context_info = NULL; |
580 | __block void (*context_release)(const void *) = NULL; | |
cf7d2af9 A |
581 | dispatch_sync(__portQueue(), ^{ |
582 | isValid = __CFMachPortIsValid(mp); | |
8ca704e1 A |
583 | if (!isValid) return; |
584 | if (mp->_context.retain) { | |
585 | context_info = (void *)mp->_context.retain(mp->_context.info); | |
586 | context_release = mp->_context.release; | |
587 | } else { | |
588 | context_info = mp->_context.info; | |
589 | } | |
cf7d2af9 A |
590 | }); |
591 | if (!isValid) return NULL; | |
592 | ||
cf7d2af9 A |
593 | mp->_callout(mp, msg, size, context_info); |
594 | ||
9ce05555 | 595 | if (context_release) { |
cf7d2af9 | 596 | context_release(context_info); |
9ce05555 | 597 | } |
cf7d2af9 | 598 | CHECK_FOR_FORK_RET(NULL); |
9ce05555 A |
599 | return NULL; |
600 | } | |
601 | ||
602 | CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) { | |
cf7d2af9 A |
603 | CHECK_FOR_FORK_RET(NULL); |
604 | __CFGenericValidateType(mp, CFMachPortGetTypeID()); | |
7c97c3e0 | 605 | if (!CFMachPortIsValid(mp)) return NULL; |
cf7d2af9 A |
606 | __block CFRunLoopSourceRef result = NULL; |
607 | dispatch_sync(__portQueue(), ^{ | |
608 | if (!__CFMachPortIsValid(mp)) return; | |
7c97c3e0 A |
609 | if (NULL != mp->_source && !CFRunLoopSourceIsValid(mp->_source)) { |
610 | CFRelease(mp->_source); | |
611 | mp->_source = NULL; | |
612 | } | |
cf7d2af9 A |
613 | if (NULL == mp->_source) { |
614 | CFRunLoopSourceContext1 context; | |
615 | context.version = 1; | |
616 | context.info = (void *)mp; | |
617 | context.retain = (const void *(*)(const void *))CFRetain; | |
618 | context.release = (void (*)(const void *))CFRelease; | |
619 | context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription; | |
620 | context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual; | |
621 | context.hash = (CFHashCode (*)(const void *))__CFMachPortHash; | |
622 | context.getPort = __CFMachPortGetPort; | |
623 | context.perform = __CFMachPortPerform; | |
624 | mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context); | |
625 | } | |
626 | result = mp->_source ? (CFRunLoopSourceRef)CFRetain(mp->_source) : NULL; | |
627 | }); | |
9ce05555 A |
628 | return result; |
629 | } | |
630 |