| /* |
| * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. |
| * |
| * This source code is a modified version of the CoreFoundation sources released by Apple Inc. under |
| * the terms of the APSL version 2.0 (see below). |
| * |
| * For information about changes from the original Apple source release can be found by reviewing the |
| * source control system for the project at https://sourceforge.net/svn/?group_id=246198. |
| * |
| * The original license information is as follows: |
| * |
| * Copyright (c) 2008 Apple Inc. All rights reserved. |
| * |
| * @APPLE_LICENSE_HEADER_START@ |
| * |
| * This file contains Original Code and/or Modifications of Original Code |
| * as defined in and that are subject to the Apple Public Source License |
| * Version 2.0 (the 'License'). You may not use this file except in |
| * compliance with the License. Please obtain a copy of the License at |
| * http://www.opensource.apple.com/apsl/ and read it before using this |
| * file. |
| * |
| * The Original Code and all software distributed under the License are |
| * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| * Please see the License for the specific language governing rights and |
| * limitations under the License. |
| * |
| * @APPLE_LICENSE_HEADER_END@ |
| */ |
| /* CFMachPort.c |
| Copyright 1998-2002, Apple, Inc. All rights reserved. |
| Responsibility: Christopher Kane |
| */ |
| |
| /* |
| [The following dissertation was written mostly for the |
| benefit of open source developers.] |
| |
| Writing a run loop source can be a tricky business, but |
| for CFMachPort that part is relatively straightforward. |
| Thus, it makes a good example for study. Particularly |
| interesting for examination is the process of caching |
| the objects in a non-retaining cache, the invalidation |
| and deallocation sequences, locking for thread-safety, |
| and how the invalidation callback is used. |
| |
| CFMachPort is a version 1 CFRunLoopSource, implemented |
| by a few functions. See CFMachPortCreateRunLoopSource() |
| for details on how the run loop source is setup. Note |
| how the source is kept track of by the CFMachPort, so |
| that it can be returned again and again from that function. |
| This helps not only reduce the amount of memory expended |
| in run loop source objects, but eliminates redundant |
| registrations with the run loop and the excess time and |
| memory that would consume. It also allows the CFMachPort |
| to propogate its own invalidation to the run loop source |
| representing it. |
| |
| CFMachPortCreateWithPort() is the funnel point for the |
| creation of CFMachPort instances. The cache is first |
| probed to see if an instance with that port is already |
| available, and return that. The object is next allocated |
| and mostly initialized, before it is registered for death |
| notification. This is because cleaning up the memory is |
| simpler than trying to get rid of the registration if |
| memory allocation later fails. The new object must be at |
| least partially initialized (into a harmless state) so |
| that it can be safely invalidated/deallocated if something |
| fails later in creation. Any object allocated with |
| _CFRuntimeCreateInstance() may only be disposed by using |
| CFRelease() (never CFAllocatorDeallocate!) so the class |
| deallocation function __CFMachPortDeallocate() must be |
| able to handle that, and initializing the object to have |
| NULL fields and whatnot makes that possible. The creation |
| eventually inserts the new object in the cache. |
| |
| A CFMachPort may be explicitly invalidated, autoinvalidated |
| due to the death of the port (that process is not discussed |
| further here), or invalidated as part of the deallocation |
| process when the last reference is released. For |
| convenience, in all cases this is done through |
| CFMachPortInvalidate(). To prevent the CFMachPort from |
| being freed in mid-function due to the callouts, the object |
| is retained at the beginning of the function. But if this |
| invalidation is due to the object being deallocated, a |
| retain and then release at the end of the function would |
| cause a recursive call to __CFMachPortDeallocate(). The |
| retain protection should be immaterial though at that stage. |
| Invalidation also removes the object from the cache; though |
| the object itself is not yet destroyed, invalidation makes |
| it "useless". |
| |
| The best way to learn about the locking is to look through |
| the code -- it's fairly straightforward. The one thing |
| worth calling attention to is how locks must be unlocked |
| before invoking any user-defined callout function, and |
| usually retaken after it returns. This supports reentrancy |
| (which is distinct from thread-safety). |
| |
| The invalidation callback, if one has been set, is called |
| at invalidation time, but before the object has been torn |
| down so that the port and other properties may be retrieved |
| from the object in the callback. Note that if the callback |
| is attempted to be set after the CFMachPort is invalid, |
| the function is simply called. This helps with certain |
| race conditions where the invalidation notification might |
| be lost. Only the owner/creator of a CFMachPort should |
| really be setting the invalidation callback. |
| |
| Now, the CFMachPort is not retained/released around all |
| callouts, but the callout may release the last reference. |
| Also, sometimes it is friendly to retain/release the |
| user-defined "info" around callouts, so that clients |
| don't have to worry about that. These may be some things |
| to think about in the future, but is usually overkill. |
| |
| In general, with higher level functionalities in the system, |
| it isn't even possible for a process to fork() and the child |
| not exec(), but continue running, since the higher levels |
| have done one-time initializations that aren't going to |
| happen again. |
| |
| - Chris Kane |
| |
| */ |
| |
| #if DEPLOYMENT_TARGET_MACOSX |
| |
| #include <CoreFoundation/CFMachPort.h> |
| #include <CoreFoundation/CFRunLoop.h> |
| #include <CoreFoundation/CFDictionary.h> |
| #include <CoreFoundation/CFByteOrder.h> |
| #include <mach/mach.h> |
| #include <mach/mach_error.h> |
| #include <mach/notify.h> |
| #include <unistd.h> |
| #include "CFInternal.h" |
| #include <dlfcn.h> |
| |
| static CFSpinLock_t __CFAllMachPortsLock = CFSpinLockInit; |
| static CFMutableDictionaryRef __CFAllMachPorts = NULL; |
| static mach_port_t __CFNotifyRawMachPort = MACH_PORT_NULL; |
| static CFMachPortRef __CFNotifyMachPort = NULL; |
| |
| struct __CFMachPort { |
| CFRuntimeBase _base; |
| CFSpinLock_t _lock; |
| mach_port_t _port; /* immutable; invalidated */ |
| mach_port_t _oldnotify; /* immutable; invalidated */ |
| CFRunLoopSourceRef _source; /* immutable, once created; invalidated */ |
| CFMachPortInvalidationCallBack _icallout; |
| CFMachPortCallBack _callout; /* immutable */ |
| CFMachPortContext _context; /* immutable; invalidated */ |
| }; |
| |
| /* Bit 0 in the base reserved bits is used for invalid state */ |
| /* Bit 1 in the base reserved bits is used for has-receive-ref state */ |
| /* Bit 2 in the base reserved bits is used for has-send-ref state */ |
| /* Bit 3 in the base reserved bits is used for is-deallocing state */ |
| |
| CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0); |
| } |
| |
| CF_INLINE void __CFMachPortSetValid(CFMachPortRef mp) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0, 1); |
| } |
| |
| CF_INLINE void __CFMachPortUnsetValid(CFMachPortRef mp) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0, 0); |
| } |
| |
| CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1); |
| } |
| |
| CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1, 1); |
| } |
| |
| CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2); |
| } |
| |
| CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2, 1); |
| } |
| |
| CF_INLINE Boolean __CFMachPortIsDeallocing(CFMachPortRef mp) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3); |
| } |
| |
| CF_INLINE void __CFMachPortSetIsDeallocing(CFMachPortRef mp) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3, 1); |
| } |
| |
| CF_INLINE void __CFMachPortLock(CFMachPortRef mp) { |
| __CFSpinLock(&(mp->_lock)); |
| } |
| |
| CF_INLINE void __CFMachPortUnlock(CFMachPortRef mp) { |
| __CFSpinUnlock(&(mp->_lock)); |
| } |
| |
| void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) { |
| CFRunLoopSourceRef source; |
| if (NULL == __CFNotifyMachPort) return; |
| source = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, __CFNotifyMachPort, -1000); |
| CFRunLoopAddSource(rl, source, mode); |
| CFRelease(source); |
| } |
| |
| static void __CFNotifyDeadMachPort(CFMachPortRef port, void *msg, CFIndex size, void *info) { |
| mach_msg_header_t *header = (mach_msg_header_t *)msg; |
| mach_port_t dead_port = MACH_PORT_NULL; |
| if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) { |
| dead_port = ((mach_dead_name_notification_t *)msg)->not_port; |
| if (((mach_dead_name_notification_t *)msg)->NDR.int_rep != NDR_record.int_rep) { |
| dead_port = CFSwapInt32(dead_port); |
| } |
| } else if (header && header->msgh_id == MACH_NOTIFY_PORT_DELETED) { |
| dead_port = ((mach_port_deleted_notification_t *)msg)->not_port; |
| if (((mach_port_deleted_notification_t *)msg)->NDR.int_rep != NDR_record.int_rep) { |
| dead_port = CFSwapInt32(dead_port); |
| } |
| } else { |
| return; |
| } |
| |
| CFMachPortRef existing; |
| /* If the CFMachPort has already been invalidated, it won't be found here. */ |
| __CFSpinLock(&__CFAllMachPortsLock); |
| if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)(uintptr_t)dead_port, (const void **)&existing)) { |
| CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(uintptr_t)dead_port); |
| CFRetain(existing); |
| __CFSpinUnlock(&__CFAllMachPortsLock); |
| __CFMachPortLock(existing); |
| mach_port_t old_port = existing->_oldnotify; |
| existing->_oldnotify = MACH_PORT_NULL; |
| __CFMachPortUnlock(existing); |
| if (MACH_PORT_NULL != old_port) { |
| header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) | MACH_MSGH_BITS_COMPLEX; |
| header->msgh_local_port = MACH_PORT_NULL; |
| header->msgh_remote_port = old_port; |
| mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
| } |
| CFMachPortInvalidate(existing); |
| CFRelease(existing); |
| } else { |
| __CFSpinUnlock(&__CFAllMachPortsLock); |
| } |
| |
| if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) { |
| /* Delete port reference we got for this notification */ |
| mach_port_deallocate(mach_task_self(), dead_port); |
| } |
| } |
| |
| static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) { |
| CFMachPortRef mp1 = (CFMachPortRef)cf1; |
| CFMachPortRef mp2 = (CFMachPortRef)cf2; |
| return (mp1->_port == mp2->_port); |
| } |
| |
| static CFHashCode __CFMachPortHash(CFTypeRef cf) { |
| CHECK_FOR_FORK(); |
| CFMachPortRef mp = (CFMachPortRef)cf; |
| return (CFHashCode)mp->_port; |
| } |
| |
| static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) { |
| CFMachPortRef mp = (CFMachPortRef)cf; |
| CFStringRef result; |
| const char *locked; |
| CFStringRef contextDesc = NULL; |
| locked = mp->_lock ? "Yes" : "No"; |
| if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) { |
| contextDesc = mp->_context.copyDescription(mp->_context.info); |
| } |
| if (NULL == contextDesc) { |
| contextDesc = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info); |
| } |
| void *addr = (void*)mp->_callout; |
| Dl_info info; |
| const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; |
| result = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort %p [%p]>{locked = %s, valid = %s, port = %p, source = %p, callout = %s (%p), context = %@}"), cf, CFGetAllocator(mp), locked, (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, name, addr, (NULL != contextDesc ? contextDesc : CFSTR("<no description>"))); |
| if (NULL != contextDesc) { |
| CFRelease(contextDesc); |
| } |
| return result; |
| } |
| |
| static void __CFMachPortDeallocate(CFTypeRef cf) { |
| CHECK_FOR_FORK(); |
| CFMachPortRef mp = (CFMachPortRef)cf; |
| __CFMachPortSetIsDeallocing(mp); |
| CFMachPortInvalidate(mp); |
| // MUST deallocate the send right FIRST if necessary, |
| // then the receive right if necessary. Don't ask me why; |
| // if it's done in the other order the port will leak. |
| if (__CFMachPortHasSend(mp)) { |
| mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_SEND, -1); |
| } |
| if (__CFMachPortHasReceive(mp)) { |
| mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, -1); |
| } |
| } |
| |
| static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID; |
| |
| static const CFRuntimeClass __CFMachPortClass = { |
| 0, |
| "CFMachPort", |
| NULL, // init |
| NULL, // copy |
| __CFMachPortDeallocate, |
| __CFMachPortEqual, |
| __CFMachPortHash, |
| NULL, // |
| __CFMachPortCopyDescription |
| }; |
| |
| __private_extern__ void __CFMachPortInitialize(void) { |
| __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass); |
| } |
| |
| CFTypeID CFMachPortGetTypeID(void) { |
| return __kCFMachPortTypeID; |
| } |
| |
| CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) { |
| CFMachPortRef result; |
| mach_port_t port; |
| kern_return_t ret; |
| if (shouldFreeInfo) *shouldFreeInfo = true; |
| ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); |
| if (KERN_SUCCESS != ret) { |
| return NULL; |
| } |
| ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); |
| if (KERN_SUCCESS != ret) { |
| mach_port_destroy(mach_task_self(), port); |
| return NULL; |
| } |
| result = CFMachPortCreateWithPort(allocator, port, callout, context, shouldFreeInfo); |
| if (NULL != result) { |
| __CFMachPortSetHasReceive(result); |
| __CFMachPortSetHasSend(result); |
| } |
| return result; |
| } |
| |
| /* Note: any receive or send rights that the port contains coming in will |
| * not be cleaned up by CFMachPort; it will increment and decrement |
| * references on the port if the kernel ever allows that in the future, |
| * but will not cleanup any references you got when you got the port. */ |
| CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) { |
| CHECK_FOR_FORK(); |
| CFMachPortRef memory; |
| SInt32 size; |
| Boolean didCreateNotifyPort = false; |
| CFRunLoopSourceRef source; |
| if (shouldFreeInfo) *shouldFreeInfo = true; |
| __CFSpinLock(&__CFAllMachPortsLock); |
| if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)(uintptr_t)port, (const void **)&memory)) { |
| CFRetain(memory); |
| __CFSpinUnlock(&__CFAllMachPortsLock); |
| return (CFMachPortRef)(memory); |
| } |
| size = sizeof(struct __CFMachPort) - sizeof(CFRuntimeBase); |
| memory = (CFMachPortRef)_CFRuntimeCreateInstance(allocator, __kCFMachPortTypeID, size, NULL); |
| if (NULL == memory) { |
| __CFSpinUnlock(&__CFAllMachPortsLock); |
| return NULL; |
| } |
| __CFMachPortUnsetValid(memory); |
| memory->_lock = CFSpinLockInit; |
| memory->_port = port; |
| memory->_source = NULL; |
| memory->_icallout = NULL; |
| memory->_context.info = NULL; |
| memory->_context.retain = NULL; |
| memory->_context.release = NULL; |
| memory->_context.copyDescription = NULL; |
| if (MACH_PORT_NULL == __CFNotifyRawMachPort) { |
| kern_return_t ret; |
| ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &__CFNotifyRawMachPort); |
| if (KERN_SUCCESS != ret) { |
| __CFSpinUnlock(&__CFAllMachPortsLock); |
| CFRelease(memory); |
| return NULL; |
| } |
| didCreateNotifyPort = true; |
| } |
| // Do not register for notifications on the notify port |
| if (MACH_PORT_NULL != __CFNotifyRawMachPort && port != __CFNotifyRawMachPort) { |
| mach_port_t old_port; |
| kern_return_t ret; |
| old_port = MACH_PORT_NULL; |
| ret = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 0, __CFNotifyRawMachPort, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old_port); |
| if (ret != KERN_SUCCESS) { |
| __CFSpinUnlock(&__CFAllMachPortsLock); |
| CFRelease(memory); |
| return NULL; |
| } |
| memory->_oldnotify = old_port; |
| } |
| __CFMachPortSetValid(memory); |
| memory->_callout = callout; |
| if (NULL != context) { |
| CF_WRITE_BARRIER_MEMMOVE(&memory->_context, context, sizeof(CFMachPortContext)); |
| memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info; |
| } |
| if (NULL == __CFAllMachPorts) { |
| __CFAllMachPorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); |
| _CFDictionarySetCapacity(__CFAllMachPorts, 20); |
| } |
| CFDictionaryAddValue(__CFAllMachPorts, (void *)(uintptr_t)port, memory); |
| __CFSpinUnlock(&__CFAllMachPortsLock); |
| if (didCreateNotifyPort) { |
| // __CFNotifyMachPort ends up in cache |
| CFMachPortRef mp = CFMachPortCreateWithPort(kCFAllocatorSystemDefault, __CFNotifyRawMachPort, __CFNotifyDeadMachPort, NULL, NULL); |
| __CFMachPortSetHasReceive(mp); |
| __CFNotifyMachPort = mp; |
| } |
| if (NULL != __CFNotifyMachPort) { |
| // We do this so that it gets into each thread's run loop, since |
| // we don't know which run loop is the main thread's, and that's |
| // not necessarily the "right" one anyway. This won't happen for |
| // the call which creates the __CFNotifyMachPort itself, but that's |
| // OK since it will happen in the invocation of this function |
| // from which that call was triggered. |
| source = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, __CFNotifyMachPort, -1000); |
| CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); |
| CFRelease(source); |
| } |
| if (shouldFreeInfo) *shouldFreeInfo = false; |
| return memory; |
| } |
| |
| mach_port_t CFMachPortGetPort(CFMachPortRef mp) { |
| CHECK_FOR_FORK(); |
| CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, mach_port_t, mp, "machPort"); |
| __CFGenericValidateType(mp, __kCFMachPortTypeID); |
| return mp->_port; |
| } |
| |
| void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) { |
| __CFGenericValidateType(mp, __kCFMachPortTypeID); |
| CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); |
| CF_WRITE_BARRIER_MEMMOVE(context, &mp->_context, sizeof(CFMachPortContext)); |
| } |
| |
| void CFMachPortInvalidate(CFMachPortRef mp) { |
| CHECK_FOR_FORK(); |
| CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, void, mp, "invalidate"); |
| __CFGenericValidateType(mp, __kCFMachPortTypeID); |
| if (!__CFMachPortIsDeallocing(mp)) { |
| CFRetain(mp); |
| } |
| __CFSpinLock(&__CFAllMachPortsLock); |
| if (NULL != __CFAllMachPorts) { |
| CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(uintptr_t)(mp->_port)); |
| } |
| __CFSpinUnlock(&__CFAllMachPortsLock); |
| __CFMachPortLock(mp); |
| if (__CFMachPortIsValid(mp)) { |
| CFRunLoopSourceRef source; |
| void *info; |
| mach_port_t old_port = mp->_oldnotify; |
| CFMachPortInvalidationCallBack callout = mp->_icallout; |
| __CFMachPortUnsetValid(mp); |
| __CFMachPortUnlock(mp); |
| if (NULL != callout) { |
| callout(mp, mp->_context.info); |
| } |
| __CFMachPortLock(mp); |
| // For hashing and equality purposes, cannot get rid of _port here |
| source = mp->_source; |
| mp->_source = NULL; |
| info = mp->_context.info; |
| mp->_context.info = NULL; |
| __CFMachPortUnlock(mp); |
| if (NULL != mp->_context.release) { |
| mp->_context.release(info); |
| } |
| if (NULL != source) { |
| CFRunLoopSourceInvalidate(source); |
| CFRelease(source); |
| } |
| if (MACH_PORT_NULL != old_port) { |
| mach_port_deallocate(mach_task_self(), old_port); |
| } |
| } else { |
| __CFMachPortUnlock(mp); |
| } |
| if (!__CFMachPortIsDeallocing(mp)) { |
| CFRelease(mp); |
| } |
| } |
| |
| Boolean CFMachPortIsValid(CFMachPortRef mp) { |
| CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, Boolean, mp, "isValid"); |
| __CFGenericValidateType(mp, __kCFMachPortTypeID); |
| return __CFMachPortIsValid(mp); |
| } |
| |
| CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) { |
| __CFGenericValidateType(mp, __kCFMachPortTypeID); |
| return mp->_icallout; |
| } |
| |
| void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) { |
| __CFGenericValidateType(mp, __kCFMachPortTypeID); |
| if (!__CFMachPortIsValid(mp) && NULL != callout) { |
| callout(mp, mp->_context.info); |
| } else { |
| mp->_icallout = callout; |
| } |
| } |
| |
| /* Returns the number of messages queued for a receive port. */ |
| CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) { |
| CHECK_FOR_FORK(); |
| mach_port_status_t status; |
| mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT; |
| kern_return_t ret; |
| ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num); |
| return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount; |
| } |
| |
| static mach_port_t __CFMachPortGetPort(void *info) { |
| CFMachPortRef mp = (CFMachPortRef)info; |
| return mp->_port; |
| } |
| |
| static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { |
| CHECK_FOR_FORK(); |
| CFMachPortRef mp = (CFMachPortRef)info; |
| void *context_info; |
| void (*context_release)(const void *); |
| __CFMachPortLock(mp); |
| if (!__CFMachPortIsValid(mp)) { |
| __CFMachPortUnlock(mp); |
| return NULL; |
| } |
| if (NULL != mp->_context.retain) { |
| context_info = (void *)mp->_context.retain(mp->_context.info); |
| context_release = mp->_context.release; |
| } else { |
| context_info = mp->_context.info; |
| context_release = NULL; |
| } |
| __CFMachPortUnlock(mp); |
| mp->_callout(mp, msg, size, mp->_context.info); |
| CHECK_FOR_FORK(); |
| if (context_release) { |
| context_release(context_info); |
| } |
| return NULL; |
| } |
| |
| CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) { |
| CHECK_FOR_FORK(); |
| CFRunLoopSourceRef result = NULL; |
| __CFGenericValidateType(mp, __kCFMachPortTypeID); |
| __CFMachPortLock(mp); |
| if (!__CFMachPortIsValid(mp)) { |
| __CFMachPortUnlock(mp); |
| return NULL; |
| } |
| if (NULL == mp->_source) { |
| CFRunLoopSourceContext1 context; |
| context.version = 1; |
| context.info = (void *)mp; |
| context.retain = (const void *(*)(const void *))CFRetain; |
| context.release = (void (*)(const void *))CFRelease; |
| context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription; |
| context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual; |
| context.hash = (CFHashCode (*)(const void *))__CFMachPortHash; |
| context.getPort = __CFMachPortGetPort; |
| context.perform = __CFMachPortPerform; |
| mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context); |
| } |
| if (NULL != mp->_source) { |
| result = (CFRunLoopSourceRef)CFRetain(mp->_source); |
| } |
| __CFMachPortUnlock(mp); |
| return result; |
| } |
| |
| #endif |