]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
a0e5f449c25729fde6008cd7874709b83da23e17
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
1 /*
2 * Copyright (c) 2000-2005, 2008-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * March 31, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include "SCDynamicStoreInternal.h"
35 #include "config.h" /* MiG generated file */
36 #include "SCD.h"
37
38 #if !TARGET_OS_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090))
39 #define HAVE_MACHPORT_GUARDS
40 #endif
41
42
43 static CFStringRef
44 notifyMPCopyDescription(const void *info)
45 {
46 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
47
48 return CFStringCreateWithFormat(NULL,
49 NULL,
50 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
51 store);
52 }
53
54
55 static void
56 rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
57 {
58 mach_no_senders_notification_t *buf = msg;
59 mach_msg_id_t msgid = buf->not_header.msgh_id;
60 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
61 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
62
63 if (msgid == MACH_NOTIFY_NO_SENDERS) {
64 /* the server died, disable additional callbacks */
65 #ifdef DEBUG
66 SC_log(LOG_DEBUG, " notifier port closed");
67 #endif /* DEBUG */
68
69 #ifdef DEBUG
70 if (port != storePrivate->rlsNotifyPort) {
71 SC_log(LOG_DEBUG, "why is port != rlsNotifyPort?");
72 }
73 #endif /* DEBUG */
74
75 /* re-establish notification and inform the client */
76 (void)__SCDynamicStoreReconnectNotifications(store);
77 }
78
79 #ifdef DEBUG
80 SC_log(LOG_DEBUG, "mach port callback, signal RLS");
81 #endif /* DEBUG */
82
83 /* signal the real runloop source */
84 if (storePrivate->rls != NULL) {
85 CFRunLoopSourceSignal(storePrivate->rls);
86 }
87
88 return;
89 }
90
91
92 static void
93 rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
94 {
95 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
96 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
97
98 #ifdef DEBUG
99 SC_log(LOG_DEBUG, "schedule notifications for mode %@",
100 (rl != NULL) ? mode : CFSTR("libdispatch"));
101 #endif /* DEBUG */
102
103 if (storePrivate->rlsNotifyPort == NULL) {
104 CFMachPortContext context = { 0
105 , (void *)store
106 , CFRetain
107 , CFRelease
108 , notifyMPCopyDescription
109 };
110 kern_return_t kr;
111 mach_port_t oldNotify;
112 #ifdef HAVE_MACHPORT_GUARDS
113 mach_port_options_t opts;
114 #endif // HAVE_MACHPORT_GUARDS
115 mach_port_t port;
116 int sc_status;
117
118 #ifdef DEBUG
119 SC_log(LOG_DEBUG, " activate callback runloop source");
120 #endif /* DEBUG */
121
122 /* allocate a mach port for the SCDynamicStore notifications */
123
124 retry_allocate :
125
126 #ifdef HAVE_MACHPORT_GUARDS
127 bzero(&opts, sizeof(opts));
128 opts.flags = MPO_CONTEXT_AS_GUARD|MPO_INSERT_SEND_RIGHT;
129
130 kr = mach_port_construct(mach_task_self(), &opts, store, &port);
131 #else // HAVE_MACHPORT_GUARDS
132 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
133 #endif // HAVE_MACHPORT_GUARDS
134
135 if (kr != KERN_SUCCESS) {
136 SC_log(LOG_NOTICE, "could not allocate mach port: %s", mach_error_string(kr));
137 if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) {
138 sleep(1);
139 goto retry_allocate;
140 } else {
141 return;
142 }
143 }
144
145 #ifndef HAVE_MACHPORT_GUARDS
146 kr = mach_port_insert_right(mach_task_self(),
147 port,
148 port,
149 MACH_MSG_TYPE_MAKE_SEND);
150 if (kr != KERN_SUCCESS) {
151 /*
152 * We can't insert a send right into our own port! This should
153 * only happen if someone stomped on OUR port (so let's leave
154 * the port alone).
155 */
156 SC_log(LOG_NOTICE, "mach_port_insert_right() failed: %s", mach_error_string(kr));
157 return;
158 }
159 #endif // HAVE_MACHPORT_GUARDS
160
161 /* Request a notification when/if the server dies */
162 kr = mach_port_request_notification(mach_task_self(),
163 port,
164 MACH_NOTIFY_NO_SENDERS,
165 1,
166 port,
167 MACH_MSG_TYPE_MAKE_SEND_ONCE,
168 &oldNotify);
169 if (kr != KERN_SUCCESS) {
170 /*
171 * We can't request a notification for our own port! This should
172 * only happen if someone stomped on OUR port (so let's leave
173 * the port alone).
174 */
175 SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(kr));
176 return;
177 }
178
179 if (oldNotify != MACH_PORT_NULL) {
180 SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL");
181 }
182
183 #ifdef DEBUG
184 SC_log(LOG_DEBUG, " establish notification request with SCDynamicStore server");
185 #endif /* DEBUG */
186
187 os_activity_scope(storePrivate->activity);
188
189 retry :
190
191 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
192 kr = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
193
194 if (__SCDynamicStoreCheckRetryAndHandleError(store,
195 kr,
196 &sc_status,
197 "rlsSchedule notifyviaport()")) {
198 goto retry;
199 }
200
201 if (kr != KERN_SUCCESS) {
202 if ((kr == MACH_SEND_INVALID_DEST) || (kr == MIG_SERVER_DIED)) {
203 /* remove the send right that we tried (but failed) to pass to the server */
204 (void) mach_port_deallocate(mach_task_self(), port);
205 }
206
207 /* remove our receive right */
208 #ifdef HAVE_MACHPORT_GUARDS
209 (void) mach_port_destruct(mach_task_self(), port, 0, store);
210 #else // HAVE_MACHPORT_GUARDS
211 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
212 #endif // HAVE_MACHPORT_GUARDS
213 return;
214 }
215
216 if (sc_status != kSCStatusOK) {
217 /* something [else] didn't work, remove our receive right */
218 #ifdef HAVE_MACHPORT_GUARDS
219 (void) mach_port_destruct(mach_task_self(), port, 0, store);
220 #else // HAVE_MACHPORT_GUARDS
221 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
222 #endif // HAVE_MACHPORT_GUARDS
223 return;
224 }
225
226 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
227 storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore",
228 port,
229 rlsCallback,
230 &context);
231
232 if (rl != NULL) {
233 storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0);
234 storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
235 }
236 }
237
238 if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
239 /* set notifier active */
240 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
241
242 if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
243 /*
244 * if we are not already scheduled with this runLoop / runLoopMode
245 */
246 CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode);
247 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort));
248 }
249
250 _SC_schedule(store, rl, mode, storePrivate->rlList);
251 }
252
253 return;
254 }
255
256
257 static void
258 rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
259 {
260 CFIndex n = 0;
261 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
262 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
263
264 #ifdef DEBUG
265 SC_log(LOG_DEBUG, "cancel notifications for mode %@",
266 (rl != NULL) ? mode : CFSTR("libdispatch"));
267 #endif /* DEBUG */
268
269 if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
270 if (_SC_unschedule(store, rl, mode, storePrivate->rlList, FALSE)) {
271 /*
272 * if currently scheduled on this runLoop / runLoopMode
273 */
274 n = CFArrayGetCount(storePrivate->rlList);
275 if (n == 0 || !_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
276 /*
277 * if we are no longer scheduled to receive notifications for
278 * this runLoop / runLoopMode
279 */
280 CFRunLoopRemoveSource(rl, storePrivate->rlsNotifyRLS, mode);
281 }
282 }
283 }
284
285 if (n == 0) {
286 int sc_status;
287 kern_return_t kr;
288
289 #ifdef DEBUG
290 SC_log(LOG_DEBUG, " cancel callback runloop source");
291 #endif /* DEBUG */
292 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
293 "*** rlsCancel",
294 CFMachPortGetPort(storePrivate->rlsNotifyPort));
295
296 if (storePrivate->rls != NULL) {
297 // Remove the reference we took on the rls. We do not invalidate
298 // the runloop source and let the client do it when appropriate.
299 CFRelease(storePrivate->rls);
300 storePrivate->rls = NULL;
301 }
302
303 if (storePrivate->rlList != NULL) {
304 CFRelease(storePrivate->rlList);
305 storePrivate->rlList = NULL;
306 }
307
308 if (storePrivate->rlsNotifyRLS != NULL) {
309 /* invalidate & remove the run loop source */
310 CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS);
311 CFRelease(storePrivate->rlsNotifyRLS);
312 storePrivate->rlsNotifyRLS = NULL;
313 }
314
315 if (storePrivate->rlsNotifyPort != NULL) {
316 mach_port_t mp;
317
318 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
319 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
320 "*** rlsCancel (before invalidating/releasing CFMachPort)",
321 mp);
322
323 /* invalidate and release port */
324 CFMachPortInvalidate(storePrivate->rlsNotifyPort);
325 CFRelease(storePrivate->rlsNotifyPort);
326 storePrivate->rlsNotifyPort = NULL;
327
328 /* and, finally, remove our receive right */
329 #ifdef HAVE_MACHPORT_GUARDS
330 (void) mach_port_destruct(mach_task_self(), mp, 0, store);
331 #else // HAVE_MACHPORT_GUARDS
332 (void) mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
333 #endif // HAVE_MACHPORT_GUARDS
334 }
335
336 #ifdef DEBUG
337 SC_log(LOG_DEBUG, " cancel notification request with SCDynamicStore server");
338 #endif /* DEBUG */
339
340 os_activity_scope(storePrivate->activity);
341
342 if (storePrivate->server != MACH_PORT_NULL) {
343 kr = notifycancel(storePrivate->server, (int *)&sc_status);
344
345 (void) __SCDynamicStoreCheckRetryAndHandleError(store,
346 kr,
347 &sc_status,
348 "rlsCancel notifycancel()");
349
350 if (kr != KERN_SUCCESS) {
351 return;
352 }
353 }
354
355 /* set notifier inactive */
356 storePrivate->notifyStatus = NotifierNotRegistered;
357 }
358
359 return;
360 }
361
362
363 static void
364 rlsPerform(void *info)
365 {
366 CFArrayRef changedKeys = NULL;
367 void *context_info;
368 void (*context_release)(const void *);
369 SCDynamicStoreCallBack rlsFunction;
370 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
371 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
372
373 #ifdef DEBUG
374 SC_log(LOG_DEBUG, " executing notification function");
375 #endif /* DEBUG */
376
377 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
378 if (storePrivate->disconnectForceCallBack) {
379 storePrivate->disconnectForceCallBack = FALSE;
380 if (changedKeys == NULL) {
381 changedKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
382 }
383 } else if ((changedKeys == NULL) || (CFArrayGetCount(changedKeys) == 0)) {
384 /* if no changes or something happened to the server */
385 goto done;
386 }
387
388 rlsFunction = storePrivate->rlsFunction;
389
390 if (storePrivate->rlsContext.retain != NULL) {
391 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
392 context_release = storePrivate->rlsContext.release;
393 } else {
394 context_info = storePrivate->rlsContext.info;
395 context_release = NULL;
396 }
397 if (rlsFunction != NULL) {
398 SC_log(LOG_DEBUG, "exec SCDynamicStore callout");
399 (*rlsFunction)(store, changedKeys, context_info);
400 }
401 if (context_release != NULL) {
402 context_release(context_info);
403 }
404
405 done :
406
407 #ifdef DEBUG
408 SC_log(LOG_DEBUG, " done!");
409 #endif /* DEBUG */
410
411 if (changedKeys != NULL) {
412 CFRelease(changedKeys);
413 }
414
415 return;
416 }
417
418
419 static CFStringRef
420 rlsCopyDescription(const void *info)
421 {
422 CFMutableStringRef result;
423 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
424 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
425
426 result = CFStringCreateMutable(NULL, 0);
427 CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {"));
428 CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store);
429 if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
430 CFStringRef description = NULL;
431
432 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
433
434 if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) {
435 description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info);
436 }
437 if (description == NULL) {
438 description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info);
439 }
440 if (description == NULL) {
441 description = CFRetain(CFSTR("<no description>"));
442 }
443 CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
444 CFRelease(description);
445 }
446 CFStringAppendFormat(result, NULL, CFSTR("}"));
447
448 return result;
449 }
450
451
452 CFRunLoopSourceRef
453 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
454 SCDynamicStoreRef store,
455 CFIndex order)
456 {
457 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
458
459 if (store == NULL) {
460 /* sorry, you must provide a session */
461 _SCErrorSet(kSCStatusNoStoreSession);
462 return NULL;
463 }
464
465 if (storePrivate->server == MACH_PORT_NULL) {
466 /* sorry, you must have an open session to play */
467 _SCErrorSet(kSCStatusNoStoreServer);
468 return NULL;
469 }
470
471 switch (storePrivate->notifyStatus) {
472 case NotifierNotRegistered :
473 case Using_NotifierInformViaRunLoop :
474 /* OK to enable runloop notification */
475 break;
476 default :
477 /* sorry, you can only have one notification registered at once */
478 _SCErrorSet(kSCStatusNotifierActive);
479 return NULL;
480 }
481
482 if (storePrivate->rls == NULL) {
483 CFRunLoopSourceContext context = { 0 // version
484 , (void *)store // info
485 , CFRetain // retain
486 , CFRelease // release
487 , rlsCopyDescription // copyDescription
488 , CFEqual // equal
489 , CFHash // hash
490 , rlsSchedule // schedule
491 , rlsCancel // cancel
492 , rlsPerform // perform
493 };
494
495 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
496 if (storePrivate->rls == NULL) {
497 _SCErrorSet(kSCStatusFailed);
498 }
499 }
500
501 if (storePrivate->rls != NULL) {
502 CFRetain(storePrivate->rls);
503 }
504
505 return storePrivate->rls;
506 }
507
508
509 Boolean
510 SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue)
511 {
512 dispatch_group_t drainGroup = NULL;
513 dispatch_queue_t drainQueue = NULL;
514 dispatch_group_t group = NULL;
515 mach_port_t mp;
516 Boolean ok = FALSE;
517 dispatch_source_t source;
518 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
519
520 if (store == NULL) {
521 // sorry, you must provide a session
522 _SCErrorSet(kSCStatusNoStoreSession);
523 return FALSE;
524 }
525
526 if (queue == NULL) {
527 if (storePrivate->dispatchQueue == NULL) {
528 _SCErrorSet(kSCStatusInvalidArgument);
529 return FALSE;
530 }
531
532 ok = TRUE;
533 goto cleanup;
534 }
535
536 if (storePrivate->server == MACH_PORT_NULL) {
537 // sorry, you must have an open session to play
538 _SCErrorSet(kSCStatusNoStoreServer);
539 return FALSE;
540 }
541
542 if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
543 _SCErrorSet(kSCStatusInvalidArgument);
544 return FALSE;
545 }
546
547 if (storePrivate->notifyStatus != NotifierNotRegistered) {
548 // sorry, you can only have one notification registered at once...
549 _SCErrorSet(kSCStatusNotifierActive);
550 return FALSE;
551 }
552
553 /*
554 * mark our using of the SCDynamicStore notifications, create and schedule
555 * the notification port (storePrivate->rlsNotifyPort), and a bunch of other
556 * "setup"
557 */
558 storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
559 rlsSchedule((void*)store, NULL, NULL);
560 if (storePrivate->rlsNotifyPort == NULL) {
561 /* if we could not schedule the notification */
562 _SCErrorSet(kSCStatusFailed);
563 goto cleanup;
564 }
565
566 // retain the dispatch queue
567 storePrivate->dispatchQueue = queue;
568 dispatch_retain(storePrivate->dispatchQueue);
569
570 //
571 // We've taken a reference to the callers dispatch_queue and we
572 // want to hold on to that reference until we've processed any/all
573 // notifications. To facilitate this we create a group, dispatch
574 // any notification blocks to via that group, and when the caller
575 // has told us to stop the notifications (unschedule) we wait for
576 // the group to empty and use the group's finalizer to release
577 // our reference to the SCDynamicStore.
578 //
579 group = dispatch_group_create();
580 storePrivate->dispatchGroup = group;
581 CFRetain(store);
582 dispatch_set_context(storePrivate->dispatchGroup, (void *)store);
583 dispatch_set_finalizer_f(storePrivate->dispatchGroup, (dispatch_function_t)CFRelease);
584
585 // create a dispatch source for the mach notifications
586 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
587 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
588 if (source == NULL) {
589 SC_log(LOG_NOTICE, "dispatch_source_create() failed");
590 _SCErrorSet(kSCStatusFailed);
591 goto cleanup;
592 }
593
594 dispatch_source_set_event_handler(source, ^{
595 kern_return_t kr;
596 mach_msg_id_t msgid;
597 union {
598 u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE];
599 mach_msg_empty_rcv_t msg;
600 mach_no_senders_notification_t no_senders;
601 } notify_msg;
602
603 kr = mach_msg(&notify_msg.msg.header, // msg
604 MACH_RCV_MSG, // options
605 0, // send_size
606 sizeof(notify_msg), // rcv_size
607 mp, // rcv_name
608 MACH_MSG_TIMEOUT_NONE, // timeout
609 MACH_PORT_NULL); // notify
610 if (kr != KERN_SUCCESS) {
611 SC_log(LOG_NOTICE, "mach_msg() failed, kr=0x%x", kr);
612 return;
613 }
614
615 msgid = notify_msg.msg.header.msgh_id;
616
617 #ifdef DEBUG
618 SC_log(LOG_DEBUG, "dispatch source callback, queue rlsPerform");
619 #endif /* DEBUG */
620
621 CFRetain(store);
622 dispatch_group_async(group, queue, ^{
623 if (msgid == MACH_NOTIFY_NO_SENDERS) {
624 // re-establish notification and inform the client
625 (void)__SCDynamicStoreReconnectNotifications(store);
626 }
627 rlsPerform(storePrivate);
628 CFRelease(store);
629 });
630 });
631
632 dispatch_source_set_cancel_handler(source, ^{
633 dispatch_release(source);
634 });
635
636 storePrivate->dispatchSource = source;
637 dispatch_resume(source);
638
639 return TRUE;
640
641 cleanup :
642
643 CFRetain(store);
644
645 if (storePrivate->dispatchSource != NULL) {
646 dispatch_source_cancel(storePrivate->dispatchSource);
647 storePrivate->dispatchSource = NULL;
648 }
649 drainGroup = storePrivate->dispatchGroup;
650 storePrivate->dispatchGroup = NULL;
651 drainQueue = storePrivate->dispatchQueue;
652 storePrivate->dispatchQueue = NULL;
653
654 rlsCancel((void*)store, NULL, NULL);
655
656 if ((drainGroup != NULL) && (drainQueue != NULL)) {
657 dispatch_group_notify(drainGroup, drainQueue, ^{
658 // release group/queue references
659 dispatch_release(drainQueue);
660 dispatch_release(drainGroup); // releases our store reference
661 });
662 }
663
664 storePrivate->notifyStatus = NotifierNotRegistered;
665
666 CFRelease(store);
667
668 return ok;
669 }