Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
21 kB · 752 lines
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753/* * Copyright (c) 2000-2005, 2008-2019 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@ */
/* * Modification History * * June 1, 2001 Allan Nathanson <ajn@apple.com> * - public API conversion * * March 31, 2000 Allan Nathanson <ajn@apple.com> * - initial revision */
#include "SCDynamicStoreInternal.h"#include "config.h" /* MiG generated file */#include "SCD.h"
#if !TARGET_OS_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090))#define HAVE_MACHPORT_GUARDS#endif
static voidremoveReceiveRight(SCDynamicStoreRef store, mach_port_t port){ /* remove our receive right */#ifdef HAVE_MACHPORT_GUARDS (void) mach_port_destruct(mach_task_self(), port, 0, (mach_port_context_t)store);#else // HAVE_MACHPORT_GUARDS (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);#endif // HAVE_MACHPORT_GUARDS return;}
#define MAX_INVALID_RIGHT_RETRY 3
static mach_port_taddNotifyPort(SCDynamicStoreRef store){ kern_return_t kr; mach_port_t oldNotify;#ifdef HAVE_MACHPORT_GUARDS mach_port_options_t opts;#endif // HAVE_MACHPORT_GUARDS mach_port_t port; int sc_status; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; int try = 0;
/* allocate a mach port for the SCDynamicStore notifications */
retry_allocate :
#ifdef HAVE_MACHPORT_GUARDS memset(&opts, 0, sizeof(opts)); opts.flags = MPO_CONTEXT_AS_GUARD|MPO_INSERT_SEND_RIGHT;
kr = mach_port_construct(mach_task_self(), &opts, (mach_port_context_t)store, &port);#else // HAVE_MACHPORT_GUARDS kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);#endif // HAVE_MACHPORT_GUARDS
if (kr != KERN_SUCCESS) { SC_log(LOG_NOTICE, "could not allocate mach port: %s", mach_error_string(kr)); if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) { usleep(50 * 1000); // sleep 50ms between attempts goto retry_allocate; } goto fail; }
#ifndef HAVE_MACHPORT_GUARDS kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); if (kr != KERN_SUCCESS) { /* * We can't insert a send right into our own port! This should * only happen if someone stomped on OUR port (so let's leave * the port alone). */ SC_log(LOG_NOTICE, "mach_port_insert_right() failed: %s", mach_error_string(kr)); goto fail; }#endif // HAVE_MACHPORT_GUARDS
/* Request a notification when/if the server dies */ kr = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 1, port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldNotify); if (kr != KERN_SUCCESS) { /* * We can't request a notification for our own port! This should * only happen if someone stomped on OUR port (so let's leave * the port alone). */ SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(kr)); goto fail; }
if (oldNotify != MACH_PORT_NULL) { SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL"); }
#ifdef DEBUG SC_log(LOG_DEBUG, "+ establish notification request w/port=0x%x (%d) with SCDynamicStore server (%@)", port, port, (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));#endif /* DEBUG */
retry :
__MACH_PORT_DEBUG(TRUE, "*** addNotifyPort", port); kr = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
if (kr == MACH_SEND_INVALID_RIGHT) { /* * the notifyviaport() call was not able to pass/process * the [notify port] send right. This was most likely due * to the [SCDynamicStore] server having restarted. We * handle this issue by removing the remaining receive * right, reallocating the notify port, and retrying * the operation. */ SC_log((try++ < MAX_INVALID_RIGHT_RETRY) ? LOG_INFO : LOG_ERR, "SCDynamicStore callback notifyviaport() w/port 0x%x (%d) failed (try %d): %s", port, port, try, mach_error_string(kr));
removeReceiveRight(store, port); }
if (__SCDynamicStoreCheckRetryAndHandleError(store, kr, &sc_status, "SCDynamicStore callback notifyviaport()")) { if (kr == MACH_SEND_INVALID_RIGHT) { if (try <= MAX_INVALID_RIGHT_RETRY) { goto retry_allocate; } goto fail; } goto retry; }
if (try > 0) { SC_log(LOG_INFO, "SCDynamicStore callback notifyviaport() succeeded after %d retr%s w/port 0x%x (%d)", try, (try == 1) ? "y" : "ies", port, port); }
if (kr != KERN_SUCCESS) { if ((kr == MACH_SEND_INVALID_DEST) || (kr == MIG_SERVER_DIED)) { /* remove the send right that we tried (but failed) to pass to the server */ (void) mach_port_deallocate(mach_task_self(), port); } removeReceiveRight(store, port); goto fail; }
if (sc_status != kSCStatusOK) { /* something [else] didn't work */ removeReceiveRight(store, port); kr = sc_status; goto fail; }
return port;
fail :
_SCErrorSet(kr); return MACH_PORT_NULL;}
static CFStringRefnotifyMPCopyDescription(const void *info){ SCDynamicStoreRef store = (SCDynamicStoreRef)info;
return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore notification MP> {store = %p}"), store);}
static voidrlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info){#ifndef DEBUG#pragma unused(port)#endif /* DEBUG */#pragma unused(size) mach_no_senders_notification_t *buf = msg; mach_msg_id_t msgid = buf->not_header.msgh_id; SCDynamicStoreRef store = (SCDynamicStoreRef)info; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
#ifdef DEBUG SC_log(LOG_DEBUG, "mach port callback, %ssignal RLS(%@)", (msgid == MACH_NOTIFY_NO_SENDERS) ? "reconnect and " : "", (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));#endif /* DEBUG */
if (msgid == MACH_NOTIFY_NO_SENDERS) { /* the server died, disable additional callbacks */#ifdef DEBUG SC_log(LOG_DEBUG, " notifier port closed");#endif /* DEBUG */
#ifdef DEBUG if (port != storePrivate->rlsNotifyPort) { SC_log(LOG_DEBUG, "why is port != rlsNotifyPort?"); }#endif /* DEBUG */
/* re-establish notification and inform the client */ (void)__SCDynamicStoreReconnectNotifications(store); }
/* signal the real runloop source */ if (storePrivate->rls != NULL) { CFRunLoopSourceSignal(storePrivate->rls); }
return;}
static voidrlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode){ SCDynamicStoreRef store = (SCDynamicStoreRef)info; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
#ifdef DEBUG SC_log(LOG_DEBUG, "schedule notifications for mode %@", mode);#endif /* DEBUG */
if (storePrivate->rlsNotifyPort == NULL) { CFMachPortContext context = { 0 , (void *)store , CFRetain , CFRelease , notifyMPCopyDescription }; mach_port_t port;
#ifdef DEBUG SC_log(LOG_DEBUG, " activate callback runloop source");#endif /* DEBUG */
port = addNotifyPort(store); if (port == MACH_PORT_NULL) { return; }
__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after addNotifyPort)", port); storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore", port, rlsCallback, &context);
if (rl != NULL) { storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0); storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); } }
if (storePrivate->rlsNotifyRLS != NULL) { /* set notifier active */ storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) { /* * if we are not already scheduled with this runLoop / runLoopMode */ CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode); __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort)); }
_SC_schedule(store, rl, mode, storePrivate->rlList); }
return;}
static voidrlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode){ CFIndex n = 0; SCDynamicStoreRef store = (SCDynamicStoreRef)info; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
#ifdef DEBUG SC_log(LOG_DEBUG, "cancel notifications for mode %@", mode);#endif /* DEBUG */
if (storePrivate->rlsNotifyRLS != NULL) { if (_SC_unschedule(store, rl, mode, storePrivate->rlList, FALSE)) { /* * if currently scheduled on this runLoop / runLoopMode */ n = CFArrayGetCount(storePrivate->rlList); if (n == 0 || !_SC_isScheduled(store, rl, mode, storePrivate->rlList)) { /* * if we are no longer scheduled to receive notifications for * this runLoop / runLoopMode */ CFRunLoopRemoveSource(rl, storePrivate->rlsNotifyRLS, mode); } } }
if (n == 0) { int sc_status; kern_return_t kr;
#ifdef DEBUG SC_log(LOG_DEBUG, " cancel callback runloop source");#endif /* DEBUG */ __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL), "*** rlsCancel", CFMachPortGetPort(storePrivate->rlsNotifyPort));
if (storePrivate->rls != NULL) { // Remove the reference we took on the rls. We do not invalidate // the runloop source and let the client do it when appropriate. CFRelease(storePrivate->rls); storePrivate->rls = NULL; }
if (storePrivate->rlList != NULL) { CFRelease(storePrivate->rlList); storePrivate->rlList = NULL; }
if (storePrivate->rlsNotifyRLS != NULL) { /* invalidate & remove the run loop source */ CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS); CFRelease(storePrivate->rlsNotifyRLS); storePrivate->rlsNotifyRLS = NULL; }
if (storePrivate->rlsNotifyPort != NULL) { mach_port_t mp;
mp = CFMachPortGetPort(storePrivate->rlsNotifyPort); __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL), "*** rlsCancel (before invalidating/releasing CFMachPort)", mp);
/* invalidate and release port */ CFMachPortInvalidate(storePrivate->rlsNotifyPort); CFRelease(storePrivate->rlsNotifyPort); storePrivate->rlsNotifyPort = NULL;
/* and, finally, remove our receive right */ removeReceiveRight(store, mp); }
#ifdef DEBUG SC_log(LOG_DEBUG, " cancel notification request with SCDynamicStore server");#endif /* DEBUG */
if (storePrivate->server != MACH_PORT_NULL) { kr = notifycancel(storePrivate->server, (int *)&sc_status);
(void) __SCDynamicStoreCheckRetryAndHandleError(store, kr, &sc_status, "rlsCancel notifycancel()");
if (kr != KERN_SUCCESS) { return; } }
/* set notifier inactive */ storePrivate->notifyStatus = NotifierNotRegistered; }
return;}
static voidrlsPerform(void *info){ CFArrayRef changedKeys = NULL; void *context_info; void (*context_release)(const void *); SCDynamicStoreCallBack rlsFunction; SCDynamicStoreRef store = (SCDynamicStoreRef)info; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
#ifdef DEBUG SC_log(LOG_DEBUG, "handling SCDynamicStore changes (%@)", (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));#endif /* DEBUG */
changedKeys = SCDynamicStoreCopyNotifiedKeys(store); if (storePrivate->disconnectForceCallBack) { storePrivate->disconnectForceCallBack = FALSE; if (changedKeys == NULL) { changedKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); } } else if ((changedKeys == NULL) || (CFArrayGetCount(changedKeys) == 0)) { /* if no changes or something happened to the server */ goto done; }
rlsFunction = storePrivate->rlsFunction;
if (storePrivate->rlsContext.retain != NULL) { context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info); context_release = storePrivate->rlsContext.release; } else { context_info = storePrivate->rlsContext.info; context_release = NULL; } if (rlsFunction != NULL) { SC_log(LOG_DEBUG, "+ exec SCDynamicStore callout"); (*rlsFunction)(store, changedKeys, context_info); SC_log(LOG_DEBUG, "+ done"); } if (context_release != NULL) { context_release(context_info); }
done :
#ifdef DEBUG SC_log(LOG_DEBUG, "done");#endif /* DEBUG */
if (changedKeys != NULL) { CFRelease(changedKeys); }
return;}
static CFStringRefrlsCopyDescription(const void *info){ CFMutableStringRef result; SCDynamicStoreRef store = (SCDynamicStoreRef)info; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
result = CFStringCreateMutable(NULL, 0); CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {")); CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store); if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) { CFStringRef description = NULL;
CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) { description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info); } if (description == NULL) { description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info); } if (description == NULL) { description = CFRetain(CFSTR("<no description>")); } CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description); CFRelease(description); } CFStringAppendFormat(result, NULL, CFSTR("}"));
return result;}
CFRunLoopSourceRefSCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, SCDynamicStoreRef store, CFIndex order){ SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
if (store == NULL) { /* sorry, you must provide a session */ _SCErrorSet(kSCStatusNoStoreSession); return NULL; }
if (storePrivate->server == MACH_PORT_NULL) { /* sorry, you must have an open session to play */ _SCErrorSet(kSCStatusNoStoreServer); return NULL; }
switch (storePrivate->notifyStatus) { case NotifierNotRegistered : case Using_NotifierInformViaRunLoop : /* OK to enable runloop notification */ break; default : /* sorry, you can only have one notification registered at once */ _SCErrorSet(kSCStatusNotifierActive); return NULL; }
if (storePrivate->rls == NULL) { CFRunLoopSourceContext context = { 0 // version , (void *)store // info , CFRetain // retain , CFRelease // release , rlsCopyDescription // copyDescription , CFEqual // equal , CFHash // hash , rlsSchedule // schedule , rlsCancel // cancel , rlsPerform // perform };
storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context); if (storePrivate->rls == NULL) { _SCErrorSet(kSCStatusFailed); } }
if (storePrivate->rls != NULL) { CFRetain(storePrivate->rls); }
return storePrivate->rls;}
BooleanSCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue){ dispatch_group_t drainGroup = NULL; dispatch_queue_t drainQueue = NULL; dispatch_group_t group = NULL; mach_port_t mp; Boolean ok = FALSE; dispatch_source_t source; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
if (store == NULL) { // sorry, you must provide a session _SCErrorSet(kSCStatusNoStoreSession); return FALSE; }
if (queue == NULL) { if (storePrivate->dispatchQueue == NULL) { // if not scheduled _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
#ifdef DEBUG SC_log(LOG_DEBUG, "unschedule notifications from dispatch queue (%@)", (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));#endif /* DEBUG */
ok = TRUE; goto cleanup; }
if (storePrivate->server == MACH_PORT_NULL) { // sorry, you must have an open session to play _SCErrorSet(kSCStatusNoStoreServer); return FALSE; }
if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL) || (storePrivate->notifyStatus != NotifierNotRegistered)) { // if already scheduled _SCErrorSet(kSCStatusNotifierActive); return FALSE; }
#ifdef DEBUG SC_log(LOG_DEBUG, "schedule notifications for dispatch queue (%@)", (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));#endif /* DEBUG */
// // mark our using of the SCDynamicStore notifications, create and schedule // the notification source/port (storePrivate->dispatchSource), and a bunch // of other "setup" // storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
mp = addNotifyPort(store); if (mp == MACH_PORT_NULL) { // if we could not schedule the notification _SCErrorSet(kSCStatusFailed);#ifdef DEBUG SC_log(LOG_DEBUG, "addNotifyPort() failed (%@)", (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));#endif /* DEBUG */ goto cleanup; }
// retain the dispatch queue storePrivate->dispatchQueue = queue; dispatch_retain(storePrivate->dispatchQueue);
// // We've taken a reference to the callers dispatch_queue and we // want to hold on to that reference until we've processed any/all // notifications. To facilitate this we create a group, dispatch // any notification blocks to via that group, and when the caller // has told us to stop the notifications (unschedule) we wait for // the group to empty and use the group's finalizer to release // our reference to the SCDynamicStore. // group = dispatch_group_create(); storePrivate->dispatchGroup = group; CFRetain(store); dispatch_set_context(storePrivate->dispatchGroup, (void *)store); dispatch_set_finalizer_f(storePrivate->dispatchGroup, (dispatch_function_t)CFRelease);
// create a dispatch source for the mach notifications source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue); if (source == NULL) { SC_log(LOG_NOTICE, "dispatch_source_create() failed");
// remove our receive right removeReceiveRight(store, mp);
_SCErrorSet(kSCStatusFailed); goto cleanup; }
dispatch_source_set_event_handler(source, ^{ kern_return_t kr; mach_msg_id_t msgid; union { u_int8_t buf1[sizeof(mach_msg_empty_rcv_t) + MAX_TRAILER_SIZE]; u_int8_t buf2[sizeof(mach_no_senders_notification_t) + MAX_TRAILER_SIZE]; mach_msg_empty_rcv_t msg; mach_no_senders_notification_t no_senders; } notify_msg;
kr = mach_msg(¬ify_msg.msg.header, // msg MACH_RCV_MSG, // options 0, // send_size sizeof(notify_msg), // rcv_size mp, // rcv_name MACH_MSG_TIMEOUT_NONE, // timeout MACH_PORT_NULL); // notify if (kr != KERN_SUCCESS) { SC_log(LOG_NOTICE, "mach_msg() failed, kr=0x%x", kr); return; }
msgid = notify_msg.msg.header.msgh_id; mach_msg_destroy(¬ify_msg.msg.header);
#ifdef DEBUG SC_log(LOG_DEBUG, "dispatch source callback, queue rlsPerform (%@)", (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));#endif /* DEBUG */
CFRetain(store); dispatch_group_async(group, queue, ^{ if (msgid == MACH_NOTIFY_NO_SENDERS) { // re-establish notification and inform the client (void)__SCDynamicStoreReconnectNotifications(store); } rlsPerform(storePrivate); CFRelease(store); }); });
dispatch_source_set_cancel_handler(source, ^{ __MACH_PORT_DEBUG(TRUE, "*** SCDynamicStoreSetDispatchQueue (before cancel)", mp);
// remove our receive right removeReceiveRight(store, mp);
// release source dispatch_release(source); });
storePrivate->dispatchSource = source; dispatch_resume(source);
return TRUE;
cleanup :
CFRetain(store);
if (storePrivate->dispatchSource != NULL) { dispatch_source_cancel(storePrivate->dispatchSource); storePrivate->dispatchSource = NULL; } drainGroup = storePrivate->dispatchGroup; storePrivate->dispatchGroup = NULL; drainQueue = storePrivate->dispatchQueue; storePrivate->dispatchQueue = NULL;
if ((drainGroup != NULL) && (drainQueue != NULL)) { dispatch_group_notify(drainGroup, drainQueue, ^{ // release group/queue references dispatch_release(drainQueue); dispatch_release(drainGroup); // releases our store reference }); }
storePrivate->notifyStatus = NotifierNotRegistered;
CFRelease(store);
return ok;}