]> git.saurik.com Git - apple/cf.git/blame - CFMachPort.c
CF-1153.18.tar.gz
[apple/cf.git] / CFMachPort.c
CommitLineData
9ce05555 1/*
e29e285d 2 * Copyright (c) 2015 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
46static 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
57enum {
58 kCFMachPortStateReady = 0,
59 kCFMachPortStateInvalidating = 1,
60 kCFMachPortStateInvalid = 2,
61 kCFMachPortStateDeallocating = 3
62};
9ce05555
A
63
64struct __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
83CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) {
bd5b749c 84 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1);
9ce05555
A
85}
86
87CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) {
bd5b749c 88 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
9ce05555
A
89}
90
91CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) {
bd5b749c 92 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2);
9ce05555
A
93}
94
95CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) {
bd5b749c 96 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2, 1);
9ce05555
A
97}
98
cf7d2af9 99CF_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 104CF_INLINE void __CFMachPortSetHasSend2(CFMachPortRef mp) {
bd5b749c 105 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
9ce05555 106}
d7384798 107*/
cf7d2af9
A
108CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) {
109 return kCFMachPortStateReady == mp->_state;
9ce05555
A
110}
111
9ce05555 112
9ce05555 113void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) {
9ce05555
A
114}
115
116static 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
122static CFHashCode __CFMachPortHash(CFTypeRef cf) {
123 CFMachPortRef mp = (CFMachPortRef)cf;
9ce05555
A
124 return (CFHashCode)mp->_port;
125}
126
127static 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
147CF_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 172static 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 225static CFLock_t __CFAllMachPortsLock = CFLockInit;
856091c5
A
226
227#if AVOID_WEAK_COLLECTIONS
cf7d2af9
A
228static CFMutableArrayRef __CFAllMachPorts = NULL;
229#else
230static __CFPointerArray *__CFAllMachPorts = nil;
231#endif
232
a48904a4 233static Boolean __CFMachPortCheck(mach_port_t) __attribute__((noinline));
cf7d2af9
A
234static 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 240static 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
291static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID;
292
293static 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 305CFTypeID 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 315CFMachPortRef _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);
e29e285d
A
407 dispatch_retain(sem);
408 dispatch_source_set_cancel_handler(theSource, ^{ dispatch_semaphore_signal(sem); dispatch_release(sem); dispatch_release(theSource); });
a48904a4
A
409 dispatch_source_set_event_handler(theSource, ^{ __CFMachPortChecker(false); });
410 memory->_dsrc_sem = sem;
411 memory->_dsrc = theSource;
412 dispatch_resume(theSource);
413 }
856091c5
A
414 }
415 }
416
7c97c3e0
A
417 if (mp && !CFMachPortIsValid(mp)) { // must do this outside lock to avoid deadlock
418 CFRelease(mp);
419 mp = NULL;
420 }
cf7d2af9
A
421 return mp;
422}
423
424CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
425 return _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true);
426}
427
428CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
429 if (shouldFreeInfo) *shouldFreeInfo = true;
430 CHECK_FOR_FORK_RET(NULL);
431 mach_port_t port = MACH_PORT_NULL;
432 kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
433 if (KERN_SUCCESS == ret) {
434 ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
9ce05555 435 }
cf7d2af9
A
436 if (KERN_SUCCESS != ret) {
437 if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port);
438 return NULL;
9ce05555 439 }
cf7d2af9
A
440 CFMachPortRef result = _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true);
441 if (NULL == result) {
442 if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port);
443 return NULL;
9ce05555 444 }
cf7d2af9
A
445 __CFMachPortSetHasReceive(result);
446 __CFMachPortSetHasSend(result);
447 return result;
448}
449
450void CFMachPortInvalidate(CFMachPortRef mp) {
451 CHECK_FOR_FORK_RET();
856091c5 452 CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), void, (NSMachPort *)mp, invalidate);
cf7d2af9
A
453 __CFGenericValidateType(mp, CFMachPortGetTypeID());
454 CFRetain(mp);
856091c5
A
455 CFRunLoopSourceRef source = NULL;
456 Boolean wasReady = false;
d7384798
A
457 __CFLock(&__CFAllMachPortsLock); // take this lock first
458 __CFLock(&mp->_lock);
856091c5
A
459 wasReady = (mp->_state == kCFMachPortStateReady);
460 if (wasReady) {
461 mp->_state = kCFMachPortStateInvalidating;
462 OSMemoryBarrier();
463#if AVOID_WEAK_COLLECTIONS
464 for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) {
465 CFMachPortRef p = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx);
466 if (p == mp) {
467 CFArrayRemoveValueAtIndex(__CFAllMachPorts, idx);
468 break;
469 }
470 }
cf7d2af9 471#else
856091c5
A
472 for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) {
473 CFMachPortRef p = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx];
474 if (p == mp) {
475 [__CFAllMachPorts removePointerAtIndex:idx];
476 break;
cf7d2af9 477 }
cf7d2af9 478 }
856091c5
A
479#endif
480 if (mp->_dsrc) {
481 dispatch_source_cancel(mp->_dsrc);
482 mp->_dsrc = NULL;
cf7d2af9 483 }
856091c5
A
484 source = mp->_source;
485 mp->_source = NULL;
486 }
d7384798
A
487 __CFUnlock(&mp->_lock);
488 __CFUnlock(&__CFAllMachPortsLock); // release this lock last
856091c5 489 if (wasReady) {
d7384798 490 __CFLock(&mp->_lock);
856091c5 491 __CFMachPortInvalidateLocked(source, mp);
d7384798 492 __CFUnlock(&mp->_lock);
9ce05555 493 }
cf7d2af9 494 CFRelease(mp);
9ce05555
A
495}
496
497mach_port_t CFMachPortGetPort(CFMachPortRef mp) {
cf7d2af9 498 CHECK_FOR_FORK_RET(0);
856091c5 499 CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), mach_port_t, (NSMachPort *)mp, machPort);
cf7d2af9 500 __CFGenericValidateType(mp, CFMachPortGetTypeID());
9ce05555
A
501 return mp->_port;
502}
503
504void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) {
cf7d2af9 505 __CFGenericValidateType(mp, CFMachPortGetTypeID());
9ce05555 506 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
cf7d2af9 507 objc_memmove_collectable(context, &mp->_context, sizeof(CFMachPortContext));
9ce05555
A
508}
509
510Boolean CFMachPortIsValid(CFMachPortRef mp) {
856091c5 511 CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), Boolean, (NSMachPort *)mp, isValid);
cf7d2af9 512 __CFGenericValidateType(mp, CFMachPortGetTypeID());
7c97c3e0
A
513 if (!__CFMachPortIsValid(mp)) return false;
514 mach_port_type_t type = 0;
515 kern_return_t ret = mach_port_type(mach_task_self(), mp->_port, &type);
516 if (KERN_SUCCESS != ret || (type & ~(MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_SEND_ONCE|MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_DNREQUEST))) {
7c97c3e0
A
517 return false;
518 }
519 return true;
9ce05555
A
520}
521
522CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) {
cf7d2af9 523 __CFGenericValidateType(mp, CFMachPortGetTypeID());
d7384798 524 __CFLock(&mp->_lock);
856091c5 525 CFMachPortInvalidationCallBack cb = mp->_icallout;
d7384798 526 __CFUnlock(&mp->_lock);
856091c5 527 return cb;
9ce05555
A
528}
529
cf7d2af9
A
530/* After the CFMachPort has started going invalid, or done invalid, you can't change this, and
531 we'll only do the callout directly on a transition from NULL to non-NULL. */
9ce05555 532void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) {
cf7d2af9
A
533 CHECK_FOR_FORK_RET();
534 __CFGenericValidateType(mp, CFMachPortGetTypeID());
a48904a4
A
535 if (callout) {
536 mach_port_type_t type = 0;
537 kern_return_t ret = mach_port_type(mach_task_self(), mp->_port, &type);
538 if (KERN_SUCCESS != ret || 0 == (type & MACH_PORT_TYPE_SEND_RIGHTS)) {
539 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);
540 }
541 }
d7384798 542 __CFLock(&mp->_lock);
cf7d2af9
A
543 if (__CFMachPortIsValid(mp) || !callout) {
544 mp->_icallout = callout;
545 } else if (!mp->_icallout && callout) {
d7384798 546 __CFUnlock(&mp->_lock);
cf7d2af9 547 callout(mp, mp->_context.info);
d7384798 548 __CFLock(&mp->_lock);
9ce05555 549 } else {
cf7d2af9 550 CFLog(kCFLogLevelWarning, CFSTR("CFMachPortSetInvalidationCallBack(): attempt to set invalidation callback (%p) on invalid CFMachPort (%p) thwarted"), callout, mp);
9ce05555 551 }
d7384798 552 __CFUnlock(&mp->_lock);
9ce05555
A
553}
554
cf7d2af9 555/* Returns the number of messages queued for a receive port. */
9ce05555 556CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) {
cf7d2af9
A
557 CHECK_FOR_FORK_RET(0);
558 __CFGenericValidateType(mp, CFMachPortGetTypeID());
9ce05555
A
559 mach_port_status_t status;
560 mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT;
cf7d2af9 561 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
562 return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount;
563}
564
9ce05555 565static mach_port_t __CFMachPortGetPort(void *info) {
cf7d2af9 566 CFMachPortRef mp = (CFMachPortRef)info;
9ce05555
A
567 return mp->_port;
568}
569
a48904a4 570CF_PRIVATE void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
cf7d2af9
A
571 CHECK_FOR_FORK_RET(NULL);
572 CFMachPortRef mp = (CFMachPortRef)info;
d7384798 573 __CFLock(&mp->_lock);
856091c5
A
574 Boolean isValid = __CFMachPortIsValid(mp);
575 void *context_info = NULL;
576 void (*context_release)(const void *) = NULL;
577 if (isValid) {
a48904a4
A
578 if (mp->retain) {
579 context_info = (void *)mp->retain(mp->_context.info);
580 context_release = mp->release;
856091c5
A
581 } else {
582 context_info = mp->_context.info;
583 }
584 }
d7384798 585 __CFUnlock(&mp->_lock);
a48904a4
A
586 if (isValid) {
587 mp->_callout(mp, msg, size, context_info);
cf7d2af9 588
a48904a4
A
589 if (context_release) {
590 context_release(context_info);
591 }
592 CHECK_FOR_FORK_RET(NULL);
9ce05555
A
593 }
594 return NULL;
595}
596
a48904a4 597
d7384798
A
598
599
9ce05555 600CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) {
cf7d2af9
A
601 CHECK_FOR_FORK_RET(NULL);
602 __CFGenericValidateType(mp, CFMachPortGetTypeID());
7c97c3e0 603 if (!CFMachPortIsValid(mp)) return NULL;
856091c5 604 CFRunLoopSourceRef result = NULL;
d7384798 605 __CFLock(&mp->_lock);
856091c5
A
606 if (__CFMachPortIsValid(mp)) {
607 if (NULL != mp->_source && !CFRunLoopSourceIsValid(mp->_source)) {
608 CFRelease(mp->_source);
609 mp->_source = NULL;
610 }
611 if (NULL == mp->_source) {
612 CFRunLoopSourceContext1 context;
613 context.version = 1;
614 context.info = (void *)mp;
615 context.retain = (const void *(*)(const void *))CFRetain;
616 context.release = (void (*)(const void *))CFRelease;
617 context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription;
618 context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual;
619 context.hash = (CFHashCode (*)(const void *))__CFMachPortHash;
620 context.getPort = __CFMachPortGetPort;
621 context.perform = __CFMachPortPerform;
622 mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
623 }
624 result = mp->_source ? (CFRunLoopSourceRef)CFRetain(mp->_source) : NULL;
625 }
d7384798 626 __CFUnlock(&mp->_lock);
9ce05555
A
627 return result;
628}
629