Skip to content

Commit dd8412d

Browse files
DEBUG COMMIT
1 parent e88c172 commit dd8412d

File tree

2 files changed

+135
-12
lines changed

2 files changed

+135
-12
lines changed

Monal/Classes/MLNotificationQueue.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ NS_ASSUME_NONNULL_BEGIN
1818
-(NSUInteger) clear;
1919

2020
+(instancetype) currentQueue;
21+
-(void) addObserver:(id) observer selector:(SEL) aSelector name:(NSNotificationName) aName object:(id) anObject NS_SWIFT_NAME(addObserver(_:selector:name:object:));
2122
-(void) postNotificationName:(NSNotificationName) notificationName object:(id _Nullable) notificationObject userInfo:(id _Nullable) notificationUserInfo NS_SWIFT_NAME(post(name:object:userInfo:));
2223
-(void) postNotificationName:(NSNotificationName) notificationName object:(id _Nullable) notificationObject NS_SWIFT_NAME(post(name:object:));
2324
-(void) postNotification:(NSNotification* _Nonnull) notification NS_SWIFT_NAME(post(name:));
25+
-(void) removeObserver:(id) observer;
2426

2527
@property (readonly, strong) NSString* name;
2628
-(NSString*) description;

Monal/Classes/MLNotificationQueue.m

Lines changed: 133 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,27 @@
88

99
#import <monalxmpp/MLNotificationQueue.h>
1010

11+
@interface ObserverEntry : NSObject
12+
@property (nonatomic, weak) id observer;
13+
@property (nonatomic) SEL selector;
14+
@property (nonatomic, strong) NSMethodSignature* sig;
15+
@property (nonatomic, strong) IMP imp;
16+
@property (nonatomic, weak) id object;
17+
@end
18+
@implementation ObserverEntry
19+
-(NSString*) description
20+
{
21+
return [NSString stringWithFormat:@"ObserverEntry: selector %@ on %@", self.selector, self.observer];
22+
}
23+
@end
24+
1125
@interface MLNotificationQueue()
1226
{
1327
NSString* _queueName;
1428
NSMutableArray* _entries;
1529
id _lowerQueue; //use id because this could be an MLNotificationQueue *or* [NSNotificationCenter defaultCenter]
30+
//for direct delivery without queueing
31+
NSMutableDictionary<NSNotificationName, NSMutableArray<ObserverEntry*>*>* _directObservers;
1632
}
1733
+(NSMutableArray*) getThreadLocalNotificationQueueStack;
1834
@end
@@ -39,6 +55,7 @@ +(void) queueNotificationsInBlock:(monal_void_block_t) block onQueue:(NSString*)
3955
//flush the queue to the next queue in our stack (or send them to the notification center if no queue is left on the stack)
4056
//don't use the flush deallocate because we want our flush to be "inline" thread-wise
4157
[queue flush];
58+
[queue bubbleUpDirectObservers];
4259
//this will deallocate our queue (flushing was already done before)
4360
queue = nil;
4461
}
@@ -55,17 +72,47 @@ +(instancetype) currentQueue
5572
-(void) postNotificationName:(NSNotificationName) notificationName object:(id _Nullable) notificationObject userInfo:(id _Nullable) notificationUserInfo
5673
{
5774
DDLogDebug(@"Queueing notification: %@, object = %@, userInfo = %@", notificationName, notificationObject, notificationUserInfo);
58-
//create queue entry (handle nil arguments)
59-
NSMutableDictionary* entry = [NSMutableDictionary new];
60-
entry[@"name"] = notificationName;
61-
if(notificationObject != nil)
62-
entry[@"obj"] = notificationObject;
63-
if(notificationUserInfo != nil)
64-
entry[@"userInfo"] = notificationUserInfo;
65-
66-
//add entry to our queue
75+
NSNotification* notification = [NSNotification notificationWithName:notificationName object:notificationObject userInfo:notificationUserInfo];
6776
@synchronized(_entries) {
68-
[_entries addObject:entry];
77+
[_entries addObject:notification];
78+
}
79+
80+
//allow observers in the same notification queue to receive the notification immediately rather than on queue flush
81+
NSArray* observers = @[];
82+
@synchronized(_directObservers) {
83+
if(_directObservers[notificationName] != nil)
84+
observers = [_directObservers[notificationName] copy];
85+
}
86+
if(observers.count > 0)
87+
{
88+
MLNotificationQueue* currentQueue = [MLNotificationQueue currentQueue];
89+
DDLogDebug(@"Calling direct handlers for notification created in the same queue (%@): %@", currentQueue.name, notificationName);
90+
for(let entry in observers[notificationName])
91+
{
92+
if(entry.observer == nil)
93+
continue;
94+
if(entry.object != nil && entry.object != notificationObject)
95+
continue;
96+
if(![entry.observer respondsToSelector:entry.selector])
97+
continue;
98+
99+
switch(entry.sig.numberOfArguments)
100+
{
101+
case 2:
102+
//-(void) handler;
103+
void (*func)(id, SEL) = (void (*)(id, SEL))entry.imp;
104+
func(entry.observer, entry.selector);
105+
break;
106+
case 3:
107+
//-(void) handler:(NSNotification*);
108+
void (*func)(id, SEL, NSNotification*) = (void (*)(id, SEL, NSNotification*))entry.imp;
109+
func(entry.observer, entry.selector, notification);
110+
break;
111+
default:
112+
unreachable(@"We should never reach this because of previous sanity checks!");
113+
break;
114+
}
115+
}
69116
}
70117
}
71118

