From 33691693fa37c78a19f82142da10b040d2de1405 Mon Sep 17 00:00:00 2001 From: rfm Date: Sat, 8 Dec 2007 19:56:39 +0000 Subject: [PATCH] Fix for mutation of timers array during firing of a timer. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@25706 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 5 +++++ Source/NSRunLoop.m | 49 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1ea6207c2..8208741be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-12-08 Richard Frith-Macdonald + + * Source/NSRunLoop.m: Deal with mutation of timers array during + firing of a timer. + 2007-12-07 Richard Frith-Macdonald * Headers/Additions/GNUstepBase/GSConfig.h.in: diff --git a/Source/NSRunLoop.m b/Source/NSRunLoop.m index f8e03e48c..b5adbd870 100644 --- a/Source/NSRunLoop.m +++ b/Source/NSRunLoop.m @@ -860,26 +860,55 @@ static inline BOOL timerInvalidated(NSTimer *t) d = timerDate(t); if ([d timeIntervalSinceReferenceDate] > now) { - when = [timerDate(t) copy]; + when = [d copy]; break; } - /* Firing will also increment its fireDate, if it is repeating. */ + /* When firing the timer we must remove it from + * the loop so that if the -fire methods re-runs + * the loop we do not get recursive entry into + * the timer. This appears to be the behavior + * in MacOS-X also. + */ + GSIArrayRemoveItemAtIndexNoRelease(timers, i); [t fire]; GSPrivateNotifyASAP(); /* Post notifications. */ IF_NO_GC([arp emptyPool]); now = GSTimeNow(); - /* Increment fire date unless timer is invalidated or the - * timeout handler has already updated it. + /* The -fire method could have re-run the current run loop + * and caused timers to have been added (not a problem), + * or invalidated and/or removed. In the latter case the + * timers array could have shrunk, so we must check that + * our loop index is not too large. */ - if (timerInvalidated(t) == NO && timerDate(t) == d) + if (i > GSIArrayCount(timers)) { - d = [[NSDate alloc] - initWithTimeIntervalSinceReferenceDate: - now + [t timeInterval]]; - [t setFireDate: d]; - RELEASE(d); + i = GSIArrayCount(timers); + } + if (timerInvalidated(t) == NO) + { + /* Increment fire date unless the timeout handler + * has already updated it. Then put the timer back + * in the array so that it can fire again next + * time this method is called. + */ + if (timerDate(t) == d) + { + d = [[NSDate alloc] + initWithTimeIntervalSinceReferenceDate: + now + [t timeInterval]]; + [t setFireDate: d]; + RELEASE(d); + } + GSIArrayInsertItemNoRetain(timers, (GSIArrayItem)((id)t), i); + } + else + { + /* The timer was invalidated, so we can release it as we + * aren't p[utting it back in the array. + */ + RELEASE(t); } } _currentMode = savedMode;