2 * Copyright (c) 2005 Apple Computer, 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
28 #if !defined(__WIN32__)
30 #include <CoreFoundation/CFMessagePort.h>
31 #include <CoreFoundation/CFRunLoop.h>
32 #include <CoreFoundation/CFMachPort.h>
33 #include <CoreFoundation/CFDictionary.h>
34 #include <CoreFoundation/CFByteOrder.h>
36 #include "CFInternal.h"
37 #include <mach/mach.h>
38 #include <mach/message.h>
39 #include <mach/mach_error.h>
40 #include <servers/bootstrap.h>
42 #include <mach/mach_time.h>
45 #define __kCFMessagePortMaxNameLengthMax 255
47 #if defined(BOOTSTRAP_MAX_NAME_LEN)
48 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
50 #define __kCFMessagePortMaxNameLength 128
53 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
54 #undef __kCFMessagePortMaxNameLength
55 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
58 static CFSpinLock_t __CFAllMessagePortsLock
= 0;
59 static CFMutableDictionaryRef __CFAllLocalMessagePorts
= NULL
;
60 static CFMutableDictionaryRef __CFAllRemoteMessagePorts
= NULL
;
62 struct __CFMessagePort
{
66 CFMachPortRef _port
; /* immutable; invalidated */
67 CFMutableDictionaryRef _replies
;
69 CFMachPortRef _replyPort
; /* only used by remote port; immutable once created; invalidated */
70 CFRunLoopSourceRef _source
; /* only used by local port; immutable once created; invalidated */
71 CFMessagePortInvalidationCallBack _icallout
;
72 CFMessagePortCallBack _callout
; /* only used by local port; immutable */
73 CFMessagePortContext _context
; /* not part of remote port; immutable; invalidated */
76 /* Bit 0 in the base reserved bits is used for invalid state */
77 /* Bit 2 of the base reserved bits is used for is-remote state */
78 /* Bit 3 in the base reserved bits is used for is-deallocing state */
80 CF_INLINE Boolean
__CFMessagePortIsValid(CFMessagePortRef ms
) {
81 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 0, 0);
84 CF_INLINE
void __CFMessagePortSetValid(CFMessagePortRef ms
) {
85 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 0, 0, 1);
88 CF_INLINE
void __CFMessagePortUnsetValid(CFMessagePortRef ms
) {
89 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 0, 0, 0);
92 CF_INLINE Boolean
__CFMessagePortIsRemote(CFMessagePortRef ms
) {
93 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 2, 2);
96 CF_INLINE
void __CFMessagePortSetRemote(CFMessagePortRef ms
) {
97 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 2, 2, 1);
100 CF_INLINE
void __CFMessagePortUnsetRemote(CFMessagePortRef ms
) {
101 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 2, 2, 0);
104 CF_INLINE Boolean
__CFMessagePortIsDeallocing(CFMessagePortRef ms
) {
105 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 3, 3);
108 CF_INLINE
void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms
) {
109 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 3, 3, 1);
112 CF_INLINE
void __CFMessagePortLock(CFMessagePortRef ms
) {
113 __CFSpinLock(&(ms
->_lock
));
116 CF_INLINE
void __CFMessagePortUnlock(CFMessagePortRef ms
) {
117 __CFSpinUnlock(&(ms
->_lock
));
121 #define __CFMessagePortMaxInlineBytes 4096*10
123 struct __CFMessagePortMachMsg0
{
126 uint8_t bytes
[__CFMessagePortMaxInlineBytes
];
129 struct __CFMessagePortMachMsg1
{
130 mach_msg_descriptor_t desc
;
134 struct __CFMessagePortMachMessage
{
135 mach_msg_header_t head
;
136 mach_msg_body_t body
;
138 struct __CFMessagePortMachMsg0 msg0
;
139 struct __CFMessagePortMachMsg1 msg1
;
143 static struct __CFMessagePortMachMessage
*__CFMessagePortCreateMessage(CFAllocatorRef allocator
, bool reply
, mach_port_t port
, mach_port_t replyPort
, int32_t convid
, int32_t msgid
, const uint8_t *bytes
, int32_t byteslen
) {
144 struct __CFMessagePortMachMessage
*msg
;
145 int32_t size
= sizeof(mach_msg_header_t
) + sizeof(mach_msg_body_t
);
146 if (byteslen
< __CFMessagePortMaxInlineBytes
) {
147 size
+= 2 * sizeof(int32_t) + ((byteslen
+ 3) & ~0x3);
149 size
+= sizeof(struct __CFMessagePortMachMsg1
);
151 msg
= CFAllocatorAllocate(allocator
, size
, 0);
152 msg
->head
.msgh_id
= convid
;
153 msg
->head
.msgh_size
= size
;
154 msg
->head
.msgh_remote_port
= port
;
155 msg
->head
.msgh_local_port
= replyPort
;
156 msg
->head
.msgh_reserved
= 0;
157 // msg->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort ? MACH_MSG_TYPE_MAKE_SEND : 0));
158 msg
->head
.msgh_bits
= MACH_MSGH_BITS((reply
? MACH_MSG_TYPE_MOVE_SEND_ONCE
: MACH_MSG_TYPE_COPY_SEND
), (MACH_PORT_NULL
!= replyPort
? MACH_MSG_TYPE_MAKE_SEND_ONCE
: 0));
159 if (byteslen
< __CFMessagePortMaxInlineBytes
) {
160 msg
->body
.msgh_descriptor_count
= 0;
161 msg
->contents
.msg0
.msgid
= CFSwapInt32HostToLittle(msgid
);
162 msg
->contents
.msg0
.byteslen
= CFSwapInt32HostToLittle(byteslen
);
163 if (NULL
!= bytes
&& 0 < byteslen
) {
164 memmove(msg
->contents
.msg0
.bytes
, bytes
, byteslen
);
166 memset(msg
->contents
.msg0
.bytes
+ byteslen
, 0, ((byteslen
+ 3) & ~0x3) - byteslen
);
168 msg
->head
.msgh_bits
|= MACH_MSGH_BITS_COMPLEX
;
169 msg
->body
.msgh_descriptor_count
= 1;
170 msg
->contents
.msg1
.desc
.out_of_line
.deallocate
= false;
171 msg
->contents
.msg1
.desc
.out_of_line
.copy
= MACH_MSG_VIRTUAL_COPY
;
172 msg
->contents
.msg1
.desc
.out_of_line
.address
= (void *)bytes
;
173 msg
->contents
.msg1
.desc
.out_of_line
.size
= byteslen
;
174 msg
->contents
.msg1
.desc
.out_of_line
.type
= MACH_MSG_OOL_DESCRIPTOR
;
175 msg
->contents
.msg1
.msgid
= CFSwapInt32HostToLittle(msgid
);
180 static Boolean
__CFMessagePortNativeSetNameLocal(CFMachPortRef port
, uint8_t *portname
) {
183 task_get_bootstrap_port(mach_task_self(), &bp
);
184 ret
= bootstrap_register(bp
, portname
, CFMachPortGetPort(port
));
185 if (ret
!= KERN_SUCCESS
) CFLog(0, CFSTR("CFMessagePort: bootstrap_register(): failed %d (0x%x), port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret
, ret
, CFMachPortGetPort(port
), portname
);
186 return (ret
== KERN_SUCCESS
) ? true : false;
189 static Boolean
__CFMessagePortEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
190 CFMessagePortRef ms1
= (CFMessagePortRef
)cf1
;
191 CFMessagePortRef ms2
= (CFMessagePortRef
)cf2
;
192 return CFEqual(ms1
->_port
, ms2
->_port
);
195 static CFHashCode
__CFMessagePortHash(CFTypeRef cf
) {
196 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
197 return CFHash(ms
->_port
);
200 static CFStringRef
__CFMessagePortCopyDescription(CFTypeRef cf
) {
201 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
204 CFStringRef contextDesc
= NULL
;
205 locked
= ms
->_lock
? "Yes" : "No";
206 if (!__CFMessagePortIsRemote(ms
)) {
207 if (NULL
!= ms
->_context
.info
&& NULL
!= ms
->_context
.copyDescription
) {
208 contextDesc
= ms
->_context
.copyDescription(ms
->_context
.info
);
210 if (NULL
== contextDesc
) {
211 contextDesc
= CFStringCreateWithFormat(CFGetAllocator(ms
), NULL
, CFSTR("<CFMessagePort context %p>"), ms
->_context
.info
);
213 result
= CFStringCreateWithFormat(CFGetAllocator(ms
), NULL
, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@}"), cf
, CFGetAllocator(ms
), locked
, (__CFMessagePortIsValid(ms
) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms
) ? "Yes" : "No"), ms
->_name
);
215 result
= CFStringCreateWithFormat(CFGetAllocator(ms
), NULL
, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@, source = %p, callout = %p, context = %@}"), cf
, CFGetAllocator(ms
), locked
, (__CFMessagePortIsValid(ms
) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms
) ? "Yes" : "No"), ms
->_name
, ms
->_source
, ms
->_callout
, (NULL
!= contextDesc
? contextDesc
: CFSTR("<no description>")));
217 if (NULL
!= contextDesc
) {
218 CFRelease(contextDesc
);
223 static void __CFMessagePortDeallocate(CFTypeRef cf
) {
224 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
225 __CFMessagePortSetIsDeallocing(ms
);
226 CFMessagePortInvalidate(ms
);
227 // Delay cleanup of _replies until here so that invalidation during
228 // SendRequest does not cause _replies to disappear out from under that function.
229 if (NULL
!= ms
->_replies
) {
230 CFRelease(ms
->_replies
);
232 if (NULL
!= ms
->_name
) {
233 CFRelease(ms
->_name
);
235 if (NULL
!= ms
->_port
) {
236 CFMachPortInvalidate(ms
->_port
);
237 CFRelease(ms
->_port
);
241 static CFTypeID __kCFMessagePortTypeID
= _kCFRuntimeNotATypeID
;
243 static const CFRuntimeClass __CFMessagePortClass
= {
248 __CFMessagePortDeallocate
,
249 __CFMessagePortEqual
,
252 __CFMessagePortCopyDescription
255 __private_extern__
void __CFMessagePortInitialize(void) {
256 __kCFMessagePortTypeID
= _CFRuntimeRegisterClass(&__CFMessagePortClass
);
259 CFTypeID
CFMessagePortGetTypeID(void) {
260 return __kCFMessagePortTypeID
;
263 static CFStringRef
__CFMessagePortSanitizeStringName(CFAllocatorRef allocator
, CFStringRef name
, uint8_t **utfnamep
, CFIndex
*utfnamelenp
) {
267 utfname
= CFAllocatorAllocate(allocator
, __kCFMessagePortMaxNameLength
+ 1, 0);
268 CFStringGetBytes(name
, CFRangeMake(0, CFStringGetLength(name
)), kCFStringEncodingUTF8
, 0, false, utfname
, __kCFMessagePortMaxNameLength
, &utflen
);
269 utfname
[utflen
] = '\0';
270 /* A new string is created, because the original string may have been
271 truncated to the max length, and we want the string name to definitely
272 match the raw UTF-8 chunk that has been created. Also, this is useful
273 to get a constant string in case the original name string was mutable. */
274 result
= CFStringCreateWithBytes(allocator
, utfname
, utflen
, kCFStringEncodingUTF8
, false);
275 if (NULL
!= utfnamep
) {
278 CFAllocatorDeallocate(allocator
, utfname
);
280 if (NULL
!= utfnamelenp
) {
281 *utfnamelenp
= utflen
;
286 static void __CFMessagePortDummyCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
287 // not supposed to be implemented
290 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port
, void *info
) {
291 // info has been setup as the CFMessagePort owning the CFMachPort
292 CFMessagePortInvalidate(info
);
295 CFMessagePortRef
CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
296 CFMessagePortRef memory
;
297 CFMachPortRef native
;
298 CFMachPortContext ctx
;
299 uint8_t *utfname
= NULL
;
302 if (shouldFreeInfo
) *shouldFreeInfo
= true;
304 name
= __CFMessagePortSanitizeStringName(allocator
, name
, &utfname
, NULL
);
306 __CFSpinLock(&__CFAllMessagePortsLock
);
308 CFMessagePortRef existing
;
309 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
310 __CFSpinUnlock(&__CFAllMessagePortsLock
);
312 CFAllocatorDeallocate(allocator
, utfname
);
313 return (CFMessagePortRef
)CFRetain(existing
);
316 size
= sizeof(struct __CFMessagePort
) - sizeof(CFRuntimeBase
);
317 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
318 if (NULL
== memory
) {
319 __CFSpinUnlock(&__CFAllMessagePortsLock
);
323 CFAllocatorDeallocate(allocator
, utfname
);
324 CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to allocate object"));
327 __CFMessagePortUnsetValid(memory
);
328 __CFMessagePortUnsetRemote(memory
);
330 memory
->_name
= name
;
331 memory
->_port
= NULL
;
332 memory
->_replies
= NULL
;
333 memory
->_convCounter
= 0;
334 memory
->_replyPort
= NULL
;
335 memory
->_source
= NULL
;
336 memory
->_icallout
= NULL
;
337 memory
->_callout
= callout
;
338 memory
->_context
.info
= NULL
;
339 memory
->_context
.retain
= NULL
;
340 memory
->_context
.release
= NULL
;
341 memory
->_context
.copyDescription
= NULL
;
346 ctx
.copyDescription
= NULL
;
347 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
348 if (!native
) CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to allocate CFMachPortRef"));
349 if (NULL
!= native
&& NULL
!= name
&& !__CFMessagePortNativeSetNameLocal(native
, utfname
)) {
350 CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to name Mach port (%@)"), name
);
351 CFMachPortInvalidate(native
);
355 CFAllocatorDeallocate(allocator
, utfname
);
356 if (NULL
== native
) {
357 __CFSpinUnlock(&__CFAllMessagePortsLock
);
358 // name is released by deallocation
362 memory
->_port
= native
;
363 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
364 __CFMessagePortSetValid(memory
);
365 if (NULL
!= context
) {
366 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
367 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
370 if (NULL
== __CFAllLocalMessagePorts
) {
371 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
373 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
375 __CFSpinUnlock(&__CFAllMessagePortsLock
);
376 if (shouldFreeInfo
) *shouldFreeInfo
= false;
380 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
381 CFMessagePortRef memory
;
382 CFMachPortRef native
;
383 CFMachPortContext ctx
;
384 uint8_t *utfname
= NULL
;
386 mach_port_t bp
, port
;
389 name
= __CFMessagePortSanitizeStringName(allocator
, name
, &utfname
, NULL
);
393 __CFSpinLock(&__CFAllMessagePortsLock
);
395 CFMessagePortRef existing
;
396 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
397 __CFSpinUnlock(&__CFAllMessagePortsLock
);
399 CFAllocatorDeallocate(allocator
, utfname
);
400 return (CFMessagePortRef
)CFRetain(existing
);
403 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
404 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
405 if (NULL
== memory
) {
406 __CFSpinUnlock(&__CFAllMessagePortsLock
);
410 CFAllocatorDeallocate(allocator
, utfname
);
413 __CFMessagePortUnsetValid(memory
);
414 __CFMessagePortSetRemote(memory
);
416 memory
->_name
= name
;
417 memory
->_port
= NULL
;
418 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, NULL
, NULL
);
419 memory
->_convCounter
= 0;
420 memory
->_replyPort
= NULL
;
421 memory
->_source
= NULL
;
422 memory
->_icallout
= NULL
;
423 memory
->_callout
= NULL
;
428 ctx
.copyDescription
= NULL
;
429 task_get_bootstrap_port(mach_task_self(), &bp
);
430 ret
= bootstrap_look_up(bp
, utfname
, &port
);
431 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
432 CFAllocatorDeallocate(allocator
, utfname
);
433 if (NULL
== native
) {
434 __CFSpinUnlock(&__CFAllMessagePortsLock
);
435 // name is released by deallocation
439 memory
->_port
= native
;
440 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
441 __CFMessagePortSetValid(memory
);
443 if (NULL
== __CFAllRemoteMessagePorts
) {
444 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
446 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
448 __CFSpinUnlock(&__CFAllMessagePortsLock
);
449 return (CFMessagePortRef
)memory
;
452 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
453 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
454 return __CFMessagePortIsRemote(ms
);
457 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
458 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
462 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
463 CFAllocatorRef allocator
= CFGetAllocator(ms
);
464 uint8_t *utfname
= NULL
;
466 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
467 // if (__CFMessagePortIsRemote(ms)) return false;
468 //#warning CF: make this an assertion
469 // and assert than newName is non-NULL
470 name
= __CFMessagePortSanitizeStringName(allocator
, name
, &utfname
, NULL
);
474 __CFSpinLock(&__CFAllMessagePortsLock
);
476 CFMessagePortRef existing
;
477 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
478 __CFSpinUnlock(&__CFAllMessagePortsLock
);
480 CFAllocatorDeallocate(allocator
, utfname
);
484 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
485 if (!__CFMessagePortNativeSetNameLocal(ms
->_port
, utfname
)) {
486 __CFSpinUnlock(&__CFAllMessagePortsLock
);
488 CFAllocatorDeallocate(allocator
, utfname
);
491 if (NULL
== __CFAllLocalMessagePorts
) {
492 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
494 if (NULL
!= ms
->_name
) {
495 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
496 CFRelease(ms
->_name
);
499 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
501 __CFSpinUnlock(&__CFAllMessagePortsLock
);
502 CFAllocatorDeallocate(allocator
, utfname
);
506 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
507 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
508 //#warning CF: assert that this is a local port
509 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
510 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
513 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
514 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
515 if (!__CFMessagePortIsDeallocing(ms
)) {
518 __CFMessagePortLock(ms
);
519 if (__CFMessagePortIsValid(ms
)) {
520 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
521 CFRunLoopSourceRef source
= ms
->_source
;
522 CFMachPortRef replyPort
= ms
->_replyPort
;
523 CFMachPortRef port
= ms
->_port
;
524 CFStringRef name
= ms
->_name
;
527 __CFMessagePortUnsetValid(ms
);
528 if (!__CFMessagePortIsRemote(ms
)) {
529 info
= ms
->_context
.info
;
530 ms
->_context
.info
= NULL
;
533 ms
->_replyPort
= NULL
;
534 __CFMessagePortUnlock(ms
);
536 __CFSpinLock(&__CFAllMessagePortsLock
);
537 if (NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
538 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
540 __CFSpinUnlock(&__CFAllMessagePortsLock
);
541 if (NULL
!= callout
) {
544 // We already know we're going invalid, don't need this callback
545 // anymore; plus, this solves a reentrancy deadlock; also, this
546 // must be done before the deallocate of the Mach port, to
547 // avoid a race between the notification message which could be
548 // handled in another thread, and this NULL'ing out.
549 CFMachPortSetInvalidationCallBack(port
, NULL
);
550 // For hashing and equality purposes, cannot get rid of _port here
551 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
552 ms
->_context
.release(info
);
554 if (NULL
!= source
) {
555 CFRunLoopSourceInvalidate(source
);
558 if (NULL
!= replyPort
) {
559 CFMachPortInvalidate(replyPort
);
560 CFRelease(replyPort
);
562 if (__CFMessagePortIsRemote(ms
)) {
563 // Get rid of our extra ref on the Mach port gotten from bs server
564 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
567 __CFMessagePortUnlock(ms
);
569 if (!__CFMessagePortIsDeallocing(ms
)) {
574 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
575 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
576 if (!__CFMessagePortIsValid(ms
)) return false;
577 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
578 CFMessagePortInvalidate(ms
);
581 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
582 CFMessagePortInvalidate(ms
);
585 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
586 CFMessagePortInvalidate(ms
);
592 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
593 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
594 return ms
->_icallout
;
597 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
598 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
599 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
600 callout(ms
, ms
->_context
.info
);
602 ms
->_icallout
= callout
;
606 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
607 CFMessagePortRef ms
= info
;
608 struct __CFMessagePortMachMessage
*msgp
= msg
;
609 struct __CFMessagePortMachMessage
*replymsg
;
610 __CFMessagePortLock(ms
);
611 if (!__CFMessagePortIsValid(ms
)) {
612 __CFMessagePortUnlock(ms
);
615 // assert: (int32_t)msgp->head.msgh_id < 0
616 if (CFDictionaryContainsKey(ms
->_replies
, (void *)msgp
->head
.msgh_id
)) {
617 CFDataRef reply
= NULL
;
618 replymsg
= (struct __CFMessagePortMachMessage
*)msg
;
619 if (0 == replymsg
->body
.msgh_descriptor_count
) {
620 int32_t byteslen
= CFSwapInt32LittleToHost(replymsg
->contents
.msg0
.byteslen
);
622 reply
= CFDataCreate(kCFAllocatorSystemDefault
, replymsg
->contents
.msg0
.bytes
, byteslen
);
624 reply
= (void *)0xffffffff; // means NULL data
627 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
628 reply
= CFDataCreate(kCFAllocatorSystemDefault
, replymsg
->contents
.msg1
.desc
.out_of_line
.address
, replymsg
->contents
.msg1
.desc
.out_of_line
.size
);
629 vm_deallocate(mach_task_self(), (vm_address_t
)replymsg
->contents
.msg1
.desc
.out_of_line
.address
, replymsg
->contents
.msg1
.desc
.out_of_line
.size
);
631 CFDictionarySetValue(ms
->_replies
, (void *)msgp
->head
.msgh_id
, (void *)reply
);
632 } else { /* discard message */
633 if (1 == msgp
->body
.msgh_descriptor_count
) {
634 vm_deallocate(mach_task_self(), (vm_address_t
)msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
);
637 __CFMessagePortUnlock(ms
);
640 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
641 struct __CFMessagePortMachMessage
*sendmsg
;
642 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
643 CFRunLoopSourceRef source
= NULL
;
644 CFDataRef reply
= NULL
;
646 uint32_t sendOpts
= 0, sendTimeOut
= 0;
647 int32_t desiredReply
;
648 Boolean didRegister
= false;
651 //#warning CF: This should be an assert
652 // if (!__CFMessagePortIsRemote(remote)) return -999;
653 if (!__CFMessagePortIsValid(remote
)) return kCFMessagePortIsInvalid
;
654 __CFMessagePortLock(remote
);
655 if (NULL
== remote
->_replyPort
) {
656 CFMachPortContext context
;
658 context
.info
= remote
;
659 context
.retain
= (const void *(*)(const void *))CFRetain
;
660 context
.release
= (void (*)(const void *))CFRelease
;
661 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
662 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
664 remote
->_convCounter
++;
665 desiredReply
= -remote
->_convCounter
;
666 CFDictionarySetValue(remote
->_replies
, (void *)desiredReply
, NULL
);
667 sendmsg
= __CFMessagePortCreateMessage(CFGetAllocator(remote
), false, CFMachPortGetPort(remote
->_port
), (replyMode
!= NULL
? CFMachPortGetPort(remote
->_replyPort
) : MACH_PORT_NULL
), -desiredReply
, msgid
, (data
? CFDataGetBytePtr(data
) : NULL
), (data
? CFDataGetLength(data
) : 0));
668 __CFMessagePortUnlock(remote
);
669 if (replyMode
!= NULL
) {
670 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
671 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
673 CFRunLoopAddSource(currentRL
, source
, replyMode
);
676 if (sendTimeout
< 10.0*86400) {
677 // anything more than 10 days is no timeout!
678 sendOpts
= MACH_SEND_TIMEOUT
;
679 sendTimeout
*= 1000.0;
680 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
681 sendTimeOut
= floor(sendTimeout
);
683 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->head
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
684 CFAllocatorDeallocate(CFGetAllocator(remote
), sendmsg
);
685 if (KERN_SUCCESS
!= ret
) {
687 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
690 if (MACH_SEND_TIMED_OUT
== ret
) return kCFMessagePortSendTimeout
;
691 return kCFMessagePortTransportError
;
693 if (replyMode
== NULL
) {
694 return kCFMessagePortSuccess
;
696 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
697 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
698 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
700 CFRunLoopRunInMode(replyMode
, __CFTSRToTimeInterval(termTSR
- mach_absolute_time()), true);
701 // warning: what, if anything, should be done if remote is now invalid?
702 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)desiredReply
);
703 if (NULL
!= reply
|| termTSR
< (int64_t)mach_absolute_time()) {
706 if (!CFMessagePortIsValid(remote
)) {
707 // no reason that reply port alone should go invalid so we don't check for that
711 // Should we uninstall the notify port? A complex question...
713 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
717 CFDictionaryRemoveValue(remote
->_replies
, (void *)desiredReply
);
719 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: -5;
721 if (NULL
!= returnDatap
) {
722 *returnDatap
= ((void *)0xffffffff == reply
) ? NULL
: reply
;
723 } else if ((void *)0xffffffff != reply
) {
726 CFDictionaryRemoveValue(remote
->_replies
, (void *)desiredReply
);
728 return kCFMessagePortSuccess
;
731 static mach_port_t
__CFMessagePortGetPort(void *info
) {
732 CFMessagePortRef ms
= info
;
733 return CFMachPortGetPort(ms
->_port
);
736 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
737 CFMessagePortRef ms
= info
;
738 struct __CFMessagePortMachMessage
*msgp
= msg
;
739 struct __CFMessagePortMachMessage
*replymsg
;
741 void (*context_release
)(const void *);
742 CFDataRef returnData
, data
= NULL
;
743 void *return_bytes
= NULL
;
744 CFIndex return_len
= 0;
747 __CFMessagePortLock(ms
);
748 if (!__CFMessagePortIsValid(ms
)) {
749 __CFMessagePortUnlock(ms
);
752 // assert: 0 < (int32_t)msgp->head.msgh_id
753 if (NULL
!= ms
->_context
.retain
) {
754 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
755 context_release
= ms
->_context
.release
;
757 context_info
= ms
->_context
.info
;
758 context_release
= NULL
;
760 __CFMessagePortUnlock(ms
);
761 /* Create no-copy, no-free-bytes wrapper CFData */
762 if (0 == msgp
->body
.msgh_descriptor_count
) {
763 int32_t byteslen
= CFSwapInt32LittleToHost(msgp
->contents
.msg0
.byteslen
);
764 msgid
= CFSwapInt32LittleToHost(msgp
->contents
.msg0
.msgid
);
766 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, msgp
->contents
.msg0
.bytes
, byteslen
, kCFAllocatorNull
);
769 msgid
= CFSwapInt32LittleToHost(msgp
->contents
.msg1
.msgid
);
770 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
, kCFAllocatorNull
);
772 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
773 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
774 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
775 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
776 bytes inline in the Mach message, so can release the returnData object
777 here. In cases (3) and (5), we'll send the data out-of-line, we need to
778 create a copy of the memory, which we'll have the kernel autodeallocate
779 for us on send. In case (4) also, the bytes in the return data may be part
780 of the bytes in "data" that we sent into the callout, so if the incoming
781 data was received out of line, we wouldn't be able to clean up the out-of-line
782 wad until the message was sent either, if we didn't make the copy. */
783 if (NULL
!= returnData
) {
784 return_len
= CFDataGetLength(returnData
);
785 if (return_len
< __CFMessagePortMaxInlineBytes
) {
786 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
789 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
790 /* vm_copy would only be a win here if the source address
791 is page aligned; it is a lose in all other cases, since
792 the kernel will just do the memmove for us (but not in
794 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
797 replymsg
= __CFMessagePortCreateMessage(allocator
, true, msgp
->head
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)msgp
->head
.msgh_id
, msgid
, return_bytes
, return_len
);
798 if (1 == replymsg
->body
.msgh_descriptor_count
) {
799 replymsg
->contents
.msg1
.desc
.out_of_line
.deallocate
= true;
801 if (data
) CFRelease(data
);
802 if (1 == msgp
->body
.msgh_descriptor_count
) {
803 vm_deallocate(mach_task_self(), (vm_address_t
)msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
);
805 if (returnData
) CFRelease(returnData
);
806 if (context_release
) {
807 context_release(context_info
);
812 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
813 CFRunLoopSourceRef result
= NULL
;
814 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
815 //#warning CF: This should be an assert
816 // if (__CFMessagePortIsRemote(ms)) return NULL;
817 __CFMessagePortLock(ms
);
818 if (NULL
== ms
->_source
&& __CFMessagePortIsValid(ms
)) {
819 CFRunLoopSourceContext1 context
;
821 context
.info
= (void *)ms
;
822 context
.retain
= (const void *(*)(const void *))CFRetain
;
823 context
.release
= (void (*)(const void *))CFRelease
;
824 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
825 context
.equal
= (Boolean (*)(const void *, const void *))__CFMessagePortEqual
;
826 context
.hash
= (CFHashCode (*)(const void *))__CFMessagePortHash
;
827 context
.getPort
= __CFMessagePortGetPort
;
828 context
.perform
= __CFMessagePortPerform
;
829 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
831 if (NULL
!= ms
->_source
) {
832 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
834 __CFMessagePortUnlock(ms
);
838 #endif /* !__WIN32__ */