2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright 1998-2002, Apple, Inc. All rights reserved.
27 Responsibility: Christopher Kane
30 #if !defined(__WIN32__)
32 #include <CoreFoundation/CFMessagePort.h>
33 #include <CoreFoundation/CFRunLoop.h>
34 #include <CoreFoundation/CFMachPort.h>
35 #include <CoreFoundation/CFDictionary.h>
36 #include <CoreFoundation/CFByteOrder.h>
38 #include "CFInternal.h"
39 #include <mach/mach.h>
40 #include <mach/message.h>
41 #include <mach/mach_error.h>
42 #include <servers/bootstrap.h>
44 #include <mach/mach_time.h>
46 #define __kCFMessagePortMaxNameLengthMax 255
48 #if defined(BOOTSTRAP_MAX_NAME_LEN)
49 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
51 #define __kCFMessagePortMaxNameLength 128
54 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
55 #undef __kCFMessagePortMaxNameLength
56 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
59 static CFSpinLock_t __CFAllMessagePortsLock
= 0;
60 static CFMutableDictionaryRef __CFAllLocalMessagePorts
= NULL
;
61 static CFMutableDictionaryRef __CFAllRemoteMessagePorts
= NULL
;
63 struct __CFMessagePort
{
67 CFMachPortRef _port
; /* immutable; invalidated */
68 CFMutableDictionaryRef _replies
;
70 CFMachPortRef _replyPort
; /* only used by remote port; immutable once created; invalidated */
71 CFRunLoopSourceRef _source
; /* only used by local port; immutable once created; invalidated */
72 CFMessagePortInvalidationCallBack _icallout
;
73 CFMessagePortCallBack _callout
; /* only used by local port; immutable */
74 CFMessagePortContext _context
; /* not part of remote port; immutable; invalidated */
77 /* Bit 0 in the base reserved bits is used for invalid state */
78 /* Bit 2 of the base reserved bits is used for is-remote state */
79 /* Bit 3 in the base reserved bits is used for is-deallocing state */
81 CF_INLINE Boolean
__CFMessagePortIsValid(CFMessagePortRef ms
) {
82 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 0, 0);
85 CF_INLINE
void __CFMessagePortSetValid(CFMessagePortRef ms
) {
86 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 0, 0, 1);
89 CF_INLINE
void __CFMessagePortUnsetValid(CFMessagePortRef ms
) {
90 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 0, 0, 0);
93 CF_INLINE Boolean
__CFMessagePortIsRemote(CFMessagePortRef ms
) {
94 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 2, 2);
97 CF_INLINE
void __CFMessagePortSetRemote(CFMessagePortRef ms
) {
98 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 2, 2, 1);
101 CF_INLINE
void __CFMessagePortUnsetRemote(CFMessagePortRef ms
) {
102 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 2, 2, 0);
105 CF_INLINE Boolean
__CFMessagePortIsDeallocing(CFMessagePortRef ms
) {
106 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_info
, 3, 3);
109 CF_INLINE
void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms
) {
110 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_info
, 3, 3, 1);
113 CF_INLINE
void __CFMessagePortLock(CFMessagePortRef ms
) {
114 __CFSpinLock(&(ms
->_lock
));
117 CF_INLINE
void __CFMessagePortUnlock(CFMessagePortRef ms
) {
118 __CFSpinUnlock(&(ms
->_lock
));
122 #define __CFMessagePortMaxInlineBytes 4096*10
124 struct __CFMessagePortMachMsg0
{
127 uint8_t bytes
[__CFMessagePortMaxInlineBytes
];
130 struct __CFMessagePortMachMsg1
{
131 mach_msg_descriptor_t desc
;
135 struct __CFMessagePortMachMessage
{
136 mach_msg_header_t head
;
137 mach_msg_body_t body
;
139 struct __CFMessagePortMachMsg0 msg0
;
140 struct __CFMessagePortMachMsg1 msg1
;
144 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
) {
145 struct __CFMessagePortMachMessage
*msg
;
146 int32_t size
= sizeof(mach_msg_header_t
) + sizeof(mach_msg_body_t
);
147 if (byteslen
< __CFMessagePortMaxInlineBytes
) {
148 size
+= 2 * sizeof(int32_t) + ((byteslen
+ 3) & ~0x3);
150 size
+= sizeof(struct __CFMessagePortMachMsg1
);
152 msg
= CFAllocatorAllocate(allocator
, size
, 0);
153 msg
->head
.msgh_id
= convid
;
154 msg
->head
.msgh_size
= size
;
155 msg
->head
.msgh_remote_port
= port
;
156 msg
->head
.msgh_local_port
= replyPort
;
157 msg
->head
.msgh_reserved
= 0;
158 // msg->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort ? MACH_MSG_TYPE_MAKE_SEND : 0));
159 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));
160 if (byteslen
< __CFMessagePortMaxInlineBytes
) {
161 msg
->body
.msgh_descriptor_count
= 0;
162 msg
->contents
.msg0
.msgid
= CFSwapInt32HostToLittle(msgid
);
163 msg
->contents
.msg0
.byteslen
= CFSwapInt32HostToLittle(byteslen
);
164 if (NULL
!= bytes
&& 0 < byteslen
) {
165 memmove(msg
->contents
.msg0
.bytes
, bytes
, byteslen
);
167 memset(msg
->contents
.msg0
.bytes
+ byteslen
, 0, ((byteslen
+ 3) & ~0x3) - byteslen
);
169 msg
->head
.msgh_bits
|= MACH_MSGH_BITS_COMPLEX
;
170 msg
->body
.msgh_descriptor_count
= 1;
171 msg
->contents
.msg1
.desc
.out_of_line
.deallocate
= false;
172 msg
->contents
.msg1
.desc
.out_of_line
.copy
= MACH_MSG_VIRTUAL_COPY
;
173 msg
->contents
.msg1
.desc
.out_of_line
.address
= (void *)bytes
;
174 msg
->contents
.msg1
.desc
.out_of_line
.size
= byteslen
;
175 msg
->contents
.msg1
.desc
.out_of_line
.type
= MACH_MSG_OOL_DESCRIPTOR
;
176 msg
->contents
.msg1
.msgid
= CFSwapInt32HostToLittle(msgid
);
181 static Boolean
__CFMessagePortNativeSetNameLocal(CFMachPortRef port
, uint8_t *portname
) {
184 task_get_bootstrap_port(mach_task_self(), &bp
);
185 ret
= bootstrap_register(bp
, portname
, CFMachPortGetPort(port
));
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
);
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 (NULL
!= native
&& NULL
!= name
&& !__CFMessagePortNativeSetNameLocal(native
, utfname
)) {
348 CFMachPortInvalidate(native
);
352 CFAllocatorDeallocate(allocator
, utfname
);
353 if (NULL
== native
) {
354 __CFSpinUnlock(&__CFAllMessagePortsLock
);
355 // name is released by deallocation
359 memory
->_port
= native
;
360 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
361 __CFMessagePortSetValid(memory
);
362 if (NULL
!= context
) {
363 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
364 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
367 if (NULL
== __CFAllLocalMessagePorts
) {
368 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
370 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
372 __CFSpinUnlock(&__CFAllMessagePortsLock
);
373 if (shouldFreeInfo
) *shouldFreeInfo
= false;
377 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
378 CFMessagePortRef memory
;
379 CFMachPortRef native
;
380 CFMachPortContext ctx
;
381 uint8_t *utfname
= NULL
;
383 mach_port_t bp
, port
;
386 name
= __CFMessagePortSanitizeStringName(allocator
, name
, &utfname
, NULL
);
390 __CFSpinLock(&__CFAllMessagePortsLock
);
392 CFMessagePortRef existing
;
393 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
394 __CFSpinUnlock(&__CFAllMessagePortsLock
);
396 CFAllocatorDeallocate(allocator
, utfname
);
397 return (CFMessagePortRef
)CFRetain(existing
);
400 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
401 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
402 if (NULL
== memory
) {
403 __CFSpinUnlock(&__CFAllMessagePortsLock
);
407 CFAllocatorDeallocate(allocator
, utfname
);
410 __CFMessagePortUnsetValid(memory
);
411 __CFMessagePortSetRemote(memory
);
413 memory
->_name
= name
;
414 memory
->_port
= NULL
;
415 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, NULL
, NULL
);
416 memory
->_convCounter
= 0;
417 memory
->_replyPort
= NULL
;
418 memory
->_source
= NULL
;
419 memory
->_icallout
= NULL
;
420 memory
->_callout
= NULL
;
425 ctx
.copyDescription
= NULL
;
426 task_get_bootstrap_port(mach_task_self(), &bp
);
427 ret
= bootstrap_look_up(bp
, utfname
, &port
);
428 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
429 CFAllocatorDeallocate(allocator
, utfname
);
430 if (NULL
== native
) {
431 __CFSpinUnlock(&__CFAllMessagePortsLock
);
432 // name is released by deallocation
436 memory
->_port
= native
;
437 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
438 __CFMessagePortSetValid(memory
);
440 if (NULL
== __CFAllRemoteMessagePorts
) {
441 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
443 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
445 __CFSpinUnlock(&__CFAllMessagePortsLock
);
446 return (CFMessagePortRef
)memory
;
449 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
450 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
451 return __CFMessagePortIsRemote(ms
);
454 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
455 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
459 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
460 CFAllocatorRef allocator
= CFGetAllocator(ms
);
461 uint8_t *utfname
= NULL
;
463 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
464 // if (__CFMessagePortIsRemote(ms)) return false;
465 //#warning CF: make this an assertion
466 // and assert than newName is non-NULL
467 name
= __CFMessagePortSanitizeStringName(allocator
, name
, &utfname
, NULL
);
471 __CFSpinLock(&__CFAllMessagePortsLock
);
473 CFMessagePortRef existing
;
474 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
475 __CFSpinUnlock(&__CFAllMessagePortsLock
);
477 CFAllocatorDeallocate(allocator
, utfname
);
481 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
482 if (!__CFMessagePortNativeSetNameLocal(ms
->_port
, utfname
)) {
483 __CFSpinUnlock(&__CFAllMessagePortsLock
);
485 CFAllocatorDeallocate(allocator
, utfname
);
488 if (NULL
== __CFAllLocalMessagePorts
) {
489 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
491 if (NULL
!= ms
->_name
) {
492 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
493 CFRelease(ms
->_name
);
496 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
498 __CFSpinUnlock(&__CFAllMessagePortsLock
);
499 CFAllocatorDeallocate(allocator
, utfname
);
503 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
504 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
505 //#warning CF: assert that this is a local port
506 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
507 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
510 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
511 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
512 if (!__CFMessagePortIsDeallocing(ms
)) {
515 __CFMessagePortLock(ms
);
516 if (__CFMessagePortIsValid(ms
)) {
517 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
518 CFRunLoopSourceRef source
= ms
->_source
;
519 CFMachPortRef replyPort
= ms
->_replyPort
;
520 CFMachPortRef port
= ms
->_port
;
521 CFStringRef name
= ms
->_name
;
524 __CFMessagePortUnsetValid(ms
);
525 if (!__CFMessagePortIsRemote(ms
)) {
526 info
= ms
->_context
.info
;
527 ms
->_context
.info
= NULL
;
530 ms
->_replyPort
= NULL
;
531 __CFMessagePortUnlock(ms
);
533 __CFSpinLock(&__CFAllMessagePortsLock
);
534 if (NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
535 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
537 __CFSpinUnlock(&__CFAllMessagePortsLock
);
538 if (NULL
!= callout
) {
541 // We already know we're going invalid, don't need this callback
542 // anymore; plus, this solves a reentrancy deadlock; also, this
543 // must be done before the deallocate of the Mach port, to
544 // avoid a race between the notification message which could be
545 // handled in another thread, and this NULL'ing out.
546 CFMachPortSetInvalidationCallBack(port
, NULL
);
547 // For hashing and equality purposes, cannot get rid of _port here
548 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
549 ms
->_context
.release(info
);
551 if (NULL
!= source
) {
552 CFRunLoopSourceInvalidate(source
);
555 if (NULL
!= replyPort
) {
556 CFMachPortInvalidate(replyPort
);
557 CFRelease(replyPort
);
559 if (__CFMessagePortIsRemote(ms
)) {
560 // Get rid of our extra ref on the Mach port gotten from bs server
561 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
564 __CFMessagePortUnlock(ms
);
566 if (!__CFMessagePortIsDeallocing(ms
)) {
571 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
572 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
573 if (!__CFMessagePortIsValid(ms
)) return false;
574 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
575 CFMessagePortInvalidate(ms
);
578 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
579 CFMessagePortInvalidate(ms
);
582 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
583 CFMessagePortInvalidate(ms
);
589 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
590 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
591 return ms
->_icallout
;
594 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
595 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
596 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
597 callout(ms
, ms
->_context
.info
);
599 ms
->_icallout
= callout
;
603 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
604 CFMessagePortRef ms
= info
;
605 struct __CFMessagePortMachMessage
*msgp
= msg
;
606 struct __CFMessagePortMachMessage
*replymsg
;
607 __CFMessagePortLock(ms
);
608 if (!__CFMessagePortIsValid(ms
)) {
609 __CFMessagePortUnlock(ms
);
612 // assert: (int32_t)msgp->head.msgh_id < 0
613 if (CFDictionaryContainsKey(ms
->_replies
, (void *)msgp
->head
.msgh_id
)) {
614 CFDataRef reply
= NULL
;
615 replymsg
= (struct __CFMessagePortMachMessage
*)msg
;
616 if (0 == replymsg
->body
.msgh_descriptor_count
) {
617 int32_t byteslen
= CFSwapInt32LittleToHost(replymsg
->contents
.msg0
.byteslen
);
619 reply
= CFDataCreate(kCFAllocatorSystemDefault
, replymsg
->contents
.msg0
.bytes
, byteslen
);
621 reply
= (void *)0xffffffff; // means NULL data
624 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
625 reply
= CFDataCreate(kCFAllocatorSystemDefault
, replymsg
->contents
.msg1
.desc
.out_of_line
.address
, replymsg
->contents
.msg1
.desc
.out_of_line
.size
);
626 vm_deallocate(mach_task_self(), (vm_address_t
)replymsg
->contents
.msg1
.desc
.out_of_line
.address
, replymsg
->contents
.msg1
.desc
.out_of_line
.size
);
628 CFDictionarySetValue(ms
->_replies
, (void *)msgp
->head
.msgh_id
, (void *)reply
);
629 } else { /* discard message */
630 if (1 == msgp
->body
.msgh_descriptor_count
) {
631 vm_deallocate(mach_task_self(), (vm_address_t
)msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
);
634 __CFMessagePortUnlock(ms
);
637 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
638 struct __CFMessagePortMachMessage
*sendmsg
;
639 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
640 CFRunLoopSourceRef source
= NULL
;
641 CFDataRef reply
= NULL
;
643 uint32_t sendOpts
= 0, sendTimeOut
= 0;
644 int32_t desiredReply
;
645 Boolean didRegister
= false;
648 //#warning CF: This should be an assert
649 // if (!__CFMessagePortIsRemote(remote)) return -999;
650 if (!__CFMessagePortIsValid(remote
)) return kCFMessagePortIsInvalid
;
651 __CFMessagePortLock(remote
);
652 if (NULL
== remote
->_replyPort
) {
653 CFMachPortContext context
;
655 context
.info
= remote
;
656 context
.retain
= (const void *(*)(const void *))CFRetain
;
657 context
.release
= (void (*)(const void *))CFRelease
;
658 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
659 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
661 remote
->_convCounter
++;
662 desiredReply
= -remote
->_convCounter
;
663 CFDictionarySetValue(remote
->_replies
, (void *)desiredReply
, NULL
);
664 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));
665 __CFMessagePortUnlock(remote
);
666 if (replyMode
!= NULL
) {
667 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
668 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
670 CFRunLoopAddSource(currentRL
, source
, replyMode
);
673 if (sendTimeout
< 10.0*86400) {
674 // anything more than 10 days is no timeout!
675 sendOpts
= MACH_SEND_TIMEOUT
;
676 sendTimeout
*= 1000.0;
677 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
678 sendTimeOut
= floor(sendTimeout
);
680 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->head
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
681 CFAllocatorDeallocate(CFGetAllocator(remote
), sendmsg
);
682 if (KERN_SUCCESS
!= ret
) {
684 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
687 if (MACH_SEND_TIMED_OUT
== ret
) return kCFMessagePortSendTimeout
;
688 return kCFMessagePortTransportError
;
690 if (replyMode
== NULL
) {
691 return kCFMessagePortSuccess
;
693 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
694 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
695 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
697 CFRunLoopRunInMode(replyMode
, __CFTSRToTimeInterval(termTSR
- mach_absolute_time()), true);
698 // warning: what, if anything, should be done if remote is now invalid?
699 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)desiredReply
);
700 if (NULL
!= reply
|| termTSR
< (int64_t)mach_absolute_time()) {
703 if (!CFMessagePortIsValid(remote
)) {
704 // no reason that reply port alone should go invalid so we don't check for that
708 // Should we uninstall the notify port? A complex question...
710 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
714 CFDictionaryRemoveValue(remote
->_replies
, (void *)desiredReply
);
716 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: -5;
718 if (NULL
!= returnDatap
) {
719 *returnDatap
= ((void *)0xffffffff == reply
) ? NULL
: reply
;
720 } else if ((void *)0xffffffff != reply
) {
723 CFDictionaryRemoveValue(remote
->_replies
, (void *)desiredReply
);
725 return kCFMessagePortSuccess
;
728 static mach_port_t
__CFMessagePortGetPort(void *info
) {
729 CFMessagePortRef ms
= info
;
730 return CFMachPortGetPort(ms
->_port
);
733 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
734 CFMessagePortRef ms
= info
;
735 struct __CFMessagePortMachMessage
*msgp
= msg
;
736 struct __CFMessagePortMachMessage
*replymsg
;
738 void (*context_release
)(const void *);
739 CFDataRef returnData
, data
= NULL
;
740 void *return_bytes
= NULL
;
741 CFIndex return_len
= 0;
744 __CFMessagePortLock(ms
);
745 if (!__CFMessagePortIsValid(ms
)) {
746 __CFMessagePortUnlock(ms
);
749 // assert: 0 < (int32_t)msgp->head.msgh_id
750 if (NULL
!= ms
->_context
.retain
) {
751 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
752 context_release
= ms
->_context
.release
;
754 context_info
= ms
->_context
.info
;
755 context_release
= NULL
;
757 __CFMessagePortUnlock(ms
);
758 /* Create no-copy, no-free-bytes wrapper CFData */
759 if (0 == msgp
->body
.msgh_descriptor_count
) {
760 int32_t byteslen
= CFSwapInt32LittleToHost(msgp
->contents
.msg0
.byteslen
);
761 msgid
= CFSwapInt32LittleToHost(msgp
->contents
.msg0
.msgid
);
763 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, msgp
->contents
.msg0
.bytes
, byteslen
, kCFAllocatorNull
);
766 msgid
= CFSwapInt32LittleToHost(msgp
->contents
.msg1
.msgid
);
767 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
, kCFAllocatorNull
);
769 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
770 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
771 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
772 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
773 bytes inline in the Mach message, so can release the returnData object
774 here. In cases (3) and (5), we'll send the data out-of-line, we need to
775 create a copy of the memory, which we'll have the kernel autodeallocate
776 for us on send. In case (4) also, the bytes in the return data may be part
777 of the bytes in "data" that we sent into the callout, so if the incoming
778 data was received out of line, we wouldn't be able to clean up the out-of-line
779 wad until the message was sent either, if we didn't make the copy. */
780 if (NULL
!= returnData
) {
781 return_len
= CFDataGetLength(returnData
);
782 if (return_len
< __CFMessagePortMaxInlineBytes
) {
783 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
786 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
787 /* vm_copy would only be a win here if the source address
788 is page aligned; it is a lose in all other cases, since
789 the kernel will just do the memmove for us (but not in
791 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
794 replymsg
= __CFMessagePortCreateMessage(allocator
, true, msgp
->head
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)msgp
->head
.msgh_id
, msgid
, return_bytes
, return_len
);
795 if (1 == replymsg
->body
.msgh_descriptor_count
) {
796 replymsg
->contents
.msg1
.desc
.out_of_line
.deallocate
= true;
798 if (data
) CFRelease(data
);
799 if (1 == msgp
->body
.msgh_descriptor_count
) {
800 vm_deallocate(mach_task_self(), (vm_address_t
)msgp
->contents
.msg1
.desc
.out_of_line
.address
, msgp
->contents
.msg1
.desc
.out_of_line
.size
);
802 if (returnData
) CFRelease(returnData
);
803 if (context_release
) {
804 context_release(context_info
);
809 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
810 CFRunLoopSourceRef result
= NULL
;
811 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
812 //#warning CF: This should be an assert
813 // if (__CFMessagePortIsRemote(ms)) return NULL;
814 __CFMessagePortLock(ms
);
815 if (NULL
== ms
->_source
&& __CFMessagePortIsValid(ms
)) {
816 CFRunLoopSourceContext1 context
;
818 context
.info
= (void *)ms
;
819 context
.retain
= (const void *(*)(const void *))CFRetain
;
820 context
.release
= (void (*)(const void *))CFRelease
;
821 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
822 context
.equal
= (Boolean (*)(const void *, const void *))__CFMessagePortEqual
;
823 context
.hash
= (CFHashCode (*)(const void *))__CFMessagePortHash
;
824 context
.getPort
= __CFMessagePortGetPort
;
825 context
.perform
= __CFMessagePortPerform
;
826 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
828 if (NULL
!= ms
->_source
) {
829 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
831 __CFMessagePortUnlock(ms
);
835 #endif /* !__WIN32__ */