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