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:
rfm 2009-09-10 16:41:06 +00:00
parent b510b0d659
commit 4f13b89771
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). method to only fire one timer (excluding the private housekeeping one).
Changing to only fire one timer actually allows the code to be simpler 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 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> 2009-09-08 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -62,6 +62,7 @@
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <math.h>
#include <time.h> #include <time.h>
#include <limits.h> #include <limits.h>
#include <string.h> /* for memset() */ #include <string.h> /* for memset() */
@ -813,6 +814,20 @@ static inline BOOL timerInvalidated(NSTimer *t)
GSIArray timers; GSIArray timers;
unsigned i; 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 %@", NSDebugMLLog(@"NSRunLoop", @"add timer for %f in %@",
[[timer fireDate] timeIntervalSinceReferenceDate], mode); [[timer fireDate] timeIntervalSinceReferenceDate], mode);
@ -875,10 +890,38 @@ updateTimer(NSTimer *t, NSDate *d, NSTimeInterval now)
NSTimeInterval ti = [d timeIntervalSinceReferenceDate]; NSTimeInterval ti = [d timeIntervalSinceReferenceDate];
NSTimeInterval increment = [t timeInterval]; NSTimeInterval increment = [t timeInterval];
ti += increment; if (increment <= 0.0)
while (ti < now)
{ {
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]; d = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: ti];
[t setFireDate: d]; [t setFireDate: d];

View file

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