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-2012, 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>
44 #include <dispatch/private.h>
46 extern pid_t
getpid(void);
48 #define __kCFMessagePortMaxNameLengthMax 255
50 #if defined(BOOTSTRAP_MAX_NAME_LEN)
51 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
53 #define __kCFMessagePortMaxNameLength 128
56 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
57 #undef __kCFMessagePortMaxNameLength
58 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
61 #define __CFMessagePortMaxDataSize 0x60000000L
63 static CFSpinLock_t __CFAllMessagePortsLock
= CFSpinLockInit
;
64 static CFMutableDictionaryRef __CFAllLocalMessagePorts
= NULL
;
65 static CFMutableDictionaryRef __CFAllRemoteMessagePorts
= NULL
;
67 struct __CFMessagePort
{
71 CFMachPortRef _port
; /* immutable; invalidated */
72 CFMutableDictionaryRef _replies
;
74 int32_t _perPID
; /* zero if not per-pid, else pid */
75 CFMachPortRef _replyPort
; /* only used by remote port; immutable once created; invalidated */
76 CFRunLoopSourceRef _source
; /* only used by local port; immutable once created; invalidated */
77 dispatch_source_t _dispatchSource
; /* only used by local port; invalidated */
78 dispatch_queue_t _dispatchQ
; /* only used by local port */
79 CFMessagePortInvalidationCallBack _icallout
;
80 CFMessagePortCallBack _callout
; /* only used by local port; immutable */
81 CFMessagePortCallBackEx _calloutEx
; /* only used by local port; immutable */
82 CFMessagePortContext _context
; /* not part of remote port; immutable; invalidated */
85 /* Bit 0 in the base reserved bits is used for invalid state */
86 /* Bit 1 of the base reserved bits is used for has-extra-port-refs state */
87 /* Bit 2 of the base reserved bits is used for is-remote state */
88 /* Bit 3 in the base reserved bits is used for is-deallocing state */
90 CF_INLINE Boolean
__CFMessagePortIsValid(CFMessagePortRef ms
) {
91 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0);
94 CF_INLINE
void __CFMessagePortSetValid(CFMessagePortRef ms
) {
95 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 1);
98 CF_INLINE
void __CFMessagePortUnsetValid(CFMessagePortRef ms
) {
99 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 0);
102 CF_INLINE Boolean
__CFMessagePortExtraMachRef(CFMessagePortRef ms
) {
103 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1);
106 CF_INLINE
void __CFMessagePortSetExtraMachRef(CFMessagePortRef ms
) {
107 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 1);
110 CF_INLINE
void __CFMessagePortUnsetExtraMachRef(CFMessagePortRef ms
) {
111 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 0);
114 CF_INLINE Boolean
__CFMessagePortIsRemote(CFMessagePortRef ms
) {
115 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2);
118 CF_INLINE
void __CFMessagePortSetRemote(CFMessagePortRef ms
) {
119 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 1);
122 CF_INLINE
void __CFMessagePortUnsetRemote(CFMessagePortRef ms
) {
123 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 0);
126 CF_INLINE Boolean
__CFMessagePortIsDeallocing(CFMessagePortRef ms
) {
127 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3);
130 CF_INLINE
void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms
) {
131 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3, 1);
134 CF_INLINE
void __CFMessagePortLock(CFMessagePortRef ms
) {
135 __CFSpinLock(&(ms
->_lock
));
138 CF_INLINE
void __CFMessagePortUnlock(CFMessagePortRef ms
) {
139 __CFSpinUnlock(&(ms
->_lock
));
143 #define __CFMessagePortMaxInlineBytes 4096*10
145 struct __CFMessagePortMachMessage0
{
146 mach_msg_base_t base
;
153 struct __CFMessagePortMachMessage1
{
154 mach_msg_base_t base
;
155 mach_msg_ool_descriptor_t ool
;
161 #define MAGIC 0xF1F2F3F4
163 #define MSGP0_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage0 *)msgp)->ident
164 #define MSGP1_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage1 *)msgp)->ident
165 #define MSGP_GET(msgp, ident) \
166 ((((mach_msg_base_t *)msgp)->body.msgh_descriptor_count) ? MSGP1_FIELD(msgp, ident) : MSGP0_FIELD(msgp, ident))
168 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
) {
169 if (__CFMessagePortMaxDataSize
< byteslen
) return NULL
;
170 int32_t rounded_byteslen
= ((byteslen
+ 3) & ~0x3);
171 if (rounded_byteslen
<= __CFMessagePortMaxInlineBytes
) {
172 int32_t size
= sizeof(struct __CFMessagePortMachMessage0
) + rounded_byteslen
;
173 struct __CFMessagePortMachMessage0
*msg
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
, 0);
174 if (!msg
) return NULL
;
175 memset(msg
, 0, size
);
176 msg
->base
.header
.msgh_id
= convid
;
177 msg
->base
.header
.msgh_size
= size
;
178 msg
->base
.header
.msgh_remote_port
= port
;
179 msg
->base
.header
.msgh_local_port
= replyPort
;
180 msg
->base
.header
.msgh_reserved
= 0;
181 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));
182 msg
->base
.body
.msgh_descriptor_count
= 0;
184 msg
->msgid
= CFSwapInt32HostToLittle(msgid
);
185 msg
->byteslen
= CFSwapInt32HostToLittle(byteslen
);
186 if (NULL
!= bytes
&& 0 < byteslen
) {
187 memmove(msg
->bytes
, bytes
, byteslen
);
189 return (mach_msg_base_t
*)msg
;
191 int32_t size
= sizeof(struct __CFMessagePortMachMessage1
);
192 struct __CFMessagePortMachMessage1
*msg
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
, 0);
193 if (!msg
) return NULL
;
194 memset(msg
, 0, size
);
195 msg
->base
.header
.msgh_id
= convid
;
196 msg
->base
.header
.msgh_size
= size
;
197 msg
->base
.header
.msgh_remote_port
= port
;
198 msg
->base
.header
.msgh_local_port
= replyPort
;
199 msg
->base
.header
.msgh_reserved
= 0;
200 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));
201 msg
->base
.header
.msgh_bits
|= MACH_MSGH_BITS_COMPLEX
;
202 msg
->base
.body
.msgh_descriptor_count
= 1;
204 msg
->msgid
= CFSwapInt32HostToLittle(msgid
);
205 msg
->byteslen
= CFSwapInt32HostToLittle(byteslen
);
206 msg
->ool
.deallocate
= false;
207 msg
->ool
.copy
= MACH_MSG_VIRTUAL_COPY
;
208 msg
->ool
.address
= (void *)bytes
;
209 msg
->ool
.size
= byteslen
;
210 msg
->ool
.type
= MACH_MSG_OOL_DESCRIPTOR
;
211 return (mach_msg_base_t
*)msg
;
215 static CFStringRef
__CFMessagePortCopyDescription(CFTypeRef cf
) {
216 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
219 CFStringRef contextDesc
= NULL
;
220 locked
= ms
->_lock
? "Yes" : "No";
221 if (__CFMessagePortIsRemote(ms
)) {
222 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
);
224 if (NULL
!= ms
->_context
.info
&& NULL
!= ms
->_context
.copyDescription
) {
225 contextDesc
= ms
->_context
.copyDescription(ms
->_context
.info
);
227 if (NULL
== contextDesc
) {
228 contextDesc
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort context %p>"), ms
->_context
.info
);
230 void *addr
= ms
->_callout
? (void *)ms
->_callout
: (void *)ms
->_calloutEx
;
232 const char *name
= (dladdr(addr
, &info
) && info
.dli_saddr
== addr
&& info
.dli_sname
) ? info
.dli_sname
: "???";
233 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>")));
235 if (NULL
!= contextDesc
) {
236 CFRelease(contextDesc
);
241 static void __CFMessagePortDeallocate(CFTypeRef cf
) {
242 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
243 __CFMessagePortSetIsDeallocing(ms
);
244 CFMessagePortInvalidate(ms
);
245 // Delay cleanup of _replies until here so that invalidation during
246 // SendRequest does not cause _replies to disappear out from under that function.
247 if (NULL
!= ms
->_replies
) {
248 CFRelease(ms
->_replies
);
250 if (NULL
!= ms
->_name
) {
251 CFRelease(ms
->_name
);
253 if (NULL
!= ms
->_port
) {
254 if (__CFMessagePortExtraMachRef(ms
)) {
255 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_SEND
, -1);
256 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_RECEIVE
, -1);
258 CFMachPortInvalidate(ms
->_port
);
259 CFRelease(ms
->_port
);
262 // A remote message port for a local message port in the same process will get the
263 // same mach port, and the remote port will keep the mach port from being torn down,
264 // thus keeping the remote port from getting any sort of death notification and
265 // auto-invalidating; so we manually implement the 'auto-invalidation' here by
266 // tickling each remote port to check its state after any message port is destroyed,
267 // but most importantly after local message ports are destroyed.
268 __CFSpinLock(&__CFAllMessagePortsLock
);
269 CFMessagePortRef
*remotePorts
= NULL
;
271 if (NULL
!= __CFAllRemoteMessagePorts
) {
272 cnt
= CFDictionaryGetCount(__CFAllRemoteMessagePorts
);
273 remotePorts
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, cnt
* sizeof(CFMessagePortRef
), __kCFAllocatorGCScannedMemory
);
274 CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts
, NULL
, (const void **)remotePorts
);
275 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
276 CFRetain(remotePorts
[idx
]);
279 __CFSpinUnlock(&__CFAllMessagePortsLock
);
281 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
282 // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid
283 CFMessagePortIsValid(remotePorts
[idx
]);
284 CFRelease(remotePorts
[idx
]);
286 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, remotePorts
);
290 static CFTypeID __kCFMessagePortTypeID
= _kCFRuntimeNotATypeID
;
292 static const CFRuntimeClass __CFMessagePortClass
= {
297 __CFMessagePortDeallocate
,
301 __CFMessagePortCopyDescription
304 __private_extern__
void __CFMessagePortInitialize(void) {
305 __kCFMessagePortTypeID
= _CFRuntimeRegisterClass(&__CFMessagePortClass
);
308 CFTypeID
CFMessagePortGetTypeID(void) {
309 return __kCFMessagePortTypeID
;
312 static CFStringRef
__CFMessagePortSanitizeStringName(CFStringRef name
, uint8_t **utfnamep
, CFIndex
*utfnamelenp
) {
315 CFStringRef result
= NULL
;
316 utfname
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, __kCFMessagePortMaxNameLength
+ 1, 0);
317 CFStringGetBytes(name
, CFRangeMake(0, CFStringGetLength(name
)), kCFStringEncodingUTF8
, 0, false, utfname
, __kCFMessagePortMaxNameLength
, &utflen
);
318 utfname
[utflen
] = '\0';
319 if (strlen((const char *)utfname
) != utflen
) {
320 /* PCA 9194709: refuse to sanitize a string with an embedded nul character */
321 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
325 /* A new string is created, because the original string may have been
326 truncated to the max length, and we want the string name to definitely
327 match the raw UTF-8 chunk that has been created. Also, this is useful
328 to get a constant string in case the original name string was mutable. */
329 result
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, utfname
, utflen
, kCFStringEncodingUTF8
, false);
331 if (NULL
!= utfnamep
) {
333 } else if (NULL
!= utfname
) {
334 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
336 if (NULL
!= utfnamelenp
) {
337 *utfnamelenp
= utflen
;
342 static void __CFMessagePortDummyCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
343 // not supposed to be implemented
346 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port
, void *info
) {
347 // info has been setup as the CFMessagePort owning the CFMachPort
348 if (info
) CFMessagePortInvalidate(info
);
351 static CFMessagePortRef
__CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
, Boolean perPID
, CFMessagePortCallBackEx calloutEx
) {
352 CFMessagePortRef memory
;
353 uint8_t *utfname
= NULL
;
355 if (shouldFreeInfo
) *shouldFreeInfo
= true;
357 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
359 __CFSpinLock(&__CFAllMessagePortsLock
);
360 if (!perPID
&& NULL
!= name
) {
361 CFMessagePortRef existing
;
362 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
364 __CFSpinUnlock(&__CFAllMessagePortsLock
);
366 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
367 if (!CFMessagePortIsValid(existing
)) { // must do this outside lock to avoid deadlock
371 return (CFMessagePortRef
)(existing
);
374 __CFSpinUnlock(&__CFAllMessagePortsLock
);
375 CFIndex size
= sizeof(struct __CFMessagePort
) - sizeof(CFRuntimeBase
);
376 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
377 if (NULL
== memory
) {
381 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
384 __CFMessagePortUnsetValid(memory
);
385 __CFMessagePortUnsetExtraMachRef(memory
);
386 __CFMessagePortUnsetRemote(memory
);
387 memory
->_lock
= CFSpinLockInit
;
388 memory
->_name
= name
;
389 memory
->_port
= NULL
;
390 memory
->_replies
= NULL
;
391 memory
->_convCounter
= 0;
392 memory
->_perPID
= perPID
? getpid() : 0; // actual value not terribly useful for local ports
393 memory
->_replyPort
= NULL
;
394 memory
->_source
= NULL
;
395 memory
->_dispatchSource
= NULL
;
396 memory
->_dispatchQ
= NULL
;
397 memory
->_icallout
= NULL
;
398 memory
->_callout
= callout
;
399 memory
->_calloutEx
= calloutEx
;
400 memory
->_context
.info
= NULL
;
401 memory
->_context
.retain
= NULL
;
402 memory
->_context
.release
= NULL
;
403 memory
->_context
.copyDescription
= NULL
;
406 CFMachPortRef native
= NULL
;
409 task_get_bootstrap_port(mach_task_self(), &bs
);
411 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
412 if (ret
== KERN_SUCCESS
) {
413 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
414 if (KERN_SUCCESS
== ret
) {
415 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
416 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
417 __CFMessagePortSetExtraMachRef(memory
);
419 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
);
420 mach_port_destroy(mach_task_self(), mp
);
421 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
422 // name is released by deallocation
429 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
430 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
432 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
433 // name is released by deallocation
437 mp
= CFMachPortGetPort(native
);
438 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
439 if (ret
!= KERN_SUCCESS
) {
440 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
);
441 CFMachPortInvalidate(native
);
443 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
444 // name is released by deallocation
449 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
450 memory
->_port
= native
;
453 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
454 __CFMessagePortSetValid(memory
);
455 if (NULL
!= context
) {
456 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
457 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
459 __CFSpinLock(&__CFAllMessagePortsLock
);
460 if (!perPID
&& NULL
!= name
) {
461 CFMessagePortRef existing
;
462 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
464 __CFSpinUnlock(&__CFAllMessagePortsLock
);
466 return (CFMessagePortRef
)(existing
);
468 if (NULL
== __CFAllLocalMessagePorts
) {
469 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
471 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
473 __CFSpinUnlock(&__CFAllMessagePortsLock
);
474 if (shouldFreeInfo
) *shouldFreeInfo
= false;
478 CFMessagePortRef
CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
479 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, false, NULL
);
482 CFMessagePortRef
CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
483 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, true, NULL
);
486 CFMessagePortRef
_CFMessagePortCreateLocalEx(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, uintptr_t unused
, CFMessagePortCallBackEx calloutEx
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
487 return __CFMessagePortCreateLocal(allocator
, name
, NULL
, context
, shouldFreeInfo
, perPID
, calloutEx
);
490 static CFMessagePortRef
__CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, CFIndex pid
) {
491 CFMessagePortRef memory
;
492 CFMachPortRef native
;
493 CFMachPortContext ctx
;
494 uint8_t *utfname
= NULL
;
496 mach_port_t bp
, port
;
499 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
503 __CFSpinLock(&__CFAllMessagePortsLock
);
504 if (!perPID
&& NULL
!= name
) {
505 CFMessagePortRef existing
;
506 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
508 __CFSpinUnlock(&__CFAllMessagePortsLock
);
510 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
511 if (!CFMessagePortIsValid(existing
)) { // must do this outside lock to avoid deadlock
515 return (CFMessagePortRef
)(existing
);
518 __CFSpinUnlock(&__CFAllMessagePortsLock
);
519 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
520 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
521 if (NULL
== memory
) {
525 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
528 __CFMessagePortUnsetValid(memory
);
529 __CFMessagePortUnsetExtraMachRef(memory
);
530 __CFMessagePortSetRemote(memory
);
531 memory
->_lock
= CFSpinLockInit
;
532 memory
->_name
= name
;
533 memory
->_port
= NULL
;
534 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
535 memory
->_convCounter
= 0;
536 memory
->_perPID
= perPID
? pid
: 0;
537 memory
->_replyPort
= NULL
;
538 memory
->_source
= NULL
;
539 memory
->_dispatchSource
= NULL
;
540 memory
->_dispatchQ
= NULL
;
541 memory
->_icallout
= NULL
;
542 memory
->_callout
= NULL
;
543 memory
->_calloutEx
= NULL
;
548 ctx
.copyDescription
= NULL
;
549 task_get_bootstrap_port(mach_task_self(), &bp
);
550 ret
= bootstrap_look_up2(bp
, (char *)utfname
, &port
, perPID
? (pid_t
)pid
: 0, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
551 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
552 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
553 if (NULL
== native
) {
554 // name is released by deallocation
558 memory
->_port
= native
;
559 __CFMessagePortSetValid(memory
);
560 __CFSpinLock(&__CFAllMessagePortsLock
);
561 if (!perPID
&& NULL
!= name
) {
562 CFMessagePortRef existing
;
563 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
565 __CFSpinUnlock(&__CFAllMessagePortsLock
);
567 return (CFMessagePortRef
)(existing
);
569 if (NULL
== __CFAllRemoteMessagePorts
) {
570 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
572 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
575 __CFSpinUnlock(&__CFAllMessagePortsLock
);
576 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
577 // that set-invalidation-callback might have called back into us
578 // if the CFMachPort is already bad, but that was a no-op since
579 // there was no callback setup at the (previous) time the CFMachPort
580 // went invalid; so check for validity manually and react
581 if (!CFMachPortIsValid(native
)) {
582 CFRelease(memory
); // does the invalidate
587 return (CFMessagePortRef
)memory
;
590 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
591 return __CFMessagePortCreateRemote(allocator
, name
, false, 0);
594 CFMessagePortRef
CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator
, CFStringRef name
, CFIndex pid
) {
595 return __CFMessagePortCreateRemote(allocator
, name
, true, pid
);
598 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
599 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
600 return __CFMessagePortIsRemote(ms
);
603 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
604 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
608 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
609 CFAllocatorRef allocator
= CFGetAllocator(ms
);
610 uint8_t *utfname
= NULL
;
612 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
613 if (ms
->_perPID
|| __CFMessagePortIsRemote(ms
)) return false;
614 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
618 __CFSpinLock(&__CFAllMessagePortsLock
);
620 CFMessagePortRef existing
;
621 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
622 __CFSpinUnlock(&__CFAllMessagePortsLock
);
624 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
628 __CFSpinUnlock(&__CFAllMessagePortsLock
);
630 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
631 CFMachPortRef oldPort
= ms
->_port
;
632 CFMachPortRef native
= NULL
;
635 task_get_bootstrap_port(mach_task_self(), &bs
);
636 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
637 if (ret
== KERN_SUCCESS
) {
638 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
639 if (KERN_SUCCESS
== ret
) {
640 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
641 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
642 __CFMessagePortSetExtraMachRef(ms
);
644 mach_port_destroy(mach_task_self(), mp
);
645 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
651 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
652 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
654 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
658 mp
= CFMachPortGetPort(native
);
659 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, 0);
660 if (ret
!= KERN_SUCCESS
) {
661 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
);
662 CFMachPortInvalidate(native
);
664 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
669 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
671 if (NULL
!= oldPort
&& oldPort
!= native
) {
672 if (__CFMessagePortExtraMachRef(ms
)) {
673 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_SEND
, -1);
674 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_RECEIVE
, -1);
676 CFMachPortInvalidate(oldPort
);
679 __CFSpinLock(&__CFAllMessagePortsLock
);
680 // This relocking without checking to see if something else has grabbed
681 // that name in the cache is rather suspect, but what would that even
682 // mean has happened? We'd expect the bootstrap_* calls above to have
683 // failed for this one and not gotten this far, or failed for all of the
684 // other simultaneous attempts to get the name (and having succeeded for
685 // this one, gotten here). So we're not going to try very hard here
686 // with the thread-safety.
687 if (NULL
== __CFAllLocalMessagePorts
) {
688 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
690 if (NULL
!= ms
->_name
) {
691 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
692 CFRelease(ms
->_name
);
695 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
696 __CFSpinUnlock(&__CFAllMessagePortsLock
);
699 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
703 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
704 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
705 //#warning CF: assert that this is a local port
706 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
707 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
710 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
711 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
712 if (!__CFMessagePortIsDeallocing(ms
)) {
715 __CFMessagePortLock(ms
);
716 if (__CFMessagePortIsValid(ms
)) {
717 if (ms
->_dispatchSource
) {
718 dispatch_source_cancel(ms
->_dispatchSource
);
719 ms
->_dispatchSource
= NULL
;
720 ms
->_dispatchQ
= NULL
;
723 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
724 CFRunLoopSourceRef source
= ms
->_source
;
725 CFMachPortRef replyPort
= ms
->_replyPort
;
726 CFMachPortRef port
= ms
->_port
;
727 CFStringRef name
= ms
->_name
;
730 __CFMessagePortUnsetValid(ms
);
731 if (!__CFMessagePortIsRemote(ms
)) {
732 info
= ms
->_context
.info
;
733 ms
->_context
.info
= NULL
;
736 ms
->_replyPort
= NULL
;
738 __CFMessagePortUnlock(ms
);
740 __CFSpinLock(&__CFAllMessagePortsLock
);
741 if (0 == ms
->_perPID
&& NULL
!= name
&& NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
742 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
744 __CFSpinUnlock(&__CFAllMessagePortsLock
);
745 if (NULL
!= callout
) {
748 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
749 ms
->_context
.release(info
);
751 if (NULL
!= source
) {
752 CFRunLoopSourceInvalidate(source
);
755 if (NULL
!= replyPort
) {
756 CFMachPortInvalidate(replyPort
);
757 CFRelease(replyPort
);
759 if (__CFMessagePortIsRemote(ms
)) {
760 // Get rid of our extra ref on the Mach port gotten from bs server
761 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
764 // We already know we're going invalid, don't need this callback
765 // anymore; plus, this solves a reentrancy deadlock; also, this
766 // must be done before the deallocate of the Mach port, to
767 // avoid a race between the notification message which could be
768 // handled in another thread, and this NULL'ing out.
769 CFMachPortSetInvalidationCallBack(port
, NULL
);
770 if (__CFMessagePortExtraMachRef(ms
)) {
771 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(port
), MACH_PORT_RIGHT_SEND
, -1);
772 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(port
), MACH_PORT_RIGHT_RECEIVE
, -1);
774 CFMachPortInvalidate(port
);
778 __CFMessagePortUnlock(ms
);
780 if (!__CFMessagePortIsDeallocing(ms
)) {
785 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
786 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
787 if (!__CFMessagePortIsValid(ms
)) return false;
789 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
790 CFMessagePortInvalidate(ms
);
794 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
795 CFMessagePortInvalidate(ms
);
803 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
804 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
805 return ms
->_icallout
;
808 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
809 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
810 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
811 callout(ms
, ms
->_context
.info
);
813 ms
->_icallout
= callout
;
817 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
818 CFMessagePortRef ms
= info
;
819 mach_msg_base_t
*msgp
= msg
;
820 mach_msg_base_t
*replymsg
;
821 __CFMessagePortLock(ms
);
822 if (!__CFMessagePortIsValid(ms
)) {
823 __CFMessagePortUnlock(ms
);
827 int32_t byteslen
= 0;
829 Boolean invalidMagic
= (MSGP_GET(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_GET(msgp
, magic
)) != MAGIC
);
830 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
831 invalidComplex
= invalidComplex
|| ((msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
832 Boolean wayTooBig
= ((msgp
->body
.msgh_descriptor_count
) ? sizeof(struct __CFMessagePortMachMessage1
) : sizeof(struct __CFMessagePortMachMessage0
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
;
833 Boolean wayTooSmall
= msgp
->header
.msgh_size
< sizeof(struct __CFMessagePortMachMessage0
);
834 Boolean wrongSize
= false;
835 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
836 byteslen
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, byteslen
));
837 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
838 if (0 != msgp
->body
.msgh_descriptor_count
) {
839 wrongSize
= wrongSize
|| (MSGP1_FIELD(msgp
, ool
).size
!= byteslen
);
841 wrongSize
= wrongSize
|| (msgp
->header
.msgh_size
- sizeof(struct __CFMessagePortMachMessage0
) < byteslen
);
844 Boolean invalidMsgID
= (0 <= msgp
->header
.msgh_id
) && (msgp
->header
.msgh_id
<= INT32_MAX
); // conversation id
845 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
846 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt reply Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
847 mach_msg_destroy((mach_msg_header_t
*)msgp
);
848 __CFMessagePortUnlock(ms
);
852 if (CFDictionaryContainsKey(ms
->_replies
, (void *)(uintptr_t)msgp
->header
.msgh_id
)) {
853 CFDataRef reply
= NULL
;
854 replymsg
= (mach_msg_base_t
*)msg
;
855 if (0 == replymsg
->body
.msgh_descriptor_count
) {
856 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
857 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP0_FIELD(replymsg
, bytes
)) + byteslen
);
858 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
859 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP0_FIELD(replymsg
, bytes
), byteslen
);
861 reply
= (void *)~0; // means NULL data
864 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
865 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP1_FIELD(replymsg
, ool
).address
, MSGP1_FIELD(replymsg
, ool
).size
);
866 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(replymsg
, ool
).address
, MSGP1_FIELD(replymsg
, ool
).size
);
868 CFDictionarySetValue(ms
->_replies
, (void *)(uintptr_t)msgp
->header
.msgh_id
, (void *)reply
);
869 } else { /* discard message */
870 if (1 == msgp
->body
.msgh_descriptor_count
) {
871 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
);
874 __CFMessagePortUnlock(ms
);
877 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
878 mach_msg_base_t
*sendmsg
;
879 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
880 CFRunLoopSourceRef source
= NULL
;
881 CFDataRef reply
= NULL
;
883 uint32_t sendOpts
= 0, sendTimeOut
= 0;
884 int32_t desiredReply
;
885 Boolean didRegister
= false;
888 //#warning CF: This should be an assert
889 // if (!__CFMessagePortIsRemote(remote)) return -999;
890 if (data
&& __CFMessagePortMaxDataSize
< CFDataGetLength(data
)) {
891 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSendRequest: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
892 return kCFMessagePortTransportError
;
894 __CFMessagePortLock(remote
);
895 if (!__CFMessagePortIsValid(remote
)) {
896 __CFMessagePortUnlock(remote
);
897 return kCFMessagePortIsInvalid
;
899 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
900 if (NULL
== remote
->_replyPort
) {
901 CFMachPortContext context
;
903 context
.info
= remote
;
904 context
.retain
= (const void *(*)(const void *))CFRetain
;
905 context
.release
= (void (*)(const void *))CFRelease
;
906 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
907 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
909 remote
->_convCounter
++;
910 desiredReply
= -remote
->_convCounter
;
911 sendmsg
= __CFMessagePortCreateMessage(false, CFMachPortGetPort(remote
->_port
), (replyMode
!= NULL
? CFMachPortGetPort(remote
->_replyPort
) : MACH_PORT_NULL
), -desiredReply
, msgid
, (data
? CFDataGetBytePtr(data
) : NULL
), (data
? CFDataGetLength(data
) : 0));
913 __CFMessagePortUnlock(remote
);
915 return kCFMessagePortTransportError
;
917 if (replyMode
!= NULL
) {
918 CFDictionarySetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
, NULL
);
919 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
920 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
922 CFRunLoopAddSource(currentRL
, source
, replyMode
);
925 if (sendTimeout
< 10.0*86400) {
926 // anything more than 10 days is no timeout!
927 sendOpts
= MACH_SEND_TIMEOUT
;
928 sendTimeout
*= 1000.0;
929 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
930 sendTimeOut
= floor(sendTimeout
);
932 __CFMessagePortUnlock(remote
);
933 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->header
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
934 __CFMessagePortLock(remote
);
935 if (KERN_SUCCESS
!= ret
) {
936 // need to deallocate the send-once right that might have been created
937 if (replyMode
!= NULL
) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t
*)sendmsg
)->msgh_local_port
);
939 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
941 if (source
) CFRelease(source
);
942 __CFMessagePortUnlock(remote
);
943 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
945 return (MACH_SEND_TIMED_OUT
== ret
) ? kCFMessagePortSendTimeout
: kCFMessagePortTransportError
;
947 __CFMessagePortUnlock(remote
);
948 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
949 if (replyMode
== NULL
) {
951 return kCFMessagePortSuccess
;
953 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
954 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
956 CFRunLoopRunInMode(replyMode
, __CFTSRToTimeInterval(termTSR
- mach_absolute_time()), true);
957 // warning: what, if anything, should be done if remote is now invalid?
958 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
959 if (NULL
!= reply
|| termTSR
< (int64_t)mach_absolute_time()) {
962 if (!CFMessagePortIsValid(remote
)) {
963 // no reason that reply port alone should go invalid so we don't check for that
967 // Should we uninstall the notify port? A complex question...
969 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
971 if (source
) CFRelease(source
);
973 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
975 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: kCFMessagePortBecameInvalidError
;
977 if (NULL
!= returnDatap
) {
978 *returnDatap
= ((void *)~0 == reply
) ? NULL
: reply
;
979 } else if ((void *)~0 != reply
) {
982 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
984 return kCFMessagePortSuccess
;
987 static mach_port_t
__CFMessagePortGetPort(void *info
) {
988 CFMessagePortRef ms
= info
;
989 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
);
990 return ms
->_port
? CFMachPortGetPort(ms
->_port
) : MACH_PORT_NULL
;
993 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
994 CFMessagePortRef ms
= info
;
995 mach_msg_base_t
*msgp
= msg
;
996 mach_msg_base_t
*replymsg
;
998 void (*context_release
)(const void *);
999 CFDataRef returnData
, data
= NULL
;
1000 void *return_bytes
= NULL
;
1001 CFIndex return_len
= 0;
1004 __CFMessagePortLock(ms
);
1005 if (!__CFMessagePortIsValid(ms
)) {
1006 __CFMessagePortUnlock(ms
);
1009 if (NULL
!= ms
->_context
.retain
) {
1010 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
1011 context_release
= ms
->_context
.release
;
1013 context_info
= ms
->_context
.info
;
1014 context_release
= NULL
;
1016 __CFMessagePortUnlock(ms
);
1018 int32_t byteslen
= 0;
1020 Boolean invalidMagic
= (MSGP_GET(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_GET(msgp
, magic
)) != MAGIC
);
1021 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
1022 invalidComplex
= invalidComplex
|| ((msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
1023 Boolean wayTooBig
= ((msgp
->body
.msgh_descriptor_count
) ? sizeof(struct __CFMessagePortMachMessage1
) : sizeof(struct __CFMessagePortMachMessage0
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
;
1024 Boolean wayTooSmall
= msgp
->header
.msgh_size
< sizeof(struct __CFMessagePortMachMessage0
);
1025 Boolean wrongSize
= false;
1026 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
1027 byteslen
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, byteslen
));
1028 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
1029 if (0 != msgp
->body
.msgh_descriptor_count
) {
1030 wrongSize
= wrongSize
|| (MSGP1_FIELD(msgp
, ool
).size
!= byteslen
);
1032 wrongSize
= wrongSize
|| (msgp
->header
.msgh_size
- sizeof(struct __CFMessagePortMachMessage0
) < byteslen
);
1035 Boolean invalidMsgID
= (msgp
->header
.msgh_id
<= 0) || (INT32_MAX
< msgp
->header
.msgh_id
); // conversation id
1036 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
1037 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
1038 mach_msg_destroy((mach_msg_header_t
*)msgp
);
1042 /* Create no-copy, no-free-bytes wrapper CFData */
1043 if (0 == msgp
->body
.msgh_descriptor_count
) {
1044 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
1045 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP0_FIELD(msgp
, bytes
)) + byteslen
);
1046 msgid
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, msgid
));
1047 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
1048 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP0_FIELD(msgp
, bytes
), byteslen
, kCFAllocatorNull
);
1051 msgid
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, msgid
));
1052 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
, kCFAllocatorNull
);
1055 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
1057 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));
1058 returnData
= ms
->_calloutEx(ms
, msgid
, data
, context_info
, trailer
, 0);
1060 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
1061 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
1062 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
1063 bytes inline in the Mach message, so can release the returnData object
1064 here. In cases (3) and (5), we'll send the data out-of-line, we need to
1065 create a copy of the memory, which we'll have the kernel autodeallocate
1066 for us on send. In case (4) also, the bytes in the return data may be part
1067 of the bytes in "data" that we sent into the callout, so if the incoming
1068 data was received out of line, we wouldn't be able to clean up the out-of-line
1069 wad until the message was sent either, if we didn't make the copy. */
1070 if (NULL
!= returnData
) {
1071 return_len
= CFDataGetLength(returnData
);
1072 if (__CFMessagePortMaxDataSize
< return_len
) {
1073 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
1077 if (returnData
&& return_len
< __CFMessagePortMaxInlineBytes
) {
1078 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
1079 } else if (returnData
) {
1080 return_bytes
= NULL
;
1081 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
1082 /* vm_copy would only be a win here if the source address
1083 is page aligned; it is a lose in all other cases, since
1084 the kernel will just do the memmove for us (but not in
1085 as simple a way). */
1086 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
1089 replymsg
= __CFMessagePortCreateMessage(true, msgp
->header
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)msgp
->header
.msgh_id
, msgid
, return_bytes
, return_len
);
1090 if (1 == replymsg
->body
.msgh_descriptor_count
) {
1091 MSGP1_FIELD(replymsg
, ool
).deallocate
= true;
1093 if (data
) CFRelease(data
);
1094 if (1 == msgp
->body
.msgh_descriptor_count
) {
1095 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
);
1097 if (returnData
) CFRelease(returnData
);
1098 if (context_release
) {
1099 context_release(context_info
);
1104 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
1105 CFRunLoopSourceRef result
= NULL
;
1106 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1107 if (!CFMessagePortIsValid(ms
)) return NULL
;
1108 if (__CFMessagePortIsRemote(ms
)) return NULL
;
1109 __CFMessagePortLock(ms
);
1110 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
1111 CFRelease(ms
->_source
);
1114 if (NULL
== ms
->_source
&& NULL
== ms
->_dispatchSource
&& __CFMessagePortIsValid(ms
)) {
1115 CFRunLoopSourceContext1 context
;
1116 context
.version
= 1;
1117 context
.info
= (void *)ms
;
1118 context
.retain
= (const void *(*)(const void *))CFRetain
;
1119 context
.release
= (void (*)(const void *))CFRelease
;
1120 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
1121 context
.equal
= NULL
;
1122 context
.hash
= NULL
;
1123 context
.getPort
= __CFMessagePortGetPort
;
1124 context
.perform
= __CFMessagePortPerform
;
1125 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
1127 if (NULL
!= ms
->_source
) {
1128 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
1130 __CFMessagePortUnlock(ms
);
1134 void CFMessagePortSetDispatchQueue(CFMessagePortRef ms
, dispatch_queue_t queue
) {
1135 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1136 __CFMessagePortLock(ms
);
1137 if (!__CFMessagePortIsValid(ms
)) {
1138 __CFMessagePortUnlock(ms
);
1139 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid"));
1142 if (__CFMessagePortIsRemote(ms
)) {
1143 __CFMessagePortUnlock(ms
);
1144 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set"));
1147 if (NULL
!= ms
->_source
) {
1148 __CFMessagePortUnlock(ms
);
1149 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set"));
1153 if (ms
->_dispatchSource
) {
1154 dispatch_source_cancel(ms
->_dispatchSource
);
1155 ms
->_dispatchSource
= NULL
;
1156 ms
->_dispatchQ
= NULL
;
1160 mach_port_t port
= __CFMessagePortGetPort(ms
);
1161 if (MACH_PORT_NULL
!= port
) {
1162 static dispatch_queue_t mportQueue
= NULL
;
1163 static dispatch_once_t once
;
1164 dispatch_once(&once
, ^{
1165 mportQueue
= dispatch_queue_create("CFMessagePort Queue", NULL
);
1167 dispatch_source_t theSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, port
, 0, mportQueue
);
1168 dispatch_source_set_cancel_handler(theSource
, ^{
1169 dispatch_release(queue
);
1170 dispatch_release(theSource
);
1172 dispatch_source_set_event_handler(theSource
, ^{
1174 mach_msg_header_t
*msg
= (mach_msg_header_t
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2048, 0);
1175 msg
->msgh_size
= 2048;
1179 msg
->msgh_local_port
= port
;
1180 msg
->msgh_remote_port
= MACH_PORT_NULL
;
1183 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
);
1184 if (MACH_MSG_SUCCESS
== ret
) break;
1185 if (MACH_RCV_TOO_LARGE
!= ret
) HALT
;
1187 uint32_t newSize
= round_msg(msg
->msgh_size
+ MAX_TRAILER_SIZE
);
1188 msg
= CFAllocatorReallocate(kCFAllocatorSystemDefault
, msg
, newSize
, 0);
1189 msg
->msgh_size
= newSize
;
1192 dispatch_async(queue
, ^{
1193 mach_msg_header_t
*reply
= __CFMessagePortPerform(msg
, msg
->msgh_size
, kCFAllocatorSystemDefault
, ms
);
1194 if (NULL
!= reply
) {
1195 kern_return_t ret
= mach_msg(reply
, MACH_SEND_MSG
, reply
->msgh_size
, 0, MACH_PORT_NULL
, 0, MACH_PORT_NULL
);
1196 if (KERN_SUCCESS
!= ret
) mach_msg_destroy(reply
);
1197 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, reply
);
1199 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, msg
);
1203 ms
->_dispatchSource
= theSource
;
1205 if (ms
->_dispatchSource
) {
1206 dispatch_retain(queue
);
1207 ms
->_dispatchQ
= queue
;
1208 dispatch_resume(ms
->_dispatchSource
);
1210 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created"));
1213 __CFMessagePortUnlock(ms
);