diff --git a/ChangeLog b/ChangeLog index a621d533d..eb0796e9b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2009-11-27 Richard Frith-Macdonald + + * Source/NSNotificationQueue.m: Rewrite queue handling to attempt to + fix bug #28104 by posting notifications matching the current run loop + mode and leaving others queued. + Also, change the meanining of a nil modes argument when enqueing a + notification to match OSX and assume NSDefaultRunLoopMode. + * Source/Additions/GSXML.m: Fix minor sax callback bug. + 2009-11-27 Richard Frith-Macdonald * Source/NSPropertyList.m: When serializing 'xml' style property lists diff --git a/Source/Additions/GSXML.m b/Source/Additions/GSXML.m index be0958d03..6ef81e648 100644 --- a/Source/Additions/GSXML.m +++ b/Source/Additions/GSXML.m @@ -3484,7 +3484,7 @@ fatalErrorFunction(void *ctx, const unsigned char *msg, ...) SETCB(hasExternalSubset, hasExternalSubset); SETCB(getEntity, getEntity:); SETCB(entityDecl, entityDecl:type:public:system:content:); - SETCB(notationDecl, notationDecl:public:); + SETCB(notationDecl, notationDecl:public:system:); SETCB(attributeDecl, attributeDecl:name:type:typeDefValue:defaultValue:); SETCB(elementDecl, elementDecl:type:); SETCB(unparsedEntityDecl, unparsedEntityDecl:public:system:notationName:); diff --git a/Source/NSNotificationQueue.m b/Source/NSNotificationQueue.m index 8d5f041cd..57b17bf15 100644 --- a/Source/NSNotificationQueue.m +++ b/Source/NSNotificationQueue.m @@ -278,8 +278,7 @@ add_to_queue(NSNotificationQueueList *queue, NSNotification *notification, */ @interface NSNotificationQueue (Private) -- (void) _postNotification: (NSNotification*)notification - forModes: (NSArray*)modes; +- (NSNotificationCenter*) _center; @end /** @@ -291,6 +290,16 @@ add_to_queue(NSNotificationQueueList *queue, NSNotification *notification, */ @implementation NSNotificationQueue +static NSArray *defaultMode = nil; + ++ (void) initialize +{ + if (defaultMode == nil) + { + defaultMode = [[NSArray alloc] initWithObjects: &NSDefaultRunLoopMode + count: 1]; + } +} /** * Returns the default notification queue for use in this thread. It will @@ -515,13 +524,17 @@ add_to_queue(NSNotificationQueueList *queue, NSNotification *notification, * in which case they are removed through a call to * -dequeueNotificationsMatching:coalesceMask: . The modes argument * determines which [NSRunLoop] mode notification may be posted in (nil means - * all modes). + * NSDefaultRunLoopMode). */ - (void) enqueueNotification: (NSNotification*)notification postingStyle: (NSPostingStyle)postingStyle coalesceMask: (NSUInteger)coalesceMask forModes: (NSArray*)modes { + if (modes == nil) + { + modes = defaultMode; + } if (coalesceMask != NSNotificationNoCoalescing) { [self dequeueNotificationsMatching: notification @@ -530,11 +543,21 @@ add_to_queue(NSNotificationQueueList *queue, NSNotification *notification, switch (postingStyle) { case NSPostNow: - [self _postNotification: notification forModes: modes]; + { + NSString *mode; + + mode = [[NSRunLoop currentRunLoop] currentMode]; + if (mode == nil || [modes indexOfObject: mode] != NSNotFound) + { + [_center postNotification: notification]; + } + } break; + case NSPostASAP: add_to_queue(_asapQueue, notification, modes, _zone); break; + case NSPostWhenIdle: add_to_queue(_idleQueue, notification, modes, _zone); break; @@ -545,48 +568,82 @@ add_to_queue(NSNotificationQueueList *queue, NSNotification *notification, @implementation NSNotificationQueue (Private) -- (void) _postNotification: (NSNotification*)notification - forModes: (NSArray*)modes +- (NSNotificationCenter*) _center { - NSString *mode = [[NSRunLoop currentRunLoop] currentMode]; - - // check to see if run loop is in a valid mode - if (mode == nil || modes == nil - || [modes indexOfObject: mode] != NSNotFound) - { - [_center postNotification: notification]; - } + return _center; } @end +static void +notify(NSNotificationCenter *center, NSNotificationQueueList *list, + NSString *mode, NSZone *zone) +{ + BOOL allocated = NO; + NSNotificationQueueRegistration *buf[100]; + NSNotificationQueueRegistration **ptr = buf; + unsigned len = sizeof(buf) / sizeof(*buf); + unsigned pos = 0; + NSNotificationQueueRegistration *item = list->head; + + /* Gather matching items into a buffer. + */ + while (item != 0) + { + if (mode == nil || [item->modes indexOfObject: mode] != NSNotFound) + { + if (pos == len) + { + unsigned want; + + want = (len == 0) ? 2 : len * 2; + if (NO == allocated) + { + void *tmp; + + tmp = NSZoneMalloc(NSDefaultMallocZone(), + want * sizeof(void*)); + memcpy(tmp, (void*)ptr, len * sizeof(void*)); + ptr = tmp; + allocated = YES; + } + else + { + ptr = NSZoneRealloc(NSDefaultMallocZone(), + ptr, want * sizeof(void*)); + } + len = want; + } + ptr[pos++] = item; + } + item = item->next; + } + len = pos; // Number of items found + + if (len > 0) + { + for (pos = 0; pos < len; pos++) + { + NSNotification *notification; + + item = ptr[pos]; + notification = RETAIN(item->notification); + remove_from_queue(list, item, zone); + [center postNotification: notification]; + RELEASE(notification); + } + if (allocated) + { + NSZoneFree(NSDefaultMallocZone(), ptr); + } + } +} + /* * The following code handles sending of queued notifications by * NSRunLoop. */ -static inline void -notifyASAP(NSNotificationQueue *q, NSString *mode) -{ - NSNotificationQueueList *list = ((accessQueue)q)->_asapQueue; - - /* - * post all ASAP notifications in queue - */ - while (list->head) - { - NSNotificationQueueRegistration *item = list->head; - NSNotification *notification = item->notification; - NSArray *modes = item->modes; - - remove_from_queue_no_release(list, item); - [q _postNotification: notification forModes: modes]; - RELEASE(notification); - RELEASE(modes); - NSZoneFree(((accessQueue)q)->_zone, item); - } -} - void GSPrivateNotifyASAP(NSString *mode) { @@ -596,37 +653,14 @@ GSPrivateNotifyASAP(NSString *mode) { if (item->queue) { - notifyASAP(item->queue, mode); + notify(((accessQueue)item->queue)->_center, + ((accessQueue)item->queue)->_asapQueue, + mode, + ((accessQueue)item->queue)->_zone); } } } -static inline void -notifyIdle(NSNotificationQueue *q, NSString *mode) -{ - NSNotificationQueueList *list = ((accessQueue)q)->_idleQueue; - - /* - * post next IDLE notification in queue - */ - if (list->head) - { - NSNotificationQueueRegistration *item = list->head; - NSNotification *notification = item->notification; - NSArray *modes = item->modes; - - remove_from_queue_no_release(list, item); - [q _postNotification: notification forModes: modes]; - RELEASE(notification); - RELEASE(modes); - NSZoneFree(((accessQueue)q)->_zone, item); - } - /* - * Post all ASAP notifications. - */ - notifyASAP(q, mode); -} - void GSPrivateNotifyIdle(NSString *mode) { @@ -636,7 +670,10 @@ GSPrivateNotifyIdle(NSString *mode) { if (item->queue) { - notifyIdle(item->queue, mode); + notify(((accessQueue)item->queue)->_center, + ((accessQueue)item->queue)->_idleQueue, + mode, + ((accessQueue)item->queue)->_zone); } } } @@ -648,9 +685,18 @@ GSPrivateNotifyMore(NSString *mode) for (item = currentList(); item; item = item->next) { - if (item->queue && ((accessQueue)item->queue)->_idleQueue->head) + if (item->queue != nil) { - return YES; + NSNotificationQueueRegistration *r; + + r = ((accessQueue)item->queue)->_idleQueue->head; + while (r != 0) + { + if (mode == nil || [r->modes indexOfObject: mode] != NSNotFound) + { + return YES; + } + } } } return NO;