]> git.saurik.com Git - apple/cf.git/blob - CFMachPort.c
CF-550.43.tar.gz
[apple/cf.git] / CFMachPort.c
1 /*
2 * Copyright (c) 2010 Apple 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
24 /* CFMachPort.c
25 Copyright (c) 1998-2010, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
27 */
28
29 #include <CoreFoundation/CFMachPort.h>
30 #include <CoreFoundation/CFRunLoop.h>
31 #include <CoreFoundation/CFArray.h>
32 #include <dispatch/dispatch.h>
33 #include <libkern/OSAtomic.h>
34 #include <mach/mach.h>
35 #include <dlfcn.h>
36 #include "CFInternal.h"
37
38 #define AVOID_WEAK_COLLECTIONS 1
39
40 #if DEPLOYMENT_TARGET_EMBEDDED
41 #define AVOID_WEAK_COLLECTIONS 1
42 #endif
43
44 #if !defined(AVOID_WEAK_COLLECTIONS)
45 #import "CFPointerArray.h"
46 #endif
47
48 DISPATCH_HELPER_FUNCTIONS(port, CFMachPort)
49
50
51 enum {
52 kCFMachPortStateReady = 0,
53 kCFMachPortStateInvalidating = 1,
54 kCFMachPortStateInvalid = 2,
55 kCFMachPortStateDeallocating = 3
56 };
57
58 struct __CFMachPort {
59 CFRuntimeBase _base;
60 int32_t _state;
61 mach_port_t _port; /* immutable */
62 dispatch_source_t _dsrc;
63 dispatch_source_t _dsrc2;
64 dispatch_semaphore_t _dsrc_sem;
65 dispatch_semaphore_t _dsrc2_sem;
66 CFMachPortInvalidationCallBack _icallout;
67 CFRunLoopSourceRef _source; /* immutable, once created */
68 CFMachPortCallBack _callout; /* immutable */
69 CFMachPortContext _context; /* immutable */
70 };
71
72 /* Bit 1 in the base reserved bits is used for has-receive-ref state */
73 /* Bit 2 in the base reserved bits is used for has-send-ref state */
74 /* Bit 3 in the base reserved bits is used for has-send-ref2 state */
75
76 CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) {
77 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1);
78 }
79
80 CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) {
81 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
82 }
83
84 CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) {
85 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2);
86 }
87
88 CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) {
89 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2, 1);
90 }
91
92 CF_INLINE Boolean __CFMachPortHasSend2(CFMachPortRef mp) {
93 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3);
94 }
95
96 CF_INLINE void __CFMachPortSetHasSend2(CFMachPortRef mp) {
97 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
98 }
99
100 CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) {
101 return kCFMachPortStateReady == mp->_state;
102 }
103
104
105 void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) {
106 }
107
108 static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) {
109 CFMachPortRef mp1 = (CFMachPortRef)cf1;
110 CFMachPortRef mp2 = (CFMachPortRef)cf2;
111 return (mp1->_port == mp2->_port);
112 }
113
114 static CFHashCode __CFMachPortHash(CFTypeRef cf) {
115 CFMachPortRef mp = (CFMachPortRef)cf;
116 return (CFHashCode)mp->_port;
117 }
118
119 static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) {
120 CFMachPortRef mp = (CFMachPortRef)cf;
121 CFStringRef contextDesc = NULL;
122 if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) {
123 contextDesc = mp->_context.copyDescription(mp->_context.info);
124 }
125 if (NULL == contextDesc) {
126 contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info);
127 }
128 Dl_info info;
129 void *addr = mp->_callout;
130 const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???";
131 CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMachPort %p [%p]>{valid = %s, port = %p, source = %p, callout = %s (%p), context = %@}"), cf, CFGetAllocator(mp), (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, name, addr, contextDesc);
132 if (NULL != contextDesc) {
133 CFRelease(contextDesc);
134 }
135 return result;
136 }
137
138 static void __CFMachPortDeallocate(CFTypeRef cf) {
139 CHECK_FOR_FORK_RET();
140 CFMachPortRef mp = (CFMachPortRef)cf;
141
142 // CFMachPortRef is invalid before we get here, except under GC
143 __block CFRunLoopSourceRef source = NULL;
144 __block Boolean wasReady = false;
145 void (^block)(void) = ^{
146 wasReady = (mp->_state == kCFMachPortStateReady);
147 if (wasReady) {
148 mp->_state = kCFMachPortStateInvalidating;
149 OSMemoryBarrier();
150 if (mp->_dsrc) {
151 dispatch_source_cancel(mp->_dsrc);
152 mp->_dsrc = NULL;
153 }
154 if (mp->_dsrc2) {
155 dispatch_source_cancel(mp->_dsrc2);
156 mp->_dsrc2 = NULL;
157 }
158 source = mp->_source;
159 mp->_source = NULL;
160 }
161 };
162 if (!__portSyncDispatchIsSafe(__portQueue())) {
163 block();
164 } else {
165 dispatch_sync(__portQueue(), block);
166 }
167 if (wasReady) {
168 CFMachPortInvalidationCallBack cb = mp->_icallout;
169 if (cb) {
170 cb(mp, mp->_context.info);
171 }
172 if (NULL != source) {
173 CFRunLoopSourceInvalidate(source);
174 CFRelease(source);
175 }
176 void *info = mp->_context.info;
177 mp->_context.info = NULL;
178 if (mp->_context.release) {
179 mp->_context.release(info);
180 }
181 mp->_state = kCFMachPortStateInvalid;
182 OSMemoryBarrier();
183 }
184 mp->_state = kCFMachPortStateDeallocating;
185
186 // hand ownership of the port and semaphores to the block below
187 mach_port_t port = mp->_port;
188 dispatch_semaphore_t sem1 = mp->_dsrc_sem;
189 dispatch_semaphore_t sem2 = mp->_dsrc2_sem;
190 Boolean doSend2 = __CFMachPortHasSend2(mp), doSend = __CFMachPortHasSend(mp), doReceive = __CFMachPortHasReceive(mp);
191
192 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
193 if (sem1) {
194 dispatch_semaphore_wait(sem1, DISPATCH_TIME_FOREVER);
195 // immediate release is only safe if dispatch_semaphore_signal() does not touch the semaphore after doing the signal bit
196 dispatch_release(sem1);
197 }
198 if (sem2) {
199 dispatch_semaphore_wait(sem2, DISPATCH_TIME_FOREVER);
200 // immediate release is only safe if dispatch_semaphore_signal() does not touch the semaphore after doing the signal bit
201 dispatch_release(sem2);
202 }
203
204 // MUST deallocate the send right FIRST if necessary,
205 // then the receive right if necessary. Don't ask me why;
206 // if it's done in the other order the port will leak.
207 if (doSend2) {
208 mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1);
209 }
210 if (doSend) {
211 mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1);
212 }
213 if (doReceive) {
214 mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
215 }
216 });
217 }
218
219 #if defined(AVOID_WEAK_COLLECTIONS)
220 static CFMutableArrayRef __CFAllMachPorts = NULL;
221 #else
222 static __CFPointerArray *__CFAllMachPorts = nil;
223 #endif
224
225 static Boolean __CFMachPortCheck(mach_port_t port) {
226 mach_port_type_t type = 0;
227 kern_return_t ret = mach_port_type(mach_task_self(), port, &type);
228 return (KERN_SUCCESS != ret || (type & MACH_PORT_TYPE_DEAD_NAME)) ? false : true;
229 }
230
231 static void __CFMachPortChecker(Boolean fromTimer) { // only call on __portQueue()
232 #if defined(AVOID_WEAK_COLLECTIONS)
233 for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) {
234 CFMachPortRef mp = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx);
235 #else
236 for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) {
237 CFMachPortRef mp = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx];
238 #endif
239 if (!mp) continue;
240 // second clause cleans no-longer-wanted CFMachPorts out of our strong table
241 if (!__CFMachPortCheck(mp->_port) || (!kCFUseCollectableAllocator && 1 == CFGetRetainCount(mp))) {
242 CFRunLoopSourceRef source = NULL;
243 Boolean wasReady = (mp->_state == kCFMachPortStateReady);
244 if (wasReady) {
245 mp->_state = kCFMachPortStateInvalidating;
246 OSMemoryBarrier();
247 if (mp->_dsrc) {
248 dispatch_source_cancel(mp->_dsrc);
249 mp->_dsrc = NULL;
250 }
251 if (mp->_dsrc2) {
252 dispatch_source_cancel(mp->_dsrc2);
253 mp->_dsrc2 = NULL;
254 }
255 source = mp->_source;
256 mp->_source = NULL;
257 CFRetain(mp);
258 dispatch_async(dispatch_get_main_queue(), ^{
259 CFMachPortInvalidationCallBack cb = mp->_icallout;
260 if (cb) {
261 cb(mp, mp->_context.info);
262 }
263 if (NULL != source) {
264 CFRunLoopSourceInvalidate(source);
265 CFRelease(source);
266 }
267 void *info = mp->_context.info;
268 mp->_context.info = NULL;
269 if (mp->_context.release) {
270 mp->_context.release(info);
271 }
272 // For hashing and equality purposes, cannot get rid of _port here
273 mp->_state = kCFMachPortStateInvalid;
274 OSMemoryBarrier();
275 CFRelease(mp);
276 });
277 }
278 #if defined(AVOID_WEAK_COLLECTIONS)
279 CFArrayRemoveValueAtIndex(__CFAllMachPorts, idx);
280 #else
281 [__CFAllMachPorts removePointerAtIndex:idx];
282 #endif
283 idx--;
284 cnt--;
285 }
286 }
287 #if !defined(AVOID_WEAK_COLLECTIONS)
288 [__CFAllMachPorts compact];
289 #endif
290 };
291
292
293 static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID;
294
295 static const CFRuntimeClass __CFMachPortClass = {
296 0,
297 "CFMachPort",
298 NULL, // init
299 NULL, // copy
300 __CFMachPortDeallocate,
301 __CFMachPortEqual,
302 __CFMachPortHash,
303 NULL, //
304 __CFMachPortCopyDescription
305 };
306
307 __private_extern__ void __CFMachPortInitialize(void) {
308 __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass);
309 }
310
311 CFTypeID CFMachPortGetTypeID(void) {
312 return __kCFMachPortTypeID;
313 }
314
315 /* Note: any receive or send rights that the port contains coming in will
316 * not be cleaned up by CFMachPort; it will increment and decrement
317 * references on the port if the kernel ever allows that in the future,
318 * but will not cleanup any references you got when you got the port. */
319 CFMachPortRef _CFMachPortCreateWithPort2(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo, Boolean deathWatch) {
320 if (shouldFreeInfo) *shouldFreeInfo = true;
321 CHECK_FOR_FORK_RET(NULL);
322
323 mach_port_type_t type = 0;
324 kern_return_t ret = mach_port_type(mach_task_self(), port, &type);
325 if (KERN_SUCCESS != ret || (type & ~(MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_SEND_ONCE|MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_DNREQUEST))) {
326 if (type & ~MACH_PORT_TYPE_DEAD_NAME) {
327 CFLog(kCFLogLevelError, CFSTR("*** CFMachPortCreateWithPort(): bad Mach port parameter (0x%lx) or unsupported mysterious kind of Mach port (%d, %ld)"), (unsigned long)port, ret, (unsigned long)type);
328 }
329 return NULL;
330 }
331
332 __block CFMachPortRef mp = NULL;
333 dispatch_sync(__portQueue(), ^{
334 static Boolean portCheckerGoing = false;
335 if (!portCheckerGoing) {
336 uint64_t nanos = 63 * 1000 * 1000 * 1000ULL;
337 uint64_t leeway = 9 * 1000ULL;
338 dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, __portQueue());
339 dispatch_source_set_timer(timerSource, dispatch_time(DISPATCH_TIME_NOW, nanos), nanos, leeway);
340 dispatch_source_set_event_handler(timerSource, ^{
341 __CFMachPortChecker(true);
342 });
343 dispatch_resume(timerSource);
344 portCheckerGoing = true;
345 }
346
347 #if defined(AVOID_WEAK_COLLECTIONS)
348 for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) {
349 CFMachPortRef p = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx);
350 if (p && p->_port == port) {
351 CFRetain(p);
352 mp = p;
353 return;
354 }
355 }
356 #else
357 for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) {
358 CFMachPortRef p = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx];
359 if (p && p->_port == port) {
360 CFRetain(p);
361 mp = p;
362 return;
363 }
364 }
365 #endif
366
367 CFIndex size = sizeof(struct __CFMachPort) - sizeof(CFRuntimeBase);
368 CFMachPortRef memory = (CFMachPortRef)_CFRuntimeCreateInstance(allocator, CFMachPortGetTypeID(), size, NULL);
369 if (NULL == memory) {
370 return;
371 }
372 memory->_port = port;
373 memory->_dsrc = NULL;
374 memory->_dsrc2 = NULL;
375 memory->_dsrc_sem = NULL;
376 memory->_dsrc2_sem = NULL;
377 memory->_icallout = NULL;
378 memory->_source = NULL;
379 memory->_context.info = NULL;
380 memory->_context.retain = NULL;
381 memory->_context.release = NULL;
382 memory->_context.copyDescription = NULL;
383 memory->_callout = callout;
384 if (NULL != context) {
385 objc_memmove_collectable(&memory->_context, context, sizeof(CFMachPortContext));
386 memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info;
387 }
388 memory->_state = kCFMachPortStateReady;
389 #if defined(AVOID_WEAK_COLLECTIONS)
390 if (!__CFAllMachPorts) __CFAllMachPorts = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
391 CFArrayAppendValue(__CFAllMachPorts, memory);
392 #else
393 if (!__CFAllMachPorts) __CFAllMachPorts = [[__CFPointerArray alloc] initWithOptions:(kCFUseCollectableAllocator ? CFPointerFunctionsZeroingWeakMemory : CFPointerFunctionsStrongMemory)];
394 [__CFAllMachPorts addPointer:memory];
395 #endif
396 mp = memory;
397 if (shouldFreeInfo) *shouldFreeInfo = false;
398
399 if (type & MACH_PORT_TYPE_SEND_RIGHTS) {
400 dispatch_source_t theSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, port, DISPATCH_MACH_SEND_DEAD, __portQueue());
401 dispatch_source_set_cancel_handler(theSource, ^{
402 dispatch_release(theSource);
403 });
404 dispatch_source_set_event_handler(theSource, ^{
405 __CFMachPortChecker(false);
406 });
407 memory->_dsrc = theSource;
408 dispatch_resume(theSource);
409 }
410 if (memory->_dsrc) {
411 dispatch_source_t source = memory->_dsrc; // put these in locals so they are fully copied into the block
412 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
413 memory->_dsrc_sem = sem;
414 dispatch_source_set_cancel_handler(memory->_dsrc, ^{ dispatch_semaphore_signal(sem); dispatch_release(source); });
415 }
416 if (memory->_dsrc2) {
417 dispatch_source_t source = memory->_dsrc2;
418 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
419 memory->_dsrc2_sem = sem;
420 dispatch_source_set_cancel_handler(memory->_dsrc2, ^{ dispatch_semaphore_signal(sem); dispatch_release(source); });
421 }
422 });
423 if (mp && !CFMachPortIsValid(mp)) { // must do this outside lock to avoid deadlock
424 CFRelease(mp);
425 mp = NULL;
426 }
427 return mp;
428 }
429
430 CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
431 return _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true);
432 }
433
434 CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
435 if (shouldFreeInfo) *shouldFreeInfo = true;
436 CHECK_FOR_FORK_RET(NULL);
437 mach_port_t port = MACH_PORT_NULL;
438 kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
439 if (KERN_SUCCESS == ret) {
440 ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
441 }
442 if (KERN_SUCCESS != ret) {
443 if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port);
444 return NULL;
445 }
446 CFMachPortRef result = _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true);
447 if (NULL == result) {
448 if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port);
449 return NULL;
450 }
451 __CFMachPortSetHasReceive(result);
452 __CFMachPortSetHasSend(result);
453 return result;
454 }
455
456 void CFMachPortInvalidate(CFMachPortRef mp) {
457 CHECK_FOR_FORK_RET();
458 CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), void, mp, "invalidate");
459 __CFGenericValidateType(mp, CFMachPortGetTypeID());
460 CFRetain(mp);
461 __block CFRunLoopSourceRef source = NULL;
462 __block Boolean wasReady = false;
463 dispatch_sync(__portQueue(), ^{
464 wasReady = (mp->_state == kCFMachPortStateReady);
465 if (wasReady) {
466 mp->_state = kCFMachPortStateInvalidating;
467 OSMemoryBarrier();
468 #if defined(AVOID_WEAK_COLLECTIONS)
469 for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) {
470 CFMachPortRef p = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx);
471 if (p == mp) {
472 CFArrayRemoveValueAtIndex(__CFAllMachPorts, idx);
473 break;
474 }
475 }
476 #else
477 for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) {
478 CFMachPortRef p = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx];
479 if (p == mp) {
480 [__CFAllMachPorts removePointerAtIndex:idx];
481 break;
482 }
483 }
484 #endif
485 if (mp->_dsrc) {
486 dispatch_source_cancel(mp->_dsrc);
487 mp->_dsrc = NULL;
488 }
489 if (mp->_dsrc2) {
490 dispatch_source_cancel(mp->_dsrc2);
491 mp->_dsrc2 = NULL;
492 }
493 source = mp->_source;
494 mp->_source = NULL;
495 }
496 });
497 if (wasReady) {
498 CFMachPortInvalidationCallBack cb = mp->_icallout;
499 if (cb) {
500 cb(mp, mp->_context.info);
501 }
502 if (NULL != source) {
503 CFRunLoopSourceInvalidate(source);
504 CFRelease(source);
505 }
506 void *info = mp->_context.info;
507 mp->_context.info = NULL;
508 if (mp->_context.release) {
509 mp->_context.release(info);
510 }
511 // For hashing and equality purposes, cannot get rid of _port here
512 mp->_state = kCFMachPortStateInvalid;
513 OSMemoryBarrier();
514 }
515 CFRelease(mp);
516 }
517
518 mach_port_t CFMachPortGetPort(CFMachPortRef mp) {
519 CHECK_FOR_FORK_RET(0);
520 CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), mach_port_t, mp, "machPort");
521 __CFGenericValidateType(mp, CFMachPortGetTypeID());
522 return mp->_port;
523 }
524
525 void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) {
526 __CFGenericValidateType(mp, CFMachPortGetTypeID());
527 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
528 objc_memmove_collectable(context, &mp->_context, sizeof(CFMachPortContext));
529 }
530
531 Boolean CFMachPortIsValid(CFMachPortRef mp) {
532 CF_OBJC_FUNCDISPATCH0(CFMachPortGetTypeID(), Boolean, mp, "isValid");
533 __CFGenericValidateType(mp, CFMachPortGetTypeID());
534 if (!__CFMachPortIsValid(mp)) return false;
535 mach_port_type_t type = 0;
536 kern_return_t ret = mach_port_type(mach_task_self(), mp->_port, &type);
537 if (KERN_SUCCESS != ret || (type & ~(MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_SEND_ONCE|MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_DNREQUEST))) {
538 CFRetain(mp);
539 dispatch_async(dispatch_get_global_queue(0, 0), ^{
540 CFMachPortInvalidate(mp);
541 CFRelease(mp);
542 });
543 return false;
544 }
545 return true;
546 }
547
548 CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) {
549 __CFGenericValidateType(mp, CFMachPortGetTypeID());
550 return mp->_icallout;
551 }
552
553 /* After the CFMachPort has started going invalid, or done invalid, you can't change this, and
554 we'll only do the callout directly on a transition from NULL to non-NULL. */
555 void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) {
556 CHECK_FOR_FORK_RET();
557 __CFGenericValidateType(mp, CFMachPortGetTypeID());
558 if (__CFMachPortIsValid(mp) || !callout) {
559 mp->_icallout = callout;
560 } else if (!mp->_icallout && callout) {
561 callout(mp, mp->_context.info);
562 } else {
563 CFLog(kCFLogLevelWarning, CFSTR("CFMachPortSetInvalidationCallBack(): attempt to set invalidation callback (%p) on invalid CFMachPort (%p) thwarted"), callout, mp);
564 }
565 }
566
567 /* Returns the number of messages queued for a receive port. */
568 CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) {
569 CHECK_FOR_FORK_RET(0);
570 __CFGenericValidateType(mp, CFMachPortGetTypeID());
571 mach_port_status_t status;
572 mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT;
573 kern_return_t ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num);
574 return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount;
575 }
576
577 static mach_port_t __CFMachPortGetPort(void *info) {
578 CFMachPortRef mp = (CFMachPortRef)info;
579 return mp->_port;
580 }
581
582 static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
583 CHECK_FOR_FORK_RET(NULL);
584 CFMachPortRef mp = (CFMachPortRef)info;
585 __block Boolean isValid = false;
586 dispatch_sync(__portQueue(), ^{
587 isValid = __CFMachPortIsValid(mp);
588 });
589 if (!isValid) return NULL;
590
591 void *context_info = NULL;
592 void (*context_release)(const void *) = NULL;
593 if (mp->_context.retain) {
594 context_info = (void *)mp->_context.retain(mp->_context.info);
595 context_release = mp->_context.release;
596 } else {
597 context_info = mp->_context.info;
598 }
599
600 mp->_callout(mp, msg, size, context_info);
601
602 if (context_release) {
603 context_release(context_info);
604 }
605 CHECK_FOR_FORK_RET(NULL);
606 return NULL;
607 }
608
609 CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) {
610 CHECK_FOR_FORK_RET(NULL);
611 __CFGenericValidateType(mp, CFMachPortGetTypeID());
612 if (!CFMachPortIsValid(mp)) return NULL;
613 __block CFRunLoopSourceRef result = NULL;
614 dispatch_sync(__portQueue(), ^{
615 if (!__CFMachPortIsValid(mp)) return;
616 if (NULL != mp->_source && !CFRunLoopSourceIsValid(mp->_source)) {
617 CFRelease(mp->_source);
618 mp->_source = NULL;
619 }
620 if (NULL == mp->_source) {
621 CFRunLoopSourceContext1 context;
622 context.version = 1;
623 context.info = (void *)mp;
624 context.retain = (const void *(*)(const void *))CFRetain;
625 context.release = (void (*)(const void *))CFRelease;
626 context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription;
627 context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual;
628 context.hash = (CFHashCode (*)(const void *))__CFMachPortHash;
629 context.getPort = __CFMachPortGetPort;
630 context.perform = __CFMachPortPerform;
631 mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
632 }
633 result = mp->_source ? (CFRunLoopSourceRef)CFRetain(mp->_source) : NULL;
634 });
635 return result;
636 }
637