make timers more robust

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@28656 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2009-09-10 16:41:06 +00:00
parent 55a63e4299
commit 0836fd9bd7
3 changed files with 67 additions and 18 deletions

View file

@ -5,7 +5,11 @@
method to only fire one timer (excluding the private housekeeping one).
Changing to only fire one timer actually allows the code to be simpler
so it's a good update in its own right, not just a OSX compatibility
tweak.
tweak. I also made incrementing repeated timers a bit more robust,
checking that they provide a valid increment time interval and
removing them from the loop if they don't.
* Source/NSTimer.m: slightly tidy/clarify initialisation code and
documentation.
2009-09-08 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -62,6 +62,7 @@
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <math.h>
#include <time.h>
#include <limits.h>
#include <string.h> /* for memset() */
@ -813,6 +814,20 @@ static inline BOOL timerInvalidated(NSTimer *t)
GSIArray timers;
unsigned i;
if ([timer isKindOfClass: [NSTimer class]] == NO
|| [timer isProxy] == YES)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] not a valid timer",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if ([mode isKindOfClass: [NSString class]] == NO)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] not a valid mode",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
NSDebugMLLog(@"NSRunLoop", @"add timer for %f in %@",
[[timer fireDate] timeIntervalSinceReferenceDate], mode);
@ -875,10 +890,38 @@ updateTimer(NSTimer *t, NSDate *d, NSTimeInterval now)
NSTimeInterval ti = [d timeIntervalSinceReferenceDate];
NSTimeInterval increment = [t timeInterval];
ti += increment;
while (ti < now)
if (increment <= 0.0)
{
ti += increment;
/* Should never get here ... unless a subclass is returning
* a bad interval ... we return NO so that the timer gets
* removed from the loop.
*/
NSLog(@"WARNING timer %@ had bad interval ... removed", t);
return NO;
}
ti += increment; // Hopefully a single increment will do.
if (ti < now)
{
NSTimeInterval add;
/* Just incrementing the date was insufficieint to bring it to
* the current time, so we must have missed one or more fire
* opportunities, or the fire date has been set on the timer.
* If a fire date long ago has been set and the increment value
* is really small, we might need to increment very many times
* to get the new fire date. To avoid looping for ages, we
* calculate the number of increments needed and do them in one
* go.
*/
add = floor((now - ti) / increment);
ti += (increment * add);
if (ti < now)
{
add++;
ti += increment;
}
}
d = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: ti];
[t setFireDate: d];

View file

@ -69,22 +69,21 @@ static Class NSDate_class;
return nil;
}
/**
* <init />
/** <init />
* Initialise the receive, a newly allocated NSTimer object.<br />
* The fd argument specifies an initial fire date ... if it is not
* supplied (a nil object) then the ti argument is used to create
* a start date relative to the current time.<br />
* The ti argument specifies the time (in seconds) between the firing.
* If it is less than or equal to 0.0 then a small interval is chosen
* automatically.<br />
* The fd argument specifies an initial fire date copied by the timer...
* if it is not supplied (a nil object) then the ti argument is used to
* create a start date relative to the current time.<br />
* The f argument specifies whether the timer will fire repeatedly
* or just once.<br />
* If the selector argument is zero, then then object is an invocation
* to be used when the timer fires. otherwise, the object is sent the
* message specified by the selector and with the timer as an argument.<br />
* The fd, object and info arguments will be retained until the timer is
* invalidated.<br />
* The object and info arguments will be retained until the timer is
* invalidated.
*/
- (id) initWithFireDate: (NSDate*)fd
interval: (NSTimeInterval)ti
@ -93,26 +92,30 @@ static Class NSDate_class;
userInfo: (id)info
repeats: (BOOL)f
{
if (ti <= 0)
if (ti <= 0.0)
{
ti = 0.0001;
}
_interval = ti;
if (fd == nil)
{
_date = [[NSDate_class allocWithZone: NSDefaultMallocZone()]
initWithTimeIntervalSinceNow: _interval];
initWithTimeIntervalSinceNow: ti];
}
else
{
_date = [fd copy];
_date = [fd copyWithZone: NSDefaultMallocZone()];
}
_target = RETAIN(object);
_selector = selector;
_info = RETAIN(info);
_repeats = f;
if (_repeats == NO)
if (f == YES)
{
_repeats = YES;
_interval = ti;
}
else
{
_repeats = NO;
_interval = 0.0;
}
return self;
@ -266,7 +269,6 @@ static Class NSDate_class;
- (void) invalidate
{
/* OPENSTEP allows this method to be called multiple times. */
//NSAssert(_invalidated == NO, NSInternalInconsistencyException);
_invalidated = YES;
if (_target != nil)
{