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-2010, 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 if (!CFMessagePortIsValid(existing
)) { // must do this outside lock to avoid deadlock
367 return (CFMessagePortRef
)(existing
);
370 __CFSpinUnlock(&__CFAllMessagePortsLock
);
371 CFIndex size
= sizeof(struct __CFMessagePort
) - sizeof(CFRuntimeBase
);
372 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
373 if (NULL
== memory
) {
377 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
380 __CFMessagePortUnsetValid(memory
);
381 __CFMessagePortUnsetExtraMachRef(memory
);
382 __CFMessagePortUnsetRemote(memory
);
383 memory
->_lock
= CFSpinLockInit
;
384 memory
->_name
= name
;
385 memory
->_port
= NULL
;
386 memory
->_replies
= NULL
;
387 memory
->_convCounter
= 0;
388 memory
->_perPID
= perPID
? getpid() : 0; // actual value not terribly useful for local ports
389 memory
->_replyPort
= NULL
;
390 memory
->_source
= NULL
;
391 memory
->_dispatchSource
= NULL
;
392 memory
->_dispatchQ
= NULL
;
393 memory
->_icallout
= NULL
;
394 memory
->_callout
= callout
;
395 memory
->_context
.info
= NULL
;
396 memory
->_context
.retain
= NULL
;
397 memory
->_context
.release
= NULL
;
398 memory
->_context
.copyDescription
= NULL
;
401 CFMachPortRef native
= NULL
;
404 task_get_bootstrap_port(mach_task_self(), &bs
);
406 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
407 if (ret
== KERN_SUCCESS
) {
408 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
409 if (KERN_SUCCESS
== ret
) {
410 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
411 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
412 __CFMessagePortSetExtraMachRef(memory
);
414 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
);
415 mach_port_destroy(mach_task_self(), mp
);
416 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
417 // name is released by deallocation
424 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
425 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
427 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
428 // name is released by deallocation
432 mp
= CFMachPortGetPort(native
);
433 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
434 if (ret
!= KERN_SUCCESS
) {
435 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
);
436 CFMachPortInvalidate(native
);
438 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
439 // name is released by deallocation
444 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
445 memory
->_port
= native
;
448 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
449 __CFMessagePortSetValid(memory
);
450 if (NULL
!= context
) {
451 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
452 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
454 __CFSpinLock(&__CFAllMessagePortsLock
);
455 if (!perPID
&& NULL
!= name
) {
456 CFMessagePortRef existing
;
457 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
459 __CFSpinUnlock(&__CFAllMessagePortsLock
);
461 return (CFMessagePortRef
)(existing
);
463 if (NULL
== __CFAllLocalMessagePorts
) {
464 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
466 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
468 __CFSpinUnlock(&__CFAllMessagePortsLock
);
469 if (shouldFreeInfo
) *shouldFreeInfo
= false;
473 CFMessagePortRef
CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
474 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, false);
477 CFMessagePortRef
CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
478 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, true);
481 static CFMessagePortRef
__CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, CFIndex pid
) {
482 CFMessagePortRef memory
;
483 CFMachPortRef native
;
484 CFMachPortContext ctx
;
485 uint8_t *utfname
= NULL
;
487 mach_port_t bp
, port
;
490 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
494 __CFSpinLock(&__CFAllMessagePortsLock
);
495 if (!perPID
&& NULL
!= name
) {
496 CFMessagePortRef existing
;
497 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
499 __CFSpinUnlock(&__CFAllMessagePortsLock
);
501 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
502 if (!CFMessagePortIsValid(existing
)) { // must do this outside lock to avoid deadlock
506 return (CFMessagePortRef
)(existing
);
509 __CFSpinUnlock(&__CFAllMessagePortsLock
);
510 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
511 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
512 if (NULL
== memory
) {
516 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
519 __CFMessagePortUnsetValid(memory
);
520 __CFMessagePortUnsetExtraMachRef(memory
);
521 __CFMessagePortSetRemote(memory
);
522 memory
->_lock
= CFSpinLockInit
;
523 memory
->_name
= name
;
524 memory
->_port
= NULL
;
525 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
526 memory
->_convCounter
= 0;
527 memory
->_perPID
= perPID
? pid
: 0;
528 memory
->_replyPort
= NULL
;
529 memory
->_source
= NULL
;
530 memory
->_dispatchSource
= NULL
;
531 memory
->_dispatchQ
= NULL
;
532 memory
->_icallout
= NULL
;
533 memory
->_callout
= NULL
;
538 ctx
.copyDescription
= NULL
;
539 task_get_bootstrap_port(mach_task_self(), &bp
);
540 ret
= bootstrap_look_up2(bp
, (char *)utfname
, &port
, perPID
? (pid_t
)pid
: 0, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
541 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
542 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
543 if (NULL
== native
) {
544 // name is released by deallocation
548 memory
->_port
= native
;
549 __CFMessagePortSetValid(memory
);
550 __CFSpinLock(&__CFAllMessagePortsLock
);
551 if (!perPID
&& NULL
!= name
) {
552 CFMessagePortRef existing
;
553 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
555 __CFSpinUnlock(&__CFAllMessagePortsLock
);
557 return (CFMessagePortRef
)(existing
);
559 if (NULL
== __CFAllRemoteMessagePorts
) {
560 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
562 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
564 __CFSpinUnlock(&__CFAllMessagePortsLock
);
565 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
566 // that set-invalidation-callback might have called back into us
567 // if the CFMachPort is already bad, but that was a no-op since
568 // there was no callback setup at the (previous) time the CFMachPort
569 // went invalid; so check for validity manually and react
570 if (!CFMachPortIsValid(native
)) {
571 CFRelease(memory
); // does the invalidate
574 return (CFMessagePortRef
)memory
;
577 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
578 return __CFMessagePortCreateRemote(allocator
, name
, false, 0);
581 CFMessagePortRef
CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator
, CFStringRef name
, CFIndex pid
) {
582 return __CFMessagePortCreateRemote(allocator
, name
, true, pid
);
585 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
586 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
587 return __CFMessagePortIsRemote(ms
);
590 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
591 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
595 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
596 CFAllocatorRef allocator
= CFGetAllocator(ms
);
597 uint8_t *utfname
= NULL
;
599 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
600 if (ms
->_perPID
|| __CFMessagePortIsRemote(ms
)) return false;
601 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
605 __CFSpinLock(&__CFAllMessagePortsLock
);
607 CFMessagePortRef existing
;
608 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
609 __CFSpinUnlock(&__CFAllMessagePortsLock
);
611 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
615 __CFSpinUnlock(&__CFAllMessagePortsLock
);
617 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
618 CFMachPortRef oldPort
= ms
->_port
;
619 CFMachPortRef native
= NULL
;
622 task_get_bootstrap_port(mach_task_self(), &bs
);
623 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
624 if (ret
== KERN_SUCCESS
) {
625 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
626 if (KERN_SUCCESS
== ret
) {
627 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
628 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
629 __CFMessagePortSetExtraMachRef(ms
);
631 mach_port_destroy(mach_task_self(), mp
);
632 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
638 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
639 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
641 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
645 mp
= CFMachPortGetPort(native
);
646 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, 0);
647 if (ret
!= KERN_SUCCESS
) {
648 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
);
649 CFMachPortInvalidate(native
);
651 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
656 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
658 if (NULL
!= oldPort
&& oldPort
!= native
) {
659 if (__CFMessagePortExtraMachRef(ms
)) {
660 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_SEND
, -1);
661 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_RECEIVE
, -1);
663 CFMachPortInvalidate(oldPort
);
666 __CFSpinLock(&__CFAllMessagePortsLock
);
667 // This relocking without checking to see if something else has grabbed
668 // that name in the cache is rather suspect, but what would that even
669 // mean has happened? We'd expect the bootstrap_* calls above to have
670 // failed for this one and not gotten this far, or failed for all of the
671 // other simultaneous attempts to get the name (and having succeeded for
672 // this one, gotten here). So we're not going to try very hard here
673 // with the thread-safety.
674 if (NULL
== __CFAllLocalMessagePorts
) {
675 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
677 if (NULL
!= ms
->_name
) {
678 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
679 CFRelease(ms
->_name
);
682 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
683 __CFSpinUnlock(&__CFAllMessagePortsLock
);
686 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
690 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
691 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
692 //#warning CF: assert that this is a local port
693 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
694 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
697 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
698 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
699 if (!__CFMessagePortIsDeallocing(ms
)) {
702 __CFMessagePortLock(ms
);
703 if (__CFMessagePortIsValid(ms
)) {
704 if (ms
->_dispatchSource
) {
705 dispatch_source_cancel(ms
->_dispatchSource
);
706 ms
->_dispatchSource
= NULL
;
707 ms
->_dispatchQ
= NULL
;
710 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
711 CFRunLoopSourceRef source
= ms
->_source
;
712 CFMachPortRef replyPort
= ms
->_replyPort
;
713 CFMachPortRef port
= ms
->_port
;
714 CFStringRef name
= ms
->_name
;
717 __CFMessagePortUnsetValid(ms
);
718 if (!__CFMessagePortIsRemote(ms
)) {
719 info
= ms
->_context
.info
;
720 ms
->_context
.info
= NULL
;
723 ms
->_replyPort
= NULL
;
724 __CFMessagePortUnlock(ms
);
726 __CFSpinLock(&__CFAllMessagePortsLock
);
727 if (0 == ms
->_perPID
&& NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
728 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
730 __CFSpinUnlock(&__CFAllMessagePortsLock
);
731 if (NULL
!= callout
) {
734 // We already know we're going invalid, don't need this callback
735 // anymore; plus, this solves a reentrancy deadlock; also, this
736 // must be done before the deallocate of the Mach port, to
737 // avoid a race between the notification message which could be
738 // handled in another thread, and this NULL'ing out.
739 CFMachPortSetInvalidationCallBack(port
, NULL
);
740 // For hashing and equality purposes, cannot get rid of _port here
741 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
742 ms
->_context
.release(info
);
744 if (NULL
!= source
) {
745 CFRunLoopSourceInvalidate(source
);
748 if (NULL
!= replyPort
) {
749 CFMachPortInvalidate(replyPort
);
750 CFRelease(replyPort
);
752 if (__CFMessagePortIsRemote(ms
)) {
753 // Get rid of our extra ref on the Mach port gotten from bs server
754 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
757 __CFMessagePortUnlock(ms
);
759 if (!__CFMessagePortIsDeallocing(ms
)) {
764 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
765 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
766 if (!__CFMessagePortIsValid(ms
)) return false;
767 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
768 CFMessagePortInvalidate(ms
);
771 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
772 CFMessagePortInvalidate(ms
);
778 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
779 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
780 return ms
->_icallout
;
783 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
784 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
785 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
786 callout(ms
, ms
->_context
.info
);
788 ms
->_icallout
= callout
;
792 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
793 CFMessagePortRef ms
= info
;
794 mach_msg_base_t
*msgp
= msg
;
795 mach_msg_base_t
*replymsg
;
796 __CFMessagePortLock(ms
);
797 if (!__CFMessagePortIsValid(ms
)) {
798 __CFMessagePortUnlock(ms
);
802 int32_t byteslen
= 0;
804 Boolean invalidMagic
= (MSGP_GET(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_GET(msgp
, magic
)) != MAGIC
);
805 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
806 invalidComplex
= invalidComplex
|| ((msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
807 Boolean wayTooBig
= ((msgp
->body
.msgh_descriptor_count
) ? sizeof(struct __CFMessagePortMachMessage1
) : sizeof(struct __CFMessagePortMachMessage0
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
;
808 Boolean wayTooSmall
= msgp
->header
.msgh_size
< sizeof(struct __CFMessagePortMachMessage0
);
809 Boolean wrongSize
= false;
810 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
811 byteslen
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, byteslen
));
812 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
813 if (0 != msgp
->body
.msgh_descriptor_count
) {
814 wrongSize
= wrongSize
|| (MSGP1_FIELD(msgp
, ool
).size
!= byteslen
);
816 wrongSize
= wrongSize
|| (msgp
->header
.msgh_size
- sizeof(struct __CFMessagePortMachMessage0
) < byteslen
);
819 Boolean invalidMsgID
= (0 <= msgp
->header
.msgh_id
) && (msgp
->header
.msgh_id
<= INT32_MAX
); // conversation id
820 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
821 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt reply Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
822 mach_msg_destroy((mach_msg_header_t
*)msgp
);
823 __CFMessagePortUnlock(ms
);
827 if (CFDictionaryContainsKey(ms
->_replies
, (void *)(uintptr_t)msgp
->header
.msgh_id
)) {
828 CFDataRef reply
= NULL
;
829 replymsg
= (mach_msg_base_t
*)msg
;
830 if (0 == replymsg
->body
.msgh_descriptor_count
) {
831 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
832 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP0_FIELD(replymsg
, bytes
)) + byteslen
);
833 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
834 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP0_FIELD(replymsg
, bytes
), byteslen
);
836 reply
= (void *)~0; // means NULL data
839 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
840 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP1_FIELD(replymsg
, ool
).address
, MSGP1_FIELD(replymsg
, ool
).size
);
841 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(replymsg
, ool
).address
, MSGP1_FIELD(replymsg
, ool
).size
);
843 CFDictionarySetValue(ms
->_replies
, (void *)(uintptr_t)msgp
->header
.msgh_id
, (void *)reply
);
844 } else { /* discard message */
845 if (1 == msgp
->body
.msgh_descriptor_count
) {
846 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
);
849 __CFMessagePortUnlock(ms
);
852 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
853 mach_msg_base_t
*sendmsg
;
854 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
855 CFRunLoopSourceRef source
= NULL
;
856 CFDataRef reply
= NULL
;
858 uint32_t sendOpts
= 0, sendTimeOut
= 0;
859 int32_t desiredReply
;
860 Boolean didRegister
= false;
863 //#warning CF: This should be an assert
864 // if (!__CFMessagePortIsRemote(remote)) return -999;
865 if (data
&& __CFMessagePortMaxDataSize
< CFDataGetLength(data
)) {
866 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSendRequest: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
867 return kCFMessagePortTransportError
;
869 __CFMessagePortLock(remote
);
870 if (!__CFMessagePortIsValid(remote
)) {
871 __CFMessagePortUnlock(remote
);
872 return kCFMessagePortIsInvalid
;
874 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
875 if (NULL
== remote
->_replyPort
) {
876 CFMachPortContext context
;
878 context
.info
= remote
;
879 context
.retain
= (const void *(*)(const void *))CFRetain
;
880 context
.release
= (void (*)(const void *))CFRelease
;
881 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
882 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
884 remote
->_convCounter
++;
885 desiredReply
= -remote
->_convCounter
;
886 sendmsg
= __CFMessagePortCreateMessage(false, CFMachPortGetPort(remote
->_port
), (replyMode
!= NULL
? CFMachPortGetPort(remote
->_replyPort
) : MACH_PORT_NULL
), -desiredReply
, msgid
, (data
? CFDataGetBytePtr(data
) : NULL
), (data
? CFDataGetLength(data
) : 0));
888 __CFMessagePortUnlock(remote
);
890 return kCFMessagePortTransportError
;
892 if (replyMode
!= NULL
) {
893 CFDictionarySetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
, NULL
);
894 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
895 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
897 CFRunLoopAddSource(currentRL
, source
, replyMode
);
900 if (sendTimeout
< 10.0*86400) {
901 // anything more than 10 days is no timeout!
902 sendOpts
= MACH_SEND_TIMEOUT
;
903 sendTimeout
*= 1000.0;
904 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
905 sendTimeOut
= floor(sendTimeout
);
907 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->header
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
908 if (KERN_SUCCESS
!= ret
) {
909 // need to deallocate the send-once right that might have been created
910 if (replyMode
!= NULL
) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t
*)sendmsg
)->msgh_local_port
);
912 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
914 if (source
) CFRelease(source
);
915 __CFMessagePortUnlock(remote
);
916 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
918 return (MACH_SEND_TIMED_OUT
== ret
) ? kCFMessagePortSendTimeout
: kCFMessagePortTransportError
;
920 __CFMessagePortUnlock(remote
);
921 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
922 if (replyMode
== NULL
) {
924 return kCFMessagePortSuccess
;
926 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
927 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
929 CFRunLoopRunInMode(replyMode
, __CFTSRToTimeInterval(termTSR
- mach_absolute_time()), true);
930 // warning: what, if anything, should be done if remote is now invalid?
931 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
932 if (NULL
!= reply
|| termTSR
< (int64_t)mach_absolute_time()) {
935 if (!CFMessagePortIsValid(remote
)) {
936 // no reason that reply port alone should go invalid so we don't check for that
940 // Should we uninstall the notify port? A complex question...
942 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
944 if (source
) CFRelease(source
);
946 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
948 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: kCFMessagePortBecameInvalidError
;
950 if (NULL
!= returnDatap
) {
951 *returnDatap
= ((void *)~0 == reply
) ? NULL
: reply
;
952 } else if ((void *)~0 != reply
) {
955 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
957 return kCFMessagePortSuccess
;
960 static mach_port_t
__CFMessagePortGetPort(void *info
) {
961 CFMessagePortRef ms
= info
;
962 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
);
963 return ms
->_port
? CFMachPortGetPort(ms
->_port
) : MACH_PORT_NULL
;
966 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
967 CFMessagePortRef ms
= info
;
968 mach_msg_base_t
*msgp
= msg
;
969 mach_msg_base_t
*replymsg
;
971 void (*context_release
)(const void *);
972 CFDataRef returnData
, data
= NULL
;
973 void *return_bytes
= NULL
;
974 CFIndex return_len
= 0;
977 __CFMessagePortLock(ms
);
978 if (!__CFMessagePortIsValid(ms
)) {
979 __CFMessagePortUnlock(ms
);
982 if (NULL
!= ms
->_context
.retain
) {
983 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
984 context_release
= ms
->_context
.release
;
986 context_info
= ms
->_context
.info
;
987 context_release
= NULL
;
989 __CFMessagePortUnlock(ms
);
991 int32_t byteslen
= 0;
993 Boolean invalidMagic
= (MSGP_GET(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_GET(msgp
, magic
)) != MAGIC
);
994 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
995 invalidComplex
= invalidComplex
|| ((msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
996 Boolean wayTooBig
= ((msgp
->body
.msgh_descriptor_count
) ? sizeof(struct __CFMessagePortMachMessage1
) : sizeof(struct __CFMessagePortMachMessage0
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
;
997 Boolean wayTooSmall
= msgp
->header
.msgh_size
< sizeof(struct __CFMessagePortMachMessage0
);
998 Boolean wrongSize
= false;
999 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
1000 byteslen
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, byteslen
));
1001 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
1002 if (0 != msgp
->body
.msgh_descriptor_count
) {
1003 wrongSize
= wrongSize
|| (MSGP1_FIELD(msgp
, ool
).size
!= byteslen
);
1005 wrongSize
= wrongSize
|| (msgp
->header
.msgh_size
- sizeof(struct __CFMessagePortMachMessage0
) < byteslen
);
1008 Boolean invalidMsgID
= (msgp
->header
.msgh_id
<= 0) || (INT32_MAX
< msgp
->header
.msgh_id
); // conversation id
1009 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
1010 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
1011 mach_msg_destroy((mach_msg_header_t
*)msgp
);
1015 /* Create no-copy, no-free-bytes wrapper CFData */
1016 if (0 == msgp
->body
.msgh_descriptor_count
) {
1017 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
1018 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP0_FIELD(msgp
, bytes
)) + byteslen
);
1019 msgid
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, msgid
));
1020 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
1021 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP0_FIELD(msgp
, bytes
), byteslen
, kCFAllocatorNull
);
1024 msgid
= CFSwapInt32LittleToHost(MSGP_GET(msgp
, msgid
));
1025 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
, kCFAllocatorNull
);
1027 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
1028 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
1029 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
1030 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
1031 bytes inline in the Mach message, so can release the returnData object
1032 here. In cases (3) and (5), we'll send the data out-of-line, we need to
1033 create a copy of the memory, which we'll have the kernel autodeallocate
1034 for us on send. In case (4) also, the bytes in the return data may be part
1035 of the bytes in "data" that we sent into the callout, so if the incoming
1036 data was received out of line, we wouldn't be able to clean up the out-of-line
1037 wad until the message was sent either, if we didn't make the copy. */
1038 if (NULL
!= returnData
) {
1039 return_len
= CFDataGetLength(returnData
);
1040 if (__CFMessagePortMaxDataSize
< return_len
) {
1041 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
1045 if (returnData
&& return_len
< __CFMessagePortMaxInlineBytes
) {
1046 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
1047 } else if (returnData
) {
1048 return_bytes
= NULL
;
1049 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
1050 /* vm_copy would only be a win here if the source address
1051 is page aligned; it is a lose in all other cases, since
1052 the kernel will just do the memmove for us (but not in
1053 as simple a way). */
1054 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
1057 replymsg
= __CFMessagePortCreateMessage(true, msgp
->header
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)msgp
->header
.msgh_id
, msgid
, return_bytes
, return_len
);
1058 if (1 == replymsg
->body
.msgh_descriptor_count
) {
1059 MSGP1_FIELD(replymsg
, ool
).deallocate
= true;
1061 if (data
) CFRelease(data
);
1062 if (1 == msgp
->body
.msgh_descriptor_count
) {
1063 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP1_FIELD(msgp
, ool
).address
, MSGP1_FIELD(msgp
, ool
).size
);
1065 if (returnData
) CFRelease(returnData
);
1066 if (context_release
) {
1067 context_release(context_info
);
1072 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
1073 CFRunLoopSourceRef result
= NULL
;
1074 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1075 if (!CFMessagePortIsValid(ms
)) return NULL
;
1076 if (__CFMessagePortIsRemote(ms
)) return NULL
;
1077 __CFMessagePortLock(ms
);
1078 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
1079 CFRelease(ms
->_source
);
1082 if (NULL
== ms
->_source
&& NULL
== ms
->_dispatchSource
&& __CFMessagePortIsValid(ms
)) {
1083 CFRunLoopSourceContext1 context
;
1084 context
.version
= 1;
1085 context
.info
= (void *)ms
;
1086 context
.retain
= (const void *(*)(const void *))CFRetain
;
1087 context
.release
= (void (*)(const void *))CFRelease
;
1088 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
1089 context
.equal
= NULL
;
1090 context
.hash
= NULL
;
1091 context
.getPort
= __CFMessagePortGetPort
;
1092 context
.perform
= __CFMessagePortPerform
;
1093 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
1095 if (NULL
!= ms
->_source
) {
1096 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
1098 __CFMessagePortUnlock(ms
);
1102 void CFMessagePortSetDispatchQueue(CFMessagePortRef ms
, dispatch_queue_t queue
) {
1103 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1104 __CFMessagePortLock(ms
);
1105 if (!__CFMessagePortIsValid(ms
)) {
1106 __CFMessagePortUnlock(ms
);
1107 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid"));
1110 if (__CFMessagePortIsRemote(ms
)) {
1111 __CFMessagePortUnlock(ms
);
1112 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set"));
1115 if (NULL
!= ms
->_source
) {
1116 __CFMessagePortUnlock(ms
);
1117 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set"));
1121 if (ms
->_dispatchSource
) {
1122 dispatch_source_cancel(ms
->_dispatchSource
);
1123 ms
->_dispatchSource
= NULL
;
1124 ms
->_dispatchQ
= NULL
;
1128 mach_port_t port
= __CFMessagePortGetPort(ms
);
1129 if (MACH_PORT_NULL
!= port
) {
1130 dispatch_source_t theSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, port
, 0, __mportQueue());
1131 dispatch_source_set_cancel_handler(theSource
, ^{
1132 dispatch_release(queue
);
1133 dispatch_release(theSource
);
1135 dispatch_source_set_event_handler(theSource
, ^{
1137 mach_msg_header_t
*msg
= (mach_msg_header_t
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2048, 0);
1138 msg
->msgh_size
= 2048;
1142 msg
->msgh_local_port
= port
;
1143 msg
->msgh_remote_port
= MACH_PORT_NULL
;
1146 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
);
1147 if (MACH_MSG_SUCCESS
== ret
) break;
1148 if (MACH_RCV_TOO_LARGE
!= ret
) HALT
;
1150 uint32_t newSize
= round_msg(msg
->msgh_size
+ MAX_TRAILER_SIZE
);
1151 msg
= CFAllocatorReallocate(kCFAllocatorSystemDefault
, msg
, newSize
, 0);
1152 msg
->msgh_size
= newSize
;
1155 dispatch_async(queue
, ^{
1156 mach_msg_header_t
*reply
= __CFMessagePortPerform(msg
, msg
->msgh_size
, kCFAllocatorSystemDefault
, ms
);
1157 if (NULL
!= reply
) {
1158 kern_return_t ret
= mach_msg(reply
, MACH_SEND_MSG
, reply
->msgh_size
, 0, MACH_PORT_NULL
, 0, MACH_PORT_NULL
);
1159 if (KERN_SUCCESS
!= ret
) mach_msg_destroy(reply
);
1160 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, reply
);
1162 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, msg
);
1166 ms
->_dispatchSource
= theSource
;
1168 if (ms
->_dispatchSource
) {
1169 dispatch_retain(queue
);
1170 ms
->_dispatchQ
= queue
;
1171 dispatch_resume(ms
->_dispatchSource
);
1173 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created"));
1176 __CFMessagePortUnlock(ms
);