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