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>
44 #define __kCFMessagePortMaxNameLengthMax 255
46 #if defined(BOOTSTRAP_MAX_NAME_LEN)
47 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
49 #define __kCFMessagePortMaxNameLength 128
52 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
53 #undef __kCFMessagePortMaxNameLength
54 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
57 static CFSpinLock_t __CFAllMessagePortsLock
= 0;
58 static CFMutableDictionaryRef __CFAllLocalMessagePorts
= NULL
;
59 static CFMutableDictionaryRef __CFAllRemoteMessagePorts
= NULL
;
61 struct __CFMessagePort
{
65 CFMachPortRef _port
; /* immutable; invalidated */
66 CFMutableDictionaryRef _replies
;
68 CFMachPortRef _replyPort
; /* only used by remote port; immutable once created; invalidated */
69 CFRunLoopSourceRef _source
; /* only used by local port; immutable once created; invalidated */
70 CFMessagePortInvalidationCallBack _icallout
;
71 CFMessagePortCallBack _callout
; /* only used by local port; immutable */
72 CFMessagePortContext _context
; /* not part of remote port; immutable; invalidated */
75 /* Bit 0 in the base reserved bits is used for invalid state */
76 /* Bit 2 of the base reserved bits is used for is-remote state */
77 /* Bit 3 in the base reserved bits is used for is-deallocing state */
79 CF_INLINE Boolean
__CFMessagePortIsValid(CFMessagePortRef ms
) {
80 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 0, 0);
83 CF_INLINE
void __CFMessagePortSetValid(CFMessagePortRef ms
) {
84 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 0, 0, 1);
87 CF_INLINE
void __CFMessagePortUnsetValid(CFMessagePortRef ms
) {
88 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 0, 0, 0);
91 CF_INLINE Boolean
__CFMessagePortIsRemote(CFMessagePortRef ms
) {
92 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 2, 2);
95 CF_INLINE
void __CFMessagePortSetRemote(CFMessagePortRef ms
) {
96 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 2, 2, 1);
99 CF_INLINE
void __CFMessagePortUnsetRemote(CFMessagePortRef ms
) {
100 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 2, 2, 0);
103 CF_INLINE Boolean
__CFMessagePortIsDeallocing(CFMessagePortRef ms
) {
104 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 3, 3);
107 CF_INLINE
void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms
) {
108 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 3, 3, 1);
111 CF_INLINE
void __CFMessagePortLock(CFMessagePortRef ms
) {
112 __CFSpinLock(&(ms
->_lock
));
115 CF_INLINE
void __CFMessagePortUnlock(CFMessagePortRef ms
) {
116 __CFSpinUnlock(&(ms
->_lock
));
120 #define __CFMessagePortMaxInlineBytes 4096*10
122 struct __CFMessagePortMachMsg0
{
125 uint8_t bytes
[__CFMessagePortMaxInlineBytes
];
128 struct __CFMessagePortMachMsg1
{
129 mach_msg_descriptor_t desc
;
133 struct __CFMessagePortMachMessage
{
134 mach_msg_header_t head
;
135 mach_msg_body_t body
;
137 struct __CFMessagePortMachMsg0 msg0
;
138 struct __CFMessagePortMachMsg1 msg1
;
142 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
) {
143 struct __CFMessagePortMachMessage
*msg
;
144 int32_t size
= sizeof(mach_msg_header_t
) + sizeof(mach_msg_body_t
);
145 if (byteslen
< __CFMessagePortMaxInlineBytes
) {
146 size
+= 2 * sizeof(int32_t) + ((byteslen
+ 3) & ~0x3);
148 size
+= sizeof(struct __CFMessagePortMachMsg1
);
150 msg
= CFAllocatorAllocate(allocator
, size
, 0);
151 msg
->head
.msgh_id
= convid
;
152 msg
->head
.msgh_size
= size
;
153 msg
->head
.msgh_remote_port
= port
;
154 msg
->head
.msgh_local_port
= replyPort
;
155 msg
->head
.msgh_reserved
= 0;
156 // msg->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort ? MACH_MSG_TYPE_MAKE_SEND : 0));
157 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));
158 if (byteslen
< __CFMessagePortMaxInlineBytes
) {
159 msg
->body
.msgh_descriptor_count
= 0;
160 msg
->contents
.msg0
.msgid
= CFSwapInt32HostToLittle(msgid
);
161 msg
->contents
.msg0
.byteslen
= CFSwapInt32HostToLittle(byteslen
);
162 if (NULL
!= bytes
&& 0 < byteslen
) {
163 memmove(msg
->contents
.msg0
.bytes
, bytes
, byteslen
);
165 memset(msg
->contents
.msg0
.bytes
+ byteslen
, 0, ((byteslen
+ 3) & ~0x3) - byteslen
);
167 msg
->head
.msgh_bits
|= MACH_MSGH_BITS_COMPLEX
;
168 msg
->body
.msgh_descriptor_count
= 1;
169 msg
->contents
.msg1
.desc
.out_of_line
.deallocate
= false;
170 msg
->contents
.msg1
.desc
.out_of_line
.copy
= MACH_MSG_VIRTUAL_COPY
;
171 msg
->contents
.msg1
.desc
.out_of_line
.address
= (void *)bytes
;
172 msg
->contents
.msg1
.desc
.out_of_line
.size
= byteslen
;
173 msg
->contents
.msg1
.desc
.out_of_line
.type
= MACH_MSG_OOL_DESCRIPTOR
;
174 msg
->contents
.msg1
.msgid
= CFSwapInt32HostToLittle(msgid
);
179 static Boolean
__CFMessagePortNativeSetNameLocal(CFMachPortRef port
, uint8_t *portname
) {
182 task_get_bootstrap_port(mach_task_self(), &bp
);
183 ret
= bootstrap_register(bp
, portname
, CFMachPortGetPort(port
));
184 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
);
185 return (ret
== KERN_SUCCESS
) ? true : false;
188 static Boolean
__CFMessagePortEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
189 CFMessagePortRef ms1
= (CFMessagePortRef
)cf1
;
190 CFMessagePortRef ms2
= (CFMessagePortRef
)cf2
;
191 return CFEqual(ms1
->_port
, ms2
->_port
);
194 static CFHashCode
__CFMessagePortHash(CFTypeRef cf
) {
195 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
196 return CFHash(ms
->_port
);
199 static CFStringRef
__CFMessagePortCopyDescription(CFTypeRef cf
) {
200 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
203 CFStringRef contextDesc
= NULL
;
204 locked
= ms
->_lock
? "Yes" : "No";
205 if (!__CFMessagePortIsRemote(ms
)) {
206 if (NULL
!= ms
->_context
.info
&& NULL
!= ms
->_context
.copyDescription
) {
207 contextDesc
= ms
->_context
.copyDescription(ms
->_context
.info
);
209 if (NULL
== contextDesc
) {
210 contextDesc
= CFStringCreateWithFormat(CFGetAllocator(ms
), NULL
, CFSTR("<CFMessagePort context %p>"), ms
->_context
.info
);
212 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
);
214 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>")));
216 if (NULL
!= contextDesc
) {
217 CFRelease(contextDesc
);
222 static void __CFMessagePortDeallocate(CFTypeRef cf
) {
223 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
224 __CFMessagePortSetIsDeallocing(ms
);
225 CFMessagePortInvalidate(ms
);
226 // Delay cleanup of _replies until here so that invalidation during
227 // SendRequest does not cause _replies to disappear out from under that function.
228 if (NULL
!= ms
->_replies
) {
229 CFRelease(ms
->_replies
);
231 if (NULL
!= ms
->_name
) {
232 CFRelease(ms
->_name
);
234 if (NULL
!= ms
->_port
) {
235 CFMachPortInvalidate(ms
->_port
);
236 CFRelease(ms
->_port
);
240 static CFTypeID __kCFMessagePortTypeID
= _kCFRuntimeNotATypeID
;
242 static const CFRuntimeClass __CFMessagePortClass
= {
247 __CFMessagePortDeallocate
,
248 __CFMessagePortEqual
,
251 __CFMessagePortCopyDescription
254 __private_extern__
void __CFMessagePortInitialize(void) {
255 __kCFMessagePortTypeID
= _CFRuntimeRegisterClass(&__CFMessagePortClass
);
258 CFTypeID
CFMessagePortGetTypeID(void) {
259 return __kCFMessagePortTypeID
;
262 static CFStringRef
__CFMessagePortSanitizeStringName(CFAllocatorRef allocator
, CFStringRef name
, uint8_t **utfnamep
, CFIndex
*utfnamelenp
) {
266 utfname
= CFAllocatorAllocate(allocator
, __kCFMessagePortMaxNameLength
+ 1, 0);
267 CFStringGetBytes(name
, CFRangeMake(0, CFStringGetLength(name
)), kCFStringEncodingUTF8
, 0, false, utfname
, __kCFMessagePortMaxNameLength
, &utflen
);
268 utfname
[utflen
] = '\0';
269 /* A new string is created, because the original string may have been
270 truncated to the max length, and we want the string name to definitely
271 match the raw UTF-8 chunk that has been created. Also, this is useful
272 to get a constant string in case the original name string was mutable. */
273 result
= CFStringCreateWithBytes(allocator
, utfname
, utflen
, kCFStringEncodingUTF8
, false);
274 if (NULL
!= utfnamep
) {
277 CFAllocatorDeallocate(allocator
, utfname
);
279 if (NULL
!= utfnamelenp
) {
280 *utfnamelenp
= utflen
;
285 static void __CFMessagePortDummyCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
286 // not supposed to be implemented
289 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port
, void *info
) {
290 // info has been setup as the CFMessagePort owning the CFMachPort
291 CFMessagePortInvalidate(info
);
294 CFMessagePortRef
CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
295 CFMessagePortRef memory
;
296 CFMachPortRef native
;
297 CFMachPortContext ctx
;
298 uint8_t *utfname
= NULL
;
301 if (shouldFreeInfo
) *shouldFreeInfo
= true;
303 name
= __CFMessagePortSanitizeStringName(allocator
, name
, &utfname
, NULL
);
305 __CFSpinLock(&__CFAllMessagePortsLock
);
307 CFMessagePortRef existing
;
308 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
309 __CFSpinUnlock(&__CFAllMessagePortsLock
);
311 CFAllocatorDeallocate(allocator
, utfname
);
312 return (CFMessagePortRef
)CFRetain(existing
);
315 size
= sizeof(struct __CFMessagePort
) - sizeof(CFRuntimeBase
);
316 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
317 if (NULL
== memory
) {
318 __CFSpinUnlock(&__CFAllMessagePortsLock
);
322 CFAllocatorDeallocate(allocator
, utfname
);
323 CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to allocate object"));
326 __CFMessagePortUnsetValid(memory
);
327 __CFMessagePortUnsetRemote(memory
);
329 memory
->_name
= name
;
330 memory
->_port
= NULL
;
331 memory
->_replies
= NULL
;
332 memory
->_convCounter
= 0;
333 memory
->_replyPort
= NULL
;
334 memory
->_source
= NULL
;
335 memory
->_icallout
= NULL
;
336 memory
->_callout
= callout
;
337 memory
->_context
.info
= NULL
;
338 memory
->_context
.retain
= NULL
;
339 memory
->_context
.release
= NULL
;
340 memory
->_context
.copyDescription
= NULL
;
345 ctx
.copyDescription
= NULL
;
346 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
347 if (!native
) CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to allocate CFMachPortRef"));
348 if (NULL
!= native
&& NULL
!= name
&& !__CFMessagePortNativeSetNameLocal(native
, utfname
)) {
349 CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to name Mach port (%@)"), name
);
350 CFMachPortInvalidate(native
);
354 CFAllocatorDeallocate(allocator
, utfname
);
355 if (NULL
== native
) {
356 __CFSpinUnlock(&__CFAllMessagePortsLock
);
357 // name is released by deallocation
361 memory
->_port
= native
;
362 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
363 __CFMessagePortSetValid(memory
);
364 if (NULL
!= context
) {
365 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
366 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
369 if (NULL
== __CFAllLocalMessagePorts
) {
370 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
372 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
374 __CFSpinUnlock(&__CFAllMessagePortsLock
);
375 if (shouldFreeInfo
) *shouldFreeInfo
= false;
379 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
380 CFMessagePortRef memory
;
381 CFMachPortRef native
;
382 CFMachPortContext ctx
;
383 uint8_t *utfname
= NULL
;
385 mach_port_t bp
, port
;
388 name
= __CFMessagePortSanitizeStringName(allocator
, name
, &utfname
, NULL
);
392 __CFSpinLock(&__CFAllMessagePortsLock
);
394 CFMessagePortRef existing
;
395 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
396 __CFSpinUnlock(&__CFAllMessagePortsLock
);
398 CFAllocatorDeallocate(allocator
, utfname
);
399 return (CFMessagePortRef
)CFRetain(existing
);
402 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
403 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
404 if (NULL
== memory
) {
405 __CFSpinUnlock(&__CFAllMessagePortsLock
);
409 CFAllocatorDeallocate(allocator
, utfname
);
412 __CFMessagePortUnsetValid(memory
);
413 __CFMessagePortSetRemote(memory
);
415 memory
->_name
= name
;
416 memory
->_port
= NULL
;
417 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, NULL
, NULL
);
418 memory
->_convCounter
= 0;
419 memory
->_replyPort
= NULL
;
420 memory
->_source
= NULL
;
421 memory
->_icallout
= NULL
;
422 memory
->_callout
= NULL
;
427 ctx
.copyDescription
= NULL
;
428 task_get_bootstrap_port(mach_task_self(), &bp
);
429 ret
= bootstrap_look_up(bp
, utfname
, &port
);
430 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
431 CFAllocatorDeallocate(allocator
, utfname
);
432 if (NULL
== native
) {
433 __CFSpinUnlock(&__CFAllMessagePortsLock
);
434 // name is released by deallocation
438 memory
->_port
= native
;
439 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
440 __CFMessagePortSetValid(memory
);
442 if (NULL
== __CFAllRemoteMessagePorts
) {
443 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
445 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
447 __CFSpinUnlock(&__CFAllMessagePortsLock
);
448 return (CFMessagePortRef
)memory
;
451 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
452 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
453 return __CFMessagePortIsRemote(ms
);
456 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
457 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
461 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
462 CFAllocatorRef allocator
= CFGetAllocator(ms
);
463 uint8_t *utfname
= NULL
;
465 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
466 // if (__CFMessagePortIsRemote(ms)) return false;
467 //#warning CF: make this an assertion
468 // and assert than newName is non-NULL
469 name
= __CFMessagePortSanitizeStringName(allocator
, name
, &utfname
, NULL
);
473 __CFSpinLock(&__CFAllMessagePortsLock
);
475 CFMessagePortRef existing
;
476 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
477 __CFSpinUnlock(&__CFAllMessagePortsLock
);
479 CFAllocatorDeallocate(allocator
, utfname
);
483 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
484 if (!__CFMessagePortNativeSetNameLocal(ms
->_port
, utfname
)) {
485 __CFSpinUnlock(&__CFAllMessagePortsLock
);
487 CFAllocatorDeallocate(allocator
, utfname
);
490 if (NULL
== __CFAllLocalMessagePorts
) {
491 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
493 if (NULL
!= ms
->_name
) {
494 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
495 CFRelease(ms
->_name
);
498 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
500 __CFSpinUnlock(&__CFAllMessagePortsLock
);
501 CFAllocatorDeallocate(allocator
, utfname
);
505 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
506 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
507 //#warning CF: assert that this is a local port
508 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
509 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
512 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
513 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
514 if (!__CFMessagePortIsDeallocing(ms
)) {
517 __CFMessagePortLock(ms
);
518 if (__CFMessagePortIsValid(ms
)) {
519 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
520 CFRunLoopSourceRef source
= ms
->_source
;
521 CFMachPortRef replyPort
= ms
->_replyPort
;
522 CFMachPortRef port
= ms
->_port
;
523 CFStringRef name
= ms
->_name
;
526 __CFMessagePortUnsetValid(ms
);
527 if (!__CFMessagePortIsRemote(ms
)) {
528 info
= ms
->_context
.info
;
529 ms
->_context
.info
= NULL
;
532 ms
->_replyPort
= NULL
;
533 __CFMessagePortUnlock(ms
);
535 __CFSpinLock(&__CFAllMessagePortsLock
);
536 if (NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
537 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
539 __CFSpinUnlock(&__CFAllMessagePortsLock
);
540 if (NULL
!= callout
) {
543 // We already know we're going invalid, don't need this callback
544 // anymore; plus, this solves a reentrancy deadlock; also, this
545 // must be done before the deallocate of the Mach port, to
546 // avoid a race between the notification message which could be
547 // handled in another thread, and this NULL'ing out.
548 CFMachPortSetInvalidationCallBack(port
, NULL
);
549 // For hashing and equality purposes, cannot get rid of _port here
550 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
551 ms
->_context
.release(info
);
553 if (NULL
!= source
) {
554 CFRunLoopSourceInvalidate(source
);
557 if (NULL
!= replyPort
) {
558 CFMachPortInvalidate(replyPort
);
559 CFRelease(replyPort
);
561 if (__CFMessagePortIsRemote(ms
)) {
562 // Get rid of our extra ref on the Mach port gotten from bs server
563 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
566 __CFMessagePortUnlock(ms
);
568 if (!__CFMessagePortIsDeallocing(ms
)) {
573 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
574 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
575 if (!__CFMessagePortIsValid(ms
)) return false;
576 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
577 CFMessagePortInvalidate(ms
);
580 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
581 CFMessagePortInvalidate(ms
);
584 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
585 CFMessagePortInvalidate(ms
);
591 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
592 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
593 return ms
->_icallout
;
596 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
597 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
598 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
599 callout(ms
, ms
->_context
.info
);
601 ms
->_icallout
= callout
;
605 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
606 CFMessagePortRef ms
= info
;
607 struct __CFMessagePortMachMessage
*msgp
= msg
;
608 struct __CFMessagePortMachMessage
*replymsg
;
609 __CFMessagePortLock(ms
);
610 if (!__CFMessagePortIsValid(ms
)) {
611 __CFMessagePortUnlock(ms
);
614 // assert: (int32_t)msgp->head.msgh_id < 0
615 if (CFDictionaryContainsKey(ms
->_replies
, (void *)msgp
->head
.msgh_id
)) {
616 CFDataRef reply
= NULL
;
617 replymsg
= (struct __CFMessagePortMachMessage
*)msg
;
618 if (0 == replymsg
->body
.msgh_descriptor_count
) {
619 int32_t byteslen
= CFSwapInt32LittleToHost(replymsg
->contents
.msg0
.byteslen
);
621 reply
= CFDataCreate(kCFAllocatorSystemDefault
, replymsg
->contents
.msg0
.bytes
, byteslen
);
623 reply
= (void *)0xffffffff; // means NULL data
626 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
627 reply
= CFDataCreate(kCFAllocatorSystemDefault
, replymsg
->contents
.msg1
.desc
.out_of_line
.address
, replymsg
->contents
.msg1
.desc
.out_of_line
.size
);
628 vm_deallocate(mach_task_self(), (vm_address_t
)replymsg
->contents
.msg1
.desc
.out_of_line
.address
, replymsg
->contents
.msg1
.desc
.out_of_line
.size
);
630 CFDictionarySetValue(ms
->_replies
, (void *)msgp
->head
.msgh_id
, (void *)reply
);
631 } else { /* discard message */
632 if (1 == msgp
->body
.msgh_descriptor_count
) {
633 vm_deallocate(mach_task_self(), (vm_address_t
)msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
);
636 __CFMessagePortUnlock(ms
);
639 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
640 struct __CFMessagePortMachMessage
*sendmsg
;
641 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
642 CFRunLoopSourceRef source
= NULL
;
643 CFDataRef reply
= NULL
;
645 uint32_t sendOpts
= 0, sendTimeOut
= 0;
646 int32_t desiredReply
;
647 Boolean didRegister
= false;
650 //#warning CF: This should be an assert
651 // if (!__CFMessagePortIsRemote(remote)) return -999;
652 if (!__CFMessagePortIsValid(remote
)) return kCFMessagePortIsInvalid
;
653 __CFMessagePortLock(remote
);
654 if (NULL
== remote
->_replyPort
) {
655 CFMachPortContext context
;
657 context
.info
= remote
;
658 context
.retain
= (const void *(*)(const void *))CFRetain
;
659 context
.release
= (void (*)(const void *))CFRelease
;
660 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
661 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
663 remote
->_convCounter
++;
664 desiredReply
= -remote
->_convCounter
;
665 CFDictionarySetValue(remote
->_replies
, (void *)desiredReply
, NULL
);
666 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));
667 __CFMessagePortUnlock(remote
);
668 if (replyMode
!= NULL
) {
669 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
670 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
672 CFRunLoopAddSource(currentRL
, source
, replyMode
);
675 if (sendTimeout
< 10.0*86400) {
676 // anything more than 10 days is no timeout!
677 sendOpts
= MACH_SEND_TIMEOUT
;
678 sendTimeout
*= 1000.0;
679 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
680 sendTimeOut
= floor(sendTimeout
);
682 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->head
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
683 CFAllocatorDeallocate(CFGetAllocator(remote
), sendmsg
);
684 if (KERN_SUCCESS
!= ret
) {
686 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
689 if (MACH_SEND_TIMED_OUT
== ret
) return kCFMessagePortSendTimeout
;
690 return kCFMessagePortTransportError
;
692 if (replyMode
== NULL
) {
693 return kCFMessagePortSuccess
;
695 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
696 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
697 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
699 CFRunLoopRunInMode(replyMode
, __CFTSRToTimeInterval(termTSR
- mach_absolute_time()), true);
700 // warning: what, if anything, should be done if remote is now invalid?
701 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)desiredReply
);
702 if (NULL
!= reply
|| termTSR
< (int64_t)mach_absolute_time()) {
705 if (!CFMessagePortIsValid(remote
)) {
706 // no reason that reply port alone should go invalid so we don't check for that
710 // Should we uninstall the notify port? A complex question...
712 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
716 CFDictionaryRemoveValue(remote
->_replies
, (void *)desiredReply
);
718 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: -5;
720 if (NULL
!= returnDatap
) {
721 *returnDatap
= ((void *)0xffffffff == reply
) ? NULL
: reply
;
722 } else if ((void *)0xffffffff != reply
) {
725 CFDictionaryRemoveValue(remote
->_replies
, (void *)desiredReply
);
727 return kCFMessagePortSuccess
;
730 static mach_port_t
__CFMessagePortGetPort(void *info
) {
731 CFMessagePortRef ms
= info
;
732 return CFMachPortGetPort(ms
->_port
);
735 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
736 CFMessagePortRef ms
= info
;
737 struct __CFMessagePortMachMessage
*msgp
= msg
;
738 struct __CFMessagePortMachMessage
*replymsg
;
740 void (*context_release
)(const void *);
741 CFDataRef returnData
, data
= NULL
;
742 void *return_bytes
= NULL
;
743 CFIndex return_len
= 0;
746 __CFMessagePortLock(ms
);
747 if (!__CFMessagePortIsValid(ms
)) {
748 __CFMessagePortUnlock(ms
);
751 // assert: 0 < (int32_t)msgp->head.msgh_id
752 if (NULL
!= ms
->_context
.retain
) {
753 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
754 context_release
= ms
->_context
.release
;
756 context_info
= ms
->_context
.info
;
757 context_release
= NULL
;
759 __CFMessagePortUnlock(ms
);
760 /* Create no-copy, no-free-bytes wrapper CFData */
761 if (0 == msgp
->body
.msgh_descriptor_count
) {
762 int32_t byteslen
= CFSwapInt32LittleToHost(msgp
->contents
.msg0
.byteslen
);
763 msgid
= CFSwapInt32LittleToHost(msgp
->contents
.msg0
.msgid
);
765 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, msgp
->contents
.msg0
.bytes
, byteslen
, kCFAllocatorNull
);
768 msgid
= CFSwapInt32LittleToHost(msgp
->contents
.msg1
.msgid
);
769 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
, kCFAllocatorNull
);
771 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
772 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
773 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
774 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
775 bytes inline in the Mach message, so can release the returnData object
776 here. In cases (3) and (5), we'll send the data out-of-line, we need to
777 create a copy of the memory, which we'll have the kernel autodeallocate
778 for us on send. In case (4) also, the bytes in the return data may be part
779 of the bytes in "data" that we sent into the callout, so if the incoming
780 data was received out of line, we wouldn't be able to clean up the out-of-line
781 wad until the message was sent either, if we didn't make the copy. */
782 if (NULL
!= returnData
) {
783 return_len
= CFDataGetLength(returnData
);
784 if (return_len
< __CFMessagePortMaxInlineBytes
) {
785 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
788 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
789 /* vm_copy would only be a win here if the source address
790 is page aligned; it is a lose in all other cases, since
791 the kernel will just do the memmove for us (but not in
793 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
796 replymsg
= __CFMessagePortCreateMessage(allocator
, true, msgp
->head
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)msgp
->head
.msgh_id
, msgid
, return_bytes
, return_len
);
797 if (1 == replymsg
->body
.msgh_descriptor_count
) {
798 replymsg
->contents
.msg1
.desc
.out_of_line
.deallocate
= true;
800 if (data
) CFRelease(data
);
801 if (1 == msgp
->body
.msgh_descriptor_count
) {
802 vm_deallocate(mach_task_self(), (vm_address_t
)msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
);
804 if (returnData
) CFRelease(returnData
);
805 if (context_release
) {
806 context_release(context_info
);
811 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
812 CFRunLoopSourceRef result
= NULL
;
813 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
814 //#warning CF: This should be an assert
815 // if (__CFMessagePortIsRemote(ms)) return NULL;
816 __CFMessagePortLock(ms
);
817 if (NULL
== ms
->_source
&& __CFMessagePortIsValid(ms
)) {
818 CFRunLoopSourceContext1 context
;
820 context
.info
= (void *)ms
;
821 context
.retain
= (const void *(*)(const void *))CFRetain
;
822 context
.release
= (void (*)(const void *))CFRelease
;
823 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
824 context
.equal
= (Boolean (*)(const void *, const void *))__CFMessagePortEqual
;
825 context
.hash
= (CFHashCode (*)(const void *))__CFMessagePortHash
;
826 context
.getPort
= __CFMessagePortGetPort
;
827 context
.perform
= __CFMessagePortPerform
;
828 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
830 if (NULL
!= ms
->_source
) {
831 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
833 __CFMessagePortUnlock(ms
);
837 #endif /* !__WIN32__ */