/* extern */ void _CFTypeInvalidate(CFTypeRef obj) { CFTypeID t = CFGetTypeID(obj); /* Invalidate according to type of object. */ if (t == CFRunLoopSourceGetTypeID()) { CFRunLoopSourceInvalidate((CFRunLoopSourceRef)obj); } else if (t == CFMachPortGetTypeID()) { CFMachPortInvalidate((CFMachPortRef)obj); } else if (t == CFSocketGetTypeID()) { CFSocketInvalidate((CFSocketRef)obj); } /* For scheduled types of objects, it is invalidated by setting the client to NULL. */ else if (t == CFReadStreamGetTypeID()) { CFReadStreamSetClient((CFReadStreamRef)obj, kCFStreamEventNone, NULL, NULL); } else if (t == CFWriteStreamGetTypeID()) { CFWriteStreamSetClient((CFWriteStreamRef)obj, kCFStreamEventNone, NULL, NULL); } else if (t == CFHostGetTypeID()) { CFHostSetClient((CFHostRef)obj, NULL, NULL); } else if (t == SCNetworkReachabilityGetTypeID()) { SCNetworkReachabilitySetCallback((SCNetworkReachabilityRef)obj, NULL, NULL); } else if (t == CFRunLoopTimerGetTypeID()) { CFRunLoopTimerInvalidate((CFRunLoopTimerRef)obj); } else if (t == CFNetServiceGetTypeID()) { CFNetServiceSetClient((CFNetServiceRef)obj, NULL, NULL); } else if (t == CFNetServiceBrowserGetTypeID()) { CFNetServiceBrowserInvalidate((CFNetServiceBrowserRef)obj); } else if (t == CFNetServiceMonitorGetTypeID()) { CFNetServiceMonitorInvalidate((CFNetServiceMonitorRef)obj); } else if (t == SCNetworkReachabilityGetTypeID()) { SCNetworkConnectionStop((SCNetworkConnectionRef)obj, FALSE); } }
/* extern */ void _CFTypeScheduleOnRunLoop(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode) { CFTypeID t = CFGetTypeID(obj); CFTypeRef src = NULL; void(*fn)(CFTypeRef, CFRunLoopRef, CFStringRef); void(*fn2)(CFRunLoopRef, CFTypeRef, CFStringRef); fn = NULL; fn2 = (void(*)(CFRunLoopRef, CFTypeRef, CFStringRef))CFRunLoopAddSource; /* Get the correct source or function used for adding the object to the run loop. */ if (t == CFRunLoopSourceGetTypeID()) { src = CFRetain(obj); } else if (t == CFMachPortGetTypeID()) { src = CFMachPortCreateRunLoopSource(CFGetAllocator(obj), (CFMachPortRef)obj, 0); } else if (t == CFSocketGetTypeID()) { src = CFSocketCreateRunLoopSource(CFGetAllocator(obj), (CFSocketRef)obj, 0); } else if (t == CFReadStreamGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFReadStreamScheduleWithRunLoop; } else if (t == CFWriteStreamGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFWriteStreamScheduleWithRunLoop; } else if (t == CFHostGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFHostScheduleWithRunLoop; } else if (t == SCNetworkReachabilityGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))SCNetworkReachabilityScheduleWithRunLoop; } else if (t == CFRunLoopTimerGetTypeID()) { src = CFRetain(obj); fn2 = (void(*)(CFRunLoopRef, CFTypeRef, CFStringRef))CFRunLoopAddTimer; } else if (t == CFNetServiceGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceScheduleWithRunLoop; } else if (t == CFNetServiceBrowserGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceBrowserScheduleWithRunLoop; } else if (t == CFNetServiceMonitorGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceMonitorScheduleWithRunLoop; } else if (t == SCNetworkConnectionGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))SCNetworkConnectionScheduleWithRunLoop; } /* If a source was retrieved, need to add the source */ if (src) { fn2(runLoop, src, runLoopMode); CFRelease(src); } /* If a schedule function was retrieved, call it. */ else if (fn) { fn(obj, runLoop, runLoopMode); } }
/* extern */ void _CFTypeUnscheduleFromMultipleRunLoops(CFTypeRef obj, CFArrayRef schedules) { CFTypeID t = CFGetTypeID(obj); CFTypeRef src = NULL; void(*fn)(CFTypeRef, CFRunLoopRef, CFStringRef); void(*fn2)(CFRunLoopRef, CFTypeRef, CFStringRef); fn = NULL; fn2 = (void(*)(CFRunLoopRef, CFTypeRef, CFStringRef))CFRunLoopRemoveSource; /* Get the proper source or function for removing the object from the run loop. */ if (t == CFRunLoopSourceGetTypeID()) { src = CFRetain(obj); } else if (t == CFMachPortGetTypeID()) { src = CFMachPortCreateRunLoopSource(CFGetAllocator(obj), (CFMachPortRef)obj, 0); } else if (t == CFSocketGetTypeID()) { src = CFSocketCreateRunLoopSource(CFGetAllocator(obj), (CFSocketRef)obj, 0); } else if (t == CFReadStreamGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFReadStreamUnscheduleFromRunLoop; } else if (t == CFWriteStreamGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFWriteStreamUnscheduleFromRunLoop; } else if (t == CFHostGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFHostUnscheduleFromRunLoop; } else if (t == SCNetworkReachabilityGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))SCNetworkReachabilityUnscheduleFromRunLoop; } else if (t == CFRunLoopTimerGetTypeID()) { src = CFRetain(obj); fn2 = (void(*)(CFRunLoopRef, CFTypeRef, CFStringRef))CFRunLoopRemoveTimer; } else if (t == CFNetServiceGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceUnscheduleFromRunLoop; } else if (t == CFNetServiceBrowserGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceBrowserUnscheduleFromRunLoop; } else if (t == CFNetServiceMonitorGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceMonitorUnscheduleFromRunLoop; } else if (t == SCNetworkConnectionGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))SCNetworkConnectionUnscheduleFromRunLoop; } /* If a source was retrieved, need to remove it from the list of run loops*/ if (src) { CFIndex i, length = CFArrayGetCount(schedules); for (i = 0; i < length; i += 2) { fn2((CFRunLoopRef)CFArrayGetValueAtIndex(schedules, i), src, (CFStringRef)CFArrayGetValueAtIndex(schedules, i + 1)); } CFRelease(src); } /* If an unschedule function was retrieved, need to call it for each schedule in the list. */ else if (fn) { CFIndex i, length = CFArrayGetCount(schedules); for (i = 0; i < length; i += 2) { fn(obj, (CFRunLoopRef)CFArrayGetValueAtIndex(schedules, i), (CFStringRef)CFArrayGetValueAtIndex(schedules, i + 1)); } } }
/* extern */ void _CFTypeUnscheduleFromRunLoop(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode) { CFTypeID t = CFGetTypeID(obj); CFTypeRef src = NULL; void(*fn)(CFTypeRef, CFRunLoopRef, CFStringRef); void(*fn2)(CFRunLoopRef, CFTypeRef, CFStringRef); fn = NULL; fn2 = (void(*)(CFRunLoopRef, CFTypeRef, CFStringRef))CFRunLoopRemoveSource; /* Get the proper source or function for removing the object from the run loop. */ if (t == CFRunLoopSourceGetTypeID()) { src = CFRetain(obj); } else if (t == CFMachPortGetTypeID()) { src = CFMachPortCreateRunLoopSource(CFGetAllocator(obj), (CFMachPortRef)obj, 0); } else if (t == CFSocketGetTypeID()) { src = CFSocketCreateRunLoopSource(CFGetAllocator(obj), (CFSocketRef)obj, 0); } else if (t == CFReadStreamGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFReadStreamUnscheduleFromRunLoop; } else if (t == CFWriteStreamGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFWriteStreamUnscheduleFromRunLoop; } else if (t == CFHostGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFHostUnscheduleFromRunLoop; } else if (t == SCNetworkReachabilityGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))SCNetworkReachabilityUnscheduleFromRunLoop; } else if (t == CFRunLoopTimerGetTypeID()) { src = CFRetain(obj); fn2 = (void(*)(CFRunLoopRef, CFTypeRef, CFStringRef))CFRunLoopRemoveTimer; } else if (t == CFNetServiceGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceUnscheduleFromRunLoop; } else if (t == CFNetServiceBrowserGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceBrowserUnscheduleFromRunLoop; } else if (t == CFNetServiceMonitorGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))CFNetServiceMonitorUnscheduleFromRunLoop; } else if (t == SCNetworkConnectionGetTypeID()) { fn = (void(*)(CFTypeRef, CFRunLoopRef, CFStringRef))SCNetworkConnectionUnscheduleFromRunLoop; } /* If a source was retrieved, need to remove it */ if (src) { fn2(runLoop, src, runLoopMode); CFRelease(src); } /* If an unschedule function was retrieved, need to call it. */ else if (fn) { fn(obj, runLoop, runLoopMode); } }
void rwsched_cfsocket_callout_intercept(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { rwsched_instance_ptr_t instance; rwsched_tasklet_ptr_t sched_tasklet; rwsched_cfsocket_callback_context_ptr_t callback_context; rwsched_CFSocketRef cfsocket = (rwsched_CFSocketRef) info; // Validate input parameters __CFGenericValidateType_(s, CFSocketGetTypeID(), __PRETTY_FUNCTION__); RW_CF_TYPE_VALIDATE(cfsocket, rwsched_CFSocketRef); callback_context = &cfsocket->callback_context; instance = callback_context->instance; RW_CF_TYPE_VALIDATE(instance, rwsched_instance_ptr_t); sched_tasklet = callback_context->tasklet_info; RW_CF_TYPE_VALIDATE(sched_tasklet, rwsched_tasklet_ptr_t); // #1 - Check to see if the cfsocket matches the current blocking source for the tasklet // #2 - Otherwise check if the current tasklet is in blocking mode and then relocate the runloop source // #3 - Otherwise the current tasklet is not blocking and the event can be processed now if (sched_tasklet->blocking_mode.cfsource == callback_context->cfsource) { // Abort the runloop that is currently blocking CFRunLoopRef rl = CFRunLoopGetCurrent(); CFRunLoopStop(rl); } else if (sched_tasklet->blocking_mode.cfsource) { // The task is blocking and this event CANNOT be processed yet, so move it to a different runloop mode rwsched_cfrunloop_relocate_source(sched_tasklet, callback_context->cfsource); } else { // The current task is not blocking so we can invoke the callback now RW_ASSERT(callback_context->cf_callout); g_rwresource_track_handle = sched_tasklet->rwresource_track_handle; g_tasklet_info = sched_tasklet; struct timeval tv_begin, tv_end, tv_delta; if (sched_tasklet->instance->latency.check_threshold_ms) { gettimeofday(&tv_begin, NULL); } else { tv_begin.tv_sec = 0; } (*callback_context->cf_callout)(cfsocket, type, address, data, callback_context->cf_context.info); if (tv_begin.tv_sec && sched_tasklet->instance->latency.check_threshold_ms) { gettimeofday(&tv_end, NULL); timersub(&tv_end, &tv_begin, &tv_delta); unsigned int cbms = (tv_delta.tv_sec * 1000 + tv_delta.tv_usec / 1000); if (cbms >= sched_tasklet->instance->latency.check_threshold_ms) { char *name = rw_btrace_get_proc_name(callback_context->cf_callout); RWSCHED_LOG_EVENT(instance, SchedDebug, RWLOG_ATTR_SPRINTF("rwsched[%d] CF socket/file took %ums ctx %p callback %s", getpid(), cbms, callback_context->cf_context.info, name)); free(name); } } g_tasklet_info = 0; g_rwresource_track_handle = 0; } }
/* CF_EXPORT */ Boolean CFNetServiceBrowserSearchForServices(CFNetServiceBrowserRef b, CFStringRef domain, CFStringRef type, CFStreamError* error) { __CFNetServiceBrowser* browser = (__CFNetServiceBrowser*)b; CFStreamError extra; Boolean result = FALSE; if (!error) error = &extra; memset(error, 0, sizeof(error[0])); // Retain so it doesn't go away underneath in the case of a callout. This is really // no worry for async, but makes the memmove for the error more difficult to place // for synchronous without it being here. CFRetain(browser); // Lock down the browser to start search __CFSpinLock(&(browser->_lock)); do { int i; char properties[2][1024]; CFStringRef argProperties[] = {type, domain}; CFSocketContext ctxt = {0, browser, CFRetain, CFRelease, NULL}; if (!browser->_callback) { browser->_error.error = kCFNetServicesErrorInvalid; browser->_error.domain = kCFStreamErrorDomainNetServices; break; } // Check to see if there is an ongoing search already if (browser->_trigger) { // If it's a mdns search, don't allow another. if (CFGetTypeID(browser->_trigger) == CFSocketGetTypeID()) { browser->_error.error = kCFNetServicesErrorInProgress; browser->_error.domain = kCFStreamErrorDomainNetServices; break; } // It's just the cancel that hasn't fired yet, so cancel it. else { // Remove the trigger from run loops and modes _CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules); // Invalidate the run loop source CFRunLoopSourceInvalidate((CFRunLoopSourceRef)(browser->_trigger)); // Release the trigger now. CFRelease(browser->_trigger); browser->_trigger = NULL; } } // Convert the type and domain to c strings to pass down for (i = 0; i < (sizeof(properties) / sizeof(properties[0])); i++) { if (!argProperties[i]) properties[i][0] = '\0'; else { CFIndex bytesUsed; CFStringGetBytes(argProperties[i], CFRangeMake(0, CFStringGetLength(argProperties[i])), kCFStringEncodingUTF8, 0, FALSE, (UInt8*)properties[i], sizeof(properties[i]) - 1, &bytesUsed); properties[i][bytesUsed] = '\0'; } } browser->_domainSearch = FALSE; // Create the service search at the service discovery level browser->_error.error = DNSServiceBrowse(&browser->_browse, 0, 0, properties[0], properties[1], _BrowseReply, browser); // Fail if it did. if (browser->_error.error) { browser->_error.error = _DNSServiceErrorToCFNetServiceError(browser->_error.error); browser->_error.domain = kCFStreamErrorDomainNetServices; break; } // Create the trigger for the browse browser->_trigger = CFSocketCreateWithNative(CFGetAllocator(browser), DNSServiceRefSockFD(browser->_browse), kCFSocketReadCallBack, _SocketCallBack, &ctxt); // Make sure the CFSocket wrapper succeeded if (!browser->_trigger) { // Try to use errno for the error browser->_error.error = errno; // If it has no error in it, assume no memory if (!browser->_error.error) browser->_error.error = ENOMEM; // Correct domain and bail. browser->_error.domain = kCFStreamErrorDomainPOSIX; DNSServiceRefDeallocate(browser->_browse); browser->_browse = NULL; break; } // Tell CFSocket not to close the native socket on invalidation. CFSocketSetSocketFlags((CFSocketRef)browser->_trigger, CFSocketGetSocketFlags((CFSocketRef)browser->_trigger) & ~kCFSocketCloseOnInvalidate); // Async mode is complete at this point if (CFArrayGetCount(browser->_schedules)) { // Schedule the trigger on the run loops and modes. _CFTypeScheduleOnMultipleRunLoops(browser->_trigger, browser->_schedules); // It's now succeeded. result = TRUE; } // If there is no callback, go into synchronous mode. else { // Unlock the browser __CFSpinUnlock(&(browser->_lock)); // Wait for synchronous return result = _BrowserBlockUntilComplete(browser); // Lock down the browser __CFSpinLock(&(browser->_lock)); } } while (0); // Copy the error. memmove(error, &browser->_error, sizeof(error[0])); // Unlock the browser __CFSpinUnlock(&(browser->_lock)); // Release the earlier retain. CFRelease(browser); return result; }