2 * Copyright (c) 2010 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-2009, 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);
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
64 DISPATCH_HELPER_FUNCTIONS(mport
, CFMessagePort
)
67 static CFSpinLock_t __CFAllMessagePortsLock
= CFSpinLockInit
;
68 static CFMutableDictionaryRef __CFAllLocalMessagePorts
= NULL
;
69 static CFMutableDictionaryRef __CFAllRemoteMessagePorts
= NULL
;
71 struct __CFMessagePort
{
75 CFMachPortRef _port
; /* immutable; invalidated */
76 CFMutableDictionaryRef _replies
;
78 int32_t _perPID
; /* zero if not per-pid, else pid */
79 CFMachPortRef _replyPort
; /* only used by remote port; immutable once created; invalidated */
80 CFRunLoopSourceRef _source
; /* only used by local port; immutable once created; invalidated */
81 dispatch_source_t _dispatchSource
; /* only used by local port; invalidated */
82 dispatch_queue_t _dispatchQ
; /* only used by local port */
83 CFMessagePortInvalidationCallBack _icallout
;
84 CFMessagePortCallBack _callout
; /* only used by local port; immutable */
85 CFMessagePortContext _context
; /* not part of remote port; immutable; invalidated */
88 /* Bit 0 in the base reserved bits is used for invalid state */
89 /* Bit 1 of the base reserved bits is used for has-extra-port-refs state */
90 /* Bit 2 of the base reserved bits is used for is-remote state */
91 /* Bit 3 in the base reserved bits is used for is-deallocing state */
93 CF_INLINE Boolean
__CFMessagePortIsValid(CFMessagePortRef ms
) {
94 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0);
97 CF_INLINE
void __CFMessagePortSetValid(CFMessagePortRef ms
) {
98 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 1);
101 CF_INLINE
void __CFMessagePortUnsetValid(CFMessagePortRef ms
) {
102 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 0);
105 CF_INLINE Boolean
__CFMessagePortExtraMachRef(CFMessagePortRef ms
) {
106 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1);
109 CF_INLINE
void __CFMessagePortSetExtraMachRef(CFMessagePortRef ms
) {
110 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 1);
113 CF_INLINE
void __CFMessagePortUnsetExtraMachRef(CFMessagePortRef ms
) {
114 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 0);
117 CF_INLINE Boolean
__CFMessagePortIsRemote(CFMessagePortRef ms
) {
118 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2);
121 CF_INLINE
void __CFMessagePortSetRemote(CFMessagePortRef ms
) {
122 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 1);
125 CF_INLINE
void __CFMessagePortUnsetRemote(CFMessagePortRef ms
) {
126 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 0);
129 CF_INLINE Boolean
__CFMessagePortIsDeallocing(CFMessagePortRef ms
) {
130 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3);
133 CF_INLINE
void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms
) {
134 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3, 1);
137 CF_INLINE
void __CFMessagePortLock(CFMessagePortRef ms
) {
138 __CFSpinLock(&(ms
->_lock
));
141 CF_INLINE
void __CFMessagePortUnlock(CFMessagePortRef ms
) {
142 __CFSpinUnlock(&(ms
->_lock
));
146 #define __CFMessagePortMaxInlineBytes 4096*10
148 struct __CFMessagePortMachMessage0
{
149 mach_msg_base_t base
;
156 struct __CFMessagePortMachMessage1
{
157 mach_msg_base_t base
;
158 mach_msg_ool_descriptor_t ool
;
164 #define MAGIC 0xF1F2F3F4
166 #define MSGP0_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage0 *)msgp)->ident
167 #define MSGP1_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage1 *)msgp)->ident
168 #define MSGP_GET(msgp, ident) \
169 ((((mach_msg_base_t *)msgp)->body.msgh_descriptor_count) ? MSGP1_FIELD(msgp, ident) : MSGP0_FIELD(msgp, ident))
171 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
) {
172 if (__CFMessagePortMaxDataSize
< byteslen
) return NULL
;
173 int32_t rounded_byteslen
= ((byteslen
+ 3) & ~0x3);
174 if (rounded_byteslen
<= __CFMessagePortMaxInlineBytes
) {
175 int32_t size
= sizeof(struct __CFMessagePortMachMessage0
) + rounded_byteslen
;
176 struct __CFMessagePortMachMessage0
*msg
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
, 0);
177 if (!msg
) return NULL
;
178 memset(msg
, 0, size
);
179 msg
->base
.header
.msgh_id
= convid
;
180 msg
->base
.header
.msgh_size
= size
;
181 msg
->base
.header
.msgh_remote_port
= port
;
182 msg
->base
.header
.msgh_local_port
= replyPort
;
183 msg
->base
.header
.msgh_reserved
= 0;
184 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));
185 msg
->base
.body
.msgh_descriptor_count
= 0;
187 msg
->msgid
= CFSwapInt32HostToLittle(msgid
);
188 msg
->byteslen
= CFSwapInt32HostToLittle(byteslen
);
189 if (NULL
!= bytes
&& 0 < byteslen
) {
190 memmove(msg
->bytes
, bytes
, byteslen
);
192 return (mach_msg_base_t
*)msg
;
194 int32_t size
= sizeof(struct __CFMessagePortMachMessage1
);
195 struct __CFMessagePortMachMessage1
*msg
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
, 0);
196 if (!msg
) return NULL
;
197 memset(msg
, 0, size
);
198 msg
->base
.header
.msgh_id
= convid
;
199 msg
->base
.header
.msgh_size
= size
;
200 msg
->base
.header
.msgh_remote_port
= port
;
201 msg
->base
.header
.msgh_local_port
= replyPort
;
202 msg
->base
.header
.msgh_reserved
= 0;
203 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));
204 msg
->base
.header
.msgh_bits
|= MACH_MSGH_BITS_COMPLEX
;
205 msg
->base
.body
.msgh_descriptor_count
= 1;
207 msg
->msgid
= CFSwapInt32HostToLittle(msgid
);
208 msg
->byteslen
= CFSwapInt32HostToLittle(byteslen
);
209 msg
->ool
.deallocate
= false;
210 msg
->ool
.copy
= MACH_MSG_VIRTUAL_COPY
;
211 msg
->ool
.address
= (void *)bytes
;
212 msg
->ool
.size
= byteslen
;
213 msg
->ool
.type
= MACH_MSG_OOL_DESCRIPTOR
;
214 return (mach_msg_base_t
*)msg
;
218 static CFStringRef
__CFMessagePortCopyDescription(CFTypeRef cf
) {
219 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
222 CFStringRef contextDesc
= NULL
;
223 locked
= ms
->_lock
? "Yes" : "No";
224 if (__CFMessagePortIsRemote(ms
)) {
225 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
);
227 if (NULL
!= ms
->_context
.info
&& NULL
!= ms
->_context
.copyDescription
) {
228 contextDesc
= ms
->_context
.copyDescription(ms
->_context
.info
);
230 if (NULL
== contextDesc
) {
231 contextDesc
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort context %p>"), ms
->_context
.info
);
233 void *addr
= ms
->_callout
;
235 const char *name
= (dladdr(addr
, &info
) && info
.dli_saddr
== addr
&& info
.dli_sname
) ? info
.dli_sname
: "???";
236 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>")));
238 if (NULL
!= contextDesc
) {
239 CFRelease(contextDesc
);
244 static void __CFMessagePortDeallocate(CFTypeRef cf
) {
245 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
246 __CFMessagePortSetIsDeallocing(ms
);
247 CFMessagePortInvalidate(ms
);
248 // Delay cleanup of _replies until here so that invalidation during
249 // SendRequest does not cause _replies to disappear out from under that function.
250 if (NULL
!= ms
->_replies
) {
251 CFRelease(ms
->_replies
);
253 if (NULL
!= ms
->_name
) {
254 CFRelease(ms
->_name
);
256 if (NULL
!= ms
->_port
) {
257 if (__CFMessagePortExtraMachRef(ms
)) {
258 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_SEND
, -1);
259 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_RECEIVE
, -1);
261 CFMachPortInvalidate(ms
->_port
);
262 CFRelease(ms
->_port
);
265 // A remote message port for a local message port in the same process will get the
266 // same mach port, and the remote port will keep the mach port from being torn down,
267 // thus keeping the remote port from getting any sort of death notification and
268 // auto-invalidating; so we manually implement the 'auto-invalidation' here by
269 // tickling each remote port to check its state after any message port is destroyed,
270 // but most importantly after local message ports are destroyed.
271 __CFSpinLock(&__CFAllMessagePortsLock
);
272 CFMessagePortRef
*remotePorts
= NULL
;
274 if (NULL
!= __CFAllRemoteMessagePorts
) {
275 cnt
= CFDictionaryGetCount(__CFAllRemoteMessagePorts
);
276 remotePorts
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, cnt
* sizeof(CFMessagePortRef
), __kCFAllocatorGCScannedMemory
);
277 CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts
, NULL
, (const void **)remotePorts
);
278 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
279 CFRetain(remotePorts
[idx
]);
282 __CFSpinUnlock(&__CFAllMessagePortsLock
);
284 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
285 // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid
286 CFMessagePortIsValid(remotePorts
[idx
]);
287 CFRelease(remotePorts
[idx
]);
289 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, remotePorts
);
293 static CFTypeID __kCFMessagePortTypeID
= _kCFRuntimeNotATypeID
;
295 static const CFRuntimeClass __CFMessagePortClass
= {
300 __CFMessagePortDeallocate
,
304 __CFMessagePortCopyDescription
307 __private_extern__
void __CFMessagePortInitialize(void) {
308 __kCFMessagePortTypeID
= _CFRuntimeRegisterClass(&__CFMessagePortClass
);
311 CFTypeID
CFMessagePortGetTypeID(void) {
312 return __kCFMessagePortTypeID
;
315 static CFStringRef
__CFMessagePortSanitizeStringName(CFStringRef name
, uint8_t **utfnamep
, CFIndex
*utfnamelenp
) {
319 utfname
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, __kCFMessagePortMaxNameLength
+ 1, 0);
320 CFStringGetBytes(name
, CFRangeMake(0, CFStringGetLength(name
)), kCFStringEncodingUTF8
, 0, false, utfname
, __kCFMessagePortMaxNameLength
, &utflen
);
321 utfname
[utflen
] = '\0';
322 /* A new string is created, because the original string may have been
323 truncated to the max length, and we want the string name to definitely
324 match the raw UTF-8 chunk that has been created. Also, this is useful
325 to get a constant string in case the original name string was mutable. */
326 result
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, utfname
, utflen
, kCFStringEncodingUTF8
, false);
327 if (NULL
!= utfnamep
) {
330 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
332 if (NULL
!= utfnamelenp
) {
333 *utfnamelenp
= utflen
;
338 static void __CFMessagePortDummyCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
339 // not supposed to be implemented
342 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port
, void *info
) {
343 // info has been setup as the CFMessagePort owning the CFMachPort
344 if (info
) CFMessagePortInvalidate(info
);
347 static CFMessagePortRef
__CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
, Boolean perPID
) {
348 CFMessagePortRef memory
;
349 uint8_t *utfname
= NULL
;
351 if (shouldFreeInfo
) *shouldFreeInfo
= true;
353 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
355 __CFSpinLock(&__CFAllMessagePortsLock
);
356 if (!perPID
&& NULL
!= name
) {
357 CFMessagePortRef existing
;
358 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
360 __CFSpinUnlock(&__CFAllMessagePortsLock
);
362 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
363 return (CFMessagePortRef
)(existing
);
366 __CFSpinUnlock(&__CFAllMessagePortsLock
);
367 CFIndex size
= sizeof(struct __CFMessagePort
) - sizeof(CFRuntimeBase
);
368 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
369 if (NULL
== memory
) {
373 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
376 __CFMessagePortUnsetValid(memory
);
377 __CFMessagePortUnsetExtraMachRef(memory
);
378 __CFMessagePortUnsetRemote(memory
);
379 memory
->_lock
= CFSpinLockInit
;
380 memory
->_name
= name
;
381 memory
->_port
= NULL
;
382 memory
->_replies
= NULL
;
383 memory
->_convCounter
= 0;
384 memory
->_perPID
= perPID
? getpid() : 0; // actual value not terribly useful for local ports
385 memory
->_replyPort
= NULL
;
386 memory
->_source
= NULL
;
387 memory
->_dispatchSource
= NULL
;
388 memory
->_dispatchQ
= NULL
;
389 memory
->_icallout
= NULL
;
390 memory
->_callout
= callout
;
391 memory
->_context
.info
= NULL
;
392 memory
->_context
.retain
= NULL
;
393 memory
->_context
.release
= NULL
;
394 memory
->_context
.copyDescription
= NULL
;
397 CFMachPortRef native
= NULL
;
400 task_get_bootstrap_port(mach_task_self(), &bs
);
402 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
403 if (ret
== KERN_SUCCESS
) {
404 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
405 if (KERN_SUCCESS
== ret
) {
406 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
407 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
408 __CFMessagePortSetExtraMachRef(memory
);
410 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
);
411 mach_port_destroy(mach_task_self(), mp
);
412 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
413 // name is released by deallocation
420 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
421 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
423 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
424 // name is released by deallocation
428 mp
= CFMachPortGetPort(native
);
429 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
430 if (ret
!= KERN_SUCCESS
) {
431 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
);
432 CFMachPortInvalidate(native
);
434 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
435 // name is released by deallocation
440 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
441 memory
->_port
= native
;
444 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
445 __CFMessagePortSetValid(memory
);
446 if (NULL
!= context
) {
447 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
448 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
450 __CFSpinLock(&__CFAllMessagePortsLock
);
451 if (!perPID
&& NULL
!= name
) {
452 CFMessagePortRef existing
;
453 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
455 __CFSpinUnlock(&__CFAllMessagePortsLock
);
457 return (CFMessagePortRef
)(existing
);
459 if (NULL
== __CFAllLocalMessagePorts
) {
460 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
462 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
464 __CFSpinUnlock(&__CFAllMessagePortsLock
);
465 if (shouldFreeInfo
) *shouldFreeInfo
= false;
469 CFMessagePortRef
CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
470 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, false);
473 CFMessagePortRef
CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
474 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, true);
477 static CFMessagePortRef
__CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, CFIndex pid
) {
478 CFMessagePortRef memory
;
479 CFMachPortRef native
;
480 CFMachPortContext ctx
;
481 uint8_t *utfname
= NULL
;
483 mach_port_t bp
, port
;
486 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
490 __CFSpinLock(&__CFAllMessagePortsLock
);
491 if (!perPID
&& NULL
!= name
) {
492 CFMessagePortRef existing
;
493 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
495 __CFSpinUnlock(&__CFAllMessagePortsLock
);
497 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
498 return (CFMessagePortRef
)(existing
);
501 __CFSpinUnlock(&__CFAllMessagePortsLock
);
502 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
503 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
504 if (NULL
== memory
) {
508 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
511 __CFMessagePortUnsetValid(memory
);
512 __CFMessagePortUnsetExtraMachRef(memory
);
513 __CFMessagePortSetRemote(memory
);
514 memory
->_lock
= CFSpinLockInit
;
515 memory
->_name
= name
;
516 memory
->_port
= NULL
;
517 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
518 memory
->_convCounter
= 0;
519 memory
->_perPID
= perPID
? pid
: 0;
520 memory
->_replyPort
= NULL
;
521 memory
->_source
= NULL
;
522 memory
->_dispatchSource
= NULL
;
523 memory
->_dispatchQ
= NULL
;
524 memory
->_icallout
= NULL
;
525 memory
->_callout
= NULL
;
530 ctx
.copyDescription
= NULL
;
531 task_get_bootstrap_port(mach_task_self(), &bp
);
532 ret
= bootstrap_look_up2(bp
, (char *)utfname
, &port
, perPID
? (pid_t
)pid
: 0, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
533 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
534 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
535 if (NULL
== native
) {
536 // name is released by deallocation
540 memory
->_port
= native
;
541 __CFMessagePortSetValid(memory
);
542 __CFSpinLock(&__CFAllMessagePortsLock
);
543 if (!perPID
&& NULL
!= name
) {
544 CFMessagePortRef existing
;
545 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
547 __CFSpinUnlock(&__CFAllMessagePortsLock
);
549 return (CFMessagePortRef
)(existing
);
551 if (NULL
== __CFAllRemoteMessagePorts
) {
552 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
554 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
556 __CFSpinUnlock(&__CFAllMessagePortsLock
);
557 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
558 // that set-invalidation-callback might have called back into us
559 // if the CFMachPort is already bad, but that was a no-op since
560 // there was no callback setup at the (previous) time the CFMachPort
561 // went invalid; so check for validity manually and react
562 if (!CFMachPortIsValid(native
)) {
563 CFRelease(memory
); // does the invalidate
566 return (CFMessagePortRef
)memory
;
569 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
570 return __CFMessagePortCreateRemote(allocator
, name
, false, 0);
573 CFMessagePortRef
CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator
, CFStringRef name
, CFIndex pid
) {
574 return __CFMessagePortCreateRemote(allocator
, name
, true, pid
);
577 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
578 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
579 return __CFMessagePortIsRemote(ms
);
582 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
583 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
587 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
588 CFAllocatorRef allocator
= CFGetAllocator(ms
);
589 uint8_t *utfname
= NULL
;
591 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
592 if (ms
->_perPID
|| __CFMessagePortIsRemote(ms
)) return false;
593 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
597 __CFSpinLock(&__CFAllMessagePortsLock
);
599 CFMessagePortRef existing
;
600 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
601 __CFSpinUnlock(&__CFAllMessagePortsLock
);
603 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
607 __CFSpinUnlock(&__CFAllMessagePortsLock
);
609 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
610 CFMachPortRef oldPort
= ms
->_port
;
611 CFMachPortRef native
= NULL
;
614 task_get_bootstrap_port(mach_task_self(), &bs
);
615 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
616 if (ret
== KERN_SUCCESS
) {
617 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
618 if (KERN_SUCCESS
== ret
) {
619 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
620 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
621 __CFMessagePortSetExtraMachRef(ms
);
623 mach_port_destroy(mach_task_self(), mp
);
624 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
630 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
631 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
633 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
637 mp
= CFMachPortGetPort(native
);
638 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, 0);
639 if (ret
!= KERN_SUCCESS
) {
640 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
);
641 CFMachPortInvalidate(native
);
643 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
648 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
650 if (NULL
!= oldPort
&& oldPort
!= native
) {
651 if (__CFMessagePortExtraMachRef(ms
)) {
652 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_SEND
, -1);
653 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_RECEIVE
, -1);
655 CFMachPortInvalidate(oldPort
);
658 __CFSpinLock(&__CFAllMessagePortsLock
);
659 // This relocking without checking to see if something else has grabbed
660 // that name in the cache is rather suspect, but what would that even
661 // mean has happened? We'd expect the bootstrap_* calls above to have
662 // failed for this one and not gotten this far, or failed for all of the
663 // other simultaneous attempts to get the name (and having succeeded for
664 // this one, gotten here). So we're not going to try very hard here
665 // with the thread-safety.
666 if (NULL
== __CFAllLocalMessagePorts
) {
667 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
669 if (NULL
!= ms
->_name
) {
670 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
671 CFRelease(ms
->_name
);
674 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
675 __CFSpinUnlock(&__CFAllMessagePortsLock
);
678 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
682 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
683 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
684 //#warning CF: assert that this is a local port
685 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
686 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
689 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
690 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
691 if (!__CFMessagePortIsDeallocing(ms
)) {
694 __CFMessagePortLock(ms
);
695 if (__CFMessagePortIsValid(ms
)) {
696 if (ms
->_dispatchSource
) {
697 dispatch_cancel(ms
->_dispatchSource
);
698 ms
->_dispatchSource
= NULL
;
699 ms
->_dispatchQ
= NULL
;
702 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
703 CFRunLoopSourceRef source
= ms
->_source
;
704 CFMachPortRef replyPort
= ms
->_replyPort
;
705 CFMachPortRef port
= ms
->_port
;
706 CFStringRef name
= ms
->_name
;
709 __CFMessagePortUnsetValid(ms
);
710 if (!__CFMessagePortIsRemote(ms
)) {
711 info
= ms
->_context
.info
;
712 ms
->_context
.info
= NULL
;
715 ms
->_replyPort
= NULL
;
716 __CFMessagePortUnlock(ms
);
718 __CFSpinLock(&__CFAllMessagePortsLock
);
719 if (0 == ms
->_perPID
&& NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
720 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
722 __CFSpinUnlock(&__CFAllMessagePortsLock
);
723 if (NULL
!= callout
) {
726 // We already know we're going invalid, don't need this callback
727 // anymore; plus, this solves a reentrancy deadlock; also, this
728 // must be done before the deallocate of the Mach port, to
729 // avoid a race between the notification message which could be
730 // handled in another thread, and this NULL'ing out.
731 CFMachPortSetInvalidationCallBack(port
, NULL
);
732 // For hashing and equality purposes, cannot get rid of _port here
733 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
734 ms
->_context
.release(info
);
736 if (NULL
!= source
) {
737 CFRunLoopSourceInvalidate(source
);
740 if (NULL
!= replyPort
) {
741 CFMachPortInvalidate(replyPort
);
742 CFRelease(replyPort
);
744 if (__CFMessagePortIsRemote(ms
)) {
745 // Get rid of our extra ref on the Mach port gotten from bs server
746 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
749 __CFMessagePortUnlock(ms
);
751 if (!__CFMessagePortIsDeallocing(ms
)) {
756 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
757 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
758 if (!__CFMessagePortIsValid(ms
)) return false;
759 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
760 CFMessagePortInvalidate(ms
);
763 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
764 CFMessagePortInvalidate(ms
);
767 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
768 CFMessagePortInvalidate(ms
);
774 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
775 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
776 return ms
->_icallout
;
779 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
780 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
781 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
782 callout(ms
, ms
->_context
.info
);
784 ms
->_icallout
= callout
;
788 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
789 CFMessagePortRef ms
= info
;
790 mach_msg_base_t
*msgp
= msg
;
791 mach_msg_base_t
*replymsg
;
792 __CFMessagePortLock(ms
);
793 if (!__CFMessagePortIsValid(ms
)) {
794 __CFMessagePortUnlock(ms
);
798 int32_t byteslen
= 0;
800 Boolean invalidMagic
= (MSGP_GET(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_GET(msgp
, magic
)) != MAGIC
);
801 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
802 invalidComplex
= invalidComplex
|| ((msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
803 Boolean wayTooBig
= ((msgp
->body
.msgh_descriptor_count
) ? sizeof(struct __CFMessagePortMachMessage1
) : sizeof(struct __CFMessagePortMachMessage0
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
;
804 Boolean wayTooSmall
= msgp
->header
.msgh_size
< sizeof(struct __CFMessagePortMachMessage0
);
805 Boolean wrongSize
= false;
806 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
807 byteslen
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, byteslen
));
808 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
809 if (0 != msgp
->body
.msgh_descriptor_count
) {
810 wrongSize
= wrongSize
|| (MSGP1_FIELD(msgp
, ool
).size
!= byteslen
);
812 wrongSize
= wrongSize
|| (msgp
->header
.msgh_size
- sizeof(struct __CFMessagePortMachMessage0
) < byteslen
);
815 Boolean invalidMsgID
= (0 <= msgp
->header
.msgh_id
) && (msgp
->header
.msgh_id
<= INT32_MAX
); // conversation id
816 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
817 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt reply Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
818 mach_msg_destroy((mach_msg_header_t
*)msgp
);
819 __CFMessagePortUnlock(ms
);
823 if (CFDictionaryContainsKey(ms
->_replies
, (void *)(uintptr_t)msgp
->header
.msgh_id
)) {
824 CFDataRef reply
= NULL
;
825 replymsg
= (mach_msg_base_t
*)msg
;
826 if (0 == replymsg
->body
.msgh_descriptor_count
) {
827 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
828 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP0_FIELD(replymsg
, bytes
)) + byteslen
);
829 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
830 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP0_FIELD(replymsg
, bytes
), byteslen
);
832 reply
= (void *)~0; // means NULL data
835 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
836 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP1_FIELD(replymsg
, ool
).address
, MSGP1_FIELD(replymsg
, ool
).size
);
837 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(replymsg
, ool
).address
, MSGP1_FIELD(replymsg
, ool
).size
);
839 CFDictionarySetValue(ms
->_replies
, (void *)(uintptr_t)msgp
->header
.msgh_id
, (void *)reply
);
840 } else { /* discard message */
841 if (1 == msgp
->body
.msgh_descriptor_count
) {
842 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
);
845 __CFMessagePortUnlock(ms
);
848 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
849 mach_msg_base_t
*sendmsg
;
850 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
851 CFRunLoopSourceRef source
= NULL
;
852 CFDataRef reply
= NULL
;
854 uint32_t sendOpts
= 0, sendTimeOut
= 0;
855 int32_t desiredReply
;
856 Boolean didRegister
= false;
859 //#warning CF: This should be an assert
860 // if (!__CFMessagePortIsRemote(remote)) return -999;
861 if (data
&& __CFMessagePortMaxDataSize
< CFDataGetLength(data
)) {
862 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSendRequest: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
863 return kCFMessagePortTransportError
;
865 __CFMessagePortLock(remote
);
866 if (!__CFMessagePortIsValid(remote
)) {
867 __CFMessagePortUnlock(remote
);
868 return kCFMessagePortIsInvalid
;
870 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
871 if (NULL
== remote
->_replyPort
) {
872 CFMachPortContext context
;
874 context
.info
= remote
;
875 context
.retain
= (const void *(*)(const void *))CFRetain
;
876 context
.release
= (void (*)(const void *))CFRelease
;
877 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
878 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
880 remote
->_convCounter
++;
881 desiredReply
= -remote
->_convCounter
;
882 sendmsg
= __CFMessagePortCreateMessage(false, CFMachPortGetPort(remote
->_port
), (replyMode
!= NULL
? CFMachPortGetPort(remote
->_replyPort
) : MACH_PORT_NULL
), -desiredReply
, msgid
, (data
? CFDataGetBytePtr(data
) : NULL
), (data
? CFDataGetLength(data
) : 0));
884 __CFMessagePortUnlock(remote
);
886 return kCFMessagePortTransportError
;
888 if (replyMode
!= NULL
) {
889 CFDictionarySetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
, NULL
);
890 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
891 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
893 CFRunLoopAddSource(currentRL
, source
, replyMode
);
896 if (sendTimeout
< 10.0*86400) {
897 // anything more than 10 days is no timeout!
898 sendOpts
= MACH_SEND_TIMEOUT
;
899 sendTimeout
*= 1000.0;
900 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
901 sendTimeOut
= floor(sendTimeout
);
903 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->header
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
904 if (KERN_SUCCESS
!= ret
) {
905 // need to deallocate the send-once right that might have been created
906 if (replyMode
!= NULL
) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t
*)sendmsg
)->msgh_local_port
);
908 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
910 if (source
) CFRelease(source
);
911 __CFMessagePortUnlock(remote
);
912 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
914 return (MACH_SEND_TIMED_OUT
== ret
) ? kCFMessagePortSendTimeout
: kCFMessagePortTransportError
;
916 __CFMessagePortUnlock(remote
);
917 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
918 if (replyMode
== NULL
) {
920 return kCFMessagePortSuccess
;
922 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
923 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
925 CFRunLoopRunInMode(replyMode
, __CFTSRToTimeInterval(termTSR
- mach_absolute_time()), true);
926 // warning: what, if anything, should be done if remote is now invalid?
927 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
928 if (NULL
!= reply
|| termTSR
< (int64_t)mach_absolute_time()) {
931 if (!CFMessagePortIsValid(remote
)) {
932 // no reason that reply port alone should go invalid so we don't check for that
936 // Should we uninstall the notify port? A complex question...
938 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
940 if (source
) CFRelease(source
);
942 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
944 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: kCFMessagePortBecameInvalidError
;
946 if (NULL
!= returnDatap
) {
947 *returnDatap
= ((void *)~0 == reply
) ? NULL
: reply
;
948 } else if ((void *)~0 != reply
) {
951 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
953 return kCFMessagePortSuccess
;
956 static mach_port_t
__CFMessagePortGetPort(void *info
) {
957 CFMessagePortRef ms
= info
;
958 if (!ms
->_port
) 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
);
959 return ms
->_port
? CFMachPortGetPort(ms
->_port
) : MACH_PORT_NULL
;
962 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
963 CFMessagePortRef ms
= info
;
964 mach_msg_base_t
*msgp
= msg
;
965 mach_msg_base_t
*replymsg
;
967 void (*context_release
)(const void *);
968 CFDataRef returnData
, data
= NULL
;
969 void *return_bytes
= NULL
;
970 CFIndex return_len
= 0;
973 __CFMessagePortLock(ms
);
974 if (!__CFMessagePortIsValid(ms
)) {
975 __CFMessagePortUnlock(ms
);
978 if (NULL
!= ms
->_context
.retain
) {
979 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
980 context_release
= ms
->_context
.release
;
982 context_info
= ms
->_context
.info
;
983 context_release
= NULL
;
985 __CFMessagePortUnlock(ms
);
987 int32_t byteslen
= 0;
989 Boolean invalidMagic
= (MSGP_GET(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_GET(msgp
, magic
)) != MAGIC
);
990 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
991 invalidComplex
= invalidComplex
|| ((msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
992 Boolean wayTooBig
= ((msgp
->body
.msgh_descriptor_count
) ? sizeof(struct __CFMessagePortMachMessage1
) : sizeof(struct __CFMessagePortMachMessage0
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
;
993 Boolean wayTooSmall
= msgp
->header
.msgh_size
< sizeof(struct __CFMessagePortMachMessage0
);
994 Boolean wrongSize
= false;
995 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
996 byteslen
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, byteslen
));
997 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
998 if (0 != msgp
->body
.msgh_descriptor_count
) {
999 wrongSize
= wrongSize
|| (MSGP1_FIELD(msgp
, ool
).size
!= byteslen
);
1001 wrongSize
= wrongSize
|| (msgp
->header
.msgh_size
- sizeof(struct __CFMessagePortMachMessage0
) < byteslen
);
1004 Boolean invalidMsgID
= (msgp
->header
.msgh_id
<= 0) || (INT32_MAX
< msgp
->header
.msgh_id
); // conversation id
1005 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
1006 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
1007 mach_msg_destroy((mach_msg_header_t
*)msgp
);
1011 /* Create no-copy, no-free-bytes wrapper CFData */
1012 if (0 == msgp
->body
.msgh_descriptor_count
) {
1013 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
1014 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP0_FIELD(msgp
, bytes
)) + byteslen
);
1015 msgid
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, msgid
));
1016 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
1017 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP0_FIELD(msgp
, bytes
), byteslen
, kCFAllocatorNull
);
1020 msgid
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, msgid
));
1021 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
, kCFAllocatorNull
);
1023 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
1024 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
1025 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
1026 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
1027 bytes inline in the Mach message, so can release the returnData object
1028 here. In cases (3) and (5), we'll send the data out-of-line, we need to
1029 create a copy of the memory, which we'll have the kernel autodeallocate
1030 for us on send. In case (4) also, the bytes in the return data may be part
1031 of the bytes in "data" that we sent into the callout, so if the incoming
1032 data was received out of line, we wouldn't be able to clean up the out-of-line
1033 wad until the message was sent either, if we didn't make the copy. */
1034 if (NULL
!= returnData
) {
1035 return_len
= CFDataGetLength(returnData
);
1036 if (__CFMessagePortMaxDataSize
< return_len
) {
1037 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
1041 if (returnData
&& return_len
< __CFMessagePortMaxInlineBytes
) {
1042 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
1043 } else if (returnData
) {
1044 return_bytes
= NULL
;
1045 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
1046 /* vm_copy would only be a win here if the source address
1047 is page aligned; it is a lose in all other cases, since
1048 the kernel will just do the memmove for us (but not in
1049 as simple a way). */
1050 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
1053 replymsg
= __CFMessagePortCreateMessage(true, msgp
->header
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)msgp
->header
.msgh_id
, msgid
, return_bytes
, return_len
);
1054 if (1 == replymsg
->body
.msgh_descriptor_count
) {
1055 MSGP1_FIELD(replymsg
, ool
).deallocate
= true;
1057 if (data
) CFRelease(data
);
1058 if (1 == msgp
->body
.msgh_descriptor_count
) {
1059 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
);
1061 if (returnData
) CFRelease(returnData
);
1062 if (context_release
) {
1063 context_release(context_info
);
1068 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
1069 CFRunLoopSourceRef result
= NULL
;
1070 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1071 //#warning CF: This should be an assert
1072 // if (__CFMessagePortIsRemote(ms)) return NULL;
1073 __CFMessagePortLock(ms
);
1074 if (NULL
== ms
->_source
&& NULL
== ms
->_dispatchSource
&& __CFMessagePortIsValid(ms
)) {
1075 CFRunLoopSourceContext1 context
;
1076 context
.version
= 1;
1077 context
.info
= (void *)ms
;
1078 context
.retain
= (const void *(*)(const void *))CFRetain
;
1079 context
.release
= (void (*)(const void *))CFRelease
;
1080 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
1081 context
.equal
= NULL
;
1082 context
.hash
= NULL
;
1083 context
.getPort
= __CFMessagePortGetPort
;
1084 context
.perform
= __CFMessagePortPerform
;
1085 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
1087 if (NULL
!= ms
->_source
) {
1088 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
1090 __CFMessagePortUnlock(ms
);
1094 void CFMessagePortSetDispatchQueue(CFMessagePortRef ms
, dispatch_queue_t queue
) {
1095 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1096 __CFMessagePortLock(ms
);
1097 if (!__CFMessagePortIsValid(ms
)) {
1098 __CFMessagePortUnlock(ms
);
1099 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid"));
1102 if (__CFMessagePortIsRemote(ms
)) {
1103 __CFMessagePortUnlock(ms
);
1104 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set"));
1107 if (NULL
!= ms
->_source
) {
1108 __CFMessagePortUnlock(ms
);
1109 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set"));
1113 if (ms
->_dispatchSource
) {
1114 dispatch_cancel(ms
->_dispatchSource
);
1115 ms
->_dispatchSource
= NULL
;
1116 ms
->_dispatchQ
= NULL
;
1120 mach_port_t port
= __CFMessagePortGetPort(ms
);
1121 if (MACH_PORT_NULL
!= port
) {
1122 ms
->_dispatchSource
= dispatch_source_machport_create(port
, DISPATCH_MACHPORT_RECV
, DISPATCH_SOURCE_CREATE_SUSPENDED
, __mportQueue(), ^(dispatch_source_t source
) {
1123 long e
= 0, d
= dispatch_source_get_error(source
, &e
);
1124 if (DISPATCH_ERROR_DOMAIN_POSIX
== d
&& ECANCELED
== e
) {
1125 dispatch_release(queue
);
1126 dispatch_release(source
);
1129 if (DISPATCH_ERROR_DOMAIN_NO_ERROR
!= d
) {
1134 mach_port_t port
= dispatch_source_get_handle(source
);
1135 mach_msg_header_t
*msg
= (mach_msg_header_t
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2048, 0);
1136 msg
->msgh_size
= 2048;
1140 msg
->msgh_local_port
= port
;
1141 msg
->msgh_remote_port
= MACH_PORT_NULL
;
1144 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
);
1145 if (MACH_MSG_SUCCESS
== ret
) break;
1146 if (MACH_RCV_TOO_LARGE
!= ret
) HALT
;
1148 uint32_t newSize
= round_msg(msg
->msgh_size
+ MAX_TRAILER_SIZE
);
1149 msg
= CFAllocatorReallocate(kCFAllocatorSystemDefault
, msg
, newSize
, 0);
1150 msg
->msgh_size
= newSize
;
1153 dispatch_async(queue
, ^{
1154 mach_msg_header_t
*reply
= __CFMessagePortPerform(msg
, msg
->msgh_size
, kCFAllocatorSystemDefault
, ms
);
1155 if (NULL
!= reply
) {
1156 kern_return_t ret
= mach_msg(reply
, MACH_SEND_MSG
, reply
->msgh_size
, 0, MACH_PORT_NULL
, 0, MACH_PORT_NULL
);
1157 if (KERN_SUCCESS
!= ret
) mach_msg_destroy(reply
);
1158 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, reply
);
1160 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, msg
);
1165 if (ms
->_dispatchSource
) {
1166 dispatch_retain(queue
);
1167 ms
->_dispatchQ
= queue
;
1168 dispatch_resume(ms
->_dispatchSource
);
1170 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created"));
1173 __CFMessagePortUnlock(ms
);