/ CFNotificationCenter.c
CFNotificationCenter.c
  1  //
  2  //  CFNotificationCenter.c
  3  //  CoreFoundation
  4  //
  5  //  Copyright (c) 2014 Apportable. All rights reserved.
  6  //
  7  
  8  #include "CFBase.h"
  9  #include "CFRuntime.h"
 10  #include "CFNotificationCenter.h"
 11  #include "CFString.h"
 12  #include "CFArray.h"
 13  #include <libkern/OSAtomic.h>
 14  
 15  struct __CFNotificationCenter {
 16      CFRuntimeBase _base;
 17      OSSpinLock lock;
 18      CFMutableArrayRef observers;
 19  };
 20  
 21  static void __CFNotificationCenterDeallocate(CFTypeRef cf) {
 22      struct __CFNotificationCenter *item = (struct __CFNotificationCenter *)cf;
 23      CFRelease(item->observers);
 24  }
 25  
 26  static CFTypeID __kCFNotificationCenterTypeID = _kCFRuntimeNotATypeID;
 27  
 28  static const CFRuntimeClass __CFNotificationCenterClass = {
 29      _kCFRuntimeScannedObject,
 30      "CFNotificationCenter",
 31      NULL,   // init
 32      NULL,   // copy
 33      __CFNotificationCenterDeallocate,
 34      NULL,
 35      NULL,
 36      NULL,
 37      NULL
 38  };
 39  
 40  static void __CFNotificationCenterInitialize(void) {
 41      __kCFNotificationCenterTypeID = _CFRuntimeRegisterClass(&__CFNotificationCenterClass);
 42  }
 43  
 44  CFTypeID CFNotificationCenterGetTypeID(void) {
 45      if (__kCFNotificationCenterTypeID == _kCFRuntimeNotATypeID) {
 46          __CFNotificationCenterInitialize();
 47      }
 48      return __kCFNotificationCenterTypeID;
 49  }
 50  
 51  typedef struct {
 52      const void *observer;
 53      CFNotificationCallback callBack;
 54      CFStringRef name;
 55      const void *object;
 56      CFNotificationSuspensionBehavior suspensionBehavior;
 57      void *context;
 58      int32_t retainCount;
 59  } CFNotificationObserver;
 60  
 61  static inline CFNotificationObserver *CFNotificationObserverRetain(CFAllocatorRef allocator, CFNotificationObserver *observer) {
 62      observer->retainCount = OSAtomicIncrement32(&observer->retainCount);
 63      return observer;
 64  }
 65  
 66  static inline void CFNotificationObserverRelease(CFAllocatorRef allocator,CFNotificationObserver *observer) {
 67      if (OSAtomicDecrement32(&observer->retainCount) < 0) {
 68          CFRelease(observer->name);
 69          free(observer);
 70      }
 71  }
 72  
 73  static inline Boolean CFNotificationObserverEqual(CFNotificationObserver *observer1, CFNotificationObserver *observer2) {
 74      if (observer1 == observer2) {
 75          return true;
 76      }
 77  
 78      if (observer1->observer != observer2->observer) {
 79          return false;
 80      }
 81      
 82      if (observer1->callBack != observer2->callBack) {
 83          return false;
 84      }
 85  
 86      if (CFStringCompare(observer1->name, observer2->name, 0) != kCFCompareEqualTo) {
 87          return false;
 88      }
 89  
 90      if (observer1->object != observer2->object) {
 91          return false;
 92      }
 93  
 94      return true;
 95  }
 96  
 97  static struct __CFNotificationCenter *_CFNotificationCenterCreate(CFAllocatorRef allocator) {
 98      CFIndex size = sizeof(struct __CFNotificationCenter) - sizeof(CFRuntimeBase);
 99      struct __CFNotificationCenter *center = (struct __CFNotificationCenter *)_CFRuntimeCreateInstance(allocator, CFNotificationCenterGetTypeID(), size, NULL);
100      center->lock = OS_SPINLOCK_INIT;
101      CFArrayCallBacks callbacks = {
102          .version = 0,
103          .retain = (CFArrayRetainCallBack)&CFNotificationObserverRetain,
104          .release = (CFArrayReleaseCallBack)&CFNotificationObserverRelease,
105          .copyDescription = NULL,
106          .equal = (CFArrayEqualCallBack)&CFNotificationObserverEqual
107      };
108      center->observers = CFArrayCreateMutable(allocator, 0, &callbacks);
109      return center;
110  }
111  
112  static CFNotificationCenterRef localCenter = NULL;
113  static CFNotificationCenterRef darwinCenter = NULL;
114  static CFNotificationCenterRef distributedCenter = NULL;
115  static OSSpinLock centerLock = OS_SPINLOCK_INIT;
116  
117  CF_EXPORT CFNotificationCenterRef CFNotificationCenterGetLocalCenter(void) {
118      OSSpinLockLock(&centerLock);
119      if (localCenter == NULL) {
120          localCenter = _CFNotificationCenterCreate(kCFAllocatorDefault);
121      }
122      OSSpinLockUnlock(&centerLock);
123      return localCenter;
124  }
125  
126  CF_EXPORT CFNotificationCenterRef CFNotificationCenterGetDarwinNotifyCenter(void) {
127      OSSpinLockLock(&centerLock);
128      if (darwinCenter == NULL) {
129          darwinCenter = _CFNotificationCenterCreate(kCFAllocatorDefault);
130      }
131      OSSpinLockUnlock(&centerLock);
132      return darwinCenter;
133  }
134  CF_EXPORT CFNotificationCenterRef CFNotificationCenterGetDistributedCenter(void) {
135      OSSpinLockLock(&centerLock);
136      if (distributedCenter == NULL) {
137          distributedCenter = _CFNotificationCenterCreate(kCFAllocatorDefault);
138      }
139      OSSpinLockUnlock(&centerLock);
140      return distributedCenter;
141  }
142  
143  CF_EXPORT void CFNotificationCenterAddObserver(CFNotificationCenterRef center, const void *observer, CFNotificationCallback callBack, CFStringRef name, const void *object, CFNotificationSuspensionBehavior suspensionBehavior) {
144      CFNotificationObserver *obs = (CFNotificationObserver *)malloc(sizeof(CFNotificationObserver));
145      obs->retainCount = 0;
146      obs->observer = observer;
147      obs->callBack = callBack;
148      obs->name = CFStringCreateCopy(kCFAllocatorDefault, name);
149      obs->object = object;
150      obs->suspensionBehavior = suspensionBehavior;
151      OSSpinLockLock(&center->lock);
152      CFArrayAppendValue(center->observers, obs);
153      OSSpinLockUnlock(&center->lock);
154  }
155  
156  #define __CFMaxRemove 128
157  struct __CFNotificationRemove {
158      CFNotificationObserver* ctx;
159      int removed;
160      CFIndex removeIdx[__CFMaxRemove];
161      int more;
162  };
163  
164  void removeObserver(CFNotificationObserver *observer, struct __CFNotificationRemove *notificationRemove) {
165      if (notificationRemove->removed == __CFMaxRemove)
166      {
167          return; // No space for more this pass
168      }
169      CFNotificationObserver* ctx = notificationRemove->ctx;
170      CFNotificationCenterRef center = (CFNotificationCenterRef)ctx->context;
171      CFMutableArrayRef observers = center->observers;
172      if (observer->observer == ctx->observer) {
173          Boolean nameMatches = false;
174          Boolean objectMatches = false;
175          if (ctx->name != NULL && CFStringCompare(observer->name, ctx->name, 0) == kCFCompareEqualTo) {
176              nameMatches = true;
177          } else if (ctx->name == NULL) {
178              nameMatches = true;
179          }
180          if (ctx->object != NULL && ctx->object == observer->object) {
181              objectMatches = true;
182          } else if (ctx->object == NULL) {
183              objectMatches = true;
184          }
185          if (nameMatches && objectMatches) {
186              CFIndex idx = CFArrayGetFirstIndexOfValue(observers, CFRangeMake(0, CFArrayGetCount(observers)), observer);
187              if (notificationRemove->removed == 0 || idx > notificationRemove->removeIdx[notificationRemove->removed-1]){
188                  notificationRemove->removeIdx[notificationRemove->removed++] = idx;
189              }
190              else
191              {
192                  notificationRemove->more++;
193              }
194          }
195      }
196  }
197  
198  CF_EXPORT void CFNotificationCenterRemoveObserver(CFNotificationCenterRef center, const void *observer, CFStringRef name, const void *object) {
199      if (observer == NULL) {
200          return;
201      }
202      OSSpinLockLock(&center->lock);
203      CFNotificationObserver ctx = {
204          .observer = observer,
205          .name = name,
206          .object = object,
207          .context = center
208      };
209      struct __CFNotificationRemove notificationRemove = {
210          .ctx = &ctx,
211          .removed = 0,
212          .more = 0
213      };
214      do {
215          CFArrayApplyFunction(center->observers, CFRangeMake(0, CFArrayGetCount(center->observers)), (CFArrayApplierFunction)&removeObserver, &notificationRemove);
216          for (int i=notificationRemove.removed-1; i >= 0; i--)
217          {
218              CFArrayRemoveValueAtIndex(center->observers, notificationRemove.removeIdx[i]);
219          }
220          if (notificationRemove.removed < __CFMaxRemove && !notificationRemove.more)
221          {
222              break;
223          }
224          notificationRemove.removed = 0;
225          notificationRemove.more = 0;
226      } while(1);
227      OSSpinLockUnlock(&center->lock);
228  }
229  
230  CF_EXPORT void CFNotificationCenterRemoveEveryObserver(CFNotificationCenterRef center, const void *observer) {
231      OSSpinLockLock(&center->lock);
232      CFArrayRemoveAllValues(center->observers);
233      OSSpinLockUnlock(&center->lock);
234  }
235  
236  CF_EXPORT void CFNotificationCenterPostNotification(CFNotificationCenterRef center, CFStringRef name, const void *object, CFDictionaryRef userInfo, Boolean deliverImmediately) {
237      CFNotificationCenterPostNotificationWithOptions(center, name, object, userInfo, kCFNotificationDeliverImmediately);
238  }
239  
240  CF_EXPORT void CFNotificationCenterPostNotificationWithOptions(CFNotificationCenterRef center, CFStringRef name, const void *object, CFDictionaryRef userInfo, CFOptionFlags options) {
241      // since this is not cross process, we can just deliver all of the notifs immediately
242      OSSpinLockLock(&center->lock);
243      CFArrayRef observers = CFArrayCreateCopy(kCFAllocatorDefault, center->observers);
244      OSSpinLockUnlock(&center->lock);
245      
246      CFIndex count = CFArrayGetCount(observers);
247      for (CFIndex idx = 0; idx < count; idx++) {
248          CFNotificationObserver *observer = (CFNotificationObserver *)CFArrayGetValueAtIndex(observers, idx);
249          if (name == NULL || observer->name == NULL || CFStringCompare(observer->name, name, 0) == kCFCompareEqualTo) {
250              if (object == NULL || observer->object == NULL || object == observer->object) {
251                  observer->callBack(center, (void *)observer->observer, name, object, userInfo);
252              }
253          }
254      }
255      
256      CFRelease(observers);
257  }
258