mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Fix runloop problems where a timer is added to the loop more than once
(in different modes). git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@25694 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
0420f16e00
commit
cbb3df3248
4 changed files with 64 additions and 32 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2007-12-07 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSRunLoop.m: Keep timers unordered and check all of them
|
||||
each time round ... to avoid bug where a timer is added to more
|
||||
than one run loop mode and firing in one mode could result in badly
|
||||
ordered timers in another mode.
|
||||
Handle resetting of time for repeating timers.
|
||||
* Source/NSTimer.m: Remove resetting of fire date from ([-fire])
|
||||
and move it to the run loop for MacOS-X compatibility.
|
||||
|
||||
2007-12-06 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSRunLoop.m: Report the current mode when producing detailed
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <Foundation/NSZone.h>
|
||||
|
||||
/* To turn assertions on, define GSI_ARRAY_CHECKS */
|
||||
#define GSI_ARRAY_CHECKS 1
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
|
|
|
@ -740,6 +740,10 @@ static NSComparisonResult tSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
{
|
||||
GSRunLoopCtxt *context;
|
||||
GSIArray timers;
|
||||
unsigned i;
|
||||
|
||||
NSDebugMLLog(@"NSRunLoop", @"add timer for %f in %@",
|
||||
[[timer fireDate] timeIntervalSinceReferenceDate], mode);
|
||||
|
||||
context = NSMapGet(_contextMap, mode);
|
||||
if (context == nil)
|
||||
|
@ -749,7 +753,30 @@ static NSComparisonResult tSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
RELEASE(context);
|
||||
}
|
||||
timers = context->timers;
|
||||
GSIArrayInsertSorted(timers, (GSIArrayItem)((id)timer), tSort);
|
||||
i = GSIArrayCount(timers);
|
||||
while (i-- > 0)
|
||||
{
|
||||
if (timer == GSIArrayItemAtIndex(timers, i).obj)
|
||||
{
|
||||
return; /* Timer already present */
|
||||
}
|
||||
}
|
||||
/*
|
||||
* NB. A previous version of the timer code maintained an ordered
|
||||
* array on the theory that we could improve performance by only
|
||||
* checking the first few timers (up to the first one whose fire
|
||||
* date is in the future) each time -limitDateForMode: is called.
|
||||
* The problem with this was that it's possible for one timer to
|
||||
* be added in multiple modes (or to different run loops) and for
|
||||
* a repeated timer this could mean that the firing of the timer
|
||||
* in one mode/loop adjusts its date ... without changing the
|
||||
* ordering of the timers in the other modes/loops which contain
|
||||
* the timer. When the ordering of timers in an array was broken
|
||||
* we could get delays in processing timeouts, so we reverted to
|
||||
* simply having timers in an unordered array and checking them
|
||||
* all each time -limitDateForMode: is called.
|
||||
*/
|
||||
GSIArrayAddItem(timers, (GSIArrayItem)((id)timer));
|
||||
}
|
||||
|
||||
|
||||
|
@ -776,6 +803,7 @@ static NSComparisonResult tSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
GSIArray timers = context->timers;
|
||||
NSTimeInterval now;
|
||||
NSTimer *t;
|
||||
unsigned i;
|
||||
|
||||
/*
|
||||
* Save current time so we don't keep redoing system call to
|
||||
|
@ -788,25 +816,32 @@ static NSComparisonResult tSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
/*
|
||||
* Fire housekeeping timer as necessary
|
||||
*/
|
||||
while ((t = context->housekeeper) != nil
|
||||
if ((t = context->housekeeper) != nil
|
||||
&& ([timerDate(t) timeIntervalSinceReferenceDate] <= now))
|
||||
{
|
||||
NSDate *next;
|
||||
|
||||
[t fire];
|
||||
IF_NO_GC([arp emptyPool]);
|
||||
now = GSTimeNow();
|
||||
next = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:
|
||||
now + [t timeInterval]];
|
||||
[t setFireDate: next];
|
||||
RELEASE(next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle normal timers ... remove invalidated timers and fire any
|
||||
* whose date has passed.
|
||||
*/
|
||||
while (GSIArrayCount(timers) != 0)
|
||||
i = GSIArrayCount(timers);
|
||||
while (i-- > 0)
|
||||
{
|
||||
NSTimer *min_timer = GSIArrayItemAtIndex(timers, 0).obj;
|
||||
NSTimer *min_timer = GSIArrayItemAtIndex(timers, i).obj;
|
||||
|
||||
if (timerInvalidated(min_timer) == YES)
|
||||
{
|
||||
GSIArrayRemoveItemAtIndex(timers, 0);
|
||||
GSIArrayRemoveItemAtIndex(timers, i);
|
||||
min_timer = nil;
|
||||
continue;
|
||||
}
|
||||
|
@ -817,18 +852,21 @@ static NSComparisonResult tSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
break;
|
||||
}
|
||||
|
||||
GSIArrayRemoveItemAtIndexNoRelease(timers, 0);
|
||||
/* Firing will also increment its fireDate, if it is repeating. */
|
||||
[min_timer fire];
|
||||
now = GSTimeNow();
|
||||
if (timerInvalidated(min_timer) == NO)
|
||||
{
|
||||
GSIArrayInsertSortedNoRetain(timers,
|
||||
(GSIArrayItem)((id)min_timer), tSort);
|
||||
NSDate *next;
|
||||
|
||||
next = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:
|
||||
now + [min_timer timeInterval]];
|
||||
[min_timer setFireDate: next];
|
||||
RELEASE(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
RELEASE(min_timer);
|
||||
GSIArrayRemoveItemAtIndex(timers, i);
|
||||
}
|
||||
GSPrivateNotifyASAP(); /* Post notifications. */
|
||||
IF_NO_GC([arp emptyPool]);
|
||||
|
|
|
@ -99,6 +99,10 @@ static Class NSDate_class;
|
|||
_selector = selector;
|
||||
_info = RETAIN(info);
|
||||
_repeats = f;
|
||||
if (_repeats == NO)
|
||||
{
|
||||
_interval = 0.0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -239,28 +243,6 @@ static Class NSDate_class;
|
|||
{
|
||||
[self invalidate];
|
||||
}
|
||||
else if (_invalidated == NO)
|
||||
{
|
||||
extern NSTimeInterval GSTimeNow();
|
||||
NSTimeInterval now = GSTimeNow();
|
||||
NSTimeInterval nxt = [_date timeIntervalSinceReferenceDate];
|
||||
int inc = -1;
|
||||
|
||||
while (nxt <= now) // xxx remove this
|
||||
{
|
||||
inc++;
|
||||
nxt += _interval;
|
||||
}
|
||||
#ifdef LOG_MISSED
|
||||
if (inc > 0)
|
||||
{
|
||||
NSLog(@"Missed %d timeouts at %f second intervals", inc, _interval);
|
||||
}
|
||||
#endif
|
||||
RELEASE(_date);
|
||||
_date = [[NSDate_class allocWithZone: NSDefaultMallocZone()]
|
||||
initWithTimeIntervalSinceReferenceDate: nxt];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -320,7 +302,8 @@ static Class NSDate_class;
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the interval between firings.
|
||||
* Returns the interval between firings, or zero if the timer
|
||||
* does not repeat.
|
||||
*/
|
||||
- (NSTimeInterval) timeInterval
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue