CFNetServiceBrowser.c [plain text]
#if 0
#pragma mark Includes
#endif
#include <CFNetwork/CFNetwork.h>
#include "CFNetworkInternal.h" // for __CFSpinLock and __CFSpinUnlock
#include "CFNetworkSchedule.h"
#include <dns_sd.h>
#if 0
#pragma mark -
#pragma mark Extern Function Declarations
#endif
extern CFNetServiceRef _CFNetServiceCreateCommon(CFAllocatorRef alloc, CFStringRef domain, CFStringRef type, CFStringRef name, UInt32 port);
#if 0
#pragma mark -
#pragma mark Constant Strings
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFNetServiceBrowserBlockingMode CFSTR("_kCFNetServiceBrowserBlockingMode")
#else
static CONST_STRING_DECL(_kCFNetServiceBrowserBlockingMode, "_kCFNetServiceBrowserBlockingMode")
#endif
static const char _kCFNetServiceBrowserClassName[] = "CFNetServiceBrowser";
#if 0
#pragma mark -
#pragma mark CFNetServiceBrowser struct
#endif
typedef struct {
CFRuntimeBase _base;
CFSpinLock_t _lock;
Boolean _domainSearch;
CFStreamError _error;
CFTypeRef _trigger;
DNSServiceRef _browse;
CFMutableDictionaryRef _found; CFMutableArrayRef _adds; CFMutableArrayRef _removes;
CFMutableArrayRef _schedules; CFNetServiceBrowserClientCallBack _callback;
CFNetServiceClientContext _client;
} __CFNetServiceBrowser;
#if 0
#pragma mark -
#pragma mark Static Function Declarations
#endif
static void _CFNetServiceBrowserRegisterClass(void);
static void _NetServiceBrowserDestroy(__CFNetServiceBrowser* browser);
static void _BrowserCancel(__CFNetServiceBrowser* browser);
static Boolean _BrowserBlockUntilComplete(__CFNetServiceBrowser* browser);
static void _DomainEnumReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
DNSServiceErrorType errorCode, const char* replyDomain, void* context);
static void _BrowseReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
DNSServiceErrorType errorCode, const char* serviceName, const char* regtype,
const char* replyDomain, void* context);
static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
#if 0
#pragma mark -
#pragma mark Globals
#endif
static _CFOnceLock _kCFNetServiceBrowserRegisterClass = _CFOnceInitializer;
static CFTypeID _kCFNetServiceBrowserTypeID = _kCFRuntimeNotATypeID;
static CFRuntimeClass* _kCFNetServiceBrowserClass = NULL;
#if 0
#pragma mark -
#pragma mark Static Function Definitions
#endif
void
_CFNetServiceBrowserRegisterClass(void) {
_kCFNetServiceBrowserClass = (CFRuntimeClass*)calloc(1, sizeof(_kCFNetServiceBrowserClass[0]));
if (_kCFNetServiceBrowserClass) {
_kCFNetServiceBrowserClass->version = 0;
_kCFNetServiceBrowserClass->className = _kCFNetServiceBrowserClassName;
_kCFNetServiceBrowserClass->finalize = (void(*)(CFTypeRef))_NetServiceBrowserDestroy;
_kCFNetServiceBrowserTypeID = _CFRuntimeRegisterClass(_kCFNetServiceBrowserClass);
}
}
void
_NetServiceBrowserDestroy(__CFNetServiceBrowser* browser) {
__CFSpinLock(&(browser->_lock));
if (browser->_client.info && browser->_client.release)
browser->_client.release(browser->_client.info);
if (browser->_trigger) {
if (browser->_schedules)
_CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules);
_CFTypeInvalidate(browser->_trigger);
CFRelease(browser->_trigger);
}
if (browser->_browse) {
DNSServiceRefDeallocate(browser->_browse);
}
if (browser->_found)
CFRelease(browser->_found);
if (browser->_adds)
CFRelease(browser->_adds);
if (browser->_removes)
CFRelease(browser->_removes);
if (browser->_schedules)
CFRelease(browser->_schedules);
}
void
_BrowserCancel(__CFNetServiceBrowser* browser) {
CFNetServiceBrowserClientCallBack cb = NULL;
CFStreamError error;
void* info = NULL;
CFRetain(browser);
__CFSpinLock(&browser->_lock);
if (browser->_trigger) {
cb = browser->_callback;
memmove(&error, &(browser->_error), sizeof(error));
info = browser->_client.info;
_CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules);
CFRunLoopSourceInvalidate((CFRunLoopSourceRef)(browser->_trigger));
CFRelease(browser->_trigger);
browser->_trigger = NULL;
}
__CFSpinUnlock(&browser->_lock);
if (cb)
cb((CFNetServiceBrowserRef)browser, 0, NULL, &error, info);
CFRelease(browser);
}
Boolean
_BrowserBlockUntilComplete(__CFNetServiceBrowser* browser) {
Boolean result = TRUE;
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFNetServiceBrowserScheduleWithRunLoop((CFNetServiceBrowserRef)browser, rl, _kCFNetServiceBrowserBlockingMode);
__CFSpinLock(&(browser->_lock));
while (browser->_trigger) {
__CFSpinUnlock(&(browser->_lock));
CFRunLoopRunInMode(_kCFNetServiceBrowserBlockingMode, DBL_MAX, TRUE);
__CFSpinLock(&(browser->_lock));
}
if (browser->_error.error)
result = FALSE;
__CFSpinUnlock(&(browser->_lock));
CFNetServiceBrowserUnscheduleFromRunLoop((CFNetServiceBrowserRef)browser, rl, _kCFNetServiceBrowserBlockingMode);
return result;
}
void
_DomainEnumReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
DNSServiceErrorType errorCode, const char* replyDomain, void* context)
{
__CFNetServiceBrowser* browser = context;
CFNetServiceBrowserClientCallBack cb = NULL;
CFStreamError error;
void* info = NULL;
CFStringRef domain = NULL;
CFRetain(browser);
__CFSpinLock(&browser->_lock);
if (browser->_browse) {
if (errorCode) {
browser->_error.error = errorCode;
browser->_error.domain = kCFStreamErrorDomainNetServices;
_CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules);
CFSocketInvalidate((CFSocketRef)(browser->_trigger));
CFRelease(browser->_trigger);
browser->_trigger = NULL;
DNSServiceRefDeallocate(browser->_browse);
browser->_browse = NULL;
}
else if (replyDomain) {
domain = CFStringCreateWithCString(CFGetAllocator(browser),
replyDomain,
kCFStringEncodingUTF8);
}
cb = browser->_callback;
memmove(&error, &(browser->_error), sizeof(error));
info = browser->_client.info;
}
__CFSpinUnlock(&browser->_lock);
if (cb && domain) {
UInt32 f = kCFNetServiceFlagIsDomain;
if (flags & kDNSServiceFlagsMoreComing)
f |= kCFNetServiceFlagMoreComing;
if (!(flags & kDNSServiceFlagsAdd))
f |= kCFNetServiceFlagRemove;
if (flags & kDNSServiceFlagsDefault)
f |= kCFNetServiceFlagIsDefault;
cb((CFNetServiceBrowserRef)browser, f, domain, &error, info);
}
if (domain)
CFRelease(domain);
CFRelease(browser);
}
void
_BrowseReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
DNSServiceErrorType errorCode, const char* serviceName, const char* regtype,
const char* replyDomain, void* context)
{
__CFNetServiceBrowser* browser = context;
CFNetServiceBrowserClientCallBack cb = NULL;
CFStreamError error = {0, 0};
void* info = NULL;
CFNetServiceRef service = NULL;
CFRetain(browser);
__CFSpinLock(&browser->_lock);
if (browser->_browse) {
if (errorCode) {
browser->_error.error = errorCode;
browser->_error.domain = kCFStreamErrorDomainNetServices;
_CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules);
CFSocketInvalidate((CFSocketRef)(browser->_trigger));
CFRelease(browser->_trigger);
browser->_trigger = NULL;
DNSServiceRefDeallocate(browser->_browse);
browser->_browse = NULL;
CFDictionaryRemoveAllValues(browser->_found);
CFArrayRemoveAllValues(browser->_adds);
CFArrayRemoveAllValues(browser->_removes);
}
else if (serviceName && regtype && replyDomain) {
CFAllocatorRef alloc = CFGetAllocator(browser);
CFStringRef domain = CFStringCreateWithCString(alloc, replyDomain, kCFStringEncodingUTF8);
CFStringRef type = CFStringCreateWithCString(alloc, regtype, kCFStringEncodingUTF8);
CFStringRef name = CFStringCreateWithCString(alloc, serviceName, kCFStringEncodingUTF8);
if (domain && type && name)
service = _CFNetServiceCreateCommon(alloc, domain, type, name, 0);
if (domain) CFRelease(domain);
if (type) CFRelease(type);
if (name) CFRelease(name);
if (service) {
UInt32 count = (UInt32)CFDictionaryGetValue(browser->_found, service);
if (flags & kDNSServiceFlagsAdd) {
count++;
if (count != 1)
CFDictionaryReplaceValue(browser->_found, service, (const void*)count);
else {
CFIndex i = CFArrayGetFirstIndexOfValue(browser->_removes,
CFRangeMake(0, CFArrayGetCount(browser->_removes)),
service);
CFDictionaryAddValue(browser->_found, service, (const void*)count);
CFArrayAppendValue(browser->_adds, service);
if (i != kCFNotFound)
CFArrayRemoveValueAtIndex(browser->_removes, i);
}
}
else {
count--;
if (count > 0)
CFDictionaryReplaceValue(browser->_found, service, (const void*)count);
else {
CFIndex i = CFArrayGetFirstIndexOfValue(browser->_adds,
CFRangeMake(0, CFArrayGetCount(browser->_adds)),
service);
CFDictionaryRemoveValue(browser->_found, service);
CFArrayAppendValue(browser->_removes, service);
if (i != kCFNotFound)
CFArrayRemoveValueAtIndex(browser->_adds, i);
}
}
CFRelease(service);
}
}
cb = browser->_callback;
memmove(&error, &(browser->_error), sizeof(error));
info = browser->_client.info;
}
if (cb && error.error) {
__CFSpinUnlock(&browser->_lock);
cb((CFNetServiceBrowserRef)browser, 0, NULL, &error, info);
}
else if (cb && ((flags & kDNSServiceFlagsMoreComing) == 0)) {
CFIndex i, adds = CFArrayGetCount(browser->_adds);
CFIndex removes = CFArrayGetCount(browser->_removes);
CFIndex total = adds + removes;
for (i = 0; i < adds; i++) {
const void* saved = browser->_trigger;
service = (CFNetServiceRef)CFArrayGetValueAtIndex(browser->_adds, i);
__CFSpinUnlock(&browser->_lock);
cb((CFNetServiceBrowserRef)browser,
(i == (total - 1)) ? 0 : kCFNetServiceFlagMoreComing,
service,
&error,
info);
__CFSpinLock(&browser->_lock);
if (saved != browser->_trigger) {
cb = NULL;
break;
}
}
if (cb) {
for (i = 0; i < removes; i++) {
const void* saved = browser->_trigger;
service = (CFNetServiceRef)CFArrayGetValueAtIndex(browser->_removes, i);
__CFSpinUnlock(&browser->_lock);
cb((CFNetServiceBrowserRef)browser,
kCFNetServiceFlagRemove | ((i == (removes - 1)) ? 0 : kCFNetServiceFlagMoreComing),
service,
&error,
info);
__CFSpinLock(&browser->_lock);
if (saved != browser->_trigger)
break;
}
}
CFArrayRemoveAllValues(browser->_adds);
CFArrayRemoveAllValues(browser->_removes);
__CFSpinUnlock(&browser->_lock);
}
else
__CFSpinUnlock(&browser->_lock);
CFRelease(browser);
}
void
_SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
DNSServiceErrorType err;
__CFNetServiceBrowser* browser = info;
(void)s; (void)type; (void)address; (void)data;
CFRetain(browser);
err = DNSServiceProcessResult(browser->_browse);
if (err) {
if (browser->_domainSearch)
_DomainEnumReply(browser->_browse, 0, 0, err, NULL, info);
else
_BrowseReply(browser->_browse, 0, 0, err, NULL, NULL, NULL, info);
}
CFRelease(browser);
}
#if 0
#pragma mark -
#pragma mark Extern Function Definitions (API)
#endif
CFTypeID
CFNetServiceBrowserGetTypeID(void) {
_CFDoOnce(&_kCFNetServiceBrowserRegisterClass, _CFNetServiceBrowserRegisterClass);
return _kCFNetServiceBrowserTypeID;
}
CFNetServiceBrowserRef
CFNetServiceBrowserCreate(CFAllocatorRef alloc, CFNetServiceBrowserClientCallBack clientCB, CFNetServiceClientContext* context) {
__CFNetServiceBrowser* result = NULL;
if (clientCB && context) {
CFTypeID class_type = CFNetServiceBrowserGetTypeID();
if (class_type != _kCFRuntimeNotATypeID) {
result = (__CFNetServiceBrowser*)_CFRuntimeCreateInstance(alloc,
class_type,
sizeof(result[0]) - sizeof(CFRuntimeBase),
NULL);
}
if (result) {
CFDictionaryValueCallBacks values = {0, NULL, NULL, NULL, NULL};
CFRuntimeBase copy = result->_base;
memset(result, 0, sizeof(result[0]));
memmove(&(result->_base), ©, sizeof(result->_base));
result->_callback = clientCB;
memmove(&(result->_client), context, sizeof(result->_client));
if (result->_client.info && result->_client.retain)
result->_client.info = (void*)(result->_client.retain(result->_client.info));
result->_schedules = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
result->_found = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &values);
result->_adds = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
result->_removes = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
if (!result->_schedules) {
CFRelease((CFTypeRef)result);
result = NULL;
}
}
}
return (CFNetServiceBrowserRef)result;
}
void
CFNetServiceBrowserInvalidate(CFNetServiceBrowserRef b) {
__CFNetServiceBrowser* browser = (__CFNetServiceBrowser*)b;
__CFSpinLock(&(browser->_lock));
if (browser->_client.info && browser->_client.release)
browser->_client.release(browser->_client.info);
if (browser->_trigger) {
_CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules);
_CFTypeInvalidate(browser->_trigger);
CFRelease(browser->_trigger);
browser->_trigger = NULL;
}
if (browser->_browse) {
DNSServiceRefDeallocate(browser->_browse);
browser->_browse = NULL;
CFDictionaryRemoveAllValues(browser->_found);
CFArrayRemoveAllValues(browser->_adds);
CFArrayRemoveAllValues(browser->_removes);
}
browser->_callback = NULL;
memset(&(browser->_client), 0, sizeof(browser->_client));
__CFSpinUnlock(&(browser->_lock));
}
Boolean
CFNetServiceBrowserSearchForDomains(CFNetServiceBrowserRef b, Boolean registrationDomains, CFStreamError* error) {
__CFNetServiceBrowser* browser = (__CFNetServiceBrowser*)b;
CFStreamError extra;
Boolean result = FALSE;
if (!error)
error = &extra;
memset(error, 0, sizeof(error[0]));
CFRetain(browser);
__CFSpinLock(&(browser->_lock));
do {
CFSocketContext ctxt = {0, browser, CFRetain, CFRelease, NULL};
if (!browser->_callback) {
browser->_error.error = kCFNetServicesErrorInvalid;
browser->_error.domain = kCFStreamErrorDomainNetServices;
break;
}
if (browser->_trigger) {
if (CFGetTypeID(browser->_trigger) == CFSocketGetTypeID()) {
browser->_error.error = kCFNetServicesErrorInProgress;
browser->_error.domain = kCFStreamErrorDomainNetServices;
break;
}
else {
_CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules);
CFRunLoopSourceInvalidate((CFRunLoopSourceRef)(browser->_trigger));
CFRelease(browser->_trigger);
browser->_trigger = NULL;
}
}
browser->_domainSearch = TRUE;
browser->_error.error = DNSServiceEnumerateDomains(&browser->_browse,
registrationDomains ? kDNSServiceFlagsRegistrationDomains : kDNSServiceFlagsBrowseDomains,
0,
_DomainEnumReply,
browser);
if (browser->_error.error) {
browser->_error.domain = kCFStreamErrorDomainNetServices;
break;
}
browser->_trigger = CFSocketCreateWithNative(CFGetAllocator(browser),
DNSServiceRefSockFD(browser->_browse),
kCFSocketReadCallBack,
_SocketCallBack,
&ctxt);
if (!browser->_trigger) {
browser->_error.error = errno;
if (!browser->_error.error)
browser->_error.error = ENOMEM;
browser->_error.domain = kCFStreamErrorDomainPOSIX;
DNSServiceRefDeallocate(browser->_browse);
browser->_browse = NULL;
break;
}
CFSocketSetSocketFlags((CFSocketRef)browser->_trigger,
CFSocketGetSocketFlags((CFSocketRef)browser->_trigger) & ~kCFSocketCloseOnInvalidate);
if (CFArrayGetCount(browser->_schedules)) {
_CFTypeScheduleOnMultipleRunLoops(browser->_trigger, browser->_schedules);
result = TRUE;
}
else {
__CFSpinUnlock(&(browser->_lock));
result = _BrowserBlockUntilComplete(browser);
__CFSpinLock(&(browser->_lock));
}
} while (0);
memmove(error, &browser->_error, sizeof(error[0]));
__CFSpinUnlock(&(browser->_lock));
CFRelease(browser);
return result;
}
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]));
CFRetain(browser);
__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;
}
if (browser->_trigger) {
if (CFGetTypeID(browser->_trigger) == CFSocketGetTypeID()) {
browser->_error.error = kCFNetServicesErrorInProgress;
browser->_error.domain = kCFStreamErrorDomainNetServices;
break;
}
else {
_CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules);
CFRunLoopSourceInvalidate((CFRunLoopSourceRef)(browser->_trigger));
CFRelease(browser->_trigger);
browser->_trigger = NULL;
}
}
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;
browser->_error.error = DNSServiceBrowse(&browser->_browse,
0,
0,
properties[0],
properties[1],
_BrowseReply,
browser);
if (browser->_error.error) {
browser->_error.domain = kCFStreamErrorDomainNetServices;
break;
}
browser->_trigger = CFSocketCreateWithNative(CFGetAllocator(browser),
DNSServiceRefSockFD(browser->_browse),
kCFSocketReadCallBack,
_SocketCallBack,
&ctxt);
if (!browser->_trigger) {
browser->_error.error = errno;
if (!browser->_error.error)
browser->_error.error = ENOMEM;
browser->_error.domain = kCFStreamErrorDomainPOSIX;
DNSServiceRefDeallocate(browser->_browse);
browser->_browse = NULL;
break;
}
CFSocketSetSocketFlags((CFSocketRef)browser->_trigger,
CFSocketGetSocketFlags((CFSocketRef)browser->_trigger) & ~kCFSocketCloseOnInvalidate);
if (CFArrayGetCount(browser->_schedules)) {
_CFTypeScheduleOnMultipleRunLoops(browser->_trigger, browser->_schedules);
result = TRUE;
}
else {
__CFSpinUnlock(&(browser->_lock));
result = _BrowserBlockUntilComplete(browser);
__CFSpinLock(&(browser->_lock));
}
} while (0);
memmove(error, &browser->_error, sizeof(error[0]));
__CFSpinUnlock(&(browser->_lock));
CFRelease(browser);
return result;
}
void
CFNetServiceBrowserStopSearch(CFNetServiceBrowserRef b, CFStreamError* error) {
__CFNetServiceBrowser* browser = (__CFNetServiceBrowser*)b;
CFStreamError extra = {kCFStreamErrorDomainNetServices , kCFNetServicesErrorCancel};
if (!error)
error = &extra;
__CFSpinLock(&(browser->_lock));
if (browser->_trigger) {
CFRunLoopSourceContext ctxt = {
0, browser, NULL, NULL, NULL, NULL, NULL, NULL, NULL, (void(*)(void*))(&_BrowserCancel) };
_CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules);
_CFTypeInvalidate(browser->_trigger);
CFRelease(browser->_trigger);
if (browser->_browse) {
DNSServiceRefDeallocate(browser->_browse);
browser->_browse = NULL;
CFDictionaryRemoveAllValues(browser->_found);
CFArrayRemoveAllValues(browser->_adds);
CFArrayRemoveAllValues(browser->_removes);
}
memmove(&(browser->_error), error, sizeof(error[0]));
browser->_trigger = CFRunLoopSourceCreate(CFGetAllocator(browser), 0, &ctxt);
if (browser->_trigger) {
CFArrayRef schedules = browser->_schedules;
CFIndex i, count = CFArrayGetCount(schedules);
_CFTypeScheduleOnMultipleRunLoops(browser->_trigger, schedules);
CFRunLoopSourceSignal((CFRunLoopSourceRef)(browser->_trigger));
for (i = 0; i < count; i += 2) {
CFRunLoopRef runloop = (CFRunLoopRef)CFArrayGetValueAtIndex(schedules, i);
if (CFRunLoopIsWaiting(runloop)) {
CFStringRef mode = CFRunLoopCopyCurrentMode(runloop);
if (mode) {
if (CFRunLoopContainsSource(runloop, (CFRunLoopSourceRef)(browser->_trigger), mode)) {
CFRunLoopWakeUp(runloop);
}
CFRelease(mode);
}
}
}
}
}
__CFSpinUnlock(&(browser->_lock));
}
void
CFNetServiceBrowserScheduleWithRunLoop(CFNetServiceBrowserRef b, CFRunLoopRef runLoop, CFStringRef runLoopMode) {
__CFNetServiceBrowser* browser = (__CFNetServiceBrowser*)b;
__CFSpinLock(&(browser->_lock));
if (_SchedulesAddRunLoopAndMode(browser->_schedules, runLoop, runLoopMode)) {
if (browser->_trigger) {
_CFTypeScheduleOnRunLoop(browser->_trigger, runLoop, runLoopMode);
}
}
__CFSpinUnlock(&(browser->_lock));
}
void
CFNetServiceBrowserUnscheduleFromRunLoop(CFNetServiceBrowserRef b, CFRunLoopRef runLoop, CFStringRef runLoopMode) {
__CFNetServiceBrowser* browser = (__CFNetServiceBrowser*)b;
__CFSpinLock(&(browser->_lock));
if (_SchedulesRemoveRunLoopAndMode(browser->_schedules, runLoop, runLoopMode)) {
if (browser->_trigger) {
_CFTypeUnscheduleFromRunLoop(browser->_trigger, runLoop, runLoopMode);
}
}
__CFSpinUnlock(&(browser->_lock));
}