#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;
mach_port_t _oldnotify;
CFRunLoopSourceRef _source;
CFMachPortInvalidationCallBack _icallout;
CFMachPortCallBack _callout;
CFMachPortContext _context;
};
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;
__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) {
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 = 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);
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, NULL, __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;
}
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;
}
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) {
CFMachPortRef mp = CFMachPortCreateWithPort(kCFAllocatorSystemDefault, __CFNotifyRawMachPort, __CFNotifyDeadMachPort, NULL, NULL);
__CFMachPortSetHasReceive(mp);
__CFNotifyMachPort = mp;
}
if (NULL != __CFNotifyMachPort) {
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);
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;
}
}
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 = info;
return mp->_port;
}
static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
CHECK_FOR_FORK();
CFMachPortRef mp = 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;
}