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