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