2 * Copyright (c) 2008 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 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
29 [The following dissertation was written mostly for the
30 benefit of open source developers.]
32 Writing a run loop source can be a tricky business, but
33 for CFMachPort that part is relatively straightforward.
34 Thus, it makes a good example for study. Particularly
35 interesting for examination is the process of caching
36 the objects in a non-retaining cache, the invalidation
37 and deallocation sequences, locking for thread-safety,
38 and how the invalidation callback is used.
40 CFMachPort is a version 1 CFRunLoopSource, implemented
41 by a few functions. See CFMachPortCreateRunLoopSource()
42 for details on how the run loop source is setup. Note
43 how the source is kept track of by the CFMachPort, so
44 that it can be returned again and again from that function.
45 This helps not only reduce the amount of memory expended
46 in run loop source objects, but eliminates redundant
47 registrations with the run loop and the excess time and
48 memory that would consume. It also allows the CFMachPort
49 to propogate its own invalidation to the run loop source
52 CFMachPortCreateWithPort() is the funnel point for the
53 creation of CFMachPort instances. The cache is first
54 probed to see if an instance with that port is already
55 available, and return that. The object is next allocated
56 and mostly initialized, before it is registered for death
57 notification. This is because cleaning up the memory is
58 simpler than trying to get rid of the registration if
59 memory allocation later fails. The new object must be at
60 least partially initialized (into a harmless state) so
61 that it can be safely invalidated/deallocated if something
62 fails later in creation. Any object allocated with
63 _CFRuntimeCreateInstance() may only be disposed by using
64 CFRelease() (never CFAllocatorDeallocate!) so the class
65 deallocation function __CFMachPortDeallocate() must be
66 able to handle that, and initializing the object to have
67 NULL fields and whatnot makes that possible. The creation
68 eventually inserts the new object in the cache.
70 A CFMachPort may be explicitly invalidated, autoinvalidated
71 due to the death of the port (that process is not discussed
72 further here), or invalidated as part of the deallocation
73 process when the last reference is released. For
74 convenience, in all cases this is done through
75 CFMachPortInvalidate(). To prevent the CFMachPort from
76 being freed in mid-function due to the callouts, the object
77 is retained at the beginning of the function. But if this
78 invalidation is due to the object being deallocated, a
79 retain and then release at the end of the function would
80 cause a recursive call to __CFMachPortDeallocate(). The
81 retain protection should be immaterial though at that stage.
82 Invalidation also removes the object from the cache; though
83 the object itself is not yet destroyed, invalidation makes
86 The best way to learn about the locking is to look through
87 the code -- it's fairly straightforward. The one thing
88 worth calling attention to is how locks must be unlocked
89 before invoking any user-defined callout function, and
90 usually retaken after it returns. This supports reentrancy
91 (which is distinct from thread-safety).
93 The invalidation callback, if one has been set, is called
94 at invalidation time, but before the object has been torn
95 down so that the port and other properties may be retrieved
96 from the object in the callback. Note that if the callback
97 is attempted to be set after the CFMachPort is invalid,
98 the function is simply called. This helps with certain
99 race conditions where the invalidation notification might
100 be lost. Only the owner/creator of a CFMachPort should
101 really be setting the invalidation callback.
103 Now, the CFMachPort is not retained/released around all
104 callouts, but the callout may release the last reference.
105 Also, sometimes it is friendly to retain/release the
106 user-defined "info" around callouts, so that clients
107 don't have to worry about that. These may be some things
108 to think about in the future, but is usually overkill.
110 In general, with higher level functionalities in the system,
111 it isn't even possible for a process to fork() and the child
112 not exec(), but continue running, since the higher levels
113 have done one-time initializations that aren't going to
120 #include <CoreFoundation/CFMachPort.h>
121 #include <CoreFoundation/CFRunLoop.h>
122 #include <CoreFoundation/CFDictionary.h>
123 #include <CoreFoundation/CFByteOrder.h>
124 #include <mach/mach.h>
125 #include <mach/mach_error.h>
126 #include <mach/notify.h>
128 #include "CFInternal.h"
131 static CFSpinLock_t __CFAllMachPortsLock
= CFSpinLockInit
;
132 static CFMutableDictionaryRef __CFAllMachPorts
= NULL
;
133 static mach_port_t __CFNotifyRawMachPort
= MACH_PORT_NULL
;
134 static CFMachPortRef __CFNotifyMachPort
= NULL
;
136 struct __CFMachPort
{
139 mach_port_t _port
; /* immutable; invalidated */
140 mach_port_t _oldnotify
; /* immutable; invalidated */
141 CFRunLoopSourceRef _source
; /* immutable, once created; invalidated */
142 CFMachPortInvalidationCallBack _icallout
;
143 CFMachPortCallBack _callout
; /* immutable */
144 CFMachPortContext _context
; /* immutable; invalidated */
147 /* Bit 0 in the base reserved bits is used for invalid state */
148 /* Bit 1 in the base reserved bits is used for has-receive-ref state */
149 /* Bit 2 in the base reserved bits is used for has-send-ref state */
150 /* Bit 3 in the base reserved bits is used for is-deallocing state */
152 CF_INLINE Boolean
__CFMachPortIsValid(CFMachPortRef mp
) {
153 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 0, 0);
156 CF_INLINE
void __CFMachPortSetValid(CFMachPortRef mp
) {
157 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 1);
160 CF_INLINE
void __CFMachPortUnsetValid(CFMachPortRef mp
) {
161 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 0);
164 CF_INLINE Boolean
__CFMachPortHasReceive(CFMachPortRef mp
) {
165 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 1, 1);
168 CF_INLINE
void __CFMachPortSetHasReceive(CFMachPortRef mp
) {
169 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 1);
172 CF_INLINE Boolean
__CFMachPortHasSend(CFMachPortRef mp
) {
173 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 2, 2);
176 CF_INLINE
void __CFMachPortSetHasSend(CFMachPortRef mp
) {
177 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 1);
180 CF_INLINE Boolean
__CFMachPortIsDeallocing(CFMachPortRef mp
) {
181 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 3, 3);
184 CF_INLINE
void __CFMachPortSetIsDeallocing(CFMachPortRef mp
) {
185 __CFBitfieldSetValue(((CFRuntimeBase
*)mp
)->_cfinfo
[CF_INFO_BITS
], 3, 3, 1);
188 CF_INLINE
void __CFMachPortLock(CFMachPortRef mp
) {
189 __CFSpinLock(&(mp
->_lock
));
192 CF_INLINE
void __CFMachPortUnlock(CFMachPortRef mp
) {
193 __CFSpinUnlock(&(mp
->_lock
));
196 void _CFMachPortInstallNotifyPort(CFRunLoopRef rl
, CFStringRef mode
) {
197 CFRunLoopSourceRef source
;
198 if (NULL
== __CFNotifyMachPort
) return;
199 source
= CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault
, __CFNotifyMachPort
, -1000);
200 CFRunLoopAddSource(rl
, source
, mode
);
204 static void __CFNotifyDeadMachPort(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
205 mach_msg_header_t
*header
= (mach_msg_header_t
*)msg
;
206 mach_port_t dead_port
= MACH_PORT_NULL
;
207 if (header
&& header
->msgh_id
== MACH_NOTIFY_DEAD_NAME
) {
208 dead_port
= ((mach_dead_name_notification_t
*)msg
)->not_port
;
209 if (((mach_dead_name_notification_t
*)msg
)->NDR
.int_rep
!= NDR_record
.int_rep
) {
210 dead_port
= CFSwapInt32(dead_port
);
212 } else if (header
&& header
->msgh_id
== MACH_NOTIFY_PORT_DELETED
) {
213 dead_port
= ((mach_port_deleted_notification_t
*)msg
)->not_port
;
214 if (((mach_port_deleted_notification_t
*)msg
)->NDR
.int_rep
!= NDR_record
.int_rep
) {
215 dead_port
= CFSwapInt32(dead_port
);
221 CFMachPortRef existing
;
222 /* If the CFMachPort has already been invalidated, it won't be found here. */
223 __CFSpinLock(&__CFAllMachPortsLock
);
224 if (NULL
!= __CFAllMachPorts
&& CFDictionaryGetValueIfPresent(__CFAllMachPorts
, (void *)(uintptr_t)dead_port
, (const void **)&existing
)) {
225 CFDictionaryRemoveValue(__CFAllMachPorts
, (void *)(uintptr_t)dead_port
);
227 __CFSpinUnlock(&__CFAllMachPortsLock
);
228 __CFMachPortLock(existing
);
229 mach_port_t old_port
= existing
->_oldnotify
;
230 existing
->_oldnotify
= MACH_PORT_NULL
;
231 __CFMachPortUnlock(existing
);
232 if (MACH_PORT_NULL
!= old_port
) {
233 header
->msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE
, 0) | MACH_MSGH_BITS_COMPLEX
;
234 header
->msgh_local_port
= MACH_PORT_NULL
;
235 header
->msgh_remote_port
= old_port
;
236 mach_msg(header
, MACH_SEND_MSG
, header
->msgh_size
, 0, MACH_PORT_NULL
, MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
);
238 CFMachPortInvalidate(existing
);
241 __CFSpinUnlock(&__CFAllMachPortsLock
);
244 if (header
&& header
->msgh_id
== MACH_NOTIFY_DEAD_NAME
) {
245 /* Delete port reference we got for this notification */
246 mach_port_deallocate(mach_task_self(), dead_port
);
250 static Boolean
__CFMachPortEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
251 CFMachPortRef mp1
= (CFMachPortRef
)cf1
;
252 CFMachPortRef mp2
= (CFMachPortRef
)cf2
;
253 return (mp1
->_port
== mp2
->_port
);
256 static CFHashCode
__CFMachPortHash(CFTypeRef cf
) {
258 CFMachPortRef mp
= (CFMachPortRef
)cf
;
259 return (CFHashCode
)mp
->_port
;
262 static CFStringRef
__CFMachPortCopyDescription(CFTypeRef cf
) {
263 CFMachPortRef mp
= (CFMachPortRef
)cf
;
266 CFStringRef contextDesc
= NULL
;
267 locked
= mp
->_lock
? "Yes" : "No";
268 if (NULL
!= mp
->_context
.info
&& NULL
!= mp
->_context
.copyDescription
) {
269 contextDesc
= mp
->_context
.copyDescription(mp
->_context
.info
);
271 if (NULL
== contextDesc
) {
272 contextDesc
= CFStringCreateWithFormat(CFGetAllocator(mp
), NULL
, CFSTR("<CFMachPort context %p>"), mp
->_context
.info
);
274 void *addr
= mp
->_callout
;
276 const char *name
= (dladdr(addr
, &info
) && info
.dli_saddr
== addr
&& info
.dli_sname
) ? info
.dli_sname
: "???";
277 result
= CFStringCreateWithFormat(CFGetAllocator(mp
), NULL
, CFSTR("<CFMachPort %p [%p]>{locked = %s, valid = %s, port = %p, source = %p, callout = %s (%p), context = %@}"), cf
, CFGetAllocator(mp
), locked
, (__CFMachPortIsValid(mp
) ? "Yes" : "No"), mp
->_port
, mp
->_source
, name
, addr
, (NULL
!= contextDesc
? contextDesc
: CFSTR("<no description>")));
278 if (NULL
!= contextDesc
) {
279 CFRelease(contextDesc
);
284 static void __CFMachPortDeallocate(CFTypeRef cf
) {
286 CFMachPortRef mp
= (CFMachPortRef
)cf
;
287 __CFMachPortSetIsDeallocing(mp
);
288 CFMachPortInvalidate(mp
);
289 // MUST deallocate the send right FIRST if necessary,
290 // then the receive right if necessary. Don't ask me why;
291 // if it's done in the other order the port will leak.
292 if (__CFMachPortHasSend(mp
)) {
293 mach_port_mod_refs(mach_task_self(), mp
->_port
, MACH_PORT_RIGHT_SEND
, -1);
295 if (__CFMachPortHasReceive(mp
)) {
296 mach_port_mod_refs(mach_task_self(), mp
->_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
300 static CFTypeID __kCFMachPortTypeID
= _kCFRuntimeNotATypeID
;
302 static const CFRuntimeClass __CFMachPortClass
= {
307 __CFMachPortDeallocate
,
311 __CFMachPortCopyDescription
314 __private_extern__
void __CFMachPortInitialize(void) {
315 __kCFMachPortTypeID
= _CFRuntimeRegisterClass(&__CFMachPortClass
);
318 CFTypeID
CFMachPortGetTypeID(void) {
319 return __kCFMachPortTypeID
;
322 CFMachPortRef
CFMachPortCreate(CFAllocatorRef allocator
, CFMachPortCallBack callout
, CFMachPortContext
*context
, Boolean
*shouldFreeInfo
) {
323 CFMachPortRef result
;
326 if (shouldFreeInfo
) *shouldFreeInfo
= true;
327 ret
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &port
);
328 if (KERN_SUCCESS
!= ret
) {
331 ret
= mach_port_insert_right(mach_task_self(), port
, port
, MACH_MSG_TYPE_MAKE_SEND
);
332 if (KERN_SUCCESS
!= ret
) {
333 mach_port_destroy(mach_task_self(), port
);
336 result
= CFMachPortCreateWithPort(allocator
, port
, callout
, context
, shouldFreeInfo
);
337 if (NULL
!= result
) {
338 __CFMachPortSetHasReceive(result
);
339 __CFMachPortSetHasSend(result
);
344 /* Note: any receive or send rights that the port contains coming in will
345 * not be cleaned up by CFMachPort; it will increment and decrement
346 * references on the port if the kernel ever allows that in the future,
347 * but will not cleanup any references you got when you got the port. */
348 CFMachPortRef
CFMachPortCreateWithPort(CFAllocatorRef allocator
, mach_port_t port
, CFMachPortCallBack callout
, CFMachPortContext
*context
, Boolean
*shouldFreeInfo
) {
350 CFMachPortRef memory
;
352 Boolean didCreateNotifyPort
= false;
353 CFRunLoopSourceRef source
;
354 if (shouldFreeInfo
) *shouldFreeInfo
= true;
355 __CFSpinLock(&__CFAllMachPortsLock
);
356 if (NULL
!= __CFAllMachPorts
&& CFDictionaryGetValueIfPresent(__CFAllMachPorts
, (void *)(uintptr_t)port
, (const void **)&memory
)) {
358 __CFSpinUnlock(&__CFAllMachPortsLock
);
359 return (CFMachPortRef
)(memory
);
361 size
= sizeof(struct __CFMachPort
) - sizeof(CFRuntimeBase
);
362 memory
= (CFMachPortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMachPortTypeID
, size
, NULL
);
363 if (NULL
== memory
) {
364 __CFSpinUnlock(&__CFAllMachPortsLock
);
367 __CFMachPortUnsetValid(memory
);
368 memory
->_lock
= CFSpinLockInit
;
369 memory
->_port
= port
;
370 memory
->_source
= NULL
;
371 memory
->_icallout
= NULL
;
372 memory
->_context
.info
= NULL
;
373 memory
->_context
.retain
= NULL
;
374 memory
->_context
.release
= NULL
;
375 memory
->_context
.copyDescription
= NULL
;
376 if (MACH_PORT_NULL
== __CFNotifyRawMachPort
) {
378 ret
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &__CFNotifyRawMachPort
);
379 if (KERN_SUCCESS
!= ret
) {
380 __CFSpinUnlock(&__CFAllMachPortsLock
);
384 didCreateNotifyPort
= true;
386 // Do not register for notifications on the notify port
387 if (MACH_PORT_NULL
!= __CFNotifyRawMachPort
&& port
!= __CFNotifyRawMachPort
) {
388 mach_port_t old_port
;
390 old_port
= MACH_PORT_NULL
;
391 ret
= mach_port_request_notification(mach_task_self(), port
, MACH_NOTIFY_DEAD_NAME
, 0, __CFNotifyRawMachPort
, MACH_MSG_TYPE_MAKE_SEND_ONCE
, &old_port
);
392 if (ret
!= KERN_SUCCESS
) {
393 __CFSpinUnlock(&__CFAllMachPortsLock
);
397 memory
->_oldnotify
= old_port
;
399 __CFMachPortSetValid(memory
);
400 memory
->_callout
= callout
;
401 if (NULL
!= context
) {
402 CF_WRITE_BARRIER_MEMMOVE(&memory
->_context
, context
, sizeof(CFMachPortContext
));
403 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
405 if (NULL
== __CFAllMachPorts
) {
406 __CFAllMachPorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
407 _CFDictionarySetCapacity(__CFAllMachPorts
, 20);
409 CFDictionaryAddValue(__CFAllMachPorts
, (void *)(uintptr_t)port
, memory
);
410 __CFSpinUnlock(&__CFAllMachPortsLock
);
411 if (didCreateNotifyPort
) {
412 // __CFNotifyMachPort ends up in cache
413 CFMachPortRef mp
= CFMachPortCreateWithPort(kCFAllocatorSystemDefault
, __CFNotifyRawMachPort
, __CFNotifyDeadMachPort
, NULL
, NULL
);
414 __CFMachPortSetHasReceive(mp
);
415 __CFNotifyMachPort
= mp
;
417 if (NULL
!= __CFNotifyMachPort
) {
418 // We do this so that it gets into each thread's run loop, since
419 // we don't know which run loop is the main thread's, and that's
420 // not necessarily the "right" one anyway. This won't happen for
421 // the call which creates the __CFNotifyMachPort itself, but that's
422 // OK since it will happen in the invocation of this function
423 // from which that call was triggered.
424 source
= CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault
, __CFNotifyMachPort
, -1000);
425 CFRunLoopAddSource(CFRunLoopGetCurrent(), source
, kCFRunLoopCommonModes
);
428 if (shouldFreeInfo
) *shouldFreeInfo
= false;
432 mach_port_t
CFMachPortGetPort(CFMachPortRef mp
) {
434 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID
, mach_port_t
, mp
, "machPort");
435 __CFGenericValidateType(mp
, __kCFMachPortTypeID
);
439 void CFMachPortGetContext(CFMachPortRef mp
, CFMachPortContext
*context
) {
440 __CFGenericValidateType(mp
, __kCFMachPortTypeID
);
441 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
442 CF_WRITE_BARRIER_MEMMOVE(context
, &mp
->_context
, sizeof(CFMachPortContext
));
445 void CFMachPortInvalidate(CFMachPortRef mp
) {
447 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID
, void, mp
, "invalidate");
448 __CFGenericValidateType(mp
, __kCFMachPortTypeID
);
449 if (!__CFMachPortIsDeallocing(mp
)) {
452 __CFSpinLock(&__CFAllMachPortsLock
);
453 if (NULL
!= __CFAllMachPorts
) {
454 CFDictionaryRemoveValue(__CFAllMachPorts
, (void *)(uintptr_t)(mp
->_port
));
456 __CFSpinUnlock(&__CFAllMachPortsLock
);
457 __CFMachPortLock(mp
);
458 if (__CFMachPortIsValid(mp
)) {
459 CFRunLoopSourceRef source
;
461 mach_port_t old_port
= mp
->_oldnotify
;
462 CFMachPortInvalidationCallBack callout
= mp
->_icallout
;
463 __CFMachPortUnsetValid(mp
);
464 __CFMachPortUnlock(mp
);
465 if (NULL
!= callout
) {
466 callout(mp
, mp
->_context
.info
);
468 __CFMachPortLock(mp
);
469 // For hashing and equality purposes, cannot get rid of _port here
470 source
= mp
->_source
;
472 info
= mp
->_context
.info
;
473 mp
->_context
.info
= NULL
;
474 __CFMachPortUnlock(mp
);
475 if (NULL
!= mp
->_context
.release
) {
476 mp
->_context
.release(info
);
478 if (NULL
!= source
) {
479 CFRunLoopSourceInvalidate(source
);
482 if (MACH_PORT_NULL
!= old_port
) {
483 mach_port_deallocate(mach_task_self(), old_port
);
486 __CFMachPortUnlock(mp
);
488 if (!__CFMachPortIsDeallocing(mp
)) {
493 Boolean
CFMachPortIsValid(CFMachPortRef mp
) {
494 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID
, Boolean
, mp
, "isValid");
495 __CFGenericValidateType(mp
, __kCFMachPortTypeID
);
496 return __CFMachPortIsValid(mp
);
499 CFMachPortInvalidationCallBack
CFMachPortGetInvalidationCallBack(CFMachPortRef mp
) {
500 __CFGenericValidateType(mp
, __kCFMachPortTypeID
);
501 return mp
->_icallout
;
504 void CFMachPortSetInvalidationCallBack(CFMachPortRef mp
, CFMachPortInvalidationCallBack callout
) {
505 __CFGenericValidateType(mp
, __kCFMachPortTypeID
);
506 if (!__CFMachPortIsValid(mp
) && NULL
!= callout
) {
507 callout(mp
, mp
->_context
.info
);
509 mp
->_icallout
= callout
;
513 /* Returns the number of messages queued for a receive port. */
514 CFIndex
CFMachPortGetQueuedMessageCount(CFMachPortRef mp
) {
516 mach_port_status_t status
;
517 mach_msg_type_number_t num
= MACH_PORT_RECEIVE_STATUS_COUNT
;
519 ret
= mach_port_get_attributes(mach_task_self(), mp
->_port
, MACH_PORT_RECEIVE_STATUS
, (mach_port_info_t
)&status
, &num
);
520 return (KERN_SUCCESS
!= ret
) ? 0 : status
.mps_msgcount
;
523 static mach_port_t
__CFMachPortGetPort(void *info
) {
524 CFMachPortRef mp
= info
;
528 static void *__CFMachPortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
530 CFMachPortRef mp
= info
;
532 void (*context_release
)(const void *);
533 __CFMachPortLock(mp
);
534 if (!__CFMachPortIsValid(mp
)) {
535 __CFMachPortUnlock(mp
);
538 if (NULL
!= mp
->_context
.retain
) {
539 context_info
= (void *)mp
->_context
.retain(mp
->_context
.info
);
540 context_release
= mp
->_context
.release
;
542 context_info
= mp
->_context
.info
;
543 context_release
= NULL
;
545 __CFMachPortUnlock(mp
);
546 mp
->_callout(mp
, msg
, size
, mp
->_context
.info
);
548 if (context_release
) {
549 context_release(context_info
);
554 CFRunLoopSourceRef
CFMachPortCreateRunLoopSource(CFAllocatorRef allocator
, CFMachPortRef mp
, CFIndex order
) {
556 CFRunLoopSourceRef result
= NULL
;
557 __CFGenericValidateType(mp
, __kCFMachPortTypeID
);
558 __CFMachPortLock(mp
);
559 if (!__CFMachPortIsValid(mp
)) {
560 __CFMachPortUnlock(mp
);
563 if (NULL
== mp
->_source
) {
564 CFRunLoopSourceContext1 context
;
566 context
.info
= (void *)mp
;
567 context
.retain
= (const void *(*)(const void *))CFRetain
;
568 context
.release
= (void (*)(const void *))CFRelease
;
569 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMachPortCopyDescription
;
570 context
.equal
= (Boolean (*)(const void *, const void *))__CFMachPortEqual
;
571 context
.hash
= (CFHashCode (*)(const void *))__CFMachPortHash
;
572 context
.getPort
= __CFMachPortGetPort
;
573 context
.perform
= __CFMachPortPerform
;
574 mp
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
576 if (NULL
!= mp
->_source
) {
577 result
= (CFRunLoopSourceRef
)CFRetain(mp
->_source
);
579 __CFMachPortUnlock(mp
);