/ CFMachPort.c
CFMachPort.c
1 /* 2 * Copyright (c) 2015 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-2014, 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 46 static dispatch_queue_t _CFMachPortQueue() { 47 static volatile dispatch_queue_t __CFMachPortQueue = NULL; 48 static dispatch_once_t onceToken; 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 }); 53 return __CFMachPortQueue; 54 } 55 56 57 enum { 58 kCFMachPortStateReady = 0, 59 kCFMachPortStateInvalidating = 1, 60 kCFMachPortStateInvalid = 2, 61 kCFMachPortStateDeallocating = 3 62 }; 63 64 struct __CFMachPort { 65 CFRuntimeBase _base; 66 int32_t _state; 67 mach_port_t _port; /* immutable */ 68 dispatch_source_t _dsrc; /* protected by _lock */ 69 dispatch_semaphore_t _dsrc_sem; /* protected by _lock */ 70 CFMachPortInvalidationCallBack _icallout; /* protected by _lock */ 71 CFRunLoopSourceRef _source; /* immutable, once created */ 72 CFMachPortCallBack _callout; /* immutable */ 73 CFMachPortContext _context; /* immutable */ 74 CFLock_t _lock; 75 const void *(*retain)(const void *info); // use these to store the real callbacks 76 void (*release)(const void *info); 77 }; 78 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 */ 81 /* Bit 3 in the base reserved bits is used for has-send-ref2 state */ 82 83 CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) { 84 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1); 85 } 86 87 CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) { 88 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1, 1); 89 } 90 91 CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) { 92 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2); 93 } 94 95 CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) { 96 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2, 1); 97 } 98 99 CF_INLINE Boolean __CFMachPortHasSend2(CFMachPortRef mp) { 100 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3); 101 } 102 /* 103 //TODO we should either use this or delete the entire Send2 flag concept 104 CF_INLINE void __CFMachPortSetHasSend2(CFMachPortRef mp) { 105 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3, 1); 106 } 107 */ 108 CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) { 109 return kCFMachPortStateReady == mp->_state; 110 } 111 112 113 void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) { 114 } 115 116 static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) { 117 CFMachPortRef mp1 = (CFMachPortRef)cf1; 118 CFMachPortRef mp2 = (CFMachPortRef)cf2; 119 return (mp1->_port == mp2->_port); 120 } 121 122 static CFHashCode __CFMachPortHash(CFTypeRef cf) { 123 CFMachPortRef mp = (CFMachPortRef)cf; 124 return (CFHashCode)mp->_port; 125 } 126 127 static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) { 128 CFMachPortRef mp = (CFMachPortRef)cf; 129 CFStringRef contextDesc = NULL; 130 if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) { 131 contextDesc = mp->_context.copyDescription(mp->_context.info); 132 } 133 if (NULL == contextDesc) { 134 contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info); 135 } 136 Dl_info info; 137 void *addr = mp->_callout; 138 const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; 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); 140 if (NULL != contextDesc) { 141 CFRelease(contextDesc); 142 } 143 return result; 144 } 145 146 // Only call with mp->_lock locked 147 CF_INLINE void __CFMachPortInvalidateLocked(CFRunLoopSourceRef source, CFMachPortRef mp) { 148 CFMachPortInvalidationCallBack cb = mp->_icallout; 149 if (cb) { 150 __CFUnlock(&mp->_lock); 151 cb(mp, mp->_context.info); 152 __CFLock(&mp->_lock); 153 } 154 if (NULL != source) { 155 __CFUnlock(&mp->_lock); 156 CFRunLoopSourceInvalidate(source); 157 CFRelease(source); 158 __CFLock(&mp->_lock); 159 } 160 void *info = mp->_context.info; 161 void (*release)(const void *info) = mp->release; 162 mp->_context.info = NULL; 163 if (release) { 164 __CFUnlock(&mp->_lock); 165 release(info); 166 __CFLock(&mp->_lock); 167 } 168 mp->_state = kCFMachPortStateInvalid; 169 OSMemoryBarrier(); 170 } 171 172 static void __CFMachPortDeallocate(CFTypeRef cf) { 173 CHECK_FOR_FORK_RET(); 174 CFMachPortRef mp = (CFMachPortRef)cf; 175 176 // CFMachPortRef is invalid before we get here, except under GC 177 __CFLock(&mp->_lock); 178 CFRunLoopSourceRef source = NULL; 179 Boolean wasReady = (mp->_state == kCFMachPortStateReady); 180 if (wasReady) { 181 mp->_state = kCFMachPortStateInvalidating; 182 OSMemoryBarrier(); 183 if (mp->_dsrc) { 184 dispatch_source_cancel(mp->_dsrc); 185 mp->_dsrc = NULL; 186 } 187 source = mp->_source; 188 mp->_source = NULL; 189 } 190 if (wasReady) { 191 __CFMachPortInvalidateLocked(source, mp); 192 } 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; 198 Boolean doSend2 = __CFMachPortHasSend2(mp), doSend = __CFMachPortHasSend(mp), doReceive = __CFMachPortHasReceive(mp); 199 200 __CFUnlock(&mp->_lock); 201 202 dispatch_async(__CFDispatchQueueGetGenericBackground(), ^{ 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 } 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 }); 222 } 223 224 // This lock protects __CFAllMachPorts. Take before any instance-specific lock. 225 static CFLock_t __CFAllMachPortsLock = CFLockInit; 226 227 #if AVOID_WEAK_COLLECTIONS 228 static CFMutableArrayRef __CFAllMachPorts = NULL; 229 #else 230 static __CFPointerArray *__CFAllMachPorts = nil; 231 #endif 232 233 static Boolean __CFMachPortCheck(mach_port_t) __attribute__((noinline)); 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); 237 return (KERN_SUCCESS != ret || (0 == (type & MACH_PORT_TYPE_PORT_RIGHTS))) ? false : true; 238 } 239 240 static void __CFMachPortChecker(Boolean fromTimer) { 241 __CFLock(&__CFAllMachPortsLock); // take this lock first before any instance-specific lock 242 #if AVOID_WEAK_COLLECTIONS 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) { 255 __CFLock(&mp->_lock); // take this lock second 256 mp->_state = kCFMachPortStateInvalidating; 257 OSMemoryBarrier(); 258 if (mp->_dsrc) { 259 dispatch_source_cancel(mp->_dsrc); 260 mp->_dsrc = NULL; 261 } 262 source = mp->_source; 263 mp->_source = NULL; 264 CFRetain(mp); 265 __CFUnlock(&mp->_lock); 266 dispatch_async(dispatch_get_main_queue(), ^{ 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 269 __CFLock(&mp->_lock); 270 __CFMachPortInvalidateLocked(source, mp); 271 __CFUnlock(&mp->_lock); 272 CFRelease(mp); 273 }); 274 } 275 #if AVOID_WEAK_COLLECTIONS 276 CFArrayRemoveValueAtIndex(__CFAllMachPorts, idx); 277 #else 278 [__CFAllMachPorts removePointerAtIndex:idx]; 279 #endif 280 idx--; 281 cnt--; 282 } 283 } 284 #if !AVOID_WEAK_COLLECTIONS 285 [__CFAllMachPorts compact]; 286 #endif 287 __CFUnlock(&__CFAllMachPortsLock); 288 }; 289 290 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 305 CFTypeID CFMachPortGetTypeID(void) { 306 static dispatch_once_t initOnce; 307 dispatch_once(&initOnce, ^{ __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass); }); 308 return __kCFMachPortTypeID; 309 } 310 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. */ 315 CFMachPortRef _CFMachPortCreateWithPort2(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo, Boolean deathWatch) { 316 if (shouldFreeInfo) *shouldFreeInfo = true; 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); 321 if (KERN_SUCCESS != ret || (0 == (type & MACH_PORT_TYPE_PORT_RIGHTS))) { 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; 326 } 327 328 #if 0 329 static dispatch_source_t timerSource = NULL; 330 static dispatch_once_t onceToken; 331 dispatch_once(&onceToken, ^{ 332 timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_INTERVAL, 60 * 1000 /* milliseconds */, 0, _CFMachPortQueue()); 333 dispatch_source_set_event_handler(timerSource, ^{ 334 __CFMachPortChecker(true); 335 }); 336 dispatch_resume(timerSource); 337 }); 338 #endif 339 340 CFMachPortRef mp = NULL; 341 __CFLock(&__CFAllMachPortsLock); 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 } 351 #else 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 } 360 #endif 361 __CFUnlock(&__CFAllMachPortsLock); 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; 371 memory->_dsrc_sem = NULL; 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; 378 memory->retain = NULL; 379 memory->release = NULL; 380 memory->_callout = callout; 381 memory->_lock = CFLockInit; 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 memory->retain = context->retain; 386 memory->release = context->release; 387 memory->_context.retain = (void *)0xAAAAAAAAAACCCAAA; 388 memory->_context.release = (void *)0xAAAAAAAAAABBBAAA; 389 } 390 memory->_state = kCFMachPortStateReady; 391 __CFLock(&__CFAllMachPortsLock); 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 399 __CFUnlock(&__CFAllMachPortsLock); 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()); 405 if (theSource) { 406 dispatch_semaphore_t sem = dispatch_semaphore_create(0); 407 dispatch_retain(sem); 408 dispatch_source_set_cancel_handler(theSource, ^{ dispatch_semaphore_signal(sem); dispatch_release(sem); dispatch_release(theSource); }); 409 dispatch_source_set_event_handler(theSource, ^{ __CFMachPortChecker(false); }); 410 memory->_dsrc_sem = sem; 411 memory->_dsrc = theSource; 412 dispatch_resume(theSource); 413 } 414 } 415 } 416 417 if (mp && !CFMachPortIsValid(mp)) { // must do this outside lock to avoid deadlock 418 CFRelease(mp); 419 mp = NULL; 420 } 421 return mp; 422 } 423 424 CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) { 425 return _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true); 426 } 427 428 CFMachPortRef 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); 435 } 436 if (KERN_SUCCESS != ret) { 437 if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port); 438 return NULL; 439 } 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; 444 } 445 __CFMachPortSetHasReceive(result); 446 __CFMachPortSetHasSend(result); 447 return result; 448 } 449 450 void CFMachPortInvalidate(CFMachPortRef mp) { 451 CHECK_FOR_FORK_RET(); 452 CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), void, (NSMachPort *)mp, invalidate); 453 __CFGenericValidateType(mp, CFMachPortGetTypeID()); 454 CFRetain(mp); 455 CFRunLoopSourceRef source = NULL; 456 Boolean wasReady = false; 457 __CFLock(&__CFAllMachPortsLock); // take this lock first 458 __CFLock(&mp->_lock); 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 } 471 #else 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; 477 } 478 } 479 #endif 480 if (mp->_dsrc) { 481 dispatch_source_cancel(mp->_dsrc); 482 mp->_dsrc = NULL; 483 } 484 source = mp->_source; 485 mp->_source = NULL; 486 } 487 __CFUnlock(&mp->_lock); 488 __CFUnlock(&__CFAllMachPortsLock); // release this lock last 489 if (wasReady) { 490 __CFLock(&mp->_lock); 491 __CFMachPortInvalidateLocked(source, mp); 492 __CFUnlock(&mp->_lock); 493 } 494 CFRelease(mp); 495 } 496 497 mach_port_t CFMachPortGetPort(CFMachPortRef mp) { 498 CHECK_FOR_FORK_RET(0); 499 CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), mach_port_t, (NSMachPort *)mp, machPort); 500 __CFGenericValidateType(mp, CFMachPortGetTypeID()); 501 return mp->_port; 502 } 503 504 void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) { 505 __CFGenericValidateType(mp, CFMachPortGetTypeID()); 506 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); 507 objc_memmove_collectable(context, &mp->_context, sizeof(CFMachPortContext)); 508 } 509 510 Boolean CFMachPortIsValid(CFMachPortRef mp) { 511 CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), Boolean, (NSMachPort *)mp, isValid); 512 __CFGenericValidateType(mp, CFMachPortGetTypeID()); 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))) { 517 return false; 518 } 519 return true; 520 } 521 522 CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) { 523 __CFGenericValidateType(mp, CFMachPortGetTypeID()); 524 __CFLock(&mp->_lock); 525 CFMachPortInvalidationCallBack cb = mp->_icallout; 526 __CFUnlock(&mp->_lock); 527 return cb; 528 } 529 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. */ 532 void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) { 533 CHECK_FOR_FORK_RET(); 534 __CFGenericValidateType(mp, CFMachPortGetTypeID()); 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 } 542 __CFLock(&mp->_lock); 543 if (__CFMachPortIsValid(mp) || !callout) { 544 mp->_icallout = callout; 545 } else if (!mp->_icallout && callout) { 546 __CFUnlock(&mp->_lock); 547 callout(mp, mp->_context.info); 548 __CFLock(&mp->_lock); 549 } else { 550 CFLog(kCFLogLevelWarning, CFSTR("CFMachPortSetInvalidationCallBack(): attempt to set invalidation callback (%p) on invalid CFMachPort (%p) thwarted"), callout, mp); 551 } 552 __CFUnlock(&mp->_lock); 553 } 554 555 /* Returns the number of messages queued for a receive port. */ 556 CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) { 557 CHECK_FOR_FORK_RET(0); 558 __CFGenericValidateType(mp, CFMachPortGetTypeID()); 559 mach_port_status_t status; 560 mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT; 561 kern_return_t ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num); 562 return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount; 563 } 564 565 static mach_port_t __CFMachPortGetPort(void *info) { 566 CFMachPortRef mp = (CFMachPortRef)info; 567 return mp->_port; 568 } 569 570 CF_PRIVATE void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { 571 CHECK_FOR_FORK_RET(NULL); 572 CFMachPortRef mp = (CFMachPortRef)info; 573 __CFLock(&mp->_lock); 574 Boolean isValid = __CFMachPortIsValid(mp); 575 void *context_info = NULL; 576 void (*context_release)(const void *) = NULL; 577 if (isValid) { 578 if (mp->retain) { 579 context_info = (void *)mp->retain(mp->_context.info); 580 context_release = mp->release; 581 } else { 582 context_info = mp->_context.info; 583 } 584 } 585 __CFUnlock(&mp->_lock); 586 if (isValid) { 587 mp->_callout(mp, msg, size, context_info); 588 589 if (context_release) { 590 context_release(context_info); 591 } 592 CHECK_FOR_FORK_RET(NULL); 593 } 594 return NULL; 595 } 596 597 598 599 600 CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) { 601 CHECK_FOR_FORK_RET(NULL); 602 __CFGenericValidateType(mp, CFMachPortGetTypeID()); 603 if (!CFMachPortIsValid(mp)) return NULL; 604 CFRunLoopSourceRef result = NULL; 605 __CFLock(&mp->_lock); 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 } 626 __CFUnlock(&mp->_lock); 627 return result; 628 } 629