/*
- * Copyright (c) 2000-2005, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2005, 2008-2013 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <Availability.h>
#include <TargetConditionals.h>
#include <sys/cdefs.h>
-#if !TARGET_OS_IPHONE
#include <dispatch/dispatch.h>
-#endif // !TARGET_OS_IPHONE
#include <mach/mach.h>
#include <mach/mach_error.h>
#include "config.h" /* MiG generated file */
-static void
-informCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
-{
- SCDynamicStoreRef store = (SCDynamicStoreRef)info;
- SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
- mach_msg_empty_rcv_t *buf = msg;
- mach_msg_id_t msgid = buf->header.msgh_id;
- SCDynamicStoreCallBack_v1 cbFunc = storePrivate->callbackFunction;
- void *cbArg = storePrivate->callbackArgument;
-
- if (msgid == MACH_NOTIFY_NO_SENDERS) {
- /* the server died, disable additional callbacks */
-#ifdef DEBUG
- SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier"));
-#endif /* DEBUG */
- } else if (cbFunc == NULL) {
- /* there is no (longer) a callback function, disable additional callbacks */
-#ifdef DEBUG
- SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" no callback function, disabling notifier"));
-#endif /* DEBUG */
- } else {
-#ifdef DEBUG
- SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
-#endif /* DEBUG */
- if ((*cbFunc)(store, cbArg)) {
- /*
- * callback function returned success.
- */
- return;
- } else {
-#ifdef DEBUG
- SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" callback returned error, disabling notifier"));
-#endif /* DEBUG */
- }
- }
-
-#ifdef DEBUG
- if (port != storePrivate->callbackPort) {
- SCLog(_sc_verbose, LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?"));
- }
-#endif /* DEBUG */
-
- /* invalidate the run loop source */
- if (storePrivate->callbackRLS != NULL) {
- CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
- CFRelease(storePrivate->callbackRLS);
- storePrivate->callbackRLS = NULL;
- }
-
- /* invalidate port */
- if (storePrivate->callbackPort != NULL) {
- __MACH_PORT_DEBUG(TRUE, "*** informCallback", CFMachPortGetPort(storePrivate->callbackPort));
- CFMachPortInvalidate(storePrivate->callbackPort);
- CFRelease(storePrivate->callbackPort);
- storePrivate->callbackPort = NULL;
- }
-
- /* disable notifier */
- storePrivate->notifyStatus = NotifierNotRegistered;
- storePrivate->callbackArgument = NULL;
- storePrivate->callbackFunction = NULL;
-
- return;
-}
-
-
static CFStringRef
notifyMPCopyDescription(const void *info)
{
}
-Boolean
-SCDynamicStoreNotifyCallback(SCDynamicStoreRef store,
- CFRunLoopRef runLoop,
- SCDynamicStoreCallBack_v1 func,
- void *arg)
-{
- SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
- kern_return_t status;
- mach_port_t port;
- mach_port_t oldNotify;
- int sc_status;
- CFMachPortContext context = { 0
- , (void *)store
- , CFRetain
- , CFRelease
- , notifyMPCopyDescription
- };
-
- if (store == NULL) {
- /* sorry, you must provide a session */
- _SCErrorSet(kSCStatusNoStoreSession);
- return FALSE;
- }
-
- if (storePrivate->server == MACH_PORT_NULL) {
- /* sorry, you must have an open session to play */
- _SCErrorSet(kSCStatusNoStoreServer);
- return FALSE;
- }
-
- if (storePrivate->notifyStatus != NotifierNotRegistered) {
- /* sorry, you can only have one notification registered at once */
- _SCErrorSet(kSCStatusNotifierActive);
- return FALSE;
- }
-
- /* Allocating port (for server response) */
- status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
- if (status != KERN_SUCCESS) {
- SCLog(TRUE, LOG_ERR, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
- _SCErrorSet(status);
- return FALSE;
- }
-
- status = mach_port_insert_right(mach_task_self(),
- port,
- port,
- MACH_MSG_TYPE_MAKE_SEND);
- if (status != 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).
- */
- SCLog(TRUE, LOG_ERR, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
- _SCErrorSet(status);
- return FALSE;
- }
-
- /* Request a notification when/if the server dies */
- status = mach_port_request_notification(mach_task_self(),
- port,
- MACH_NOTIFY_NO_SENDERS,
- 1,
- port,
- MACH_MSG_TYPE_MAKE_SEND_ONCE,
- &oldNotify);
- if (status != 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).
- */
- SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback mach_port_request_notification(): %s"), mach_error_string(status));
- _SCErrorSet(status);
- return FALSE;
- }
-
- if (oldNotify != MACH_PORT_NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback(): oldNotify != MACH_PORT_NULL"));
- }
-
- /* Requesting notification via mach port */
- status = notifyviaport(storePrivate->server,
- port,
- 0,
- (int *)&sc_status);
-
- if (status != KERN_SUCCESS) {
- if (status == MACH_SEND_INVALID_DEST) {
- /* the server's gone and our session port's dead, remove the dead name right */
- (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
- } else {
- /* we got an unexpected error, leave the [session] port alone */
- SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback notifyviaport(): %s"), mach_error_string(status));
- }
- storePrivate->server = MACH_PORT_NULL;
-
- if (status == MACH_SEND_INVALID_DEST) {
- /* remove the send right that we tried (but failed) to pass to the server */
- (void) mach_port_deallocate(mach_task_self(), port);
- }
-
- /* remove our receive right */
- (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
- _SCErrorSet(status);
- return FALSE;
- }
-
- if (sc_status != kSCStatusOK) {
- _SCErrorSet(sc_status);
- return FALSE;
- }
-
- /* set notifier active */
- storePrivate->notifyStatus = Using_NotifierInformViaCallback;
-
- /* Creating/adding a run loop source for the port */
- __MACH_PORT_DEBUG(TRUE, "*** SCDynamicStoreNotifyCallback", port);
- storePrivate->callbackArgument = arg;
- storePrivate->callbackFunction = func;
- storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, informCallback, &context, NULL);
- storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
- CFRunLoopAddSource(runLoop, storePrivate->callbackRLS, kCFRunLoopDefaultMode);
-
- return TRUE;
-}
-
-
static void
rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
- mach_msg_empty_rcv_t *buf = msg;
- mach_msg_id_t msgid = buf->header.msgh_id;
+ 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;
#endif /* DEBUG */
#ifdef DEBUG
- if (port != storePrivate->callbackPort) {
- SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != callbackPort?"));
+ if (port != storePrivate->rlsNotifyPort) {
+ SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != rlsNotifyPort?"));
}
#endif /* DEBUG */
- /* invalidate the run loop source(s) */
- if (storePrivate->callbackRLS != NULL) {
- CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
- CFRelease(storePrivate->callbackRLS);
- storePrivate->callbackRLS = NULL;
- }
-
- /* invalidate port */
- if (storePrivate->callbackPort != NULL) {
- __MACH_PORT_DEBUG(TRUE, "*** rlsCallback w/MACH_NOTIFY_NO_SENDERS", CFMachPortGetPort(storePrivate->callbackPort));
- CFMachPortInvalidate(storePrivate->callbackPort);
- CFRelease(storePrivate->callbackPort);
- storePrivate->callbackPort = NULL;
- }
-
- return;
+ /* re-establish notification and inform the client */
+ (void)__SCDynamicStoreReconnectNotifications(store);
}
/* signal the real runloop source */
}
-static void
-portInvalidate(CFMachPortRef port, void *info) {
- mach_port_t mp = CFMachPortGetPort(port);
-
- __MACH_PORT_DEBUG(TRUE, "*** portInvalidate", mp);
- /* remove our receive right */
- (void)mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
-}
-
-
static void
rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
{
(rl != NULL) ? mode : CFSTR("libdispatch"));
#endif /* DEBUG */
- if (storePrivate->rlsRefs++ == 0) {
- CFMachPortContext context = { 0
- , (void *)store
- , CFRetain
- , CFRelease
- , notifyMPCopyDescription
- };
+ if (storePrivate->rlList == NULL) {
+ CFMachPortContext context = { 0
+ , (void *)store
+ , CFRetain
+ , CFRelease
+ , notifyMPCopyDescription
+ };
mach_port_t oldNotify;
mach_port_t port;
int sc_status;
SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
}
+ retry :
+
__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
- if (status != KERN_SUCCESS) {
- if (status == MACH_SEND_INVALID_DEST) {
- /* the server's gone and our session port's dead, remove the dead name right */
- (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
- } else {
- /* we got an unexpected error, leave the [session] port alone */
- SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule notifyviaport(): %s"), mach_error_string(status));
- }
- storePrivate->server = MACH_PORT_NULL;
- if (status == MACH_SEND_INVALID_DEST) {
+ if (__SCDynamicStoreCheckRetryAndHandleError(store,
+ status,
+ &sc_status,
+ "rlsSchedule notifyviaport()")) {
+ goto retry;
+ }
+
+ if (status != KERN_SUCCESS) {
+ if ((status == MACH_SEND_INVALID_DEST) || (status == 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);
}
return;
}
- __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
- storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, rlsCallback, &context, NULL);
- if (storePrivate->callbackPort == NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("*** CFMachPortCreateWithPort returned NULL while attempting to schedule"));
- SCLog(TRUE, LOG_ERR, CFSTR("*** a SCDynamicStore notification. Did this process call \"fork\" without"));
- SCLog(TRUE, LOG_ERR, CFSTR("*** calling \"exec\""));
-
- /* the server's gone and our session port's dead, remove the dead name right */
- (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
- storePrivate->server = MACH_PORT_NULL;
-
- /* remove our receive right */
+ if (sc_status != kSCStatusOK) {
+ /* something [else] didn't work, remove our receive right */
(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
return;
}
- CFMachPortSetInvalidationCallBack(storePrivate->callbackPort, portInvalidate);
- storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
+
+ __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
+ storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore",
+ port,
+ rlsCallback,
+ &context);
+ storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0);
+
+ storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
- if ((rl != NULL) && (storePrivate->callbackRLS != NULL)) {
- CFRunLoopAddSource(rl, storePrivate->callbackRLS, mode);
- __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->callbackPort));
+ if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
+ 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 void
rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
{
+ CFIndex n = 0;
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
(rl != NULL) ? mode : CFSTR("libdispatch"));
#endif /* DEBUG */
- if ((rl != NULL) && (storePrivate->callbackRLS != NULL)) {
- CFRunLoopRemoveSource(rl, storePrivate->callbackRLS, mode);
+ if ((rl != NULL) && (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 (--storePrivate->rlsRefs == 0) {
+ if (n == 0) {
int sc_status;
kern_return_t status;
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
#endif /* DEBUG */
- __MACH_PORT_DEBUG((storePrivate->callbackPort != NULL),
+ __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
"*** rlsCancel",
- CFMachPortGetPort(storePrivate->callbackPort));
+ CFMachPortGetPort(storePrivate->rlsNotifyPort));
+
+ if (storePrivate->rlList != NULL) {
+ CFRelease(storePrivate->rlList);
+ storePrivate->rlList = NULL;
+ }
- if (storePrivate->callbackRLS != NULL) {
+ if (storePrivate->rlsNotifyRLS != NULL) {
/* invalidate & remove the run loop source */
- CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
- CFRelease(storePrivate->callbackRLS);
- storePrivate->callbackRLS = NULL;
+ CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS);
+ CFRelease(storePrivate->rlsNotifyRLS);
+ storePrivate->rlsNotifyRLS = NULL;
}
- if (storePrivate->callbackPort != NULL) {
- /* invalidate port */
- __MACH_PORT_DEBUG((storePrivate->callbackPort != NULL),
- "*** rlsCancel (before invalidating CFMachPort)",
- CFMachPortGetPort(storePrivate->callbackPort));
- CFMachPortInvalidate(storePrivate->callbackPort);
- CFRelease(storePrivate->callbackPort);
- storePrivate->callbackPort = 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 */
+ (void)mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
}
if (storePrivate->server != MACH_PORT_NULL) {
status = notifycancel(storePrivate->server, (int *)&sc_status);
+
+ (void) __SCDynamicStoreCheckRetryAndHandleError(store,
+ status,
+ &sc_status,
+ "rlsCancel notifycancel()");
+
if (status != KERN_SUCCESS) {
- if (status == MACH_SEND_INVALID_DEST) {
- /* the server's gone and our session port's dead, remove the dead name right */
- (void) mach_port_deallocate(mach_task_self(), storePrivate->server);
- } else {
- /* we got an unexpected error, leave the [session] port alone */
- SCLog(TRUE, LOG_ERR, CFSTR("rlsCancel notifycancel(): %s"), mach_error_string(status));
- }
- storePrivate->server = MACH_PORT_NULL;
return;
}
}
#endif /* DEBUG */
changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
- if (changedKeys == NULL) {
- /* if no changes or something happened to the server */
- return;
- }
-
- if (CFArrayGetCount(changedKeys) == 0) {
- goto done;
+ if (storePrivate->disconnectForceCallBack) {
+ storePrivate->disconnectForceCallBack = FALSE;
+ if (changedKeys == NULL) {
+ changedKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
+ }
+ } else {
+ if (changedKeys == NULL) {
+ /* if no changes or something happened to the server */
+ return;
+ } else if (CFArrayGetCount(changedKeys) == 0) {
+ goto done;
+ }
}
rlsFunction = storePrivate->rlsFunction;
context_info = storePrivate->rlsContext.info;
context_release = NULL;
}
- (*rlsFunction)(store, changedKeys, context_info);
- if (context_release) {
+ if (rlsFunction != NULL) {
+ (*rlsFunction)(store, changedKeys, context_info);
+ }
+ if (context_release != NULL) {
context_release(context_info);
}
SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
- if (storePrivate->notifyStatus != Using_NotifierInformViaRunLoop) {
- /* mark RLS active */
- storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
- /* keep a reference to the store */
- CFRetain(store);
+ switch (storePrivate->notifyStatus) {
+ case NotifierNotRegistered :
+ /* mark RLS active */
+ storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
+ /* keep a reference to the store */
+ CFRetain(store);
+ break;
+ case Using_NotifierInformViaRunLoop :
+ break;
+ default :
+ SCLog(TRUE, LOG_ERR, CFSTR("rlsRetain() error: notifyStatus=%d"), storePrivate->notifyStatus);
+ break;
}
return cf;
SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
- /* mark RLS inactive */
- storePrivate->notifyStatus = NotifierNotRegistered;
- storePrivate->rls = NULL;
+ switch (storePrivate->notifyStatus) {
+ case NotifierNotRegistered :
+ break;
+ case Using_NotifierInformViaRunLoop :
+ /* mark RLS inactive */
+ storePrivate->notifyStatus = NotifierNotRegistered;
+ storePrivate->rls = NULL;
- /* release our reference to the store */
- CFRelease(store);
+ /* release our reference to the store */
+ CFRelease(store);
+ break;
+ default :
+ SCLog(TRUE, LOG_ERR, CFSTR("rlsRelease() error: notifyStatus=%d"), storePrivate->notifyStatus);
+ break;
+ }
return;
}
}
CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
CFRelease(description);
- } else {
- CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->callbackFunction);
- CFStringAppendFormat(result, NULL, CFSTR(", info = %p"), storePrivate->callbackArgument);
}
CFStringAppendFormat(result, NULL, CFSTR("}"));
}
-#if !TARGET_OS_IPHONE
-static boolean_t
-SCDynamicStoreNotifyMIGCallback(mach_msg_header_t *message, mach_msg_header_t *reply)
-{
- SCDynamicStorePrivateRef storePrivate;
-
- storePrivate = dispatch_get_context(dispatch_get_current_queue());
- if (storePrivate != NULL) {
- CFRetain(storePrivate);
- dispatch_async(storePrivate->dispatchQueue, ^{
- rlsPerform(storePrivate);
- CFRelease(storePrivate);
- });
- }
- reply->msgh_remote_port = MACH_PORT_NULL;
- return false;
-}
-
-
Boolean
SCDynamicStoreSetDispatchQueue(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 */
+ // sorry, you must provide a session
_SCErrorSet(kSCStatusNoStoreSession);
return FALSE;
}
+ if (queue == NULL) {
+ if (storePrivate->dispatchQueue == NULL) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ ok = TRUE;
+ goto cleanup;
+ }
+
if (storePrivate->server == MACH_PORT_NULL) {
- /* sorry, you must have an open session to play */
+ // sorry, you must have an open session to play
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
- if (queue != NULL) {
- mach_port_t mp;
+ if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
- if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return FALSE;
- }
+ if (storePrivate->notifyStatus != NotifierNotRegistered) {
+ // sorry, you can only have one notification registered at once...
+ _SCErrorSet(kSCStatusNotifierActive);
+ return FALSE;
+ }
- if (storePrivate->notifyStatus != NotifierNotRegistered) {
- /* sorry, you can only have one notification registered at once... */
- _SCErrorSet(kSCStatusNotifierActive);
- return FALSE;
+ /*
+ * mark our using of the SCDynamicStore notifications, create and schedule
+ * the notification port (storePrivate->rlsNotifyPort), and a bunch of other
+ * "setup"
+ */
+ storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
+ rlsSchedule((void*)store, NULL, NULL);
+ if (storePrivate->rlsNotifyPort == NULL) {
+ /* if we could not schedule the notification */
+ _SCErrorSet(kSCStatusFailed);
+ 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
+ mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
+ source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
+ if (source == NULL) {
+ SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_source_create() failed"));
+ _SCErrorSet(kSCStatusFailed);
+ goto cleanup;
+ }
+
+ dispatch_source_set_event_handler(source, ^{
+ kern_return_t kr;
+ mach_msg_id_t msgid;
+ union {
+ u_int8_t buf[sizeof(mach_msg_empty_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) {
+ SCLog(TRUE, LOG_ERR,
+ CFSTR("SCDynamicStore notification handler, kr=0x%x"),
+ kr);
+ return;
}
- /*
- * mark our using of the SCDynamicStore notifications, create and schedule
- * the notification port (storePrivate->callbackPort), and a bunch of other
- * "setup"
- */
- storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
- rlsSchedule((void*)store, NULL, NULL);
- storePrivate->dispatchQueue = queue;
- dispatch_retain(storePrivate->dispatchQueue);
-
- /*
- * create a dispatch queue for the mach notifications source, we'll use
- * this queue's context to carry the store pointer for the callback code.
- */
- storePrivate->callbackQueue = dispatch_queue_create("com.apple.SCDynamicStore.notifications", NULL);
- if (storePrivate->callbackQueue == NULL){
- SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_queue_create() failed"));
- _SCErrorSet(kSCStatusFailed);
- goto cleanup;
- }
- CFRetain(store); // Note: will be released when the dispatch queue is released
- dispatch_set_context(storePrivate->callbackQueue, (void *)store);
- dispatch_set_finalizer_f(storePrivate->callbackQueue, (dispatch_function_t)CFRelease);
-
- /*
- * create a dispatch source for the mach notifications
- */
- mp = CFMachPortGetPort(storePrivate->callbackPort);
- storePrivate->callbackSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
- mp,
- 0,
- storePrivate->callbackQueue);
- if (storePrivate->callbackSource == NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_source_create() failed"));
- _SCErrorSet(kSCStatusFailed);
- goto cleanup;
- }
- dispatch_source_set_event_handler(storePrivate->callbackSource, ^{
- dispatch_mig_server(storePrivate->callbackSource,
- sizeof(mach_msg_header_t),
- SCDynamicStoreNotifyMIGCallback);
+ msgid = notify_msg.msg.header.msgh_id;
+
+ 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_resume(storePrivate->callbackSource);
+ });
- ok = TRUE;
- goto done;
- } else {
- if (storePrivate->dispatchQueue == NULL) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return FALSE;
- }
+ dispatch_source_set_cancel_handler(source, ^{
+ dispatch_release(source);
+ });
- ok = TRUE;
- }
+ storePrivate->dispatchSource = source;
+ dispatch_resume(source);
+
+ return TRUE;
cleanup :
- if (storePrivate->callbackSource != NULL) {
- dispatch_source_cancel(storePrivate->callbackSource);
- if (storePrivate->callbackQueue != dispatch_get_current_queue()) {
- // ensure the cancellation has completed
- dispatch_sync(storePrivate->callbackQueue, ^{});
- }
- dispatch_release(storePrivate->callbackSource);
- storePrivate->callbackSource = NULL;
- }
- if (storePrivate->callbackQueue != NULL) {
- dispatch_release(storePrivate->callbackQueue);
- storePrivate->callbackQueue = NULL;
+ CFRetain(store);
+
+ if (storePrivate->dispatchSource != NULL) {
+ dispatch_source_cancel(storePrivate->dispatchSource);
+ storePrivate->dispatchSource = NULL;
}
- dispatch_release(storePrivate->dispatchQueue);
+ drainGroup = storePrivate->dispatchGroup;
+ storePrivate->dispatchGroup = NULL;
+ drainQueue = storePrivate->dispatchQueue;
storePrivate->dispatchQueue = NULL;
+
rlsCancel((void*)store, NULL, NULL);
+
+ if (drainGroup != NULL) {
+ dispatch_group_notify(drainGroup, drainQueue, ^{
+ // release group/queue references
+ dispatch_release(drainQueue);
+ dispatch_release(drainGroup); // releases our store reference
+ });
+ }
+
storePrivate->notifyStatus = NotifierNotRegistered;
- done :
+ CFRelease(store);
return ok;
}
-#endif // !TARGET_OS_IPHONE