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