2 * Copyright (c) 2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1998-2011, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
29 #include <CoreFoundation/CFMessagePort.h>
30 #include <CoreFoundation/CFRunLoop.h>
31 #include <CoreFoundation/CFMachPort.h>
32 #include <CoreFoundation/CFDictionary.h>
33 #include <CoreFoundation/CFByteOrder.h>
35 #include "CFInternal.h"
36 #include <mach/mach.h>
37 #include <mach/message.h>
38 #include <mach/mach_error.h>
39 #include <bootstrap_priv.h>
41 #include <mach/mach_time.h>
43 #include <dispatch/dispatch.h>
45 extern pid_t
getpid(void);
47 #define __kCFMessagePortMaxNameLengthMax 255
49 #if defined(BOOTSTRAP_MAX_NAME_LEN)
50 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
52 #define __kCFMessagePortMaxNameLength 128
55 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
56 #undef __kCFMessagePortMaxNameLength
57 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
60 #define __CFMessagePortMaxDataSize 0x60000000L
62 //#pragma GCC diagnostic push
63 #pragma GCC diagnostic ignored "-Wunused-function"
64 DISPATCH_HELPER_FUNCTIONS(mport
, CFMessagePort
)
65 //#pragma GCC diagnostic pop
68 static CFSpinLock_t __CFAllMessagePortsLock
= CFSpinLockInit
;
69 static CFMutableDictionaryRef __CFAllLocalMessagePorts
= NULL
;
70 static CFMutableDictionaryRef __CFAllRemoteMessagePorts
= NULL
;
72 struct __CFMessagePort
{
76 CFMachPortRef _port
; /* immutable; invalidated */
77 CFMutableDictionaryRef _replies
;
79 int32_t _perPID
; /* zero if not per-pid, else pid */
80 CFMachPortRef _replyPort
; /* only used by remote port; immutable once created; invalidated */
81 CFRunLoopSourceRef _source
; /* only used by local port; immutable once created; invalidated */
82 dispatch_source_t _dispatchSource
; /* only used by local port; invalidated */
83 dispatch_queue_t _dispatchQ
; /* only used by local port */
84 CFMessagePortInvalidationCallBack _icallout
;
85 CFMessagePortCallBack _callout
; /* only used by local port; immutable */
86 CFMessagePortCallBackEx _calloutEx
; /* only used by local port; immutable */
87 CFMessagePortContext _context
; /* not part of remote port; immutable; invalidated */
90 /* Bit 0 in the base reserved bits is used for invalid state */
91 /* Bit 1 of the base reserved bits is used for has-extra-port-refs state */
92 /* Bit 2 of the base reserved bits is used for is-remote state */
93 /* Bit 3 in the base reserved bits is used for is-deallocing state */
95 CF_INLINE Boolean
__CFMessagePortIsValid(CFMessagePortRef ms
) {
96 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0);
99 CF_INLINE
void __CFMessagePortSetValid(CFMessagePortRef ms
) {
100 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 1);
103 CF_INLINE
void __CFMessagePortUnsetValid(CFMessagePortRef ms
) {
104 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 0);
107 CF_INLINE Boolean
__CFMessagePortExtraMachRef(CFMessagePortRef ms
) {
108 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1);
111 CF_INLINE
void __CFMessagePortSetExtraMachRef(CFMessagePortRef ms
) {
112 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 1);
115 CF_INLINE
void __CFMessagePortUnsetExtraMachRef(CFMessagePortRef ms
) {
116 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 0);
119 CF_INLINE Boolean
__CFMessagePortIsRemote(CFMessagePortRef ms
) {
120 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2);
123 CF_INLINE
void __CFMessagePortSetRemote(CFMessagePortRef ms
) {
124 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 1);
127 CF_INLINE
void __CFMessagePortUnsetRemote(CFMessagePortRef ms
) {
128 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 0);
131 CF_INLINE Boolean
__CFMessagePortIsDeallocing(CFMessagePortRef ms
) {
132 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3);
135 CF_INLINE
void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms
) {
136 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3, 1);
139 CF_INLINE
void __CFMessagePortLock(CFMessagePortRef ms
) {
140 __CFSpinLock(&(ms
->_lock
));
143 CF_INLINE
void __CFMessagePortUnlock(CFMessagePortRef ms
) {
144 __CFSpinUnlock(&(ms
->_lock
));
148 #define __CFMessagePortMaxInlineBytes 4096*10
150 struct __CFMessagePortMachMessage0
{
151 mach_msg_base_t base
;
158 struct __CFMessagePortMachMessage1
{
159 mach_msg_base_t base
;
160 mach_msg_ool_descriptor_t ool
;
166 #define MAGIC 0xF1F2F3F4
168 #define MSGP0_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage0 *)msgp)->ident
169 #define MSGP1_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage1 *)msgp)->ident
170 #define MSGP_GET(msgp, ident) \
171 ((((mach_msg_base_t *)msgp)->body.msgh_descriptor_count) ? MSGP1_FIELD(msgp, ident) : MSGP0_FIELD(msgp, ident))
173 static mach_msg_base_t
*__CFMessagePortCreateMessage(bool reply
, mach_port_t port
, mach_port_t replyPort
, int32_t convid
, int32_t msgid
, const uint8_t *bytes
, int32_t byteslen
) {
174 if (__CFMessagePortMaxDataSize
< byteslen
) return NULL
;
175 int32_t rounded_byteslen
= ((byteslen
+ 3) & ~0x3);
176 if (rounded_byteslen
<= __CFMessagePortMaxInlineBytes
) {
177 int32_t size
= sizeof(struct __CFMessagePortMachMessage0
) + rounded_byteslen
;
178 struct __CFMessagePortMachMessage0
*msg
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
, 0);
179 if (!msg
) return NULL
;
180 memset(msg
, 0, size
);
181 msg
->base
.header
.msgh_id
= convid
;
182 msg
->base
.header
.msgh_size
= size
;
183 msg
->base
.header
.msgh_remote_port
= port
;
184 msg
->base
.header
.msgh_local_port
= replyPort
;
185 msg
->base
.header
.msgh_reserved
= 0;
186 msg
->base
.header
.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));
187 msg
->base
.body
.msgh_descriptor_count
= 0;
189 msg
->msgid
= CFSwapInt32HostToLittle(msgid
);
190 msg
->byteslen
= CFSwapInt32HostToLittle(byteslen
);
191 if (NULL
!= bytes
&& 0 < byteslen
) {
192 memmove(msg
->bytes
, bytes
, byteslen
);
194 return (mach_msg_base_t
*)msg
;
196 int32_t size
= sizeof(struct __CFMessagePortMachMessage1
);
197 struct __CFMessagePortMachMessage1
*msg
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
, 0);
198 if (!msg
) return NULL
;
199 memset(msg
, 0, size
);
200 msg
->base
.header
.msgh_id
= convid
;
201 msg
->base
.header
.msgh_size
= size
;
202 msg
->base
.header
.msgh_remote_port
= port
;
203 msg
->base
.header
.msgh_local_port
= replyPort
;
204 msg
->base
.header
.msgh_reserved
= 0;
205 msg
->base
.header
.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));
206 msg
->base
.header
.msgh_bits
|= MACH_MSGH_BITS_COMPLEX
;
207 msg
->base
.body
.msgh_descriptor_count
= 1;
209 msg
->msgid
= CFSwapInt32HostToLittle(msgid
);
210 msg
->byteslen
= CFSwapInt32HostToLittle(byteslen
);
211 msg
->ool
.deallocate
= false;
212 msg
->ool
.copy
= MACH_MSG_VIRTUAL_COPY
;
213 msg
->ool
.address
= (void *)bytes
;
214 msg
->ool
.size
= byteslen
;
215 msg
->ool
.type
= MACH_MSG_OOL_DESCRIPTOR
;
216 return (mach_msg_base_t
*)msg
;
220 static CFStringRef
__CFMessagePortCopyDescription(CFTypeRef cf
) {
221 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
224 CFStringRef contextDesc
= NULL
;
225 locked
= ms
->_lock
? "Yes" : "No";
226 if (__CFMessagePortIsRemote(ms
)) {
227 result
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, 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
);
229 if (NULL
!= ms
->_context
.info
&& NULL
!= ms
->_context
.copyDescription
) {
230 contextDesc
= ms
->_context
.copyDescription(ms
->_context
.info
);
232 if (NULL
== contextDesc
) {
233 contextDesc
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort context %p>"), ms
->_context
.info
);
235 void *addr
= ms
->_callout
? (void *)ms
->_callout
: (void *)ms
->_calloutEx
;
237 const char *name
= (dladdr(addr
, &info
) && info
.dli_saddr
== addr
&& info
.dli_sname
) ? info
.dli_sname
: "???";
238 result
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@, source = %p, callout = %s (%p), context = %@}"), cf
, CFGetAllocator(ms
), locked
, (__CFMessagePortIsValid(ms
) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms
) ? "Yes" : "No"), ms
->_name
, ms
->_source
, name
, addr
, (NULL
!= contextDesc
? contextDesc
: CFSTR("<no description>")));
240 if (NULL
!= contextDesc
) {
241 CFRelease(contextDesc
);
246 static void __CFMessagePortDeallocate(CFTypeRef cf
) {
247 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
248 __CFMessagePortSetIsDeallocing(ms
);
249 CFMessagePortInvalidate(ms
);
250 // Delay cleanup of _replies until here so that invalidation during
251 // SendRequest does not cause _replies to disappear out from under that function.
252 if (NULL
!= ms
->_replies
) {
253 CFRelease(ms
->_replies
);
255 if (NULL
!= ms
->_name
) {
256 CFRelease(ms
->_name
);
258 if (NULL
!= ms
->_port
) {
259 if (__CFMessagePortExtraMachRef(ms
)) {
260 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_SEND
, -1);
261 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_RECEIVE
, -1);
263 CFMachPortInvalidate(ms
->_port
);
264 CFRelease(ms
->_port
);
267 // A remote message port for a local message port in the same process will get the
268 // same mach port, and the remote port will keep the mach port from being torn down,
269 // thus keeping the remote port from getting any sort of death notification and
270 // auto-invalidating; so we manually implement the 'auto-invalidation' here by
271 // tickling each remote port to check its state after any message port is destroyed,
272 // but most importantly after local message ports are destroyed.
273 __CFSpinLock(&__CFAllMessagePortsLock
);
274 CFMessagePortRef
*remotePorts
= NULL
;
276 if (NULL
!= __CFAllRemoteMessagePorts
) {
277 cnt
= CFDictionaryGetCount(__CFAllRemoteMessagePorts
);
278 remotePorts
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, cnt
* sizeof(CFMessagePortRef
), __kCFAllocatorGCScannedMemory
);
279 CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts
, NULL
, (const void **)remotePorts
);
280 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
281 CFRetain(remotePorts
[idx
]);
284 __CFSpinUnlock(&__CFAllMessagePortsLock
);
286 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
287 // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid
288 CFMessagePortIsValid(remotePorts
[idx
]);
289 CFRelease(remotePorts
[idx
]);
291 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, remotePorts
);
295 static CFTypeID __kCFMessagePortTypeID
= _kCFRuntimeNotATypeID
;
297 static const CFRuntimeClass __CFMessagePortClass
= {
302 __CFMessagePortDeallocate
,
306 __CFMessagePortCopyDescription
309 __private_extern__
void __CFMessagePortInitialize(void) {
310 __kCFMessagePortTypeID
= _CFRuntimeRegisterClass(&__CFMessagePortClass
);
313 CFTypeID
CFMessagePortGetTypeID(void) {
314 return __kCFMessagePortTypeID
;
317 static CFStringRef
__CFMessagePortSanitizeStringName(CFStringRef name
, uint8_t **utfnamep
, CFIndex
*utfnamelenp
) {
320 CFStringRef result
= NULL
;
321 utfname
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, __kCFMessagePortMaxNameLength
+ 1, 0);
322 CFStringGetBytes(name
, CFRangeMake(0, CFStringGetLength(name
)), kCFStringEncodingUTF8
, 0, false, utfname
, __kCFMessagePortMaxNameLength
, &utflen
);
323 utfname
[utflen
] = '\0';
324 if (strlen(utfname
) != utflen
) {
325 /* PCA 9194709: refuse to sanitize a string with an embedded nul character */
326 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
330 /* A new string is created, because the original string may have been
331 truncated to the max length, and we want the string name to definitely
332 match the raw UTF-8 chunk that has been created. Also, this is useful
333 to get a constant string in case the original name string was mutable. */
334 result
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, utfname
, utflen
, kCFStringEncodingUTF8
, false);
336 if (NULL
!= utfnamep
) {
338 } else if (NULL
!= utfname
) {
339 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
341 if (NULL
!= utfnamelenp
) {
342 *utfnamelenp
= utflen
;
347 static void __CFMessagePortDummyCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
348 // not supposed to be implemented
351 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port
, void *info
) {
352 // info has been setup as the CFMessagePort owning the CFMachPort
353 if (info
) CFMessagePortInvalidate(info
);
356 static CFMessagePortRef
__CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
, Boolean perPID
, CFMessagePortCallBackEx calloutEx
) {
357 CFMessagePortRef memory
;
358 uint8_t *utfname
= NULL
;
360 if (shouldFreeInfo
) *shouldFreeInfo
= true;
362 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
364 __CFSpinLock(&__CFAllMessagePortsLock
);
365 if (!perPID
&& NULL
!= name
) {
366 CFMessagePortRef existing
;
367 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
369 __CFSpinUnlock(&__CFAllMessagePortsLock
);
371 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
372 if (!CFMessagePortIsValid(existing
)) { // must do this outside lock to avoid deadlock
376 return (CFMessagePortRef
)(existing
);
379 __CFSpinUnlock(&__CFAllMessagePortsLock
);
380 CFIndex size
= sizeof(struct __CFMessagePort
) - sizeof(CFRuntimeBase
);
381 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
382 if (NULL
== memory
) {
386 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
389 __CFMessagePortUnsetValid(memory
);
390 __CFMessagePortUnsetExtraMachRef(memory
);
391 __CFMessagePortUnsetRemote(memory
);
392 memory
->_lock
= CFSpinLockInit
;
393 memory
->_name
= name
;
394 memory
->_port
= NULL
;
395 memory
->_replies
= NULL
;
396 memory
->_convCounter
= 0;
397 memory
->_perPID
= perPID
? getpid() : 0; // actual value not terribly useful for local ports
398 memory
->_replyPort
= NULL
;
399 memory
->_source
= NULL
;
400 memory
->_dispatchSource
= NULL
;
401 memory
->_dispatchQ
= NULL
;
402 memory
->_icallout
= NULL
;
403 memory
->_callout
= callout
;
404 memory
->_calloutEx
= calloutEx
;
405 memory
->_context
.info
= NULL
;
406 memory
->_context
.retain
= NULL
;
407 memory
->_context
.release
= NULL
;
408 memory
->_context
.copyDescription
= NULL
;
411 CFMachPortRef native
= NULL
;
414 task_get_bootstrap_port(mach_task_self(), &bs
);
416 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
417 if (ret
== KERN_SUCCESS
) {
418 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
419 if (KERN_SUCCESS
== ret
) {
420 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
421 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
422 __CFMessagePortSetExtraMachRef(memory
);
424 CFLog(kCFLogLevelDebug
, CFSTR("*** CFMessagePort: mach_port_insert_member() after bootstrap_check_in(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'"), ret
, ret
, bootstrap_strerror(ret
), mp
, utfname
);
425 mach_port_destroy(mach_task_self(), mp
);
426 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
427 // name is released by deallocation
434 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
435 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
437 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
438 // name is released by deallocation
442 mp
= CFMachPortGetPort(native
);
443 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
444 if (ret
!= KERN_SUCCESS
) {
445 CFLog(kCFLogLevelDebug
, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret
, ret
, bootstrap_strerror(ret
), mp
, utfname
);
446 CFMachPortInvalidate(native
);
448 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
449 // name is released by deallocation
454 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
455 memory
->_port
= native
;
458 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
459 __CFMessagePortSetValid(memory
);
460 if (NULL
!= context
) {
461 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
462 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
464 __CFSpinLock(&__CFAllMessagePortsLock
);
465 if (!perPID
&& NULL
!= name
) {
466 CFMessagePortRef existing
;
467 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
469 __CFSpinUnlock(&__CFAllMessagePortsLock
);
471 return (CFMessagePortRef
)(existing
);
473 if (NULL
== __CFAllLocalMessagePorts
) {
474 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
476 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
478 __CFSpinUnlock(&__CFAllMessagePortsLock
);
479 if (shouldFreeInfo
) *shouldFreeInfo
= false;
483 CFMessagePortRef
CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
484 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, false, NULL
);
487 CFMessagePortRef
CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
488 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, true, NULL
);
491 CFMessagePortRef
_CFMessagePortCreateLocalEx(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, uintptr_t unused
, CFMessagePortCallBackEx calloutEx
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
492 return __CFMessagePortCreateLocal(allocator
, name
, NULL
, context
, shouldFreeInfo
, perPID
, calloutEx
);
495 static CFMessagePortRef
__CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, CFIndex pid
) {
496 CFMessagePortRef memory
;
497 CFMachPortRef native
;
498 CFMachPortContext ctx
;
499 uint8_t *utfname
= NULL
;
501 mach_port_t bp
, port
;
504 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
508 __CFSpinLock(&__CFAllMessagePortsLock
);
509 if (!perPID
&& NULL
!= name
) {
510 CFMessagePortRef existing
;
511 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
513 __CFSpinUnlock(&__CFAllMessagePortsLock
);
515 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
516 if (!CFMessagePortIsValid(existing
)) { // must do this outside lock to avoid deadlock
520 return (CFMessagePortRef
)(existing
);
523 __CFSpinUnlock(&__CFAllMessagePortsLock
);
524 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
525 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
526 if (NULL
== memory
) {
530 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
533 __CFMessagePortUnsetValid(memory
);
534 __CFMessagePortUnsetExtraMachRef(memory
);
535 __CFMessagePortSetRemote(memory
);
536 memory
->_lock
= CFSpinLockInit
;
537 memory
->_name
= name
;
538 memory
->_port
= NULL
;
539 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
540 memory
->_convCounter
= 0;
541 memory
->_perPID
= perPID
? pid
: 0;
542 memory
->_replyPort
= NULL
;
543 memory
->_source
= NULL
;
544 memory
->_dispatchSource
= NULL
;
545 memory
->_dispatchQ
= NULL
;
546 memory
->_icallout
= NULL
;
547 memory
->_callout
= NULL
;
548 memory
->_calloutEx
= NULL
;
553 ctx
.copyDescription
= NULL
;
554 task_get_bootstrap_port(mach_task_self(), &bp
);
555 ret
= bootstrap_look_up2(bp
, (char *)utfname
, &port
, perPID
? (pid_t
)pid
: 0, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
556 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
557 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
558 if (NULL
== native
) {
559 // name is released by deallocation
563 memory
->_port
= native
;
564 __CFMessagePortSetValid(memory
);
565 __CFSpinLock(&__CFAllMessagePortsLock
);
566 if (!perPID
&& NULL
!= name
) {
567 CFMessagePortRef existing
;
568 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
570 __CFSpinUnlock(&__CFAllMessagePortsLock
);
572 return (CFMessagePortRef
)(existing
);
574 if (NULL
== __CFAllRemoteMessagePorts
) {
575 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
577 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
579 __CFSpinUnlock(&__CFAllMessagePortsLock
);
580 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
581 // that set-invalidation-callback might have called back into us
582 // if the CFMachPort is already bad, but that was a no-op since
583 // there was no callback setup at the (previous) time the CFMachPort
584 // went invalid; so check for validity manually and react
585 if (!CFMachPortIsValid(native
)) {
586 CFRelease(memory
); // does the invalidate
589 return (CFMessagePortRef
)memory
;
592 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
593 return __CFMessagePortCreateRemote(allocator
, name
, false, 0);
596 CFMessagePortRef
CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator
, CFStringRef name
, CFIndex pid
) {
597 return __CFMessagePortCreateRemote(allocator
, name
, true, pid
);
600 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
601 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
602 return __CFMessagePortIsRemote(ms
);
605 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
606 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
610 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
611 CFAllocatorRef allocator
= CFGetAllocator(ms
);
612 uint8_t *utfname
= NULL
;
614 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
615 if (ms
->_perPID
|| __CFMessagePortIsRemote(ms
)) return false;
616 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
620 __CFSpinLock(&__CFAllMessagePortsLock
);
622 CFMessagePortRef existing
;
623 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
624 __CFSpinUnlock(&__CFAllMessagePortsLock
);
626 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
630 __CFSpinUnlock(&__CFAllMessagePortsLock
);
632 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
633 CFMachPortRef oldPort
= ms
->_port
;
634 CFMachPortRef native
= NULL
;
637 task_get_bootstrap_port(mach_task_self(), &bs
);
638 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
639 if (ret
== KERN_SUCCESS
) {
640 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
641 if (KERN_SUCCESS
== ret
) {
642 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
643 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
644 __CFMessagePortSetExtraMachRef(ms
);
646 mach_port_destroy(mach_task_self(), mp
);
647 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
653 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
654 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
656 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
660 mp
= CFMachPortGetPort(native
);
661 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, 0);
662 if (ret
!= KERN_SUCCESS
) {
663 CFLog(kCFLogLevelDebug
, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret
, ret
, bootstrap_strerror(ret
), mp
, utfname
);
664 CFMachPortInvalidate(native
);
666 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
671 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
673 if (NULL
!= oldPort
&& oldPort
!= native
) {
674 if (__CFMessagePortExtraMachRef(ms
)) {
675 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_SEND
, -1);
676 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_RECEIVE
, -1);
678 CFMachPortInvalidate(oldPort
);
681 __CFSpinLock(&__CFAllMessagePortsLock
);
682 // This relocking without checking to see if something else has grabbed
683 // that name in the cache is rather suspect, but what would that even
684 // mean has happened? We'd expect the bootstrap_* calls above to have
685 // failed for this one and not gotten this far, or failed for all of the
686 // other simultaneous attempts to get the name (and having succeeded for
687 // this one, gotten here). So we're not going to try very hard here
688 // with the thread-safety.
689 if (NULL
== __CFAllLocalMessagePorts
) {
690 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
692 if (NULL
!= ms
->_name
) {
693 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
694 CFRelease(ms
->_name
);
697 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
698 __CFSpinUnlock(&__CFAllMessagePortsLock
);
701 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
705 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
706 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
707 //#warning CF: assert that this is a local port
708 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
709 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
712 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
713 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
714 if (!__CFMessagePortIsDeallocing(ms
)) {
717 __CFMessagePortLock(ms
);
718 if (__CFMessagePortIsValid(ms
)) {
719 if (ms
->_dispatchSource
) {
720 dispatch_source_cancel(ms
->_dispatchSource
);
721 ms
->_dispatchSource
= NULL
;
722 ms
->_dispatchQ
= NULL
;
725 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
726 CFRunLoopSourceRef source
= ms
->_source
;
727 CFMachPortRef replyPort
= ms
->_replyPort
;
728 CFMachPortRef port
= ms
->_port
;
729 CFStringRef name
= ms
->_name
;
732 __CFMessagePortUnsetValid(ms
);
733 if (!__CFMessagePortIsRemote(ms
)) {
734 info
= ms
->_context
.info
;
735 ms
->_context
.info
= NULL
;
738 ms
->_replyPort
= NULL
;
740 __CFMessagePortUnlock(ms
);
742 __CFSpinLock(&__CFAllMessagePortsLock
);
743 if (0 == ms
->_perPID
&& NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
744 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
746 __CFSpinUnlock(&__CFAllMessagePortsLock
);
747 if (NULL
!= callout
) {
750 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
751 ms
->_context
.release(info
);
753 if (NULL
!= source
) {
754 CFRunLoopSourceInvalidate(source
);
757 if (NULL
!= replyPort
) {
758 CFMachPortInvalidate(replyPort
);
759 CFRelease(replyPort
);
761 if (__CFMessagePortIsRemote(ms
)) {
762 // Get rid of our extra ref on the Mach port gotten from bs server
763 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
766 // We already know we're going invalid, don't need this callback
767 // anymore; plus, this solves a reentrancy deadlock; also, this
768 // must be done before the deallocate of the Mach port, to
769 // avoid a race between the notification message which could be
770 // handled in another thread, and this NULL'ing out.
771 CFMachPortSetInvalidationCallBack(port
, NULL
);
772 if (__CFMessagePortExtraMachRef(ms
)) {
773 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(port
), MACH_PORT_RIGHT_SEND
, -1);
774 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(port
), MACH_PORT_RIGHT_RECEIVE
, -1);
776 CFMachPortInvalidate(port
);
780 __CFMessagePortUnlock(ms
);
782 if (!__CFMessagePortIsDeallocing(ms
)) {
787 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
788 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
789 if (!__CFMessagePortIsValid(ms
)) return false;
790 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
791 CFMessagePortInvalidate(ms
);
794 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
795 CFMessagePortInvalidate(ms
);
801 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
802 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
803 return ms
->_icallout
;
806 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
807 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
808 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
809 callout(ms
, ms
->_context
.info
);
811 ms
->_icallout
= callout
;
815 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
816 CFMessagePortRef ms
= info
;
817 mach_msg_base_t
*msgp
= msg
;
818 mach_msg_base_t
*replymsg
;
819 __CFMessagePortLock(ms
);
820 if (!__CFMessagePortIsValid(ms
)) {
821 __CFMessagePortUnlock(ms
);
825 int32_t byteslen
= 0;
827 Boolean invalidMagic
= (MSGP_GET(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_GET(msgp
, magic
)) != MAGIC
);
828 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
829 invalidComplex
= invalidComplex
|| ((msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
830 Boolean wayTooBig
= ((msgp
->body
.msgh_descriptor_count
) ? sizeof(struct __CFMessagePortMachMessage1
) : sizeof(struct __CFMessagePortMachMessage0
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
;
831 Boolean wayTooSmall
= msgp
->header
.msgh_size
< sizeof(struct __CFMessagePortMachMessage0
);
832 Boolean wrongSize
= false;
833 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
834 byteslen
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, byteslen
));
835 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
836 if (0 != msgp
->body
.msgh_descriptor_count
) {
837 wrongSize
= wrongSize
|| (MSGP1_FIELD(msgp
, ool
).size
!= byteslen
);
839 wrongSize
= wrongSize
|| (msgp
->header
.msgh_size
- sizeof(struct __CFMessagePortMachMessage0
) < byteslen
);
842 Boolean invalidMsgID
= (0 <= msgp
->header
.msgh_id
) && (msgp
->header
.msgh_id
<= INT32_MAX
); // conversation id
843 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
844 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt reply Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
845 mach_msg_destroy((mach_msg_header_t
*)msgp
);
846 __CFMessagePortUnlock(ms
);
850 if (CFDictionaryContainsKey(ms
->_replies
, (void *)(uintptr_t)msgp
->header
.msgh_id
)) {
851 CFDataRef reply
= NULL
;
852 replymsg
= (mach_msg_base_t
*)msg
;
853 if (0 == replymsg
->body
.msgh_descriptor_count
) {
854 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
855 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP0_FIELD(replymsg
, bytes
)) + byteslen
);
856 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
857 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP0_FIELD(replymsg
, bytes
), byteslen
);
859 reply
= (void *)~0; // means NULL data
862 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
863 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP1_FIELD(replymsg
, ool
).address
, MSGP1_FIELD(replymsg
, ool
).size
);
864 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(replymsg
, ool
).address
, MSGP1_FIELD(replymsg
, ool
).size
);
866 CFDictionarySetValue(ms
->_replies
, (void *)(uintptr_t)msgp
->header
.msgh_id
, (void *)reply
);
867 } else { /* discard message */
868 if (1 == msgp
->body
.msgh_descriptor_count
) {
869 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
);
872 __CFMessagePortUnlock(ms
);
875 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
876 mach_msg_base_t
*sendmsg
;
877 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
878 CFRunLoopSourceRef source
= NULL
;
879 CFDataRef reply
= NULL
;
881 uint32_t sendOpts
= 0, sendTimeOut
= 0;
882 int32_t desiredReply
;
883 Boolean didRegister
= false;
886 //#warning CF: This should be an assert
887 // if (!__CFMessagePortIsRemote(remote)) return -999;
888 if (data
&& __CFMessagePortMaxDataSize
< CFDataGetLength(data
)) {
889 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSendRequest: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
890 return kCFMessagePortTransportError
;
892 __CFMessagePortLock(remote
);
893 if (!__CFMessagePortIsValid(remote
)) {
894 __CFMessagePortUnlock(remote
);
895 return kCFMessagePortIsInvalid
;
897 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
898 if (NULL
== remote
->_replyPort
) {
899 CFMachPortContext context
;
901 context
.info
= remote
;
902 context
.retain
= (const void *(*)(const void *))CFRetain
;
903 context
.release
= (void (*)(const void *))CFRelease
;
904 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
905 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
907 remote
->_convCounter
++;
908 desiredReply
= -remote
->_convCounter
;
909 sendmsg
= __CFMessagePortCreateMessage(false, CFMachPortGetPort(remote
->_port
), (replyMode
!= NULL
? CFMachPortGetPort(remote
->_replyPort
) : MACH_PORT_NULL
), -desiredReply
, msgid
, (data
? CFDataGetBytePtr(data
) : NULL
), (data
? CFDataGetLength(data
) : 0));
911 __CFMessagePortUnlock(remote
);
913 return kCFMessagePortTransportError
;
915 if (replyMode
!= NULL
) {
916 CFDictionarySetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
, NULL
);
917 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
918 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
920 CFRunLoopAddSource(currentRL
, source
, replyMode
);
923 if (sendTimeout
< 10.0*86400) {
924 // anything more than 10 days is no timeout!
925 sendOpts
= MACH_SEND_TIMEOUT
;
926 sendTimeout
*= 1000.0;
927 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
928 sendTimeOut
= floor(sendTimeout
);
930 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->header
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
931 if (KERN_SUCCESS
!= ret
) {
932 // need to deallocate the send-once right that might have been created
933 if (replyMode
!= NULL
) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t
*)sendmsg
)->msgh_local_port
);
935 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
937 if (source
) CFRelease(source
);
938 __CFMessagePortUnlock(remote
);
939 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
941 return (MACH_SEND_TIMED_OUT
== ret
) ? kCFMessagePortSendTimeout
: kCFMessagePortTransportError
;
943 __CFMessagePortUnlock(remote
);
944 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
945 if (replyMode
== NULL
) {
947 return kCFMessagePortSuccess
;
949 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
950 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
952 CFRunLoopRunInMode(replyMode
, __CFTSRToTimeInterval(termTSR
- mach_absolute_time()), true);
953 // warning: what, if anything, should be done if remote is now invalid?
954 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
955 if (NULL
!= reply
|| termTSR
< (int64_t)mach_absolute_time()) {
958 if (!CFMessagePortIsValid(remote
)) {
959 // no reason that reply port alone should go invalid so we don't check for that
963 // Should we uninstall the notify port? A complex question...
965 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
967 if (source
) CFRelease(source
);
969 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
971 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: kCFMessagePortBecameInvalidError
;
973 if (NULL
!= returnDatap
) {
974 *returnDatap
= ((void *)~0 == reply
) ? NULL
: reply
;
975 } else if ((void *)~0 != reply
) {
978 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
980 return kCFMessagePortSuccess
;
983 static mach_port_t
__CFMessagePortGetPort(void *info
) {
984 CFMessagePortRef ms
= info
;
985 if (!ms
->_port
&& __CFMessagePortIsValid(ms
)) CFLog(kCFLogLevelWarning
, CFSTR("*** Warning: A local CFMessagePort (%p) is being put in a run loop or dispatch queue, but it has not been named yet, so this will be a no-op and no messages are going to be received, even if named later."), info
);
986 return ms
->_port
? CFMachPortGetPort(ms
->_port
) : MACH_PORT_NULL
;
989 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
990 CFMessagePortRef ms
= info
;
991 mach_msg_base_t
*msgp
= msg
;
992 mach_msg_base_t
*replymsg
;
994 void (*context_release
)(const void *);
995 CFDataRef returnData
, data
= NULL
;
996 void *return_bytes
= NULL
;
997 CFIndex return_len
= 0;
1000 __CFMessagePortLock(ms
);
1001 if (!__CFMessagePortIsValid(ms
)) {
1002 __CFMessagePortUnlock(ms
);
1005 if (NULL
!= ms
->_context
.retain
) {
1006 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
1007 context_release
= ms
->_context
.release
;
1009 context_info
= ms
->_context
.info
;
1010 context_release
= NULL
;
1012 __CFMessagePortUnlock(ms
);
1014 int32_t byteslen
= 0;
1016 Boolean invalidMagic
= (MSGP_GET(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_GET(msgp
, magic
)) != MAGIC
);
1017 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
1018 invalidComplex
= invalidComplex
|| ((msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
1019 Boolean wayTooBig
= ((msgp
->body
.msgh_descriptor_count
) ? sizeof(struct __CFMessagePortMachMessage1
) : sizeof(struct __CFMessagePortMachMessage0
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
;
1020 Boolean wayTooSmall
= msgp
->header
.msgh_size
< sizeof(struct __CFMessagePortMachMessage0
);
1021 Boolean wrongSize
= false;
1022 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
1023 byteslen
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, byteslen
));
1024 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
1025 if (0 != msgp
->body
.msgh_descriptor_count
) {
1026 wrongSize
= wrongSize
|| (MSGP1_FIELD(msgp
, ool
).size
!= byteslen
);
1028 wrongSize
= wrongSize
|| (msgp
->header
.msgh_size
- sizeof(struct __CFMessagePortMachMessage0
) < byteslen
);
1031 Boolean invalidMsgID
= (msgp
->header
.msgh_id
<= 0) || (INT32_MAX
< msgp
->header
.msgh_id
); // conversation id
1032 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
1033 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
1034 mach_msg_destroy((mach_msg_header_t
*)msgp
);
1038 /* Create no-copy, no-free-bytes wrapper CFData */
1039 if (0 == msgp
->body
.msgh_descriptor_count
) {
1040 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
1041 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP0_FIELD(msgp
, bytes
)) + byteslen
);
1042 msgid
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, msgid
));
1043 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
1044 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP0_FIELD(msgp
, bytes
), byteslen
, kCFAllocatorNull
);
1047 msgid
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, msgid
));
1048 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
, kCFAllocatorNull
);
1051 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
1053 mach_msg_trailer_t
*trailer
= (mach_msg_trailer_t
*)(((uintptr_t)&(msgp
->header
) + msgp
->header
.msgh_size
+ sizeof(natural_t
) - 1) & ~(sizeof(natural_t
) - 1));
1054 returnData
= ms
->_calloutEx(ms
, msgid
, data
, context_info
, trailer
, 0);
1056 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
1057 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
1058 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
1059 bytes inline in the Mach message, so can release the returnData object
1060 here. In cases (3) and (5), we'll send the data out-of-line, we need to
1061 create a copy of the memory, which we'll have the kernel autodeallocate
1062 for us on send. In case (4) also, the bytes in the return data may be part
1063 of the bytes in "data" that we sent into the callout, so if the incoming
1064 data was received out of line, we wouldn't be able to clean up the out-of-line
1065 wad until the message was sent either, if we didn't make the copy. */
1066 if (NULL
!= returnData
) {
1067 return_len
= CFDataGetLength(returnData
);
1068 if (__CFMessagePortMaxDataSize
< return_len
) {
1069 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
1073 if (returnData
&& return_len
< __CFMessagePortMaxInlineBytes
) {
1074 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
1075 } else if (returnData
) {
1076 return_bytes
= NULL
;
1077 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
1078 /* vm_copy would only be a win here if the source address
1079 is page aligned; it is a lose in all other cases, since
1080 the kernel will just do the memmove for us (but not in
1081 as simple a way). */
1082 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
1085 replymsg
= __CFMessagePortCreateMessage(true, msgp
->header
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)msgp
->header
.msgh_id
, msgid
, return_bytes
, return_len
);
1086 if (1 == replymsg
->body
.msgh_descriptor_count
) {
1087 MSGP1_FIELD(replymsg
, ool
).deallocate
= true;
1089 if (data
) CFRelease(data
);
1090 if (1 == msgp
->body
.msgh_descriptor_count
) {
1091 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
);
1093 if (returnData
) CFRelease(returnData
);
1094 if (context_release
) {
1095 context_release(context_info
);
1100 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
1101 CFRunLoopSourceRef result
= NULL
;
1102 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1103 if (!CFMessagePortIsValid(ms
)) return NULL
;
1104 if (__CFMessagePortIsRemote(ms
)) return NULL
;
1105 __CFMessagePortLock(ms
);
1106 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
1107 CFRelease(ms
->_source
);
1110 if (NULL
== ms
->_source
&& NULL
== ms
->_dispatchSource
&& __CFMessagePortIsValid(ms
)) {
1111 CFRunLoopSourceContext1 context
;
1112 context
.version
= 1;
1113 context
.info
= (void *)ms
;
1114 context
.retain
= (const void *(*)(const void *))CFRetain
;
1115 context
.release
= (void (*)(const void *))CFRelease
;
1116 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
1117 context
.equal
= NULL
;
1118 context
.hash
= NULL
;
1119 context
.getPort
= __CFMessagePortGetPort
;
1120 context
.perform
= __CFMessagePortPerform
;
1121 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
1123 if (NULL
!= ms
->_source
) {
1124 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
1126 __CFMessagePortUnlock(ms
);
1130 void CFMessagePortSetDispatchQueue(CFMessagePortRef ms
, dispatch_queue_t queue
) {
1131 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1132 __CFMessagePortLock(ms
);
1133 if (!__CFMessagePortIsValid(ms
)) {
1134 __CFMessagePortUnlock(ms
);
1135 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid"));
1138 if (__CFMessagePortIsRemote(ms
)) {
1139 __CFMessagePortUnlock(ms
);
1140 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set"));
1143 if (NULL
!= ms
->_source
) {
1144 __CFMessagePortUnlock(ms
);
1145 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set"));
1149 if (ms
->_dispatchSource
) {
1150 dispatch_source_cancel(ms
->_dispatchSource
);
1151 ms
->_dispatchSource
= NULL
;
1152 ms
->_dispatchQ
= NULL
;
1156 mach_port_t port
= __CFMessagePortGetPort(ms
);
1157 if (MACH_PORT_NULL
!= port
) {
1158 dispatch_source_t theSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, port
, 0, __mportQueue());
1159 dispatch_source_set_cancel_handler(theSource
, ^{
1160 dispatch_release(queue
);
1161 dispatch_release(theSource
);
1163 dispatch_source_set_event_handler(theSource
, ^{
1165 mach_msg_header_t
*msg
= (mach_msg_header_t
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2048, 0);
1166 msg
->msgh_size
= 2048;
1170 msg
->msgh_local_port
= port
;
1171 msg
->msgh_remote_port
= MACH_PORT_NULL
;
1174 kern_return_t ret
= mach_msg(msg
, MACH_RCV_MSG
|MACH_RCV_LARGE
|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV
), 0, msg
->msgh_size
, port
, 0, MACH_PORT_NULL
);
1175 if (MACH_MSG_SUCCESS
== ret
) break;
1176 if (MACH_RCV_TOO_LARGE
!= ret
) HALT
;
1178 uint32_t newSize
= round_msg(msg
->msgh_size
+ MAX_TRAILER_SIZE
);
1179 msg
= CFAllocatorReallocate(kCFAllocatorSystemDefault
, msg
, newSize
, 0);
1180 msg
->msgh_size
= newSize
;
1183 dispatch_async(queue
, ^{
1184 mach_msg_header_t
*reply
= __CFMessagePortPerform(msg
, msg
->msgh_size
, kCFAllocatorSystemDefault
, ms
);
1185 if (NULL
!= reply
) {
1186 kern_return_t ret
= mach_msg(reply
, MACH_SEND_MSG
, reply
->msgh_size
, 0, MACH_PORT_NULL
, 0, MACH_PORT_NULL
);
1187 if (KERN_SUCCESS
!= ret
) mach_msg_destroy(reply
);
1188 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, reply
);
1190 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, msg
);
1194 ms
->_dispatchSource
= theSource
;
1196 if (ms
->_dispatchSource
) {
1197 dispatch_retain(queue
);
1198 ms
->_dispatchQ
= queue
;
1199 dispatch_resume(ms
->_dispatchSource
);
1201 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created"));
1204 __CFMessagePortUnlock(ms
);