2 * Copyright (c) 2015 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@
25 Copyright (c) 1998-2014, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
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>
37 #include "CFInternal.h"
40 #define AVOID_WEAK_COLLECTIONS 1
42 #if !AVOID_WEAK_COLLECTIONS
43 #import "CFPointerArray.h"
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
);
53 return __CFMachPortQueue
;
58 kCFMachPortStateReady
= 0,
59 kCFMachPortStateInvalidating
= 1,
60 kCFMachPortStateInvalid
= 2,
61 kCFMachPortStateDeallocating
= 3
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 */
75 const void *(*retain
)(const void *info
); // use these to store the real callbacks
76 void (*release
)(const void *info
);
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 */
83 CF_INLINE Boolean
__CFMachPortHasReceive(CFMachPortRef mp
) {
84 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 1, 1);
87 CF_INLINE
void __CFMachPortSetHasReceive(CFMachPortRef mp
) {
88 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 1);
91 CF_INLINE Boolean
__CFMachPortHasSend(CFMachPortRef mp
) {
92 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 2, 2);
95 CF_INLINE
void __CFMachPortSetHasSend(CFMachPortRef mp
) {
96 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 1);
99 CF_INLINE Boolean
__CFMachPortHasSend2(CFMachPortRef mp
) {
100 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 3, 3);
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);
108 CF_INLINE Boolean
__CFMachPortIsValid(CFMachPortRef mp
) {
109 return kCFMachPortStateReady
== mp
->_state
;
113 void _CFMachPortInstallNotifyPort(CFRunLoopRef rl
, CFStringRef mode
) {
116 static Boolean
__CFMachPortEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
117 CFMachPortRef mp1
= (CFMachPortRef
)cf1
;
118 CFMachPortRef mp2
= (CFMachPortRef
)cf2
;
119 return (mp1
->_port
== mp2
->_port
);
122 static CFHashCode
__CFMachPortHash(CFTypeRef cf
) {
123 CFMachPortRef mp
= (CFMachPortRef
)cf
;
124 return (CFHashCode
)mp
->_port
;
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
);
133 if (NULL
== contextDesc
) {
134 contextDesc
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMachPort context %p>"), mp
->_context
.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
);
146 // Only call with mp->_lock locked
147 CF_INLINE
void __CFMachPortInvalidateLocked(CFRunLoopSourceRef source
, CFMachPortRef mp
) {
148 CFMachPortInvalidationCallBack cb
= mp
->_icallout
;
150 __CFUnlock(&mp
->_lock
);
151 cb(mp
, mp
->_context
.info
);
152 __CFLock(&mp
->_lock
);
154 if (NULL
!= source
) {
155 __CFUnlock(&mp
->_lock
);
156 CFRunLoopSourceInvalidate(source
);
158 __CFLock(&mp
->_lock
);
160 void *info
= mp
->_context
.info
;
161 void (*release
)(const void *info
) = mp
->release
;
162 mp
->_context
.info
= NULL
;
164 __CFUnlock(&mp
->_lock
);
166 __CFLock(&mp
->_lock
);
168 mp
->_state
= kCFMachPortStateInvalid
;
172 static void __CFMachPortDeallocate(CFTypeRef cf
) {
173 CHECK_FOR_FORK_RET();
174 CFMachPortRef mp
= (CFMachPortRef
)cf
;
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
);
181 mp
->_state
= kCFMachPortStateInvalidating
;
184 dispatch_source_cancel(mp
->_dsrc
);
187 source
= mp
->_source
;
191 __CFMachPortInvalidateLocked(source
, mp
);
193 mp
->_state
= kCFMachPortStateDeallocating
;
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
);
200 __CFUnlock(&mp
->_lock
);
202 dispatch_async(__CFDispatchQueueGetGenericBackground(), ^{
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
);
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.
213 mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_SEND
, -1);
216 mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_SEND
, -1);
219 mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_RECEIVE
, -1);
224 // This lock protects __CFAllMachPorts. Take before any instance-specific lock.
225 static CFLock_t __CFAllMachPortsLock
= CFLockInit
;
227 #if AVOID_WEAK_COLLECTIONS
228 static CFMutableArrayRef __CFAllMachPorts
= NULL
;
230 static __CFPointerArray
*__CFAllMachPorts
= nil
;
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;
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
);
246 for (CFIndex idx
= 0, cnt
= __CFAllMachPorts
? [__CFAllMachPorts count
] : 0; idx
< cnt
; idx
++) {
247 CFMachPortRef mp
= (CFMachPortRef
)[__CFAllMachPorts pointerAtIndex
:idx
];
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
);
255 __CFLock(&mp
->_lock
); // take this lock second
256 mp
->_state
= kCFMachPortStateInvalidating
;
259 dispatch_source_cancel(mp
->_dsrc
);
262 source
= mp
->_source
;
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
);
275 #if AVOID_WEAK_COLLECTIONS
276 CFArrayRemoveValueAtIndex(__CFAllMachPorts
, idx
);
278 [__CFAllMachPorts removePointerAtIndex
:idx
];
284 #if !AVOID_WEAK_COLLECTIONS
285 [__CFAllMachPorts compact
];
287 __CFUnlock(&__CFAllMachPortsLock
);
291 static CFTypeID __kCFMachPortTypeID
= _kCFRuntimeNotATypeID
;
293 static const CFRuntimeClass __CFMachPortClass
= {
298 __CFMachPortDeallocate
,
302 __CFMachPortCopyDescription
305 CFTypeID
CFMachPortGetTypeID(void) {
306 static dispatch_once_t initOnce
;
307 dispatch_once(&initOnce
, ^{ __kCFMachPortTypeID
= _CFRuntimeRegisterClass(&__CFMachPortClass
); });
308 return __kCFMachPortTypeID
;
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
);
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
);
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);
336 dispatch_resume(timerSource
);
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
) {
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
) {
361 __CFUnlock(&__CFAllMachPortsLock
);
364 CFIndex size
= sizeof(struct __CFMachPort
) - sizeof(CFRuntimeBase
);
365 CFMachPortRef memory
= (CFMachPortRef
)_CFRuntimeCreateInstance(allocator
, CFMachPortGetTypeID(), size
, NULL
);
366 if (NULL
== memory
) {
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;
390 memory
->_state
= kCFMachPortStateReady
;
391 __CFLock(&__CFAllMachPortsLock
);
392 #if AVOID_WEAK_COLLECTIONS
393 if (!__CFAllMachPorts
) __CFAllMachPorts
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
);
394 CFArrayAppendValue(__CFAllMachPorts
, memory
);
396 if (!__CFAllMachPorts
) __CFAllMachPorts
= [[__CFPointerArray alloc
] initWithOptions
:(kCFUseCollectableAllocator
? CFPointerFunctionsZeroingWeakMemory
: CFPointerFunctionsStrongMemory
)];
397 [__CFAllMachPorts addPointer
:memory
];
399 __CFUnlock(&__CFAllMachPortsLock
);
401 if (shouldFreeInfo
) *shouldFreeInfo
= false;
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());
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
);
417 if (mp
&& !CFMachPortIsValid(mp
)) { // must do this outside lock to avoid deadlock
424 CFMachPortRef
CFMachPortCreateWithPort(CFAllocatorRef allocator
, mach_port_t port
, CFMachPortCallBack callout
, CFMachPortContext
*context
, Boolean
*shouldFreeInfo
) {
425 return _CFMachPortCreateWithPort2(allocator
, port
, callout
, context
, shouldFreeInfo
, true);
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
);
436 if (KERN_SUCCESS
!= ret
) {
437 if (MACH_PORT_NULL
!= port
) mach_port_destroy(mach_task_self(), port
);
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
);
445 __CFMachPortSetHasReceive(result
);
446 __CFMachPortSetHasSend(result
);
450 void CFMachPortInvalidate(CFMachPortRef mp
) {
451 CHECK_FOR_FORK_RET();
452 CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), void, (NSMachPort
*)mp
, invalidate
);
453 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
455 CFRunLoopSourceRef source
= NULL
;
456 Boolean wasReady
= false;
457 __CFLock(&__CFAllMachPortsLock
); // take this lock first
458 __CFLock(&mp
->_lock
);
459 wasReady
= (mp
->_state
== kCFMachPortStateReady
);
461 mp
->_state
= kCFMachPortStateInvalidating
;
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
);
467 CFArrayRemoveValueAtIndex(__CFAllMachPorts
, idx
);
472 for (CFIndex idx
= 0, cnt
= __CFAllMachPorts
? [__CFAllMachPorts count
] : 0; idx
< cnt
; idx
++) {
473 CFMachPortRef p
= (CFMachPortRef
)[__CFAllMachPorts pointerAtIndex
:idx
];
475 [__CFAllMachPorts removePointerAtIndex
:idx
];
481 dispatch_source_cancel(mp
->_dsrc
);
484 source
= mp
->_source
;
487 __CFUnlock(&mp
->_lock
);
488 __CFUnlock(&__CFAllMachPortsLock
); // release this lock last
490 __CFLock(&mp
->_lock
);
491 __CFMachPortInvalidateLocked(source
, mp
);
492 __CFUnlock(&mp
->_lock
);
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());
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
));
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
))) {
522 CFMachPortInvalidationCallBack
CFMachPortGetInvalidationCallBack(CFMachPortRef mp
) {
523 __CFGenericValidateType(mp
, CFMachPortGetTypeID());
524 __CFLock(&mp
->_lock
);
525 CFMachPortInvalidationCallBack cb
= mp
->_icallout
;
526 __CFUnlock(&mp
->_lock
);
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());
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
);
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
);
550 CFLog(kCFLogLevelWarning
, CFSTR("CFMachPortSetInvalidationCallBack(): attempt to set invalidation callback (%p) on invalid CFMachPort (%p) thwarted"), callout
, mp
);
552 __CFUnlock(&mp
->_lock
);
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
;
565 static mach_port_t
__CFMachPortGetPort(void *info
) {
566 CFMachPortRef mp
= (CFMachPortRef
)info
;
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
;
579 context_info
= (void *)mp
->retain(mp
->_context
.info
);
580 context_release
= mp
->release
;
582 context_info
= mp
->_context
.info
;
585 __CFUnlock(&mp
->_lock
);
587 mp
->_callout(mp
, msg
, size
, context_info
);
589 if (context_release
) {
590 context_release(context_info
);
592 CHECK_FOR_FORK_RET(NULL
);
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
);
611 if (NULL
== mp
->_source
) {
612 CFRunLoopSourceContext1 context
;
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
);
624 result
= mp
->_source
? (CFRunLoopSourceRef
)CFRetain(mp
->_source
) : NULL
;
626 __CFUnlock(&mp
->_lock
);