2 * Copyright (c) 2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFMachPort.h>
29 #include <CoreFoundation/CFRunLoop.h>
30 #include <CoreFoundation/CFArray.h>
31 #include <dispatch/dispatch.h>
32 #include <libkern/OSAtomic.h>
33 #include <mach/mach.h>
35 #include "CFInternal.h"
37 #define AVOID_WEAK_COLLECTIONS 1
39 #if !defined(AVOID_WEAK_COLLECTIONS)
40 #import "CFPointerArray.h"
43 DISPATCH_HELPER_FUNCTIONS(port
, CFMachPort
)
47 kCFMachPortStateReady
= 0,
48 kCFMachPortStateInvalidating
= 1,
49 kCFMachPortStateInvalid
= 2,
50 kCFMachPortStateDeallocating
= 3
56 mach_port_t _port
; /* immutable */
57 dispatch_source_t _dsrc
;
58 dispatch_source_t _dsrc2
;
59 dispatch_semaphore_t _dsrc_sem
;
60 dispatch_semaphore_t _dsrc2_sem
;
61 CFMachPortInvalidationCallBack _icallout
;
62 CFRunLoopSourceRef _source
; /* immutable, once created */
63 CFMachPortCallBack _callout
; /* immutable */
64 CFMachPortContext _context
; /* immutable */
67 /* Bit 1 in the base reserved bits is used for has-receive-ref state */
68 /* Bit 2 in the base reserved bits is used for has-send-ref state */
69 /* Bit 3 in the base reserved bits is used for has-send-ref2 state */
71 CF_INLINE Boolean
__CFMachPortHasReceive(CFMachPortRef mp
) {
72 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 1, 1);
75 CF_INLINE
void __CFMachPortSetHasReceive(CFMachPortRef mp
) {
76 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 1);
79 CF_INLINE Boolean
__CFMachPortHasSend(CFMachPortRef mp
) {
80 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 2, 2);
83 CF_INLINE
void __CFMachPortSetHasSend(CFMachPortRef mp
) {
84 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 1);
87 CF_INLINE Boolean
__CFMachPortHasSend2(CFMachPortRef mp
) {
88 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 3, 3);
91 CF_INLINE
void __CFMachPortSetHasSend2(CFMachPortRef mp
) {
92 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 3, 3, 1);
95 CF_INLINE Boolean
__CFMachPortIsValid(CFMachPortRef mp
) {
96 return kCFMachPortStateReady
== mp
->_state
;
100 void _CFMachPortInstallNotifyPort(CFRunLoopRef rl
, CFStringRef mode
) {
103 static Boolean
__CFMachPortEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
104 CFMachPortRef mp1
= (CFMachPortRef
)cf1
;
105 CFMachPortRef mp2
= (CFMachPortRef
)cf2
;
106 return (mp1
->_port
== mp2
->_port
);
109 static CFHashCode
__CFMachPortHash(CFTypeRef cf
) {
110 CFMachPortRef mp
= (CFMachPortRef
)cf
;
111 return (CFHashCode
)mp
->_port
;
114 static CFStringRef
__CFMachPortCopyDescription(CFTypeRef cf
) {
115 CFMachPortRef mp
= (CFMachPortRef
)cf
;
116 CFStringRef contextDesc
= NULL
;
117 if (NULL
!= mp
->_context
.info
&& NULL
!= mp
->_context
.copyDescription
) {
118 contextDesc
= mp
->_context
.copyDescription(mp
->_context
.info
);
120 if (NULL
== contextDesc
) {
121 contextDesc
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMachPort context %p>"), mp
->_context
.info
);
124 void *addr
= mp
->_callout
;
125 const char *name
= (dladdr(addr
, &info
) && info
.dli_saddr
== addr
&& info
.dli_sname
) ? info
.dli_sname
: "???";
126 CFStringRef result
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMachPort %p [%p]>{valid = %s, port = %p, source = %p, callout = %s (%p), context = %@}"), cf
, CFGetAllocator(mp
), (__CFMachPortIsValid(mp
) ? "Yes" : "No"), mp
->_port
, mp
->_source
, name
, addr
, contextDesc
);
127 if (NULL
!= contextDesc
) {
128 CFRelease(contextDesc
);
133 static void __CFMachPortDeallocate(CFTypeRef cf
) {
134 CHECK_FOR_FORK_RET();
135 CFMachPortRef mp
= (CFMachPortRef
)cf
;
137 // CFMachPortRef is invalid before we get here, except under GC
138 __block CFRunLoopSourceRef source
= NULL
;
139 __block Boolean wasReady
= false;
140 void (^block
)(void) = ^{
141 wasReady
= (mp
->_state
== kCFMachPortStateReady
);
143 mp
->_state
= kCFMachPortStateInvalidating
;
146 dispatch_source_cancel(mp
->_dsrc
);
150 dispatch_source_cancel(mp
->_dsrc2
);
153 source
= mp
->_source
;
157 if (!__portSyncDispatchIsSafe(__portQueue())) {
160 dispatch_sync(__portQueue(), block
);
163 CFMachPortInvalidationCallBack cb
= mp
->_icallout
;
165 cb(mp
, mp
->_context
.info
);
167 if (NULL
!= source
) {
168 CFRunLoopSourceInvalidate(source
);
171 void *info
= mp
->_context
.info
;
172 mp
->_context
.info
= NULL
;
173 if (mp
->_context
.release
) {
174 mp
->_context
.release(info
);
176 mp
->_state
= kCFMachPortStateInvalid
;
179 mp
->_state
= kCFMachPortStateDeallocating
;
181 // hand ownership of the port and semaphores to the block below
182 mach_port_t port
= mp
->_port
;
183 dispatch_semaphore_t sem1
= mp
->_dsrc_sem
;
184 dispatch_semaphore_t sem2
= mp
->_dsrc2_sem
;
185 Boolean doSend2
= __CFMachPortHasSend2(mp
), doSend
= __CFMachPortHasSend(mp
), doReceive
= __CFMachPortHasReceive(mp
);
187 dispatch_async(dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_LOW
), ^{
189 dispatch_semaphore_wait(sem1
, DISPATCH_TIME_FOREVER
);
190 // immediate release is only safe if dispatch_semaphore_signal() does not touch the semaphore after doing the signal bit
191 dispatch_release(sem1
);
194 dispatch_semaphore_wait(sem2
, DISPATCH_TIME_FOREVER
);
195 // immediate release is only safe if dispatch_semaphore_signal() does not touch the semaphore after doing the signal bit
196 dispatch_release(sem2
);
199 // MUST deallocate the send right FIRST if necessary,
200 // then the receive right if necessary. Don't ask me why;
201 // if it's done in the other order the port will leak.
203 mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_SEND
, -1);
206 mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_SEND
, -1);
209 mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_RECEIVE
, -1);
214 #if defined(AVOID_WEAK_COLLECTIONS)
215 static CFMutableArrayRef __CFAllMachPorts
= NULL
;
217 static __CFPointerArray
*__CFAllMachPorts
= nil
;
220 static Boolean
__CFMachPortCheck(mach_port_t port
) {
221 mach_port_type_t type
= 0;
222 kern_return_t ret
= mach_port_type(mach_task_self(), port
, &type
);
223 return (KERN_SUCCESS
!= ret
|| (type
& MACH_PORT_TYPE_DEAD_NAME
)) ? false : true;
226 static void __CFMachPortChecker(Boolean fromTimer
) { // only call on __portQueue()
227 #if defined(AVOID_WEAK_COLLECTIONS)
228 for (CFIndex idx
= 0, cnt
= __CFAllMachPorts
? CFArrayGetCount(__CFAllMachPorts
) : 0; idx
< cnt
; idx
++) {
229 CFMachPortRef mp
= (CFMachPortRef
)CFArrayGetValueAtIndex(__CFAllMachPorts
, idx
);
231 for (CFIndex idx
= 0, cnt
= __CFAllMachPorts
? [__CFAllMachPorts count
] : 0; idx
< cnt
; idx
++) {
232 CFMachPortRef mp
= (CFMachPortRef
)[__CFAllMachPorts pointerAtIndex
:idx
];
235 // second clause cleans no-longer-wanted CFMachPorts out of our strong table
236 if (!__CFMachPortCheck(mp
->_port
) || (!kCFUseCollectableAllocator
&& 1 == CFGetRetainCount(mp
))) {
237 CFRunLoopSourceRef source
= NULL
;
238 Boolean wasReady
= (mp
->_state
== kCFMachPortStateReady
);
240 mp
->_state
= kCFMachPortStateInvalidating
;
243 dispatch_source_cancel(mp
->_dsrc
);
247 dispatch_source_cancel(mp
->_dsrc2
);
250 source
= mp
->_source
;
253 dispatch_async(dispatch_get_main_queue(), ^{
254 CFMachPortInvalidationCallBack cb
= mp
->_icallout
;
256 cb(mp
, mp
->_context
.info
);
258 if (NULL
!= source
) {
259 CFRunLoopSourceInvalidate(source
);
262 void *info
= mp
->_context
.info
;
263 mp
->_context
.info
= NULL
;
264 if (mp
->_context
.release
) {
265 mp
->_context
.release(info
);
267 // For hashing and equality purposes, cannot get rid of _port here
268 mp
->_state
= kCFMachPortStateInvalid
;
273 #if defined(AVOID_WEAK_COLLECTIONS)
274 CFArrayRemoveValueAtIndex(__CFAllMachPorts
, idx
);
276 [__CFAllMachPorts removePointerAtIndex
:idx
];
282 #if !defined(AVOID_WEAK_COLLECTIONS)
283 [__CFAllMachPorts compact
];
288 static CFTypeID __kCFMachPortTypeID
= _kCFRuntimeNotATypeID
;
290 static const CFRuntimeClass __CFMachPortClass
= {
295 __CFMachPortDeallocate
,
299 __CFMachPortCopyDescription
302 __private_extern__
void __CFMachPortInitialize(void) {
303 __kCFMachPortTypeID
= _CFRuntimeRegisterClass(&__CFMachPortClass
);
306 CFTypeID
CFMachPortGetTypeID(void) {
307 return __kCFMachPortTypeID
;
310 /* Note: any receive or send rights that the port contains coming in will
311 * not be cleaned up by CFMachPort; it will increment and decrement
312 * references on the port if the kernel ever allows that in the future,
313 * but will not cleanup any references you got when you got the port. */
314 CFMachPortRef
_CFMachPortCreateWithPort2(CFAllocatorRef allocator
, mach_port_t port
, CFMachPortCallBack callout
, CFMachPortContext
*context
, Boolean
*shouldFreeInfo
, Boolean deathWatch
) {
315 if (shouldFreeInfo
) *shouldFreeInfo
= true;
316 CHECK_FOR_FORK_RET(NULL
);
318 mach_port_type_t type
= 0;
319 kern_return_t ret
= mach_port_type(mach_task_self(), port
, &type
);
320 if (KERN_SUCCESS
!= ret
|| (type
& ~(MACH_PORT_TYPE_SEND
|MACH_PORT_TYPE_SEND_ONCE
|MACH_PORT_TYPE_RECEIVE
|MACH_PORT_TYPE_DNREQUEST
))) {
321 if (type
& ~MACH_PORT_TYPE_DEAD_NAME
) {
322 CFLog(kCFLogLevelError
, CFSTR("*** CFMachPortCreateWithPort(): bad Mach port parameter (0x%lx) or unsupported mysterious kind of Mach port (%d, %ld)"), (unsigned long)port
, ret
, (unsigned long)type
);
327 __block CFMachPortRef mp
= NULL
;
328 dispatch_sync(__portQueue(), ^{
329 static Boolean portCheckerGoing
= false;
330 if (!portCheckerGoing
) {
331 uint64_t nanos
= 63 * 1000 * 1000 * 1000ULL;
333 (void)dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL
, nanos
, leeway
, NULL
, __portQueue(), ^(dispatch_source_t source
) {
334 long e
= 0, d
= dispatch_source_get_error(source
, &e
);
335 if (DISPATCH_ERROR_DOMAIN_POSIX
== d
&& ECANCELED
== e
) {
336 dispatch_release(source
);
339 if (DISPATCH_ERROR_DOMAIN_NO_ERROR
!= d
) {
342 __CFMachPortChecker(true);
344 portCheckerGoing
= true;
347 #if defined(AVOID_WEAK_COLLECTIONS)
348 for (CFIndex idx
= 0, cnt
= __CFAllMachPorts
? CFArrayGetCount(__CFAllMachPorts
) : 0; idx
< cnt
; idx
++) {
349 CFMachPortRef p
= (CFMachPortRef
)CFArrayGetValueAtIndex(__CFAllMachPorts
, idx
);
350 if (p
&& p
->_port
== port
) {
357 for (CFIndex idx
= 0, cnt
= __CFAllMachPorts
? [__CFAllMachPorts count
] : 0; idx
< cnt
; idx
++) {
358 CFMachPortRef p
= (CFMachPortRef
)[__CFAllMachPorts pointerAtIndex
:idx
];
359 if (p
&& p
->_port
== port
) {
367 CFIndex size
= sizeof(struct __CFMachPort
) - sizeof(CFRuntimeBase
);
368 CFMachPortRef memory
= (CFMachPortRef
)_CFRuntimeCreateInstance(allocator
, CFMachPortGetTypeID(), size
, NULL
);
369 if (NULL
== memory
) {
372 memory
->_port
= port
;
373 memory
->_dsrc
= NULL
;
374 memory
->_dsrc2
= NULL
;
375 memory
->_dsrc_sem
= NULL
;
376 memory
->_dsrc2_sem
= NULL
;
377 memory
->_icallout
= NULL
;
378 memory
->_source
= NULL
;
379 memory
->_context
.info
= NULL
;
380 memory
->_context
.retain
= NULL
;
381 memory
->_context
.release
= NULL
;
382 memory
->_context
.copyDescription
= NULL
;
383 memory
->_callout
= callout
;
384 if (NULL
!= context
) {
385 objc_memmove_collectable(&memory
->_context
, context
, sizeof(CFMachPortContext
));
386 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
388 memory
->_state
= kCFMachPortStateReady
;
389 #if defined(AVOID_WEAK_COLLECTIONS)
390 if (!__CFAllMachPorts
) __CFAllMachPorts
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
);
391 CFArrayAppendValue(__CFAllMachPorts
, memory
);
393 if (!__CFAllMachPorts
) __CFAllMachPorts
= [[__CFPointerArray alloc
] initWithOptions
:(kCFUseCollectableAllocator
? CFPointerFunctionsZeroingWeakMemory
: CFPointerFunctionsStrongMemory
)];
394 [__CFAllMachPorts addPointer
:memory
];
397 if (shouldFreeInfo
) *shouldFreeInfo
= false;
399 if (type
& MACH_PORT_TYPE_SEND_RIGHTS
) {
400 memory
->_dsrc
= dispatch_source_machport_create(port
, DISPATCH_MACHPORT_DEAD
, NULL
, __portQueue(), ^(dispatch_source_t source
) {
401 long e
= 0, d
= dispatch_source_get_error(source
, &e
);
402 if (DISPATCH_ERROR_DOMAIN_MACH
== d
) {
403 CFLog(kCFLogLevelError
, CFSTR("*** ALERT: CFMachPort machport-dead dispatch source provided error (%d, %d)"), d
, e
);
404 dispatch_release(source
);
407 if (DISPATCH_ERROR_DOMAIN_NO_ERROR
!= d
) {
408 CFLog(kCFLogLevelError
, CFSTR("*** ALERT: CFMachPort machport-dead dispatch source provided error (%d, %d)"), d
, e
);
411 __CFMachPortChecker(false);
414 if ((type
& MACH_PORT_TYPE_RECEIVE
) && !(type
& MACH_PORT_TYPE_SEND_RIGHTS
)) {
415 memory
->_dsrc2
= dispatch_source_machport_create(port
, DISPATCH_MACHPORT_DELETED
, NULL
, __portQueue(), ^(dispatch_source_t source
) {
416 long e
= 0, d
= dispatch_source_get_error(source
, &e
);
417 if (DISPATCH_ERROR_DOMAIN_MACH
== d
) {
418 CFLog(kCFLogLevelError
, CFSTR("*** ALERT: CFMachPort machport-deleted dispatch source provided error (%d, %d)"), d
, e
);
419 dispatch_release(source
);
422 if (DISPATCH_ERROR_DOMAIN_NO_ERROR
!= d
) {
423 CFLog(kCFLogLevelError
, CFSTR("*** ALERT: CFMachPort machport-deleted dispatch source provided error (%d, %d)"), d
, e
);
426 __CFMachPortChecker(false);
430 dispatch_source_t source
= memory
->_dsrc
; // put these in locals so they are fully copied into the block
431 dispatch_semaphore_t sem
= dispatch_semaphore_create(0);
432 memory
->_dsrc_sem
= sem
;
433 dispatch_source_set_cancel_handler(memory
->_dsrc
, ^{ dispatch_semaphore_signal(sem
); dispatch_release(source
); });
435 if (memory
->_dsrc2
) {
436 dispatch_source_t source
= memory
->_dsrc2
;
437 dispatch_semaphore_t sem
= dispatch_semaphore_create(0);
438 memory
->_dsrc2_sem
= sem
;
439 dispatch_source_set_cancel_handler(memory
->_dsrc2
, ^{ dispatch_semaphore_signal(sem
); dispatch_release(source
); });
445 CFMachPortRef
CFMachPortCreateWithPort(CFAllocatorRef allocator
, mach_port_t port
, CFMachPortCallBack callout
, CFMachPortContext
*context
, Boolean
*shouldFreeInfo
) {
446 return _CFMachPortCreateWithPort2(allocator
, port
, callout
, context
, shouldFreeInfo
, true);
449 CFMachPortRef
CFMachPortCreate(CFAllocatorRef allocator
, CFMachPortCallBack callout
, CFMachPortContext
*context
, Boolean
*shouldFreeInfo
) {
450 if (shouldFreeInfo
) *shouldFreeInfo
= true;
451 CHECK_FOR_FORK_RET(NULL
);
452 mach_port_t port
= MACH_PORT_NULL
;
453 kern_return_t ret
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &port
);
454 if (KERN_SUCCESS
== ret
) {
455 ret
= mach_port_insert_right(mach_task_self(), port
, port
, MACH_MSG_TYPE_MAKE_SEND
);
457 if (KERN_SUCCESS
!= ret
) {
458 if (MACH_PORT_NULL
!= port
) mach_port_destroy(mach_task_self(), port
);
461 CFMachPortRef result
= _CFMachPortCreateWithPort2(allocator
, port
, callout
, context
, shouldFreeInfo
, true);
462 if (NULL
== result
) {
463 if (MACH_PORT_NULL
!= port
) mach_port_destroy(mach_task_self(), port
);
466 __CFMachPortSetHasReceive(result
);
467 __CFMachPortSetHasSend(result
);
471 void CFMachPortInvalidate(CFMachPortRef mp
) {
472 CHECK_FOR_FORK_RET();
473 CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), void, mp
, "invalidate");
474 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
476 __block CFRunLoopSourceRef source
= NULL
;
477 __block Boolean wasReady
= false;
478 dispatch_sync(__portQueue(), ^{
479 wasReady
= (mp
->_state
== kCFMachPortStateReady
);
481 mp
->_state
= kCFMachPortStateInvalidating
;
483 #if defined(AVOID_WEAK_COLLECTIONS)
484 for (CFIndex idx
= 0, cnt
= __CFAllMachPorts
? CFArrayGetCount(__CFAllMachPorts
) : 0; idx
< cnt
; idx
++) {
485 CFMachPortRef p
= (CFMachPortRef
)CFArrayGetValueAtIndex(__CFAllMachPorts
, idx
);
487 CFArrayRemoveValueAtIndex(__CFAllMachPorts
, idx
);
492 for (CFIndex idx
= 0, cnt
= __CFAllMachPorts
? [__CFAllMachPorts count
] : 0; idx
< cnt
; idx
++) {
493 CFMachPortRef p
= (CFMachPortRef
)[__CFAllMachPorts pointerAtIndex
:idx
];
495 [__CFAllMachPorts removePointerAtIndex
:idx
];
501 dispatch_source_cancel(mp
->_dsrc
);
505 dispatch_source_cancel(mp
->_dsrc2
);
508 source
= mp
->_source
;
513 CFMachPortInvalidationCallBack cb
= mp
->_icallout
;
515 cb(mp
, mp
->_context
.info
);
517 if (NULL
!= source
) {
518 CFRunLoopSourceInvalidate(source
);
521 void *info
= mp
->_context
.info
;
522 mp
->_context
.info
= NULL
;
523 if (mp
->_context
.release
) {
524 mp
->_context
.release(info
);
526 // For hashing and equality purposes, cannot get rid of _port here
527 mp
->_state
= kCFMachPortStateInvalid
;
533 mach_port_t
CFMachPortGetPort(CFMachPortRef mp
) {
534 CHECK_FOR_FORK_RET(0);
535 CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), mach_port_t
, mp
, "machPort");
536 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
540 void CFMachPortGetContext(CFMachPortRef mp
, CFMachPortContext
*context
) {
541 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
542 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
543 objc_memmove_collectable(context
, &mp
->_context
, sizeof(CFMachPortContext
));
546 Boolean
CFMachPortIsValid(CFMachPortRef mp
) {
547 CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), Boolean
, mp
, "isValid");
548 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
549 return __CFMachPortIsValid(mp
);
552 CFMachPortInvalidationCallBack
CFMachPortGetInvalidationCallBack(CFMachPortRef mp
) {
553 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
554 return mp
->_icallout
;
557 /* After the CFMachPort has started going invalid, or done invalid, you can't change this, and
558 we'll only do the callout directly on a transition from NULL to non-NULL. */
559 void CFMachPortSetInvalidationCallBack(CFMachPortRef mp
, CFMachPortInvalidationCallBack callout
) {
560 CHECK_FOR_FORK_RET();
561 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
562 if (__CFMachPortIsValid(mp
) || !callout
) {
563 mp
->_icallout
= callout
;
564 } else if (!mp
->_icallout
&& callout
) {
565 callout(mp
, mp
->_context
.info
);
567 CFLog(kCFLogLevelWarning
, CFSTR("CFMachPortSetInvalidationCallBack(): attempt to set invalidation callback (%p) on invalid CFMachPort (%p) thwarted"), callout
, mp
);
571 /* Returns the number of messages queued for a receive port. */
572 CFIndex
CFMachPortGetQueuedMessageCount(CFMachPortRef mp
) {
573 CHECK_FOR_FORK_RET(0);
574 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
575 mach_port_status_t status
;
576 mach_msg_type_number_t num
= MACH_PORT_RECEIVE_STATUS_COUNT
;
577 kern_return_t ret
= mach_port_get_attributes(mach_task_self(), mp
->_port
, MACH_PORT_RECEIVE_STATUS
, (mach_port_info_t
)&status
, &num
);
578 return (KERN_SUCCESS
!= ret
) ? 0 : status
.mps_msgcount
;
581 static mach_port_t
__CFMachPortGetPort(void *info
) {
582 CFMachPortRef mp
= (CFMachPortRef
)info
;
586 static void *__CFMachPortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
587 CHECK_FOR_FORK_RET(NULL
);
588 CFMachPortRef mp
= (CFMachPortRef
)info
;
589 __block Boolean isValid
= false;
590 dispatch_sync(__portQueue(), ^{
591 isValid
= __CFMachPortIsValid(mp
);
593 if (!isValid
) return NULL
;
595 void *context_info
= NULL
;
596 void (*context_release
)(const void *) = NULL
;
597 if (mp
->_context
.retain
) {
598 context_info
= (void *)mp
->_context
.retain(mp
->_context
.info
);
599 context_release
= mp
->_context
.release
;
601 context_info
= mp
->_context
.info
;
604 mp
->_callout(mp
, msg
, size
, context_info
);
606 if (context_release
) {
607 context_release(context_info
);
609 CHECK_FOR_FORK_RET(NULL
);
613 CFRunLoopSourceRef
CFMachPortCreateRunLoopSource(CFAllocatorRef allocator
, CFMachPortRef mp
, CFIndex order
) {
614 CHECK_FOR_FORK_RET(NULL
);
615 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
616 __block CFRunLoopSourceRef result
= NULL
;
617 dispatch_sync(__portQueue(), ^{
618 if (!__CFMachPortIsValid(mp
)) return;
619 if (NULL
== mp
->_source
) {
620 CFRunLoopSourceContext1 context
;
622 context
.info
= (void *)mp
;
623 context
.retain
= (const void *(*)(const void *))CFRetain
;
624 context
.release
= (void (*)(const void *))CFRelease
;
625 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMachPortCopyDescription
;
626 context
.equal
= (Boolean (*)(const void *, const void *))__CFMachPortEqual
;
627 context
.hash
= (CFHashCode (*)(const void *))__CFMachPortHash
;
628 context
.getPort
= __CFMachPortGetPort
;
629 context
.perform
= __CFMachPortPerform
;
630 mp
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
632 result
= mp
->_source
? (CFRunLoopSourceRef
)CFRetain(mp
->_source
) : NULL
;