]> git.saurik.com Git - apple/cf.git/blob - RunLoop.subproj/CFMessagePort.c
932f3a80b5ac77eb66e8d52d1ee594d22555c52a
[apple/cf.git] / RunLoop.subproj / CFMessagePort.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /* CFMessagePort.c
24 Copyright 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
28 #if !defined(__WIN32__)
29
30 #include <CoreFoundation/CFMessagePort.h>
31 #include <CoreFoundation/CFRunLoop.h>
32 #include <CoreFoundation/CFMachPort.h>
33 #include <CoreFoundation/CFDictionary.h>
34 #include <CoreFoundation/CFByteOrder.h>
35 #include <limits.h>
36 #include "CFInternal.h"
37 #include <mach/mach.h>
38 #include <mach/message.h>
39 #include <mach/mach_error.h>
40 #include <servers/bootstrap.h>
41 #include <math.h>
42 #include <mach/mach_time.h>
43
44
45 #define __kCFMessagePortMaxNameLengthMax 255
46
47 #if defined(BOOTSTRAP_MAX_NAME_LEN)
48 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
49 #else
50 #define __kCFMessagePortMaxNameLength 128
51 #endif
52
53 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
54 #undef __kCFMessagePortMaxNameLength
55 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
56 #endif
57
58 static CFSpinLock_t __CFAllMessagePortsLock = 0;
59 static CFMutableDictionaryRef __CFAllLocalMessagePorts = NULL;
60 static CFMutableDictionaryRef __CFAllRemoteMessagePorts = NULL;
61
62 struct __CFMessagePort {
63 CFRuntimeBase _base;
64 CFSpinLock_t _lock;
65 CFStringRef _name;
66 CFMachPortRef _port; /* immutable; invalidated */
67 CFMutableDictionaryRef _replies;
68 int32_t _convCounter;
69 CFMachPortRef _replyPort; /* only used by remote port; immutable once created; invalidated */
70 CFRunLoopSourceRef _source; /* only used by local port; immutable once created; invalidated */
71 CFMessagePortInvalidationCallBack _icallout;
72 CFMessagePortCallBack _callout; /* only used by local port; immutable */
73 CFMessagePortContext _context; /* not part of remote port; immutable; invalidated */
74 };
75
76 /* Bit 0 in the base reserved bits is used for invalid state */
77 /* Bit 2 of the base reserved bits is used for is-remote state */
78 /* Bit 3 in the base reserved bits is used for is-deallocing state */
79
80 CF_INLINE Boolean __CFMessagePortIsValid(CFMessagePortRef ms) {
81 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_info, 0, 0);
82 }
83
84 CF_INLINE void __CFMessagePortSetValid(CFMessagePortRef ms) {
85 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 0, 0, 1);
86 }
87
88 CF_INLINE void __CFMessagePortUnsetValid(CFMessagePortRef ms) {
89 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 0, 0, 0);
90 }
91
92 CF_INLINE Boolean __CFMessagePortIsRemote(CFMessagePortRef ms) {
93 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_info, 2, 2);
94 }
95
96 CF_INLINE void __CFMessagePortSetRemote(CFMessagePortRef ms) {
97 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 2, 2, 1);
98 }
99
100 CF_INLINE void __CFMessagePortUnsetRemote(CFMessagePortRef ms) {
101 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 2, 2, 0);
102 }
103
104 CF_INLINE Boolean __CFMessagePortIsDeallocing(CFMessagePortRef ms) {
105 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_info, 3, 3);
106 }
107
108 CF_INLINE void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms) {
109 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 3, 3, 1);
110 }
111
112 CF_INLINE void __CFMessagePortLock(CFMessagePortRef ms) {
113 __CFSpinLock(&(ms->_lock));
114 }
115
116 CF_INLINE void __CFMessagePortUnlock(CFMessagePortRef ms) {
117 __CFSpinUnlock(&(ms->_lock));
118 }
119
120 // Just a heuristic
121 #define __CFMessagePortMaxInlineBytes 4096*10
122
123 struct __CFMessagePortMachMsg0 {
124 int32_t msgid;
125 int32_t byteslen;
126 uint8_t bytes[__CFMessagePortMaxInlineBytes];
127 };
128
129 struct __CFMessagePortMachMsg1 {
130 mach_msg_descriptor_t desc;
131 int32_t msgid;
132 };
133
134 struct __CFMessagePortMachMessage {
135 mach_msg_header_t head;
136 mach_msg_body_t body;
137 union {
138 struct __CFMessagePortMachMsg0 msg0;
139 struct __CFMessagePortMachMsg1 msg1;
140 } contents;
141 };
142
143 static struct __CFMessagePortMachMessage *__CFMessagePortCreateMessage(CFAllocatorRef allocator, bool reply, mach_port_t port, mach_port_t replyPort, int32_t convid, int32_t msgid, const uint8_t *bytes, int32_t byteslen) {
144 struct __CFMessagePortMachMessage *msg;
145 int32_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
146 if (byteslen < __CFMessagePortMaxInlineBytes) {
147 size += 2 * sizeof(int32_t) + ((byteslen + 3) & ~0x3);
148 } else {
149 size += sizeof(struct __CFMessagePortMachMsg1);
150 }
151 msg = CFAllocatorAllocate(allocator, size, 0);
152 msg->head.msgh_id = convid;
153 msg->head.msgh_size = size;
154 msg->head.msgh_remote_port = port;
155 msg->head.msgh_local_port = replyPort;
156 msg->head.msgh_reserved = 0;
157 // msg->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort ? MACH_MSG_TYPE_MAKE_SEND : 0));
158 msg->head.msgh_bits = MACH_MSGH_BITS((reply ? MACH_MSG_TYPE_MOVE_SEND_ONCE : MACH_MSG_TYPE_COPY_SEND), (MACH_PORT_NULL != replyPort ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0));
159 if (byteslen < __CFMessagePortMaxInlineBytes) {
160 msg->body.msgh_descriptor_count = 0;
161 msg->contents.msg0.msgid = CFSwapInt32HostToLittle(msgid);
162 msg->contents.msg0.byteslen = CFSwapInt32HostToLittle(byteslen);
163 if (NULL != bytes && 0 < byteslen) {
164 memmove(msg->contents.msg0.bytes, bytes, byteslen);
165 }
166 memset(msg->contents.msg0.bytes + byteslen, 0, ((byteslen + 3) & ~0x3) - byteslen);
167 } else {
168 msg->head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
169 msg->body.msgh_descriptor_count = 1;
170 msg->contents.msg1.desc.out_of_line.deallocate = false;
171 msg->contents.msg1.desc.out_of_line.copy = MACH_MSG_VIRTUAL_COPY;
172 msg->contents.msg1.desc.out_of_line.address = (void *)bytes;
173 msg->contents.msg1.desc.out_of_line.size = byteslen;
174 msg->contents.msg1.desc.out_of_line.type = MACH_MSG_OOL_DESCRIPTOR;
175 msg->contents.msg1.msgid = CFSwapInt32HostToLittle(msgid);
176 }
177 return msg;
178 }
179
180 static Boolean __CFMessagePortNativeSetNameLocal(CFMachPortRef port, uint8_t *portname) {
181 mach_port_t bp;
182 kern_return_t ret;
183 task_get_bootstrap_port(mach_task_self(), &bp);
184 ret = bootstrap_register(bp, portname, CFMachPortGetPort(port));
185 if (ret != KERN_SUCCESS) CFLog(0, CFSTR("CFMessagePort: bootstrap_register(): failed %d (0x%x), port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret, ret, CFMachPortGetPort(port), portname);
186 return (ret == KERN_SUCCESS) ? true : false;
187 }
188
189 static Boolean __CFMessagePortEqual(CFTypeRef cf1, CFTypeRef cf2) {
190 CFMessagePortRef ms1 = (CFMessagePortRef)cf1;
191 CFMessagePortRef ms2 = (CFMessagePortRef)cf2;
192 return CFEqual(ms1->_port, ms2->_port);
193 }
194
195 static CFHashCode __CFMessagePortHash(CFTypeRef cf) {
196 CFMessagePortRef ms = (CFMessagePortRef)cf;
197 return CFHash(ms->_port);
198 }
199
200 static CFStringRef __CFMessagePortCopyDescription(CFTypeRef cf) {
201 CFMessagePortRef ms = (CFMessagePortRef)cf;
202 CFStringRef result;
203 const char *locked;
204 CFStringRef contextDesc = NULL;
205 locked = ms->_lock ? "Yes" : "No";
206 if (!__CFMessagePortIsRemote(ms)) {
207 if (NULL != ms->_context.info && NULL != ms->_context.copyDescription) {
208 contextDesc = ms->_context.copyDescription(ms->_context.info);
209 }
210 if (NULL == contextDesc) {
211 contextDesc = CFStringCreateWithFormat(CFGetAllocator(ms), NULL, CFSTR("<CFMessagePort context %p>"), ms->_context.info);
212 }
213 result = CFStringCreateWithFormat(CFGetAllocator(ms), NULL, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@}"), cf, CFGetAllocator(ms), locked, (__CFMessagePortIsValid(ms) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms) ? "Yes" : "No"), ms->_name);
214 } else {
215 result = CFStringCreateWithFormat(CFGetAllocator(ms), NULL, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@, source = %p, callout = %p, context = %@}"), cf, CFGetAllocator(ms), locked, (__CFMessagePortIsValid(ms) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms) ? "Yes" : "No"), ms->_name, ms->_source, ms->_callout, (NULL != contextDesc ? contextDesc : CFSTR("<no description>")));
216 }
217 if (NULL != contextDesc) {
218 CFRelease(contextDesc);
219 }
220 return result;
221 }
222
223 static void __CFMessagePortDeallocate(CFTypeRef cf) {
224 CFMessagePortRef ms = (CFMessagePortRef)cf;
225 __CFMessagePortSetIsDeallocing(ms);
226 CFMessagePortInvalidate(ms);
227 // Delay cleanup of _replies until here so that invalidation during
228 // SendRequest does not cause _replies to disappear out from under that function.
229 if (NULL != ms->_replies) {
230 CFRelease(ms->_replies);
231 }
232 if (NULL != ms->_name) {
233 CFRelease(ms->_name);
234 }
235 if (NULL != ms->_port) {
236 CFMachPortInvalidate(ms->_port);
237 CFRelease(ms->_port);
238 }
239 }
240
241 static CFTypeID __kCFMessagePortTypeID = _kCFRuntimeNotATypeID;
242
243 static const CFRuntimeClass __CFMessagePortClass = {
244 0,
245 "CFMessagePort",
246 NULL, // init
247 NULL, // copy
248 __CFMessagePortDeallocate,
249 __CFMessagePortEqual,
250 __CFMessagePortHash,
251 NULL, //
252 __CFMessagePortCopyDescription
253 };
254
255 __private_extern__ void __CFMessagePortInitialize(void) {
256 __kCFMessagePortTypeID = _CFRuntimeRegisterClass(&__CFMessagePortClass);
257 }
258
259 CFTypeID CFMessagePortGetTypeID(void) {
260 return __kCFMessagePortTypeID;
261 }
262
263 static CFStringRef __CFMessagePortSanitizeStringName(CFAllocatorRef allocator, CFStringRef name, uint8_t **utfnamep, CFIndex *utfnamelenp) {
264 uint8_t *utfname;
265 CFIndex utflen;
266 CFStringRef result;
267 utfname = CFAllocatorAllocate(allocator, __kCFMessagePortMaxNameLength + 1, 0);
268 CFStringGetBytes(name, CFRangeMake(0, CFStringGetLength(name)), kCFStringEncodingUTF8, 0, false, utfname, __kCFMessagePortMaxNameLength, &utflen);
269 utfname[utflen] = '\0';
270 /* A new string is created, because the original string may have been
271 truncated to the max length, and we want the string name to definitely
272 match the raw UTF-8 chunk that has been created. Also, this is useful
273 to get a constant string in case the original name string was mutable. */
274 result = CFStringCreateWithBytes(allocator, utfname, utflen, kCFStringEncodingUTF8, false);
275 if (NULL != utfnamep) {
276 *utfnamep = utfname;
277 } else {
278 CFAllocatorDeallocate(allocator, utfname);
279 }
280 if (NULL != utfnamelenp) {
281 *utfnamelenp = utflen;
282 }
283 return result;
284 }
285
286 static void __CFMessagePortDummyCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) {
287 // not supposed to be implemented
288 }
289
290 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port, void *info) {
291 // info has been setup as the CFMessagePort owning the CFMachPort
292 CFMessagePortInvalidate(info);
293 }
294
295 CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) {
296 CFMessagePortRef memory;
297 CFMachPortRef native;
298 CFMachPortContext ctx;
299 uint8_t *utfname = NULL;
300 CFIndex size;
301
302 if (shouldFreeInfo) *shouldFreeInfo = true;
303 if (NULL != name) {
304 name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL);
305 }
306 __CFSpinLock(&__CFAllMessagePortsLock);
307 if (NULL != name) {
308 CFMessagePortRef existing;
309 if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) {
310 __CFSpinUnlock(&__CFAllMessagePortsLock);
311 CFRelease(name);
312 CFAllocatorDeallocate(allocator, utfname);
313 return (CFMessagePortRef)CFRetain(existing);
314 }
315 }
316 size = sizeof(struct __CFMessagePort) - sizeof(CFRuntimeBase);
317 memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL);
318 if (NULL == memory) {
319 __CFSpinUnlock(&__CFAllMessagePortsLock);
320 if (NULL != name) {
321 CFRelease(name);
322 }
323 CFAllocatorDeallocate(allocator, utfname);
324 CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to allocate object"));
325 return NULL;
326 }
327 __CFMessagePortUnsetValid(memory);
328 __CFMessagePortUnsetRemote(memory);
329 memory->_lock = 0;
330 memory->_name = name;
331 memory->_port = NULL;
332 memory->_replies = NULL;
333 memory->_convCounter = 0;
334 memory->_replyPort = NULL;
335 memory->_source = NULL;
336 memory->_icallout = NULL;
337 memory->_callout = callout;
338 memory->_context.info = NULL;
339 memory->_context.retain = NULL;
340 memory->_context.release = NULL;
341 memory->_context.copyDescription = NULL;
342 ctx.version = 0;
343 ctx.info = memory;
344 ctx.retain = NULL;
345 ctx.release = NULL;
346 ctx.copyDescription = NULL;
347 native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL);
348 if (!native) CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to allocate CFMachPortRef"));
349 if (NULL != native && NULL != name && !__CFMessagePortNativeSetNameLocal(native, utfname)) {
350 CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to name Mach port (%@)"), name);
351 CFMachPortInvalidate(native);
352 CFRelease(native);
353 native = NULL;
354 }
355 CFAllocatorDeallocate(allocator, utfname);
356 if (NULL == native) {
357 __CFSpinUnlock(&__CFAllMessagePortsLock);
358 // name is released by deallocation
359 CFRelease(memory);
360 return NULL;
361 }
362 memory->_port = native;
363 CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack);
364 __CFMessagePortSetValid(memory);
365 if (NULL != context) {
366 memmove(&memory->_context, context, sizeof(CFMessagePortContext));
367 memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info;
368 }
369 if (NULL != name) {
370 if (NULL == __CFAllLocalMessagePorts) {
371 __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
372 }
373 CFDictionaryAddValue(__CFAllLocalMessagePorts, name, memory);
374 }
375 __CFSpinUnlock(&__CFAllMessagePortsLock);
376 if (shouldFreeInfo) *shouldFreeInfo = false;
377 return memory;
378 }
379
380 CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) {
381 CFMessagePortRef memory;
382 CFMachPortRef native;
383 CFMachPortContext ctx;
384 uint8_t *utfname = NULL;
385 CFIndex size;
386 mach_port_t bp, port;
387 kern_return_t ret;
388
389 name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL);
390 if (NULL == name) {
391 return NULL;
392 }
393 __CFSpinLock(&__CFAllMessagePortsLock);
394 if (NULL != name) {
395 CFMessagePortRef existing;
396 if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) {
397 __CFSpinUnlock(&__CFAllMessagePortsLock);
398 CFRelease(name);
399 CFAllocatorDeallocate(allocator, utfname);
400 return (CFMessagePortRef)CFRetain(existing);
401 }
402 }
403 size = sizeof(struct __CFMessagePort) - sizeof(CFMessagePortContext) - sizeof(CFRuntimeBase);
404 memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL);
405 if (NULL == memory) {
406 __CFSpinUnlock(&__CFAllMessagePortsLock);
407 if (NULL != name) {
408 CFRelease(name);
409 }
410 CFAllocatorDeallocate(allocator, utfname);
411 return NULL;
412 }
413 __CFMessagePortUnsetValid(memory);
414 __CFMessagePortSetRemote(memory);
415 memory->_lock = 0;
416 memory->_name = name;
417 memory->_port = NULL;
418 memory->_replies = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
419 memory->_convCounter = 0;
420 memory->_replyPort = NULL;
421 memory->_source = NULL;
422 memory->_icallout = NULL;
423 memory->_callout = NULL;
424 ctx.version = 0;
425 ctx.info = memory;
426 ctx.retain = NULL;
427 ctx.release = NULL;
428 ctx.copyDescription = NULL;
429 task_get_bootstrap_port(mach_task_self(), &bp);
430 ret = bootstrap_look_up(bp, utfname, &port);
431 native = (KERN_SUCCESS == ret) ? CFMachPortCreateWithPort(allocator, port, __CFMessagePortDummyCallback, &ctx, NULL) : NULL;
432 CFAllocatorDeallocate(allocator, utfname);
433 if (NULL == native) {
434 __CFSpinUnlock(&__CFAllMessagePortsLock);
435 // name is released by deallocation
436 CFRelease(memory);
437 return NULL;
438 }
439 memory->_port = native;
440 CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack);
441 __CFMessagePortSetValid(memory);
442 if (NULL != name) {
443 if (NULL == __CFAllRemoteMessagePorts) {
444 __CFAllRemoteMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
445 }
446 CFDictionaryAddValue(__CFAllRemoteMessagePorts, name, memory);
447 }
448 __CFSpinUnlock(&__CFAllMessagePortsLock);
449 return (CFMessagePortRef)memory;
450 }
451
452 Boolean CFMessagePortIsRemote(CFMessagePortRef ms) {
453 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
454 return __CFMessagePortIsRemote(ms);
455 }
456
457 CFStringRef CFMessagePortGetName(CFMessagePortRef ms) {
458 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
459 return ms->_name;
460 }
461
462 Boolean CFMessagePortSetName(CFMessagePortRef ms, CFStringRef name) {
463 CFAllocatorRef allocator = CFGetAllocator(ms);
464 uint8_t *utfname = NULL;
465
466 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
467 // if (__CFMessagePortIsRemote(ms)) return false;
468 //#warning CF: make this an assertion
469 // and assert than newName is non-NULL
470 name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL);
471 if (NULL == name) {
472 return false;
473 }
474 __CFSpinLock(&__CFAllMessagePortsLock);
475 if (NULL != name) {
476 CFMessagePortRef existing;
477 if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) {
478 __CFSpinUnlock(&__CFAllMessagePortsLock);
479 CFRelease(name);
480 CFAllocatorDeallocate(allocator, utfname);
481 return false;
482 }
483 }
484 if (NULL != name && (NULL == ms->_name || !CFEqual(ms->_name, name))) {
485 if (!__CFMessagePortNativeSetNameLocal(ms->_port, utfname)) {
486 __CFSpinUnlock(&__CFAllMessagePortsLock);
487 CFRelease(name);
488 CFAllocatorDeallocate(allocator, utfname);
489 return false;
490 }
491 if (NULL == __CFAllLocalMessagePorts) {
492 __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
493 }
494 if (NULL != ms->_name) {
495 CFDictionaryRemoveValue(__CFAllLocalMessagePorts, ms->_name);
496 CFRelease(ms->_name);
497 }
498 ms->_name = name;
499 CFDictionaryAddValue(__CFAllLocalMessagePorts, name, ms);
500 }
501 __CFSpinUnlock(&__CFAllMessagePortsLock);
502 CFAllocatorDeallocate(allocator, utfname);
503 return true;
504 }
505
506 void CFMessagePortGetContext(CFMessagePortRef ms, CFMessagePortContext *context) {
507 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
508 //#warning CF: assert that this is a local port
509 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
510 memmove(context, &ms->_context, sizeof(CFMessagePortContext));
511 }
512
513 void CFMessagePortInvalidate(CFMessagePortRef ms) {
514 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
515 if (!__CFMessagePortIsDeallocing(ms)) {
516 CFRetain(ms);
517 }
518 __CFMessagePortLock(ms);
519 if (__CFMessagePortIsValid(ms)) {
520 CFMessagePortInvalidationCallBack callout = ms->_icallout;
521 CFRunLoopSourceRef source = ms->_source;
522 CFMachPortRef replyPort = ms->_replyPort;
523 CFMachPortRef port = ms->_port;
524 CFStringRef name = ms->_name;
525 void *info = NULL;
526
527 __CFMessagePortUnsetValid(ms);
528 if (!__CFMessagePortIsRemote(ms)) {
529 info = ms->_context.info;
530 ms->_context.info = NULL;
531 }
532 ms->_source = NULL;
533 ms->_replyPort = NULL;
534 __CFMessagePortUnlock(ms);
535
536 __CFSpinLock(&__CFAllMessagePortsLock);
537 if (NULL != (__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts)) {
538 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts, name);
539 }
540 __CFSpinUnlock(&__CFAllMessagePortsLock);
541 if (NULL != callout) {
542 callout(ms, info);
543 }
544 // We already know we're going invalid, don't need this callback
545 // anymore; plus, this solves a reentrancy deadlock; also, this
546 // must be done before the deallocate of the Mach port, to
547 // avoid a race between the notification message which could be
548 // handled in another thread, and this NULL'ing out.
549 CFMachPortSetInvalidationCallBack(port, NULL);
550 // For hashing and equality purposes, cannot get rid of _port here
551 if (!__CFMessagePortIsRemote(ms) && NULL != ms->_context.release) {
552 ms->_context.release(info);
553 }
554 if (NULL != source) {
555 CFRunLoopSourceInvalidate(source);
556 CFRelease(source);
557 }
558 if (NULL != replyPort) {
559 CFMachPortInvalidate(replyPort);
560 CFRelease(replyPort);
561 }
562 if (__CFMessagePortIsRemote(ms)) {
563 // Get rid of our extra ref on the Mach port gotten from bs server
564 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port));
565 }
566 } else {
567 __CFMessagePortUnlock(ms);
568 }
569 if (!__CFMessagePortIsDeallocing(ms)) {
570 CFRelease(ms);
571 }
572 }
573
574 Boolean CFMessagePortIsValid(CFMessagePortRef ms) {
575 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
576 if (!__CFMessagePortIsValid(ms)) return false;
577 if (NULL != ms->_port && !CFMachPortIsValid(ms->_port)) {
578 CFMessagePortInvalidate(ms);
579 return false;
580 }
581 if (NULL != ms->_replyPort && !CFMachPortIsValid(ms->_replyPort)) {
582 CFMessagePortInvalidate(ms);
583 return false;
584 }
585 if (NULL != ms->_source && !CFRunLoopSourceIsValid(ms->_source)) {
586 CFMessagePortInvalidate(ms);
587 return false;
588 }
589 return true;
590 }
591
592 CFMessagePortInvalidationCallBack CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms) {
593 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
594 return ms->_icallout;
595 }
596
597 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms, CFMessagePortInvalidationCallBack callout) {
598 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
599 if (!__CFMessagePortIsValid(ms) && NULL != callout) {
600 callout(ms, ms->_context.info);
601 } else {
602 ms->_icallout = callout;
603 }
604 }
605
606 static void __CFMessagePortReplyCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info) {
607 CFMessagePortRef ms = info;
608 struct __CFMessagePortMachMessage *msgp = msg;
609 struct __CFMessagePortMachMessage *replymsg;
610 __CFMessagePortLock(ms);
611 if (!__CFMessagePortIsValid(ms)) {
612 __CFMessagePortUnlock(ms);
613 return;
614 }
615 // assert: (int32_t)msgp->head.msgh_id < 0
616 if (CFDictionaryContainsKey(ms->_replies, (void *)msgp->head.msgh_id)) {
617 CFDataRef reply = NULL;
618 replymsg = (struct __CFMessagePortMachMessage *)msg;
619 if (0 == replymsg->body.msgh_descriptor_count) {
620 int32_t byteslen = CFSwapInt32LittleToHost(replymsg->contents.msg0.byteslen);
621 if (0 <= byteslen) {
622 reply = CFDataCreate(kCFAllocatorSystemDefault, replymsg->contents.msg0.bytes, byteslen);
623 } else {
624 reply = (void *)0xffffffff; // means NULL data
625 }
626 } else {
627 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
628 reply = CFDataCreate(kCFAllocatorSystemDefault, replymsg->contents.msg1.desc.out_of_line.address, replymsg->contents.msg1.desc.out_of_line.size);
629 vm_deallocate(mach_task_self(), (vm_address_t)replymsg->contents.msg1.desc.out_of_line.address, replymsg->contents.msg1.desc.out_of_line.size);
630 }
631 CFDictionarySetValue(ms->_replies, (void *)msgp->head.msgh_id, (void *)reply);
632 } else { /* discard message */
633 if (1 == msgp->body.msgh_descriptor_count) {
634 vm_deallocate(mach_task_self(), (vm_address_t)msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size);
635 }
636 }
637 __CFMessagePortUnlock(ms);
638 }
639
640 SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnDatap) {
641 struct __CFMessagePortMachMessage *sendmsg;
642 CFRunLoopRef currentRL = CFRunLoopGetCurrent();
643 CFRunLoopSourceRef source = NULL;
644 CFDataRef reply = NULL;
645 int64_t termTSR;
646 uint32_t sendOpts = 0, sendTimeOut = 0;
647 int32_t desiredReply;
648 Boolean didRegister = false;
649 kern_return_t ret;
650
651 //#warning CF: This should be an assert
652 // if (!__CFMessagePortIsRemote(remote)) return -999;
653 if (!__CFMessagePortIsValid(remote)) return kCFMessagePortIsInvalid;
654 __CFMessagePortLock(remote);
655 if (NULL == remote->_replyPort) {
656 CFMachPortContext context;
657 context.version = 0;
658 context.info = remote;
659 context.retain = (const void *(*)(const void *))CFRetain;
660 context.release = (void (*)(const void *))CFRelease;
661 context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription;
662 remote->_replyPort = CFMachPortCreate(CFGetAllocator(remote), __CFMessagePortReplyCallBack, &context, NULL);
663 }
664 remote->_convCounter++;
665 desiredReply = -remote->_convCounter;
666 CFDictionarySetValue(remote->_replies, (void *)desiredReply, NULL);
667 sendmsg = __CFMessagePortCreateMessage(CFGetAllocator(remote), false, CFMachPortGetPort(remote->_port), (replyMode != NULL ? CFMachPortGetPort(remote->_replyPort) : MACH_PORT_NULL), -desiredReply, msgid, (data ? CFDataGetBytePtr(data) : NULL), (data ? CFDataGetLength(data) : 0));
668 __CFMessagePortUnlock(remote);
669 if (replyMode != NULL) {
670 source = CFMachPortCreateRunLoopSource(CFGetAllocator(remote), remote->_replyPort, -100);
671 didRegister = !CFRunLoopContainsSource(currentRL, source, replyMode);
672 if (didRegister) {
673 CFRunLoopAddSource(currentRL, source, replyMode);
674 }
675 }
676 if (sendTimeout < 10.0*86400) {
677 // anything more than 10 days is no timeout!
678 sendOpts = MACH_SEND_TIMEOUT;
679 sendTimeout *= 1000.0;
680 if (sendTimeout < 1.0) sendTimeout = 0.0;
681 sendTimeOut = floor(sendTimeout);
682 }
683 ret = mach_msg((mach_msg_header_t *)sendmsg, MACH_SEND_MSG|sendOpts, sendmsg->head.msgh_size, 0, MACH_PORT_NULL, sendTimeOut, MACH_PORT_NULL);
684 CFAllocatorDeallocate(CFGetAllocator(remote), sendmsg);
685 if (KERN_SUCCESS != ret) {
686 if (didRegister) {
687 CFRunLoopRemoveSource(currentRL, source, replyMode);
688 CFRelease(source);
689 }
690 if (MACH_SEND_TIMED_OUT == ret) return kCFMessagePortSendTimeout;
691 return kCFMessagePortTransportError;
692 }
693 if (replyMode == NULL) {
694 return kCFMessagePortSuccess;
695 }
696 CFRetain(remote); // retain during run loop to avoid invalidation causing freeing
697 _CFMachPortInstallNotifyPort(currentRL, replyMode);
698 termTSR = mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout);
699 for (;;) {
700 CFRunLoopRunInMode(replyMode, __CFTSRToTimeInterval(termTSR - mach_absolute_time()), true);
701 // warning: what, if anything, should be done if remote is now invalid?
702 reply = CFDictionaryGetValue(remote->_replies, (void *)desiredReply);
703 if (NULL != reply || termTSR < (int64_t)mach_absolute_time()) {
704 break;
705 }
706 if (!CFMessagePortIsValid(remote)) {
707 // no reason that reply port alone should go invalid so we don't check for that
708 break;
709 }
710 }
711 // Should we uninstall the notify port? A complex question...
712 if (didRegister) {
713 CFRunLoopRemoveSource(currentRL, source, replyMode);
714 CFRelease(source);
715 }
716 if (NULL == reply) {
717 CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply);
718 CFRelease(remote);
719 return CFMessagePortIsValid(remote) ? kCFMessagePortReceiveTimeout : -5;
720 }
721 if (NULL != returnDatap) {
722 *returnDatap = ((void *)0xffffffff == reply) ? NULL : reply;
723 } else if ((void *)0xffffffff != reply) {
724 CFRelease(reply);
725 }
726 CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply);
727 CFRelease(remote);
728 return kCFMessagePortSuccess;
729 }
730
731 static mach_port_t __CFMessagePortGetPort(void *info) {
732 CFMessagePortRef ms = info;
733 return CFMachPortGetPort(ms->_port);
734 }
735
736 static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
737 CFMessagePortRef ms = info;
738 struct __CFMessagePortMachMessage *msgp = msg;
739 struct __CFMessagePortMachMessage *replymsg;
740 void *context_info;
741 void (*context_release)(const void *);
742 CFDataRef returnData, data = NULL;
743 void *return_bytes = NULL;
744 CFIndex return_len = 0;
745 int32_t msgid;
746
747 __CFMessagePortLock(ms);
748 if (!__CFMessagePortIsValid(ms)) {
749 __CFMessagePortUnlock(ms);
750 return NULL;
751 }
752 // assert: 0 < (int32_t)msgp->head.msgh_id
753 if (NULL != ms->_context.retain) {
754 context_info = (void *)ms->_context.retain(ms->_context.info);
755 context_release = ms->_context.release;
756 } else {
757 context_info = ms->_context.info;
758 context_release = NULL;
759 }
760 __CFMessagePortUnlock(ms);
761 /* Create no-copy, no-free-bytes wrapper CFData */
762 if (0 == msgp->body.msgh_descriptor_count) {
763 int32_t byteslen = CFSwapInt32LittleToHost(msgp->contents.msg0.byteslen);
764 msgid = CFSwapInt32LittleToHost(msgp->contents.msg0.msgid);
765 if (0 <= byteslen) {
766 data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, msgp->contents.msg0.bytes, byteslen, kCFAllocatorNull);
767 }
768 } else {
769 msgid = CFSwapInt32LittleToHost(msgp->contents.msg1.msgid);
770 data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size, kCFAllocatorNull);
771 }
772 returnData = ms->_callout(ms, msgid, data, context_info);
773 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
774 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
775 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
776 bytes inline in the Mach message, so can release the returnData object
777 here. In cases (3) and (5), we'll send the data out-of-line, we need to
778 create a copy of the memory, which we'll have the kernel autodeallocate
779 for us on send. In case (4) also, the bytes in the return data may be part
780 of the bytes in "data" that we sent into the callout, so if the incoming
781 data was received out of line, we wouldn't be able to clean up the out-of-line
782 wad until the message was sent either, if we didn't make the copy. */
783 if (NULL != returnData) {
784 return_len = CFDataGetLength(returnData);
785 if (return_len < __CFMessagePortMaxInlineBytes) {
786 return_bytes = (void *)CFDataGetBytePtr(returnData);
787 } else {
788 return_bytes = NULL;
789 vm_allocate(mach_task_self(), (vm_address_t *)&return_bytes, return_len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
790 /* vm_copy would only be a win here if the source address
791 is page aligned; it is a lose in all other cases, since
792 the kernel will just do the memmove for us (but not in
793 as simple a way). */
794 memmove(return_bytes, CFDataGetBytePtr(returnData), return_len);
795 }
796 }
797 replymsg = __CFMessagePortCreateMessage(allocator, true, msgp->head.msgh_remote_port, MACH_PORT_NULL, -1 * (int32_t)msgp->head.msgh_id, msgid, return_bytes, return_len);
798 if (1 == replymsg->body.msgh_descriptor_count) {
799 replymsg->contents.msg1.desc.out_of_line.deallocate = true;
800 }
801 if (data) CFRelease(data);
802 if (1 == msgp->body.msgh_descriptor_count) {
803 vm_deallocate(mach_task_self(), (vm_address_t)msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size);
804 }
805 if (returnData) CFRelease(returnData);
806 if (context_release) {
807 context_release(context_info);
808 }
809 return replymsg;
810 }
811
812 CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef ms, CFIndex order) {
813 CFRunLoopSourceRef result = NULL;
814 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
815 //#warning CF: This should be an assert
816 // if (__CFMessagePortIsRemote(ms)) return NULL;
817 __CFMessagePortLock(ms);
818 if (NULL == ms->_source && __CFMessagePortIsValid(ms)) {
819 CFRunLoopSourceContext1 context;
820 context.version = 1;
821 context.info = (void *)ms;
822 context.retain = (const void *(*)(const void *))CFRetain;
823 context.release = (void (*)(const void *))CFRelease;
824 context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription;
825 context.equal = (Boolean (*)(const void *, const void *))__CFMessagePortEqual;
826 context.hash = (CFHashCode (*)(const void *))__CFMessagePortHash;
827 context.getPort = __CFMessagePortGetPort;
828 context.perform = __CFMessagePortPerform;
829 ms->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
830 }
831 if (NULL != ms->_source) {
832 result = (CFRunLoopSourceRef)CFRetain(ms->_source);
833 }
834 __CFMessagePortUnlock(ms);
835 return result;
836 }
837
838 #endif /* !__WIN32__ */
839