]> git.saurik.com Git - apple/cf.git/blob - RunLoop.subproj/CFMessagePort.c
5112eedf41de9ecd9bc3ccc3401a32f830b8d398
[apple/cf.git] / RunLoop.subproj / CFMessagePort.c
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /* CFMessagePort.c
26 Copyright 1998-2002, Apple, Inc. All rights reserved.
27 Responsibility: Christopher Kane
28 */
29
30 #if !defined(__WIN32__)
31
32 #include <CoreFoundation/CFMessagePort.h>
33 #include <CoreFoundation/CFRunLoop.h>
34 #include <CoreFoundation/CFMachPort.h>
35 #include <CoreFoundation/CFDictionary.h>
36 #include <CoreFoundation/CFByteOrder.h>
37 #include <limits.h>
38 #include "CFInternal.h"
39 #include <mach/mach.h>
40 #include <mach/message.h>
41 #include <mach/mach_error.h>
42 #include <servers/bootstrap.h>
43 #include <math.h>
44 #include <mach/mach_time.h>
45
46 #define __kCFMessagePortMaxNameLengthMax 255
47
48 #if defined(BOOTSTRAP_MAX_NAME_LEN)
49 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
50 #else
51 #define __kCFMessagePortMaxNameLength 128
52 #endif
53
54 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
55 #undef __kCFMessagePortMaxNameLength
56 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
57 #endif
58
59 static CFSpinLock_t __CFAllMessagePortsLock = 0;
60 static CFMutableDictionaryRef __CFAllLocalMessagePorts = NULL;
61 static CFMutableDictionaryRef __CFAllRemoteMessagePorts = NULL;
62
63 struct __CFMessagePort {
64 CFRuntimeBase _base;
65 CFSpinLock_t _lock;
66 CFStringRef _name;
67 CFMachPortRef _port; /* immutable; invalidated */
68 CFMutableDictionaryRef _replies;
69 int32_t _convCounter;
70 CFMachPortRef _replyPort; /* only used by remote port; immutable once created; invalidated */
71 CFRunLoopSourceRef _source; /* only used by local port; immutable once created; invalidated */
72 CFMessagePortInvalidationCallBack _icallout;
73 CFMessagePortCallBack _callout; /* only used by local port; immutable */
74 CFMessagePortContext _context; /* not part of remote port; immutable; invalidated */
75 };
76
77 /* Bit 0 in the base reserved bits is used for invalid state */
78 /* Bit 2 of the base reserved bits is used for is-remote state */
79 /* Bit 3 in the base reserved bits is used for is-deallocing state */
80
81 CF_INLINE Boolean __CFMessagePortIsValid(CFMessagePortRef ms) {
82 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_info, 0, 0);
83 }
84
85 CF_INLINE void __CFMessagePortSetValid(CFMessagePortRef ms) {
86 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 0, 0, 1);
87 }
88
89 CF_INLINE void __CFMessagePortUnsetValid(CFMessagePortRef ms) {
90 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 0, 0, 0);
91 }
92
93 CF_INLINE Boolean __CFMessagePortIsRemote(CFMessagePortRef ms) {
94 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_info, 2, 2);
95 }
96
97 CF_INLINE void __CFMessagePortSetRemote(CFMessagePortRef ms) {
98 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 2, 2, 1);
99 }
100
101 CF_INLINE void __CFMessagePortUnsetRemote(CFMessagePortRef ms) {
102 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 2, 2, 0);
103 }
104
105 CF_INLINE Boolean __CFMessagePortIsDeallocing(CFMessagePortRef ms) {
106 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_info, 3, 3);
107 }
108
109 CF_INLINE void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms) {
110 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 3, 3, 1);
111 }
112
113 CF_INLINE void __CFMessagePortLock(CFMessagePortRef ms) {
114 __CFSpinLock(&(ms->_lock));
115 }
116
117 CF_INLINE void __CFMessagePortUnlock(CFMessagePortRef ms) {
118 __CFSpinUnlock(&(ms->_lock));
119 }
120
121 // Just a heuristic
122 #define __CFMessagePortMaxInlineBytes 4096*10
123
124 struct __CFMessagePortMachMsg0 {
125 int32_t msgid;
126 int32_t byteslen;
127 uint8_t bytes[__CFMessagePortMaxInlineBytes];
128 };
129
130 struct __CFMessagePortMachMsg1 {
131 mach_msg_descriptor_t desc;
132 int32_t msgid;
133 };
134
135 struct __CFMessagePortMachMessage {
136 mach_msg_header_t head;
137 mach_msg_body_t body;
138 union {
139 struct __CFMessagePortMachMsg0 msg0;
140 struct __CFMessagePortMachMsg1 msg1;
141 } contents;
142 };
143
144 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) {
145 struct __CFMessagePortMachMessage *msg;
146 int32_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
147 if (byteslen < __CFMessagePortMaxInlineBytes) {
148 size += 2 * sizeof(int32_t) + ((byteslen + 3) & ~0x3);
149 } else {
150 size += sizeof(struct __CFMessagePortMachMsg1);
151 }
152 msg = CFAllocatorAllocate(allocator, size, 0);
153 msg->head.msgh_id = convid;
154 msg->head.msgh_size = size;
155 msg->head.msgh_remote_port = port;
156 msg->head.msgh_local_port = replyPort;
157 msg->head.msgh_reserved = 0;
158 // msg->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort ? MACH_MSG_TYPE_MAKE_SEND : 0));
159 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));
160 if (byteslen < __CFMessagePortMaxInlineBytes) {
161 msg->body.msgh_descriptor_count = 0;
162 msg->contents.msg0.msgid = CFSwapInt32HostToLittle(msgid);
163 msg->contents.msg0.byteslen = CFSwapInt32HostToLittle(byteslen);
164 if (NULL != bytes && 0 < byteslen) {
165 memmove(msg->contents.msg0.bytes, bytes, byteslen);
166 }
167 memset(msg->contents.msg0.bytes + byteslen, 0, ((byteslen + 3) & ~0x3) - byteslen);
168 } else {
169 msg->head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
170 msg->body.msgh_descriptor_count = 1;
171 msg->contents.msg1.desc.out_of_line.deallocate = false;
172 msg->contents.msg1.desc.out_of_line.copy = MACH_MSG_VIRTUAL_COPY;
173 msg->contents.msg1.desc.out_of_line.address = (void *)bytes;
174 msg->contents.msg1.desc.out_of_line.size = byteslen;
175 msg->contents.msg1.desc.out_of_line.type = MACH_MSG_OOL_DESCRIPTOR;
176 msg->contents.msg1.msgid = CFSwapInt32HostToLittle(msgid);
177 }
178 return msg;
179 }
180
181 static Boolean __CFMessagePortNativeSetNameLocal(CFMachPortRef port, uint8_t *portname) {
182 mach_port_t bp;
183 kern_return_t ret;
184 task_get_bootstrap_port(mach_task_self(), &bp);
185 ret = bootstrap_register(bp, portname, CFMachPortGetPort(port));
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 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 (NULL != native && NULL != name && !__CFMessagePortNativeSetNameLocal(native, utfname)) {
348 CFMachPortInvalidate(native);
349 CFRelease(native);
350 native = NULL;
351 }
352 CFAllocatorDeallocate(allocator, utfname);
353 if (NULL == native) {
354 __CFSpinUnlock(&__CFAllMessagePortsLock);
355 // name is released by deallocation
356 CFRelease(memory);
357 return NULL;
358 }
359 memory->_port = native;
360 CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack);
361 __CFMessagePortSetValid(memory);
362 if (NULL != context) {
363 memmove(&memory->_context, context, sizeof(CFMessagePortContext));
364 memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info;
365 }
366 if (NULL != name) {
367 if (NULL == __CFAllLocalMessagePorts) {
368 __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
369 }
370 CFDictionaryAddValue(__CFAllLocalMessagePorts, name, memory);
371 }
372 __CFSpinUnlock(&__CFAllMessagePortsLock);
373 if (shouldFreeInfo) *shouldFreeInfo = false;
374 return memory;
375 }
376
377 CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) {
378 CFMessagePortRef memory;
379 CFMachPortRef native;
380 CFMachPortContext ctx;
381 uint8_t *utfname = NULL;
382 CFIndex size;
383 mach_port_t bp, port;
384 kern_return_t ret;
385
386 name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL);
387 if (NULL == name) {
388 return NULL;
389 }
390 __CFSpinLock(&__CFAllMessagePortsLock);
391 if (NULL != name) {
392 CFMessagePortRef existing;
393 if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) {
394 __CFSpinUnlock(&__CFAllMessagePortsLock);
395 CFRelease(name);
396 CFAllocatorDeallocate(allocator, utfname);
397 return (CFMessagePortRef)CFRetain(existing);
398 }
399 }
400 size = sizeof(struct __CFMessagePort) - sizeof(CFMessagePortContext) - sizeof(CFRuntimeBase);
401 memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL);
402 if (NULL == memory) {
403 __CFSpinUnlock(&__CFAllMessagePortsLock);
404 if (NULL != name) {
405 CFRelease(name);
406 }
407 CFAllocatorDeallocate(allocator, utfname);
408 return NULL;
409 }
410 __CFMessagePortUnsetValid(memory);
411 __CFMessagePortSetRemote(memory);
412 memory->_lock = 0;
413 memory->_name = name;
414 memory->_port = NULL;
415 memory->_replies = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
416 memory->_convCounter = 0;
417 memory->_replyPort = NULL;
418 memory->_source = NULL;
419 memory->_icallout = NULL;
420 memory->_callout = NULL;
421 ctx.version = 0;
422 ctx.info = memory;
423 ctx.retain = NULL;
424 ctx.release = NULL;
425 ctx.copyDescription = NULL;
426 task_get_bootstrap_port(mach_task_self(), &bp);
427 ret = bootstrap_look_up(bp, utfname, &port);
428 native = (KERN_SUCCESS == ret) ? CFMachPortCreateWithPort(allocator, port, __CFMessagePortDummyCallback, &ctx, NULL) : NULL;
429 CFAllocatorDeallocate(allocator, utfname);
430 if (NULL == native) {
431 __CFSpinUnlock(&__CFAllMessagePortsLock);
432 // name is released by deallocation
433 CFRelease(memory);
434 return NULL;
435 }
436 memory->_port = native;
437 CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack);
438 __CFMessagePortSetValid(memory);
439 if (NULL != name) {
440 if (NULL == __CFAllRemoteMessagePorts) {
441 __CFAllRemoteMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
442 }
443 CFDictionaryAddValue(__CFAllRemoteMessagePorts, name, memory);
444 }
445 __CFSpinUnlock(&__CFAllMessagePortsLock);
446 return (CFMessagePortRef)memory;
447 }
448
449 Boolean CFMessagePortIsRemote(CFMessagePortRef ms) {
450 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
451 return __CFMessagePortIsRemote(ms);
452 }
453
454 CFStringRef CFMessagePortGetName(CFMessagePortRef ms) {
455 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
456 return ms->_name;
457 }
458
459 Boolean CFMessagePortSetName(CFMessagePortRef ms, CFStringRef name) {
460 CFAllocatorRef allocator = CFGetAllocator(ms);
461 uint8_t *utfname = NULL;
462
463 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
464 // if (__CFMessagePortIsRemote(ms)) return false;
465 //#warning CF: make this an assertion
466 // and assert than newName is non-NULL
467 name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL);
468 if (NULL == name) {
469 return false;
470 }
471 __CFSpinLock(&__CFAllMessagePortsLock);
472 if (NULL != name) {
473 CFMessagePortRef existing;
474 if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) {
475 __CFSpinUnlock(&__CFAllMessagePortsLock);
476 CFRelease(name);
477 CFAllocatorDeallocate(allocator, utfname);
478 return false;
479 }
480 }
481 if (NULL != name && (NULL == ms->_name || !CFEqual(ms->_name, name))) {
482 if (!__CFMessagePortNativeSetNameLocal(ms->_port, utfname)) {
483 __CFSpinUnlock(&__CFAllMessagePortsLock);
484 CFRelease(name);
485 CFAllocatorDeallocate(allocator, utfname);
486 return false;
487 }
488 if (NULL == __CFAllLocalMessagePorts) {
489 __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
490 }
491 if (NULL != ms->_name) {
492 CFDictionaryRemoveValue(__CFAllLocalMessagePorts, ms->_name);
493 CFRelease(ms->_name);
494 }
495 ms->_name = name;
496 CFDictionaryAddValue(__CFAllLocalMessagePorts, name, ms);
497 }
498 __CFSpinUnlock(&__CFAllMessagePortsLock);
499 CFAllocatorDeallocate(allocator, utfname);
500 return true;
501 }
502
503 void CFMessagePortGetContext(CFMessagePortRef ms, CFMessagePortContext *context) {
504 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
505 //#warning CF: assert that this is a local port
506 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
507 memmove(context, &ms->_context, sizeof(CFMessagePortContext));
508 }
509
510 void CFMessagePortInvalidate(CFMessagePortRef ms) {
511 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
512 if (!__CFMessagePortIsDeallocing(ms)) {
513 CFRetain(ms);
514 }
515 __CFMessagePortLock(ms);
516 if (__CFMessagePortIsValid(ms)) {
517 CFMessagePortInvalidationCallBack callout = ms->_icallout;
518 CFRunLoopSourceRef source = ms->_source;
519 CFMachPortRef replyPort = ms->_replyPort;
520 CFMachPortRef port = ms->_port;
521 CFStringRef name = ms->_name;
522 void *info = NULL;
523
524 __CFMessagePortUnsetValid(ms);
525 if (!__CFMessagePortIsRemote(ms)) {
526 info = ms->_context.info;
527 ms->_context.info = NULL;
528 }
529 ms->_source = NULL;
530 ms->_replyPort = NULL;
531 __CFMessagePortUnlock(ms);
532
533 __CFSpinLock(&__CFAllMessagePortsLock);
534 if (NULL != (__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts)) {
535 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts, name);
536 }
537 __CFSpinUnlock(&__CFAllMessagePortsLock);
538 if (NULL != callout) {
539 callout(ms, info);
540 }
541 // We already know we're going invalid, don't need this callback
542 // anymore; plus, this solves a reentrancy deadlock; also, this
543 // must be done before the deallocate of the Mach port, to
544 // avoid a race between the notification message which could be
545 // handled in another thread, and this NULL'ing out.
546 CFMachPortSetInvalidationCallBack(port, NULL);
547 // For hashing and equality purposes, cannot get rid of _port here
548 if (!__CFMessagePortIsRemote(ms) && NULL != ms->_context.release) {
549 ms->_context.release(info);
550 }
551 if (NULL != source) {
552 CFRunLoopSourceInvalidate(source);
553 CFRelease(source);
554 }
555 if (NULL != replyPort) {
556 CFMachPortInvalidate(replyPort);
557 CFRelease(replyPort);
558 }
559 if (__CFMessagePortIsRemote(ms)) {
560 // Get rid of our extra ref on the Mach port gotten from bs server
561 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port));
562 }
563 } else {
564 __CFMessagePortUnlock(ms);
565 }
566 if (!__CFMessagePortIsDeallocing(ms)) {
567 CFRelease(ms);
568 }
569 }
570
571 Boolean CFMessagePortIsValid(CFMessagePortRef ms) {
572 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
573 if (!__CFMessagePortIsValid(ms)) return false;
574 if (NULL != ms->_port && !CFMachPortIsValid(ms->_port)) {
575 CFMessagePortInvalidate(ms);
576 return false;
577 }
578 if (NULL != ms->_replyPort && !CFMachPortIsValid(ms->_replyPort)) {
579 CFMessagePortInvalidate(ms);
580 return false;
581 }
582 if (NULL != ms->_source && !CFRunLoopSourceIsValid(ms->_source)) {
583 CFMessagePortInvalidate(ms);
584 return false;
585 }
586 return true;
587 }
588
589 CFMessagePortInvalidationCallBack CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms) {
590 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
591 return ms->_icallout;
592 }
593
594 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms, CFMessagePortInvalidationCallBack callout) {
595 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
596 if (!__CFMessagePortIsValid(ms) && NULL != callout) {
597 callout(ms, ms->_context.info);
598 } else {
599 ms->_icallout = callout;
600 }
601 }
602
603 static void __CFMessagePortReplyCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info) {
604 CFMessagePortRef ms = info;
605 struct __CFMessagePortMachMessage *msgp = msg;
606 struct __CFMessagePortMachMessage *replymsg;
607 __CFMessagePortLock(ms);
608 if (!__CFMessagePortIsValid(ms)) {
609 __CFMessagePortUnlock(ms);
610 return;
611 }
612 // assert: (int32_t)msgp->head.msgh_id < 0
613 if (CFDictionaryContainsKey(ms->_replies, (void *)msgp->head.msgh_id)) {
614 CFDataRef reply = NULL;
615 replymsg = (struct __CFMessagePortMachMessage *)msg;
616 if (0 == replymsg->body.msgh_descriptor_count) {
617 int32_t byteslen = CFSwapInt32LittleToHost(replymsg->contents.msg0.byteslen);
618 if (0 <= byteslen) {
619 reply = CFDataCreate(kCFAllocatorSystemDefault, replymsg->contents.msg0.bytes, byteslen);
620 } else {
621 reply = (void *)0xffffffff; // means NULL data
622 }
623 } else {
624 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
625 reply = CFDataCreate(kCFAllocatorSystemDefault, replymsg->contents.msg1.desc.out_of_line.address, replymsg->contents.msg1.desc.out_of_line.size);
626 vm_deallocate(mach_task_self(), (vm_address_t)replymsg->contents.msg1.desc.out_of_line.address, replymsg->contents.msg1.desc.out_of_line.size);
627 }
628 CFDictionarySetValue(ms->_replies, (void *)msgp->head.msgh_id, (void *)reply);
629 } else { /* discard message */
630 if (1 == msgp->body.msgh_descriptor_count) {
631 vm_deallocate(mach_task_self(), (vm_address_t)msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size);
632 }
633 }
634 __CFMessagePortUnlock(ms);
635 }
636
637 SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnDatap) {
638 struct __CFMessagePortMachMessage *sendmsg;
639 CFRunLoopRef currentRL = CFRunLoopGetCurrent();
640 CFRunLoopSourceRef source = NULL;
641 CFDataRef reply = NULL;
642 int64_t termTSR;
643 uint32_t sendOpts = 0, sendTimeOut = 0;
644 int32_t desiredReply;
645 Boolean didRegister = false;
646 kern_return_t ret;
647
648 //#warning CF: This should be an assert
649 // if (!__CFMessagePortIsRemote(remote)) return -999;
650 if (!__CFMessagePortIsValid(remote)) return kCFMessagePortIsInvalid;
651 __CFMessagePortLock(remote);
652 if (NULL == remote->_replyPort) {
653 CFMachPortContext context;
654 context.version = 0;
655 context.info = remote;
656 context.retain = (const void *(*)(const void *))CFRetain;
657 context.release = (void (*)(const void *))CFRelease;
658 context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription;
659 remote->_replyPort = CFMachPortCreate(CFGetAllocator(remote), __CFMessagePortReplyCallBack, &context, NULL);
660 }
661 remote->_convCounter++;
662 desiredReply = -remote->_convCounter;
663 CFDictionarySetValue(remote->_replies, (void *)desiredReply, NULL);
664 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));
665 __CFMessagePortUnlock(remote);
666 if (replyMode != NULL) {
667 source = CFMachPortCreateRunLoopSource(CFGetAllocator(remote), remote->_replyPort, -100);
668 didRegister = !CFRunLoopContainsSource(currentRL, source, replyMode);
669 if (didRegister) {
670 CFRunLoopAddSource(currentRL, source, replyMode);
671 }
672 }
673 if (sendTimeout < 10.0*86400) {
674 // anything more than 10 days is no timeout!
675 sendOpts = MACH_SEND_TIMEOUT;
676 sendTimeout *= 1000.0;
677 if (sendTimeout < 1.0) sendTimeout = 0.0;
678 sendTimeOut = floor(sendTimeout);
679 }
680 ret = mach_msg((mach_msg_header_t *)sendmsg, MACH_SEND_MSG|sendOpts, sendmsg->head.msgh_size, 0, MACH_PORT_NULL, sendTimeOut, MACH_PORT_NULL);
681 CFAllocatorDeallocate(CFGetAllocator(remote), sendmsg);
682 if (KERN_SUCCESS != ret) {
683 if (didRegister) {
684 CFRunLoopRemoveSource(currentRL, source, replyMode);
685 CFRelease(source);
686 }
687 if (MACH_SEND_TIMED_OUT == ret) return kCFMessagePortSendTimeout;
688 return kCFMessagePortTransportError;
689 }
690 if (replyMode == NULL) {
691 return kCFMessagePortSuccess;
692 }
693 CFRetain(remote); // retain during run loop to avoid invalidation causing freeing
694 _CFMachPortInstallNotifyPort(currentRL, replyMode);
695 termTSR = mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout);
696 for (;;) {
697 CFRunLoopRunInMode(replyMode, __CFTSRToTimeInterval(termTSR - mach_absolute_time()), true);
698 // warning: what, if anything, should be done if remote is now invalid?
699 reply = CFDictionaryGetValue(remote->_replies, (void *)desiredReply);
700 if (NULL != reply || termTSR < (int64_t)mach_absolute_time()) {
701 break;
702 }
703 if (!CFMessagePortIsValid(remote)) {
704 // no reason that reply port alone should go invalid so we don't check for that
705 break;
706 }
707 }
708 // Should we uninstall the notify port? A complex question...
709 if (didRegister) {
710 CFRunLoopRemoveSource(currentRL, source, replyMode);
711 CFRelease(source);
712 }
713 if (NULL == reply) {
714 CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply);
715 CFRelease(remote);
716 return CFMessagePortIsValid(remote) ? kCFMessagePortReceiveTimeout : -5;
717 }
718 if (NULL != returnDatap) {
719 *returnDatap = ((void *)0xffffffff == reply) ? NULL : reply;
720 } else if ((void *)0xffffffff != reply) {
721 CFRelease(reply);
722 }
723 CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply);
724 CFRelease(remote);
725 return kCFMessagePortSuccess;
726 }
727
728 static mach_port_t __CFMessagePortGetPort(void *info) {
729 CFMessagePortRef ms = info;
730 return CFMachPortGetPort(ms->_port);
731 }
732
733 static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
734 CFMessagePortRef ms = info;
735 struct __CFMessagePortMachMessage *msgp = msg;
736 struct __CFMessagePortMachMessage *replymsg;
737 void *context_info;
738 void (*context_release)(const void *);
739 CFDataRef returnData, data = NULL;
740 void *return_bytes = NULL;
741 CFIndex return_len = 0;
742 int32_t msgid;
743
744 __CFMessagePortLock(ms);
745 if (!__CFMessagePortIsValid(ms)) {
746 __CFMessagePortUnlock(ms);
747 return NULL;
748 }
749 // assert: 0 < (int32_t)msgp->head.msgh_id
750 if (NULL != ms->_context.retain) {
751 context_info = (void *)ms->_context.retain(ms->_context.info);
752 context_release = ms->_context.release;
753 } else {
754 context_info = ms->_context.info;
755 context_release = NULL;
756 }
757 __CFMessagePortUnlock(ms);
758 /* Create no-copy, no-free-bytes wrapper CFData */
759 if (0 == msgp->body.msgh_descriptor_count) {
760 int32_t byteslen = CFSwapInt32LittleToHost(msgp->contents.msg0.byteslen);
761 msgid = CFSwapInt32LittleToHost(msgp->contents.msg0.msgid);
762 if (0 <= byteslen) {
763 data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, msgp->contents.msg0.bytes, byteslen, kCFAllocatorNull);
764 }
765 } else {
766 msgid = CFSwapInt32LittleToHost(msgp->contents.msg1.msgid);
767 data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size, kCFAllocatorNull);
768 }
769 returnData = ms->_callout(ms, msgid, data, context_info);
770 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
771 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
772 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
773 bytes inline in the Mach message, so can release the returnData object
774 here. In cases (3) and (5), we'll send the data out-of-line, we need to
775 create a copy of the memory, which we'll have the kernel autodeallocate
776 for us on send. In case (4) also, the bytes in the return data may be part
777 of the bytes in "data" that we sent into the callout, so if the incoming
778 data was received out of line, we wouldn't be able to clean up the out-of-line
779 wad until the message was sent either, if we didn't make the copy. */
780 if (NULL != returnData) {
781 return_len = CFDataGetLength(returnData);
782 if (return_len < __CFMessagePortMaxInlineBytes) {
783 return_bytes = (void *)CFDataGetBytePtr(returnData);
784 } else {
785 return_bytes = NULL;
786 vm_allocate(mach_task_self(), (vm_address_t *)&return_bytes, return_len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
787 /* vm_copy would only be a win here if the source address
788 is page aligned; it is a lose in all other cases, since
789 the kernel will just do the memmove for us (but not in
790 as simple a way). */
791 memmove(return_bytes, CFDataGetBytePtr(returnData), return_len);
792 }
793 }
794 replymsg = __CFMessagePortCreateMessage(allocator, true, msgp->head.msgh_remote_port, MACH_PORT_NULL, -1 * (int32_t)msgp->head.msgh_id, msgid, return_bytes, return_len);
795 if (1 == replymsg->body.msgh_descriptor_count) {
796 replymsg->contents.msg1.desc.out_of_line.deallocate = true;
797 }
798 if (data) CFRelease(data);
799 if (1 == msgp->body.msgh_descriptor_count) {
800 vm_deallocate(mach_task_self(), (vm_address_t)msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size);
801 }
802 if (returnData) CFRelease(returnData);
803 if (context_release) {
804 context_release(context_info);
805 }
806 return replymsg;
807 }
808
809 CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef ms, CFIndex order) {
810 CFRunLoopSourceRef result = NULL;
811 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
812 //#warning CF: This should be an assert
813 // if (__CFMessagePortIsRemote(ms)) return NULL;
814 __CFMessagePortLock(ms);
815 if (NULL == ms->_source && __CFMessagePortIsValid(ms)) {
816 CFRunLoopSourceContext1 context;
817 context.version = 1;
818 context.info = (void *)ms;
819 context.retain = (const void *(*)(const void *))CFRetain;
820 context.release = (void (*)(const void *))CFRelease;
821 context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription;
822 context.equal = (Boolean (*)(const void *, const void *))__CFMessagePortEqual;
823 context.hash = (CFHashCode (*)(const void *))__CFMessagePortHash;
824 context.getPort = __CFMessagePortGetPort;
825 context.perform = __CFMessagePortPerform;
826 ms->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
827 }
828 if (NULL != ms->_source) {
829 result = (CFRunLoopSourceRef)CFRetain(ms->_source);
830 }
831 __CFMessagePortUnlock(ms);
832 return result;
833 }
834
835 #endif /* !__WIN32__ */
836