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