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