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