Rewrite thread handling to avoid use of locks

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@21906 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-Macdonald 2005-10-29 16:07:38 +00:00
parent 9b80b6621c
commit 7a89dea8ea
2 changed files with 406 additions and 307 deletions

View file

@ -31,34 +31,38 @@
/** /**
* The GSThroughput class is used maintain statistics about the number * The GSThroughput class is used maintain statistics about the number
* of events or the duration of operations in your software. * of events or the duration of operations in your software.<br />
* For performance reasons, the class avoids locking and you must ensure
* that an instance of the class is only ever used by a single thread
* (the one in which it was created).
*/ */
@interface GSThroughput : NSObject @interface GSThroughput : NSObject
{ {
} }
/** /**
* Return all the current throughput measuring objects ... * Return all the current throughput measuring objects in the current thread...
* useful if you want to do to all instances in your process.
*/ */
+ (NSArray*) allInstances; + (NSArray*) allInstances;
/** /**
* Return a report on all GSThroughput instances ... calls * Return a report on all GSThroughput instances in the current thread...
* the [GSThroughput-description] * method of the individual * calls the [GSThroughput-description] method of the individual instances
* instances to get a report on each one. * to get a report on each one.
*/ */
+ (NSString*) description; + (NSString*) description;
/** /**
* Instructs the minitoring system to use a timer at the specified interval * Instructs the monitoring system to use a timer at the specified interval
* for keeping its idea of the current time up to date. * for keeping its idea of the current time up to date. This timer is used
* by all instances associated with the current thread.
*/ */
+ (void) setTick: (NSTimeInterval)interval; + (void) setTick: (NSTimeInterval)interval;
/** /**
* Updates the monitoring system's notion of the current time.<br /> * Updates the monitoring system's notion of the current time for all
* This should be called at the start of each (or more often) if * instances associated with the current thread.<br />
* This should be called at the start of each second (or more often) if
* you want accurate monitoring by the second. * you want accurate monitoring by the second.
*/ */
+ (void) tick; + (void) tick;
@ -84,22 +88,22 @@
/** /**
* Ends duration recording for the current event started by a matching * Ends duration recording for the current event started by a matching
* call to -startDuration.<br /> * call to the -startDuration: method.<br />
* You may use this method only if the receiver was initialised with * You may use this method only if the receiver was initialised with
* duration logging turned on. * duration logging turned on.
*/ */
- (void) endDuration; - (void) endDuration;
/** /**
* Initialises the receiver for duration logging for fifteen minute * Initialises the receiver for duration logging (in the current thread only)
* periods over the last twentyfour hours. * for fifteen minute periods over the last twentyfour hours.
*/ */
- (id) init; - (id) init;
/** <init /> /** <init />
* Initialises the receiver to maintain stats over a particular time range, * Initialises the receiver to maintain stats (for the current thread only)
* specifying whether duration statistics are to be maintained, or just * over a particular time range, specifying whether duration statistics are
* event/transation counts. * to be maintained, or just event/transaction counts.
*/ */
- (id) initWithDurations: (BOOL)aFlag - (id) initWithDurations: (BOOL)aFlag
forPeriods: (unsigned)numberOfPeriods forPeriods: (unsigned)numberOfPeriods
@ -117,16 +121,16 @@
/** /**
* Starts recording the duration of an event. This must be followed by * Starts recording the duration of an event. This must be followed by
* a matching call to -endDuration.<br /> * a matching call to the -endDuration method.<br />
* The name argument is used to identify the location of the call for
* debugging/logging purposes, and you must ensure that the string
* continues to exist up to the point where -endDuration is called,
* as the receiver will not retain it.<br />
* You may use this method only if the receiver was initialised with * You may use this method only if the receiver was initialised with
* duration logging turned on. * duration logging turned on.
*/ */
- (void) startDuration; - (void) startDuration: (NSString*)name;
/**
* Internal method called by +tick in order to update stats for this instance.
*/
- (void) update;
@end @end
#endif #endif

