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