@@ -81,6 +128,54 @@ -(void) postNotification:(NSNotification*) notification
81128
[self postNotificationName:notification.name object:notification.object userInfo:notification.userInfo];
82129
}
83130

131+
//this is compatible to [NSNotificationCenter defaultCenter]
132+
-(void) addObserver:(id) observer selector:(SEL) aSelector name:(NSNotificationName) aName object:(id) anObject
133+
{
134+
if(!observer || !aSelector || !aName)
135+
return;
136+
137+
ObserverEntry* entry = [ObserverEntry new];
138+
entry.observer = observer;
139+
entry.selector = aSelector;
140+
entry.object = anObject;
141+
entry.imp = [observer methodForSelector:aSelector];
142+
entry.sig = [observer methodSignatureForSelector:aSelector];
143+
144+
//sanity checks (do them here to have higher performance when calling handlers later)
145+
if(entry.sig.methodReturnType != "v")
146+
@throw [NSException exceptionWithName:@"NotificationQueueException" reason:@"Tried to use notification handler with non-void return value!" userInfo:@{
147+
@"sig": sig,
148+
@"sig.methodReturnType": @(sig.methodReturnType),
149+
}];
150+
if(sig.numberOfArguments > 3 || sig.numberOfArguments < 2)
151+
@throw [NSException exceptionWithName:@"NotificationQueueException" reason:@"Tried to use notification handler with unsupported argument count!" userInfo:@{
152+
@"sig": sig,
153+
@"sig.numberOfArguments": @(sig.numberOfArguments),
154+
}];
155+
156+
@synchronized(_directObservers) {
157+
if(_directObservers[aName] == nil)
158+
_directObservers[aName] = [NSMutableArray new];
159+
[_directObservers[aName] addObject:entry];
160+
}
161+
}
162+
163+
//this is compatible to [NSNotificationCenter defaultCenter]
164+
-(void) removeObserver:(id) observer
165+
{
166+
@synchronized(_directObservers) {
167+
for(let name in _directObservers)
168+
{
169+
for(let entry in [_directObservers[name] copy])
170+
if(entry.observer == observers)
171+
{
172+
DDLogDebug(@"Removing observer for notification %@: %@", name, entry);
173+
[_directObservers[name] removeObject:observer];
174+
}
175+
}
176+
}
177+
}
178+
84179
-(NSUInteger) flush
85180
{
86181
DDLogDebug(@"Flushing queue '%@', current stack: %@", [self name], [[[[self class] getThreadLocalNotificationQueueStack] reverseObjectEnumerator] allObjects]);
@@ -91,7 +186,7 @@ -(NSUInteger) flush
91186
}
92187
DDLogVerbose(@"Notifications in queue '%@': %@", [self name], toFlush);
93188
for(NSDictionary* entry in toFlush)
94-
[_lowerQueue postNotificationName:entry[@"name"] object:entry[@"obj"] userInfo:entry[@"userInfo"]];
189+
[_lowerQueue postNotificationName:entry.name object:entry.object userInfo:name.userInfo];
95190
@synchronized(_entries) {
96191
if([_entries count])
97192
@throw [NSException exceptionWithName:@"NotificationQueueException" reason:[NSString stringWithFormat:@"Tried to add more entries to queue while flushing: %@", _queueName] userInfo:nil];
@@ -121,7 +216,7 @@ -(NSString*) description
121216
NSMutableArray* queuedNotificationNames = [NSMutableArray new];
122217
@synchronized(_entries) {
123218
for(NSDictionary* entry in _entries)
124-
[queuedNotificationNames addObject:entry[@"name"]];
219+
[queuedNotificationNames addObject:entry.name];
125220
}
126221
return [NSString stringWithFormat:@"%@: %@", self.name, queuedNotificationNames];
127222
}
@@ -141,14 +236,40 @@ -(instancetype) initWithName:(NSString*) queueName
141236
_queueName = queueName;
142237
_entries = [NSMutableArray new];
143238
_lowerQueue = [MLNotificationQueue currentQueue];
239+
_directObservers = [NSMutableDictionary new];
144240
return self;
145241
}
146242

243+
-(void) bubbleUpDirectObservers
244+
{
245+
/*NSArray* observers = @[];
246+
@synchronized(_directObservers) {
247+
if(_directObservers[notificationName] != nil)
248+
{
249+
observers = [_directObservers[notificationName] copy];
250+
_directObservers = [NSMutableDictionary new];
251+
}
252+
}
253+
if(observers.count > 0)
254+
{
255+
MLNotificationQueue* currentQueue = [MLNotificationQueue currentQueue];
256+
DDLogDebug(@"Promoting direct all handlers to next queue: %@", currentQueue);
257+
for(let name in observers)
258+
for(let entry in observers[name])
259+
if(entry.observer != nil)
260+
[currentQueue addObserver:entry.observer selector:entry.selector name:name object:entry.object];
261+
}*/
262+
}
263+
147264
-(void) dealloc
148265
{
149266
//there should only be one thread calling dealloc ever (per objc runtime) --> no @synchronized needed
150267
if([_entries count])
151268
[self flush];
269+
270+
//bubble up all observers still registered with us
271+
if([_directObservers count])
272+
[self bubbleUpDirectObservers];
152273
}
153274

154275
@end

0 commit comments

Comments
 (0)