View file

@ -33,37 +33,21 @@
#include <Foundation/NSException.h> #include <Foundation/NSException.h>
#include <Foundation/NSNotification.h> #include <Foundation/NSNotification.h>
#include <Foundation/NSHashTable.h> #include <Foundation/NSHashTable.h>
#include <Foundation/NSMapTable.h>
#include <Foundation/NSLock.h>
#include <Foundation/NSAutoreleasePool.h> #include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSDebug.h> #include <Foundation/NSDebug.h>
#include <Foundation/NSSet.h>
#include <Foundation/NSTimer.h> #include <Foundation/NSTimer.h>
#include <Foundation/NSThread.h>
#include "GSThroughput.h" #include "GSThroughput.h"
#define MAXDURATION 24.0*60.0*60.0 #define MAXDURATION 24.0*60.0*60.0
@class GSThroughputThread;
static Class NSDateClass = 0; static Class NSDateClass = 0;
static SEL tiSel = 0; static SEL tiSel = 0;
static NSTimeInterval (*tiImp)(Class,SEL) = 0; static NSTimeInterval (*tiImp)(Class,SEL) = 0;
static NSTimer *theTimer = nil;
static NSTimeInterval baseTime = 0;
static NSTimeInterval lastTime = 0;
inline unsigned GSThroughputTimeTick()
{
return (lastTime - baseTime) + 1;
}
@implementation GSThroughput
static NSHashTable *GSThroughputInstances = 0;
static NSLock *GSThroughputLock = nil;
typedef struct { typedef struct {
unsigned cnt; // Number of events. unsigned cnt; // Number of events.
unsigned tick; // Start time unsigned tick; // Start time
@ -89,7 +73,9 @@ typedef struct {
unsigned period; unsigned period;
unsigned last; // last tick used unsigned last; // last tick used
NSTimeInterval started; // When duration logging started. NSTimeInterval started; // When duration logging started.
NSString *name; NSString *event; // Name of current event
NSString *name; // Name of this instance
GSThroughputThread *thread; // Thread info
} Item; } Item;
#define my ((Item*)&self[1]) #define my ((Item*)&self[1])
@ -100,110 +86,315 @@ typedef struct {
#define dminutes ((DInfo*)my->minutes) #define dminutes ((DInfo*)my->minutes)
#define dperiods ((DInfo*)my->periods) #define dperiods ((DInfo*)my->periods)
+ (NSArray*) allInstances
{
NSArray *a;
[GSThroughputLock lock]; @interface GSThroughputThread : NSObject
a = NSAllHashTableObjects(GSThroughputInstances); {
[GSThroughputLock unlock]; @public
return a; NSTimer *theTimer;
NSTimeInterval baseTime;
NSTimeInterval lastTime;
NSHashTable *instances;
} }
@end
+ (id) alloc @interface GSThroughput (Private)
+ (GSThroughputThread*) _threadInfo;
+ (void) _tick: (NSTimer*)aTimer;
+ (void) _tickForThread: (GSThroughputThread*)t;
- (void) _detach;
- (void) _update;
@end
@implementation GSThroughputThread
- (void) dealloc
{ {
return [self allocWithZone: NSDefaultMallocZone()]; if (instances != 0)
}
+ (id) allocWithZone: (NSZone*)z
{
GSThroughput *c;
c = (GSThroughput*)NSAllocateObject(self, sizeof(Item), z);
[GSThroughputLock lock];
NSHashInsert(GSThroughputInstances, (void*)c);
[GSThroughputLock unlock];
return c;
}
+ (NSString*) description
{
NSMutableString *ms;
NSHashEnumerator e;
GSThroughput *c;
ms = [NSMutableString stringWithString: [super description]];
[GSThroughputLock lock];
e = NSEnumerateHashTable(GSThroughputInstances);
while ((c = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
{ {
[ms appendFormat: @"\n%@", [c description]]; NSHashEnumerator e;
GSThroughput *i;
e = NSEnumerateHashTable(instances);
while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
{
[i _detach];
}
NSEndHashTableEnumeration(&e);
NSFreeHashTable(instances);
instances = 0;
} }
NSEndHashTableEnumeration(&e); [super dealloc];
[GSThroughputLock unlock];
return ms;
} }
+ (void) initialize - (id) init
{ {
if (GSThroughputInstances == 0) baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
{ instances = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
NSDateClass = [NSDate class]; return self;
tiSel = @selector(timeIntervalSinceReferenceDate);
tiImp
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
GSThroughputLock = [NSLock new];
GSThroughputInstances
= NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
[self setTick: 1.0];
}
} }
+ (void) setTick: (NSTimeInterval)interval @end
@implementation GSThroughput (Private)
+ (GSThroughputThread*) _threadInfo
{ {
[GSThroughputLock lock]; GSThroughputThread *t;
if (theTimer != nil)
t = [[[NSThread currentThread] threadDictionary]
objectForKey: @"GSThroughput"];
if (t == nil)
{ {
[theTimer invalidate]; t = [GSThroughputThread new];
theTimer = nil; [[[NSThread currentThread] threadDictionary] setObject: t
forKey: @"GSThroughput"];
RELEASE(t);
} }
if (interval > 0.0) return t;
{
theTimer = [NSTimer scheduledTimerWithTimeInterval: interval
target: self
selector: @selector(tick)
userInfo: 0
repeats: YES];
}
[GSThroughputLock unlock];
} }
+ (void) tick + (void) _tick: (NSTimer*)aTimer
{
[self _tickForThread: [aTimer userInfo]];
}
+ (void) _tickForThread: (GSThroughputThread*)t
{ {
NSTimeInterval now; NSTimeInterval now;
NSHashEnumerator e; NSHashEnumerator e;
GSThroughput *i; GSThroughput *i;
[GSThroughputLock lock];
/* /*
* If the clock has been reset so that time has gone backwards, * If the clock has been reset so that time has gone backwards,
* we adjust the baseTime so that lastTime >= baseTime is true. * we adjust the baseTime so that lastTime >= baseTime is true.
*/ */
now = (*tiImp)(NSDateClass, tiSel); now = (*tiImp)(NSDateClass, tiSel);
if (now < lastTime) if (now < t->lastTime)
{ {
baseTime -= (lastTime - now); t->baseTime -= (t->lastTime - now);
} }
lastTime = now; t->lastTime = now;
e = NSEnumerateHashTable(GSThroughputInstances); e = NSEnumerateHashTable(t->instances);
while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil) while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
{ {
[i update]; [i _update];
} }
NSEndHashTableEnumeration(&e); NSEndHashTableEnumeration(&e);
}
[GSThroughputLock unlock]; - (void) _detach
{
my->thread = nil;
}
- (void) _update
{
if (my->thread != nil)
{
unsigned tick = (my->thread->lastTime - my->thread->baseTime) + 1;
unsigned i;
if (my->supportDurations == YES)
{
while (my->last < tick)
{
DInfo *info;
if (my->second++ == 59)
{
info = &dminutes[my->minute];
for (i = 0; i < 60; i++)
{
DInfo *from = &dseconds[i];
info->cnt += from->cnt;
if (from->min < info->min)
{
info->min = from->min;
}
if (from->max > info->max)
{
info->max = from->max;
}
info->sum += from->sum;
}
if (my->minute++ == my->minutesPerPeriod)
{
info = &dperiods[my->period];
for (i = 0; i < my->minutesPerPeriod; i++)
{
DInfo *from = &dminutes[i];
info->cnt += from->cnt;
if (from->min > 0.0 && from->min < info->min)
{
info->min = from->min;
}
if (from->max > info->max)
{
info->max = from->max;
}
info->sum += from->sum;
}
if (my->period++ == my->numberOfPeriods)
{
my->period = 0;
}
info = &dperiods[my->period];
info->cnt = 0;
info->max = 0.0;
info->min = MAXDURATION;
info->sum = 0.0;
info->tick = tick;
my->minute = 0;
}
info = &dminutes[my->minute];
info->cnt = 0;
info->max = 0.0;
info->min = MAXDURATION;
info->sum = 0.0;
info->tick = tick;
my->second = 0;
}
info = &dseconds[my->second];
info->cnt = 0;
info->max = 0.0;
info->min = MAXDURATION;
info->sum = 0.0;
info->tick = tick;
my->last++;
}
}
else
{
while (my->last < tick)
{
CInfo *info;
if (my->second++ == 59)
{
info = &cminutes[my->minute];
for (i = 0; i < 60; i++)
{
info->cnt += cseconds[i].cnt;
}
if (my->minute++ == my->minutesPerPeriod)
{
info = &cperiods[my->period];
for (i = 0; i < my->minutesPerPeriod; i++)
{
info->cnt += cminutes[i].cnt;
}
if (my->period++ == my->numberOfPeriods)
{
my->period = 0;
}
info = &cperiods[my->period];
info->cnt = 0;
info->tick = tick;
my->minute = 0;
}
info = &cminutes[my->minute];
info->cnt = 0;
info->tick = tick;
my->second = 0;
}
info = &cseconds[my->second];
info->cnt = 0;
info->tick = tick;
my->last++;
}
}
}
}
@end
@implementation GSThroughput
+ (NSArray*) allInstances
{
GSThroughputThread *t;
NSArray *a;
t = [[[NSThread currentThread] threadDictionary]
objectForKey: @"GSThroughput"];
if (t == nil)
{
a = nil;
}
else
{
a = NSAllHashTableObjects(t->instances);
}
return a;
}
+ (NSString*) description
{
GSThroughputThread *t;
NSMutableString *ms;
ms = [NSMutableString stringWithString: [super description]];
t = [[[NSThread currentThread] threadDictionary]
objectForKey: @"GSThroughput"];
if (t != nil)
{
NSHashEnumerator e;
GSThroughput *c;
e = NSEnumerateHashTable(t->instances);
while ((c = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
{
[ms appendFormat: @"\n%@", [c description]];
}
NSEndHashTableEnumeration(&e);
}
return ms;
}
+ (void) initialize
{
if (NSDateClass == 0)
{
NSDateClass = [NSDate class];
tiSel = @selector(timeIntervalSinceReferenceDate);
tiImp
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
}
}
+ (void) setTick: (NSTimeInterval)interval
{
GSThroughputThread *t = [self _threadInfo];
if (t->theTimer != nil)
{
[t->theTimer invalidate];
t->theTimer = nil;
}
if (interval > 0.0)
{
t->theTimer = [NSTimer scheduledTimerWithTimeInterval: interval
target: self
selector: @selector(_tick:)
userInfo: t
repeats: YES];
}
}
+ (void) tick
{
[self _tickForThread: [self _threadInfo]];
} }
- (void) add: (unsigned)count - (void) add: (unsigned)count
@ -240,15 +431,17 @@ typedef struct {
- (void) dealloc - (void) dealloc
{ {
[GSThroughputLock lock];
if (my->seconds != 0) if (my->seconds != 0)
{ {
NSZoneFree(NSDefaultMallocZone(), my->seconds); NSZoneFree(NSDefaultMallocZone(), my->seconds);
} }
RELEASE(my->name); RELEASE(my->name);
NSHashRemove(GSThroughputInstances, (void*)self); if (my->thread != nil)
{
NSHashRemove(my->thread->instances, (void*)self);
my->thread = nil;
}
NSDeallocateObject(self); NSDeallocateObject(self);
[GSThroughputLock unlock];
} }
- (NSString*) description - (NSString*) description
@ -264,88 +457,93 @@ typedef struct {
} }
m = [n mutableCopy]; m = [n mutableCopy];
if (my->supportDurations == YES) if (my->thread != nil)
{ {
if (my->second > 0) NSTimeInterval baseTime = my->thread->baseTime;
{
[m appendString: @"\nCurrent minute:\n"];
for (i = 0; i < my->second; i++)
{
DInfo *info = &dseconds[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %g, %g, %g, %@\n", if (my->supportDurations == YES)
info->cnt, info->max, info->min, info->sum, {
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; if (my->second > 0)
{
[m appendString: @"\nCurrent minute:\n"];
for (i = 0; i < my->second; i++)
{
DInfo *info = &dseconds[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %g, %g, %g, %@\n",
info->cnt, info->max, info->min, info->sum,
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
}
}
if (my->minute > 0)
{
[m appendString: @"\nCurrent period:\n"];
for (i = 0; i < my->minute; i++)
{
DInfo *info = &dminutes[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %g, %g, %g, %@\n",
info->cnt, info->max, info->min, info->sum,
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
}
}
if (my->period > 0)
{
[m appendString: @"\nPrevious periods:\n"];
for (i = 0; i < my->period; i++)
{
DInfo *info = &dperiods[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %g, %g, %g, %@\n",
info->cnt, info->max, info->min, info->sum,
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
}
} }
} }
else
if (my->minute > 0)
{ {
[m appendString: @"\nCurrent period:\n"]; if (my->second > 0)
for (i = 0; i < my->minute; i++)
{ {
DInfo *info = &dminutes[i]; [m appendString: @"\nCurrent minute:\n"];
NSTimeInterval ti = info->tick + baseTime; for (i = 0; i < my->second; i++)
{
CInfo *info = &cseconds[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %g, %g, %g, %@\n", [m appendFormat: @"%u, %@\n", info->cnt,
info->cnt, info->max, info->min, info->sum, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; }
} }
}
if (my->period > 0) if (my->minute > 0)
{
[m appendString: @"\nPrevious periods:\n"];
for (i = 0; i < my->period; i++)
{ {
DInfo *info = &dperiods[i]; [m appendString: @"\nCurrent period:\n"];
NSTimeInterval ti = info->tick + baseTime; for (i = 0; i < my->minute; i++)
{
CInfo *info = &cminutes[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %g, %g, %g, %@\n", [m appendFormat: @"%u, %@\n", info->cnt,
info->cnt, info->max, info->min, info->sum, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; }
} }
}
} if (my->period > 0)
else
{
if (my->second > 0)
{
[m appendString: @"\nCurrent minute:\n"];
for (i = 0; i < my->second; i++)
{ {
CInfo *info = &cseconds[i]; [m appendString: @"\nPrevious periods:\n"];
NSTimeInterval ti = info->tick + baseTime; for (i = 0; i < my->period; i++)
{
CInfo *info = &cperiods[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %@\n", info->cnt, [m appendFormat: @"%u, %@\n", info->cnt,
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; [NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
} }
}
if (my->minute > 0)
{
[m appendString: @"\nCurrent period:\n"];
for (i = 0; i < my->minute; i++)
{
CInfo *info = &cminutes[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %@\n", info->cnt,
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
}
}
if (my->period > 0)
{
[m appendString: @"\nPrevious periods:\n"];
for (i = 0; i < my->period; i++)
{
CInfo *info = &cperiods[i];
NSTimeInterval ti = info->tick + baseTime;
[m appendFormat: @"%u, %@\n", info->cnt,
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
} }
} }
} }
@ -358,6 +556,7 @@ typedef struct {
{ {
NSAssert(my->started > 0.0, NSInternalInconsistencyException); NSAssert(my->started > 0.0, NSInternalInconsistencyException);
[self addDuration: (*tiImp)(NSDateClass, tiSel) - my->started]; [self addDuration: (*tiImp)(NSDateClass, tiSel) - my->started];
my->event = nil;
my->started = 0.0; my->started = 0.0;
} }
@ -380,11 +579,19 @@ typedef struct {
DESTROY(self); DESTROY(self);
return nil; return nil;
} }
/*
* Add this instance to the current thread.
*/
my->thread = [[self class] _threadInfo];
NSHashInsert(my->thread->instances, (void*)self);
my->supportDurations = aFlag; my->supportDurations = aFlag;
my->numberOfPeriods = numberOfPeriods; my->numberOfPeriods = numberOfPeriods;
my->minutesPerPeriod = minutesPerPeriod; my->minutesPerPeriod = minutesPerPeriod;
my->last = GSThroughputTimeTick() - 1; my->last = (my->thread->lastTime - my->thread->baseTime) + 1;
c = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate: lastTime]; c = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:
my->thread->lastTime];
my->second = [c secondOfMinute]; my->second = [c secondOfMinute];
i = [c hourOfDay] * 60 + [c minuteOfHour]; i = [c hourOfDay] * 60 + [c minuteOfHour];
my->minute = i % minutesPerPeriod; my->minute = i % minutesPerPeriod;
@ -444,131 +651,19 @@ typedef struct {
ASSIGN(my->name, name); ASSIGN(my->name, name);
} }
- (void) startDuration - (void) startDuration: (NSString*)name
{ {
NSAssert(my->supportDurations == YES && my->started == 0.0, NSAssert(my->supportDurations == YES && my->started == 0.0,
NSInternalInconsistencyException); NSInternalInconsistencyException);
if (my->event != nil)
{
[NSException raise: NSInternalInconsistencyException
format: @"-startDuration: for '%@' nested inside '%@'",
my->event, name];
}
my->started = (*tiImp)(NSDateClass, tiSel); my->started = (*tiImp)(NSDateClass, tiSel);
my->event = name;
} }
- (void) update
{
unsigned tick = GSThroughputTimeTick();
unsigned i;
if (my->supportDurations == YES)
{
while (my->last < tick)
{
DInfo *info;
if (my->second++ == 59)
{
info = &dminutes[my->minute];
for (i = 0; i < 60; i++)
{
DInfo *from = &dseconds[i];
info->cnt += from->cnt;
if (from->min < info->min)
{
info->min = from->min;
}
if (from->max > info->max)
{
info->max = from->max;
}
info->sum += from->sum;
}
if (my->minute++ == my->minutesPerPeriod)
{
info = &dperiods[my->period];
for (i = 0; i < my->minutesPerPeriod; i++)
{
DInfo *from = &dminutes[i];
info->cnt += from->cnt;
if (from->min > 0.0 && from->min < info->min)
{
info->min = from->min;
}
if (from->max > info->max)
{
info->max = from->max;
}
info->sum += from->sum;
}
if (my->period++ == my->numberOfPeriods)
{
my->period = 0;
}
info = &dperiods[my->period];
info->cnt = 0;
info->max = 0.0;
info->min = MAXDURATION;
info->sum = 0.0;
info->tick = tick;
my->minute = 0;
}
info = &dminutes[my->minute];
info->cnt = 0;
info->max = 0.0;
info->min = MAXDURATION;
info->sum = 0.0;
info->tick = tick;
my->second = 0;
}
info = &dseconds[my->second];
info->cnt = 0;
info->max = 0.0;
info->min = MAXDURATION;
info->sum = 0.0;
info->tick = tick;
my->last++;
}
}
else
{
while (my->last < tick)
{
CInfo *info;
if (my->second++ == 59)
{
info = &cminutes[my->minute];
for (i = 0; i < 60; i++)
{
info->cnt += cseconds[i].cnt;
}
if (my->minute++ == my->minutesPerPeriod)
{
info = &cperiods[my->period];
for (i = 0; i < my->minutesPerPeriod; i++)
{
info->cnt += cminutes[i].cnt;
}
if (my->period++ == my->numberOfPeriods)
{
my->period = 0;
}
info = &cperiods[my->period];
info->cnt = 0;
info->tick = tick;
my->minute = 0;
}
info = &cminutes[my->minute];
info->cnt = 0;
info->tick = tick;
my->second = 0;
}
info = &cseconds[my->second];
info->cnt = 0;
info->tick = tick;
my->last++;
}
}
}
@end @end