#if defined(__MACH__)
#include <CoreFoundation/CFMachPort.h>
#include <CoreFoundation/CFRunLoop.h>
#include <CoreFoundation/CFDictionary.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/notify.h>
#include "CFInternal.h"
static CFSpinLock_t __CFAllMachPortsLock = 0;
static CFMutableDictionaryRef __CFAllMachPorts = NULL;
static mach_port_t __CFNotifyRawMachPort = MACH_PORT_NULL;
static CFMachPortRef __CFNotifyMachPort = NULL;
static int __CFMachPortCurrentPID = 0;
struct __CFMachPort {
CFRuntimeBase _base;
CFSpinLock_t _lock;
mach_port_t _port;
mach_port_t _oldnotify;
CFRunLoopSourceRef _source;
CFMachPortInvalidationCallBack _icallout;
CFMachPortCallBack _callout;
CFMachPortContext _context;
};
CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 0, 0);
}
CF_INLINE void __CFMachPortSetValid(CFMachPortRef mp) {
__CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 0, 0, 1);
}
CF_INLINE void __CFMachPortUnsetValid(CFMachPortRef mp) {
__CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 0, 0, 0);
}
CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 1, 1);
}
CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) {
__CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 1, 1, 1);
}
CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 2, 2);
}
CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) {
__CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 2, 2, 1);
}
CF_INLINE Boolean __CFMachPortIsDeallocing(CFMachPortRef mp) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 3, 3);
}
CF_INLINE void __CFMachPortSetIsDeallocing(CFMachPortRef mp) {
__CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 3, 3, 1);
}
CF_INLINE void __CFMachPortLock(CFMachPortRef mp) {
__CFSpinLock(&(mp->_lock));
}
CF_INLINE void __CFMachPortUnlock(CFMachPortRef mp) {
__CFSpinUnlock(&(mp->_lock));
}
static void __CFMachPortDidFork(void) {
CFMachPortRef oldNotify;
__CFMachPortCurrentPID = getpid();
__CFSpinLock(&__CFAllMachPortsLock);
oldNotify = __CFNotifyMachPort;
__CFNotifyMachPort = NULL;
__CFNotifyRawMachPort = MACH_PORT_NULL;
if (NULL != __CFAllMachPorts) {
CFIndex idx, cnt;
CFMachPortRef *mps, buffer[128];
cnt = CFDictionaryGetCount(__CFAllMachPorts);
mps = (cnt <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, cnt * sizeof(CFMachPortRef), 0);
CFDictionaryGetKeysAndValues(__CFAllMachPorts, NULL, (const void **)mps);
CFRelease(__CFAllMachPorts);
__CFAllMachPorts = NULL;
__CFSpinUnlock(&__CFAllMachPortsLock);
for (idx = 0; idx < cnt; idx++) {
CFMachPortInvalidate(mps[idx]);
}
if (mps != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, mps);
} else {
__CFSpinUnlock(&__CFAllMachPortsLock);
}
if (NULL != oldNotify) {
CFRelease(oldNotify);
}
}
CF_INLINE void __CFMachPortCheckForFork(void) {
if (getpid() != __CFMachPortCurrentPID) {
__CFMachPortDidFork();
}
}
void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) {
CFRunLoopSourceRef source;
if (NULL == __CFNotifyMachPort) return;
source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, __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;
if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) {
mach_port_t dead_port = ((mach_dead_name_notification_t *)msg)->not_port;
CFMachPortRef existing;
__CFSpinLock(&__CFAllMachPortsLock);
if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)dead_port, (const void **)&existing)) {
CFDictionaryRemoveValue(__CFAllMachPorts, (void *)dead_port);
CFRetain(existing);
__CFSpinUnlock(&__CFAllMachPortsLock);
CFMachPortInvalidate(existing);
CFRelease(existing);
} else {
__CFSpinUnlock(&__CFAllMachPortsLock);
}
mach_port_deallocate(mach_task_self(), dead_port);
} else if (header && header->msgh_id == MACH_NOTIFY_PORT_DELETED) {
mach_port_t dead_port = ((mach_port_deleted_notification_t *)msg)->not_port;
CFMachPortRef existing;
__CFSpinLock(&__CFAllMachPortsLock);
if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)dead_port, (const void **)&existing)) {
CFDictionaryRemoveValue(__CFAllMachPorts, (void *)dead_port);
CFRetain(existing);
__CFSpinUnlock(&__CFAllMachPortsLock);
CFMachPortInvalidate(existing);
CFRelease(existing);
} else {
__CFSpinUnlock(&__CFAllMachPortsLock);
}
}
}
static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) {
CFMachPortRef mp1 = (CFMachPortRef)cf1;
CFMachPortRef mp2 = (CFMachPortRef)cf2;
__CFMachPortCheckForFork();
return (mp1->_port == mp2->_port);
}
static CFHashCode __CFMachPortHash(CFTypeRef cf) {
CFMachPortRef mp = (CFMachPortRef)cf;
__CFMachPortCheckForFork();
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);
}
result = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort %p [%p]>{locked = %s, valid = %s, port = %p, source = %p, callout = %p, context = %@}"), cf, CFGetAllocator(mp), locked, (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, mp->_callout, (NULL != contextDesc ? contextDesc : CFSTR("<no description>")));
if (NULL != contextDesc) {
CFRelease(contextDesc);
}
return result;
}
static void __CFMachPortDeallocate(CFTypeRef cf) {
CFMachPortRef mp = (CFMachPortRef)cf;
__CFMachPortSetIsDeallocing(mp);
CFMachPortInvalidate(mp);
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);
}
__CFMachPortCheckForFork();
}
static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFMachPortClass = {
0,
"CFMachPort",
NULL, NULL, __CFMachPortDeallocate,
__CFMachPortEqual,
__CFMachPortHash,
NULL, __CFMachPortCopyDescription
};
__private_extern__ void __CFMachPortInitialize(void) {
__kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass);
__CFMachPortCurrentPID = getpid();
}
CFTypeID CFMachPortGetTypeID(void) {
__CFMachPortCheckForFork();
return __kCFMachPortTypeID;
}
CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
CFMachPortRef result;
mach_port_t port;
kern_return_t ret;
__CFMachPortCheckForFork();
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;
}
CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
CFMachPortRef memory;
SInt32 size;
Boolean didCreateNotifyPort = false;
CFRunLoopSourceRef source;
__CFMachPortCheckForFork();
if (shouldFreeInfo) *shouldFreeInfo = true;
__CFSpinLock(&__CFAllMachPortsLock);
if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)port, (const void **)&memory)) {
__CFSpinUnlock(&__CFAllMachPortsLock);
return (CFMachPortRef)CFRetain(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 = 0;
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;
}
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) {
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 *)port, memory);
__CFSpinUnlock(&__CFAllMachPortsLock);
if (didCreateNotifyPort) {
CFMachPortRef mp = CFMachPortCreateWithPort(kCFAllocatorDefault, __CFNotifyRawMachPort, __CFNotifyDeadMachPort, NULL, NULL);
__CFMachPortSetHasReceive(mp);
__CFNotifyMachPort = mp;
}
if (NULL != __CFNotifyMachPort) {
source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, __CFNotifyMachPort, -1000);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
CFRelease(source);
}
if (shouldFreeInfo) *shouldFreeInfo = false;
return memory;
}
mach_port_t CFMachPortGetPort(CFMachPortRef mp) {
CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, mach_port_t, mp, "machPort");
__CFGenericValidateType(mp, __kCFMachPortTypeID);
__CFMachPortCheckForFork();
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__);
__CFMachPortCheckForFork();
memmove(context, &mp->_context, sizeof(CFMachPortContext));
}
void CFMachPortInvalidate(CFMachPortRef mp) {
CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, void, mp, "invalidate");
__CFGenericValidateType(mp, __kCFMachPortTypeID);
__CFMachPortCheckForFork();
if (!__CFMachPortIsDeallocing(mp)) {
CFRetain(mp);
}
__CFSpinLock(&__CFAllMachPortsLock);
if (NULL != __CFAllMachPorts) {
CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(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);
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);
__CFMachPortCheckForFork();
return __CFMachPortIsValid(mp);
}
CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) {
__CFGenericValidateType(mp, __kCFMachPortTypeID);
__CFMachPortCheckForFork();
return mp->_icallout;
}
void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) {
__CFGenericValidateType(mp, __kCFMachPortTypeID);
__CFMachPortCheckForFork();
if (!__CFMachPortIsValid(mp) && NULL != callout) {
callout(mp, mp->_context.info);
} else {
mp->_icallout = callout;
}
}
CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) {
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;
}
void CFMachPortInvalidateAll(void) {
}
static mach_port_t __CFMachPortGetPort(void *info) {
CFMachPortRef mp = info;
__CFMachPortCheckForFork();
return mp->_port;
}
static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
CFMachPortRef mp = info;
void *context_info;
void (*context_release)(const void *);
__CFMachPortCheckForFork();
__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);
if (context_release) {
context_release(context_info);
}
return NULL;
}
CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) {
CFRunLoopSourceRef result = NULL;
__CFGenericValidateType(mp, __kCFMachPortTypeID);
__CFMachPortCheckForFork();
__CFMachPortLock(mp);
#if 0
#warning CF: adding ref to receive right is disabled for now -- doesnt work in 1F
if (!__CFMachPortHasReceive(mp)) {
kern_return_t ret;
ret = mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, +1);
if (KERN_SUCCESS != ret) {
__CFMachPortUnlock(mp);
return NULL;
}
__CFMachPortSetHasReceive(mp);
}
#endif
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