/ 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(¢erLock); 119 if (localCenter == NULL) { 120 localCenter = _CFNotificationCenterCreate(kCFAllocatorDefault); 121 } 122 OSSpinLockUnlock(¢erLock); 123 return localCenter; 124 } 125 126 CF_EXPORT CFNotificationCenterRef CFNotificationCenterGetDarwinNotifyCenter(void) { 127 OSSpinLockLock(¢erLock); 128 if (darwinCenter == NULL) { 129 darwinCenter = _CFNotificationCenterCreate(kCFAllocatorDefault); 130 } 131 OSSpinLockUnlock(¢erLock); 132 return darwinCenter; 133 } 134 CF_EXPORT CFNotificationCenterRef CFNotificationCenterGetDistributedCenter(void) { 135 OSSpinLockLock(¢erLock); 136 if (distributedCenter == NULL) { 137 distributedCenter = _CFNotificationCenterCreate(kCFAllocatorDefault); 138 } 139 OSSpinLockUnlock(¢erLock); 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(¢er->lock); 152 CFArrayAppendValue(center->observers, obs); 153 OSSpinLockUnlock(¢er->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(¢er->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, ¬ificationRemove); 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(¢er->lock); 228 } 229 230 CF_EXPORT void CFNotificationCenterRemoveEveryObserver(CFNotificationCenterRef center, const void *observer) { 231 OSSpinLockLock(¢er->lock); 232 CFArrayRemoveAllValues(center->observers); 233 OSSpinLockUnlock(¢er->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(¢er->lock); 243 CFArrayRef observers = CFArrayCreateCopy(kCFAllocatorDefault, center->observers); 244 OSSpinLockUnlock(¢er->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