Performance-0.5.0/0000775000076500007650000000000012332631460013653 5ustar brains99brains99Performance-0.5.0/GSIOThreadPool.h0000664000076500007650000000725012162261422016551 0ustar brains99brains99#if !defined(INCLUDED_GSIOTHREADPOOL) #define INCLUDED_GSIOTHREADPOOL 1 /** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: September 2010 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED<=MAC_OS_X_VERSION_10_4) typedef unsigned int NSUInteger; #endif @class NSThread; @class NSLock; /** This class provides a thread pool for performing methods which need to * make use of a runloop for I/O and/or timers.
* Operations are performed on these threads using the standard * -performSelector:onThread:withObject:waitUntilDone: method ... the * pool is simply used to keep track of allocation of threads so that * you can share jobs between them.
* NB. The threading API in OSX 10.4 and earlier is incapable of supporting * this functionality ... in that case this class cannot be instantiated * and initialised. */ @interface GSIOThreadPool : NSObject { NSLock *poolLock; NSMutableArray *threads; NSTimeInterval timeout; NSUInteger maxThreads; } /** Returns an instance intended for sharing between sections of code which * wish to make use of threading by performing operations in other threads, * but which don't mind operations being interleaved with those belonging to * other sections of code.
* Always returns the same instance whenever the method is called.
* The shared pool is created with an initial size as specified by the * GSIOThreadPoolSize user default (zero if there is no such positive * integer in the defauilts system, however, you can modify that using * the -setThreads: method. */ + (GSIOThreadPool*) sharedPool; /** Selects a thread from the pool to be used for some job. */ - (NSThread*) acquireThread; /** Returns the acquire count for the specified thread. */ - (NSUInteger) countForThread: (NSThread*)aThread; /** Returns the currently configured maximum number of threads in the pool. */ - (NSUInteger) maxThreads; /** Specify the maximum number of threads in the pool (the actual number * used may be lower than this value).
* Default is 0 (no thread pooling in use).
* The pool creates threads on demand up to the specified limit (or a lower * limit if dictated by system resources) but will not destroy idle threads * unless the limit is subsequently lowered.
* Setting a value of zero means that operations are performed in the * main thread. */ - (void) setThreads: (NSUInteger)max; /** Specifies the timeout allowed for a thread to close down when the pool * is deallocated or has its size decreased. Any operations in progress in * the thread need to close down within this period. */ - (void) setTimeout: (NSTimeInterval)t; /** Returns the current timeout set for the pool. */ - (NSTimeInterval) timeout; /** Releases a thread previously selected from the pool. */ - (void) unacquireThread: (NSThread*)aThread; @end #endif Performance-0.5.0/GSThroughput.m0000664000076500007650000006151411630121345016437 0ustar brains99brains99/* -*-objc-*- */ /** Implementation of GSThroughput for GNUStep Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: October 2005 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. $Date: 2011-09-02 10:33:25 +0100 (Fri, 02 Sep 2011) $ $Revision: 33807 $ */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "GSThroughput.h" #import "GSTicker.h" NSString * const GSThroughputNotification = @"GSThroughputNotification"; NSString * const GSThroughputCountKey = @"Count"; NSString * const GSThroughputMaximumKey = @"Maximum"; NSString * const GSThroughputMinimumKey = @"Maximum"; NSString * const GSThroughputTimeKey = @"Time"; NSString * const GSThroughputTotalKey = @"Total"; #define MAXDURATION 24.0*60.0*60.0 @class GSThroughputThread; static Class NSDateClass = 0; static SEL tiSel = 0; static NSTimeInterval (*tiImp)(Class,SEL) = 0; typedef struct { unsigned cnt; // Number of events. unsigned tick; // Start time } CountInfo; typedef struct { unsigned cnt; // Number of events. NSTimeInterval max; // Longest duration NSTimeInterval min; // Shortest duration NSTimeInterval sum; // Total (sum of durations for event) unsigned tick; // Start time } DurationInfo; typedef struct { void *seconds; void *minutes; void *periods; void *total; BOOL supportDurations; BOOL notify; unsigned numberOfPeriods; unsigned minutesPerPeriod; unsigned second; unsigned minute; unsigned period; unsigned last; // last tick used NSTimeInterval started; // When duration logging started. NSString *event; // Name of current event NSString *name; // Name of this instance GSThroughputThread *thread; // Thread info } Item; #define my ((Item*)_data) #define cseconds ((CountInfo*)my->seconds) #define cminutes ((CountInfo*)my->minutes) #define cperiods ((CountInfo*)my->periods) #define dseconds ((DurationInfo*)my->seconds) #define dminutes ((DurationInfo*)my->minutes) #define dperiods ((DurationInfo*)my->periods) @interface GSThroughputThread : NSObject { @public NSHashTable *instances; } @end @interface GSThroughput (Private) + (GSThroughputThread*) _threadInfo; + (void) newSecond: (GSThroughputThread*)t; - (void) _detach; - (void) _update; @end @implementation GSThroughputThread - (void) dealloc { if (instances != 0) { NSHashEnumerator e; GSThroughput *i; e = NSEnumerateHashTable(instances); while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil) { [i _detach]; } NSEndHashTableEnumeration(&e); NSFreeHashTable(instances); instances = 0; } [super dealloc]; } - (id) init { if (nil != (self = [super init])) { instances = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); } return self; } @end @implementation GSThroughput (Private) + (GSThroughputThread*) _threadInfo { GSThroughputThread *t; t = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSThroughput"]; if (t == nil) { t = [GSThroughputThread new]; [[[NSThread currentThread] threadDictionary] setObject: t forKey: @"GSThroughput"]; [t release]; } return t; } + (void) newSecond: (GSThroughputThread*)t { NSHashEnumerator e; GSThroughput *i; e = NSEnumerateHashTable(t->instances); while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil) { [i _update]; } NSEndHashTableEnumeration(&e); } - (void) _detach { my->thread = nil; } - (void) _update { NSTimeInterval base; unsigned tick; if (my->thread == nil) { return; } base = GSTickerTimeStart(); tick = GSTickerTimeTick(); if (my->numberOfPeriods > 0) { unsigned i; if (my->supportDurations == YES) { while (my->last < tick) { DurationInfo *info; if (my->second++ == 59) { info = &dminutes[my->minute]; for (i = 0; i < 60; i++) { DurationInfo *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->notify == YES && my->last > 59) { if (info->min >= MAXDURATION) { info->min = -1.0; } [[NSNotificationCenter defaultCenter] postNotificationName: GSThroughputNotification object: self userInfo: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: info->cnt], GSThroughputCountKey, [NSNumber numberWithDouble: info->max], GSThroughputMaximumKey, [NSNumber numberWithDouble: info->min], GSThroughputMinimumKey, [NSNumber numberWithDouble: info->sum], GSThroughputTotalKey, [NSDate dateWithTimeIntervalSinceReferenceDate: base + my->last - 60], GSThroughputTimeKey, nil]]; if (info->min < 0.0) { info->min = MAXDURATION; } } if (my->minute++ == my->minutesPerPeriod - 1) { info = &dperiods[my->period]; for (i = 0; i < my->minutesPerPeriod; i++) { DurationInfo *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 - 1) { my->period = 0; } info = &dperiods[my->period]; info->cnt = 0; info->max = 0.0; info->min = MAXDURATION; info->sum = 0.0; info->tick = my->last; my->minute = 0; } info = &dminutes[my->minute]; info->cnt = 0; info->max = 0.0; info->min = MAXDURATION; info->sum = 0.0; info->tick = my->last; my->second = 0; } info = &dseconds[my->second]; info->cnt = 0; info->max = 0.0; info->min = MAXDURATION; info->sum = 0.0; info->tick = my->last; my->last++; } } else { while (my->last < tick) { CountInfo *info; if (my->second++ == 59) { info = &cminutes[my->minute]; for (i = 0; i < 60; i++) { info->cnt += cseconds[i].cnt; } if (my->notify == YES && my->last > 59) { [[NSNotificationCenter defaultCenter] postNotificationName: GSThroughputNotification object: self userInfo: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: info->cnt], GSThroughputCountKey, [NSDate dateWithTimeIntervalSinceReferenceDate: base + my->last - 60], GSThroughputTimeKey, nil]]; } if (my->minute++ == my->minutesPerPeriod - 1) { info = &cperiods[my->period]; for (i = 0; i < my->minutesPerPeriod; i++) { info->cnt += cminutes[i].cnt; } if (my->period++ == my->numberOfPeriods - 1) { my->period = 0; } info = &cperiods[my->period]; info->cnt = 0; info->tick = my->last; my->minute = 0; } info = &cminutes[my->minute]; info->cnt = 0; info->tick = my->last; my->second = 0; } info = &cseconds[my->second]; info->cnt = 0; info->tick = my->last; my->last++; } } } else { while (my->last < tick) { if (my->second++ == 59) { my->second = 0; if (my->supportDurations == YES) { DurationInfo *info = &dseconds[1]; if (my->notify == YES && my->last > 59) { if (info->min == MAXDURATION) { info->min = -1.0; } [[NSNotificationCenter defaultCenter] postNotificationName: GSThroughputNotification object: self userInfo: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: info->cnt], GSThroughputCountKey, [NSNumber numberWithDouble: info->max], GSThroughputMaximumKey, [NSNumber numberWithDouble: info->min], GSThroughputMinimumKey, [NSNumber numberWithDouble: info->sum], GSThroughputTotalKey, [NSDate dateWithTimeIntervalSinceReferenceDate: base + my->last - 60], GSThroughputTimeKey, nil]]; } info->cnt = 0; info->max = 0.0; info->min = MAXDURATION; info->sum = 0.0; } else { CountInfo *info = &cseconds[1]; if (my->notify == YES && my->last > 59) { [[NSNotificationCenter defaultCenter] postNotificationName: GSThroughputNotification object: self userInfo: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: info->cnt], GSThroughputCountKey, [NSDate dateWithTimeIntervalSinceReferenceDate: base + my->last - 60], GSThroughputTimeKey, nil]]; } info->cnt = 0; } } 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) { NSArray *a; NSEnumerator *e; GSThroughput *c; a = [NSAllHashTableObjects(t->instances) sortedArrayUsingSelector: @selector(compare:)]; e = [a objectEnumerator]; while ((c = (GSThroughput*)[e nextObject]) != nil) { [ms appendFormat: @"\n%@", [c description]]; } } return ms; } + (void) initialize { if (NSDateClass == 0) { NSDateClass = [NSDate class]; tiSel = @selector(timeIntervalSinceReferenceDate); tiImp = (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel]; } } + (void) setTick: (BOOL)aFlag { if (aFlag == YES) { GSThroughputThread *t = [self _threadInfo]; [GSTicker registerObserver: (id)self userInfo: t]; } else { [GSTicker unregisterObserver: (id)self]; } } + (void) tick { [self newSecond: [self _threadInfo]]; } - (void) add: (unsigned)count { NSAssert(my->supportDurations == NO, @"configured for durations"); if (my->numberOfPeriods == 0) { cseconds[0].cnt += count; // Total cseconds[1].cnt += count; // Current minute } else { cseconds[my->second].cnt += count; } } - (void) add: (unsigned)count duration: (NSTimeInterval)length { NSAssert(my->supportDurations == YES, @"not configured for durations"); if (count > 0) { NSTimeInterval total = length; unsigned from; unsigned to; length /= count; if (my->numberOfPeriods == 0) { from = 0; // total to = 1; // current minute } else { from = my->second; to = from; } while (from <= to) { DurationInfo *info = &dseconds[from++]; if (info->cnt == 0) { info->cnt = count; info->min = length; info->max = length; info->sum = total; } else { info->cnt += count; info->sum += total; if (length > info->max) { info->max = length; } if (length < info->min) { info->min = length; } } } } } - (void) addDuration: (NSTimeInterval)length { unsigned from; unsigned to; NSAssert(my->supportDurations == YES, @"not configured for durations"); if (my->numberOfPeriods == 0) { from = 0; // Total to = 1; // Current minute } else { from = my->second; to = from; } while (from <= to) { DurationInfo *info = &dseconds[from++]; if (info->cnt++ == 0) { info->min = length; info->max = length; info->sum = length; } else { info->sum += length; if (length > info->max) { info->max = length; } if (length < info->min) { info->min = length; } } } } - (NSComparisonResult) compare: (id)other { if ([other isKindOfClass: [GSThroughput class]] == YES) { NSString *myName = [self name]; NSString *otherName = [other name]; if (myName == nil) { myName = @""; } if (otherName == nil) { otherName = @""; } return [myName compare: otherName]; } return NSOrderedAscending; } - (void) dealloc { if (_data) { if (my->seconds != 0) { NSZoneFree(NSDefaultMallocZone(), my->seconds); } [my->name release]; if (my->thread != nil) { NSHashRemove(my->thread->instances, (void*)self); my->thread = nil; } NSZoneFree(NSDefaultMallocZone(), _data); _data = 0; } [super dealloc]; } - (NSString*) description { NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSString *n = my->name; NSMutableString *m; unsigned i; if (n == nil) { n = [super description]; } m = [n mutableCopy]; if (my->thread != nil) { NSTimeInterval baseTime = GSTickerTimeStart(); unsigned tick; if (my->numberOfPeriods == 0) { if (my->supportDurations == YES) { DurationInfo *info = &dseconds[0]; [m appendFormat: @": cnt %u, max %g, min %g, avg %g", info->cnt, info->max, info->min == MAXDURATION ? 0.0 : info->min, info->cnt == 0 ? 0 : info->sum / info->cnt]; } else { CountInfo *info = &cseconds[0]; [m appendFormat: @": cnt %u", info->cnt]; } } else { if (my->supportDurations == YES) { [m appendString: @"\nSeconds in current minute:\n"]; if (my->second > 0) { tick = 0; for (i = 0; i < my->second; i++) { DurationInfo *info = &dseconds[i]; NSTimeInterval ti = info->tick + baseTime; if (info->tick != tick) { tick = info->tick; [m appendFormat: @"%u, %g, %g, %g, %@\n", info->cnt, info->max, info->min, info->sum, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; } } } [m appendString: @"\nPrevious minutes in current period:\n"]; if (my->minute > 0) { tick = 0; for (i = 0; i < my->minute; i++) { DurationInfo *info = &dminutes[i]; NSTimeInterval ti = info->tick + baseTime; if (info->tick != tick) { tick = info->tick; [m appendFormat: @"%u, %g, %g, %g, %@\n", info->cnt, info->max, info->min, info->sum, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; } } } [m appendString: @"\nPrevious periods:\n"]; if (my->period > 0) { tick = 0; /* Periods from last cycle */ for (i = my->period; i < my->numberOfPeriods; i++) { DurationInfo *info = &dperiods[i]; NSTimeInterval ti = info->tick + baseTime; if (info->tick != tick) { tick = info->tick; [m appendFormat: @"%u, %g, %g, %g, %@\n", info->cnt, info->max, info->min, info->sum, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; } } /* Periods from current cycle */ for (i = 0; i < my->period; i++) { DurationInfo *info = &dperiods[i]; NSTimeInterval ti = info->tick + baseTime; if (info->tick != tick) { tick = info->tick; [m appendFormat: @"%u, %g, %g, %g, %@\n", info->cnt, info->max, info->min, info->sum, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; } } } } else { [m appendString: @"\nSeconds in current minute:\n"]; if (my->second > 0) { tick = 0; for (i = 0; i < my->second; i++) { CountInfo *info = &cseconds[i]; NSTimeInterval ti = info->tick + baseTime; if (info->tick != tick) { tick = info->tick; [m appendFormat: @"%u, %@\n", info->cnt, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; } } } [m appendString: @"\nPrevious minutes in current period:\n"]; if (my->minute > 0) { tick = 0; for (i = 0; i < my->minute; i++) { CountInfo *info = &cminutes[i]; NSTimeInterval ti = info->tick + baseTime; if (info->tick != tick) { tick = info->tick; [m appendFormat: @"%u, %@\n", info->cnt, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; } } } [m appendString: @"\nPrevious periods:\n"]; if (my->period > 0) { tick = 0; /* Periods from last cycle */ for (i = my->period; i < my->numberOfPeriods; i++) { CountInfo *info = &cperiods[i]; NSTimeInterval ti = info->tick + baseTime; if (info->tick != tick) { tick = info->tick; [m appendFormat: @"%u, %@\n", info->cnt, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; } } /* Periods from current cycle */ for (i = 0; i < my->period; i++) { CountInfo *info = &cperiods[i]; NSTimeInterval ti = info->tick + baseTime; if (info->tick != tick) { tick = info->tick; [m appendFormat: @"%u, %@\n", info->cnt, [NSDate dateWithTimeIntervalSinceReferenceDate: ti]]; } } } } } } [pool release]; return [m autorelease]; } - (void) endDuration { if (my->started > 0.0) { [self addDuration: (*tiImp)(NSDateClass, tiSel) - my->started]; my->event = nil; my->started = 0.0; } } - (BOOL) enableNotifications: (BOOL)flag { BOOL old = my->notify; my->notify = flag; return old; } - (void) endDuration: (unsigned)count { if (my->started > 0.0) { [self add: count duration: (*tiImp)(NSDateClass, tiSel) - my->started]; my->event = nil; my->started = 0.0; } } - (id) init { return [self initWithDurations: YES forPeriods: 96 ofLength: 15]; } - (id) initWithDurations: (BOOL)aFlag forPeriods: (unsigned)numberOfPeriods ofLength: (unsigned)minutesPerPeriod { if (nil != (self = [super init])) { NSCalendarDate *c; unsigned i; _data = (Item*)NSZoneCalloc(NSDefaultMallocZone(), 1, sizeof(Item)); /* * Add this instance to the current thread. */ my->thread = [[self class] _threadInfo]; NSHashInsert(my->thread->instances, (void*)self); my->supportDurations = aFlag; my->notify = NO; my->last = GSTickerTimeTick(); c = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate: GSTickerTimeLast()]; my->second = [c secondOfMinute]; i = [c hourOfDay] * 60 + [c minuteOfHour]; if (numberOfPeriods < 1 || minutesPerPeriod < 1) { /* If we are not using periods of N minutes, we must just be keeping * a running total recorded second by second. */ my->numberOfPeriods = 0; my->minutesPerPeriod = 0; my->minute = i; my->period = 0; if (my->supportDurations == YES) { DurationInfo *ptr; ptr = (DurationInfo*)NSZoneCalloc (NSDefaultMallocZone(), 2, sizeof(DurationInfo)); my->seconds = ptr; my->minutes = 0; my->periods = 0; dseconds[0].tick = my->last; dseconds[0].max = 0; dseconds[0].min = MAXDURATION; dseconds[0].sum = 0; dseconds[0].cnt = 0; dseconds[1].tick = my->last; dseconds[1].max = 0; dseconds[1].min = 0; dseconds[1].sum = 0; dseconds[1].cnt = 0; } else { CountInfo *ptr; ptr = (CountInfo*)NSZoneCalloc (NSDefaultMallocZone(), 2, sizeof(CountInfo)); my->seconds = ptr; my->minutes = 0; my->periods = 0; cseconds[0].tick = my->last; cseconds[0].cnt = 0; cseconds[1].tick = my->last; cseconds[1].cnt = 0; } } else { my->numberOfPeriods = numberOfPeriods; my->minutesPerPeriod = minutesPerPeriod; my->minute = i % minutesPerPeriod; my->period = (i / minutesPerPeriod) % numberOfPeriods; i = 60 + minutesPerPeriod + numberOfPeriods; if (my->supportDurations == YES) { DurationInfo *ptr; ptr = (DurationInfo*)NSZoneCalloc (NSDefaultMallocZone(), i, sizeof(DurationInfo)); my->seconds = ptr; my->minutes = ptr + 60; my->periods = ptr + 60 + minutesPerPeriod; dseconds[my->second].tick = my->last; dminutes[my->minute].tick = my->last; dperiods[my->period].tick = my->last; for (i = 0; i < my->numberOfPeriods; i++) { dperiods[i].min = MAXDURATION; } for (i = 0; i < my->minutesPerPeriod; i++) { dminutes[i].min = MAXDURATION; } for (i = 0; i < 60; i++) { dseconds[i].min = MAXDURATION; } } else { CountInfo *ptr; ptr = (CountInfo*)NSZoneCalloc (NSDefaultMallocZone(), i, sizeof(CountInfo)); my->seconds = ptr; my->minutes = ptr + 60; my->periods = ptr + 60 + minutesPerPeriod; cseconds[my->second].tick = my->last; cminutes[my->minute].tick = my->last; cperiods[my->period].tick = my->last; } } [c release]; } return self; } - (NSString*) name { return my->name; } - (void) setName: (NSString*)name { [name retain]; [my->name release]; my->name = name; } - (void) startDuration: (NSString*)name { NSAssert(my->supportDurations == YES && my->started == 0.0, NSInternalInconsistencyException); if (my->event != nil) { [NSException raise: NSInternalInconsistencyException format: @"-startDuration: for '%@' nested inside '%@'", my->event, name]; } my->started = (*tiImp)(NSDateClass, tiSel); my->event = name; } @end Performance-0.5.0/GSCache.m0000664000076500007650000005004512205115077015272 0ustar brains99brains99/* -*-objc-*- */ /** Implementation of GSCache for GNUStep Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: October 2005 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. $Date: 2013-08-21 11:45:19 +0100 (Wed, 21 Aug 2013) $ $Revision: 36999 $ */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "GSCache.h" #import "GSTicker.h" #if !defined(GNUSTEP) #include #if (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4) #define class_getInstanceSize(isa) ((struct objc_class *)isa)->instance_size #endif #endif @interface GSCache (Threading) + (void) _becomeThreaded: (NSNotification*)n; - (void) _createLock; @end @interface GSCacheItem : NSObject { @public GSCacheItem *next; GSCacheItem *prev; unsigned life; unsigned warn; unsigned when; NSUInteger size; id key; id object; } + (GSCacheItem*) newWithObject: (id)anObject forKey: (id)aKey; @end @implementation GSCacheItem + (GSCacheItem*) newWithObject: (id)anObject forKey: (id)aKey { GSCacheItem *i; i = (GSCacheItem*)NSAllocateObject(self, 0, NSDefaultMallocZone()); i->object = [anObject retain]; i->key = [aKey copy]; return i; } - (void) dealloc { [key release]; [object release]; [super dealloc]; } @end @implementation GSCache static NSHashTable *allCaches = 0; static NSRecursiveLock *allCachesLock = nil; static int itemOffset = 0; typedef struct { id delegate; void (*refresh)(id, SEL, id, id, unsigned, unsigned); BOOL (*replace)(id, SEL, id, id, unsigned, unsigned); unsigned currentObjects; NSUInteger currentSize; unsigned lifetime; unsigned maxObjects; NSUInteger maxSize; unsigned hits; unsigned misses; NSMapTable *contents; GSCacheItem *first; NSString *name; NSMutableSet *exclude; NSRecursiveLock *lock; } Item; #define my ((Item*)((void*)self + itemOffset)) /* * Add item to linked list starting at *first */ static void appendItem(GSCacheItem *item, GSCacheItem **first) { if (*first == nil) { item->next = item->prev = item; *first = item; } else { (*first)->prev->next = item; item->prev = (*first)->prev; (*first)->prev = item; item->next = *first; } } /* * Remove item from linked list starting at *first */ static void removeItem(GSCacheItem *item, GSCacheItem **first) { if (*first == item) { if (item->next == item) { *first = nil; } else { *first = item->next; } } item->next->prev = item->prev; item->prev->next = item->next; item->prev = item->next = item; } + (NSArray*) allInstances { NSArray *a; [allCachesLock lock]; a = NSAllHashTableObjects(allCaches); [allCachesLock unlock]; return a; } + (id) alloc { return [self allocWithZone: NSDefaultMallocZone()]; } + (id) allocWithZone: (NSZone*)z { GSCache *c; c = (GSCache*)NSAllocateObject(self, sizeof(Item), z); return c; } + (NSString*) description { NSMutableString *ms; NSHashEnumerator e; GSCache *c; ms = [NSMutableString stringWithString: [super description]]; [allCachesLock lock]; e = NSEnumerateHashTable(allCaches); while ((c = (GSCache*)NSNextHashEnumeratorItem(&e)) != nil) { [ms appendFormat: @"\n%@", [c description]]; } NSEndHashTableEnumeration(&e); [allCachesLock unlock]; return ms; } + (void) initialize { if (allCaches == 0) { itemOffset = class_getInstanceSize(self); allCaches = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); if ([NSThread isMultiThreaded] == YES) { [self _becomeThreaded: nil]; } else { /* If and when we become multi-threaded, the +_becomeThreaded: * method will remove us as an observer and will create a lock * for the table of all caches, then ask each cache to create * its own lock. */ [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_becomeThreaded:) name: NSWillBecomeMultiThreadedNotification object: nil]; } GSTickerTimeNow(); } } - (unsigned) currentObjects { return my->currentObjects; } - (NSUInteger) currentSize { return my->currentSize; } - (void) dealloc { [allCachesLock lock]; NSHashRemove(allCaches, (void*)self); [allCachesLock unlock]; if (my->contents != 0) { [self shrinkObjects: 0 andSize: 0]; NSFreeMapTable(my->contents); } [my->exclude release]; [my->name release]; [my->lock release]; [super dealloc]; } - (id) delegate { return my->delegate; } - (NSString*) description { NSString *n; [my->lock lock]; n = my->name; if (n == nil) { n = [super description]; } n = [NSString stringWithFormat: @" %@\n" @" Items: %u(%u)\n" @" Size: %"PRIuPTR"(%"PRIuPTR")\n" @" Life: %u\n" @" Hit: %u\n" @" Miss: %u\n", n, my->currentObjects, my->maxObjects, my->currentSize, my->maxSize, my->lifetime, my->hits, my->misses]; [my->lock unlock]; return n; } - (id) init { if (nil != (self = [super init])) { if ([NSThread isMultiThreaded] == YES) { [self _createLock]; } my->contents = NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 0); [allCachesLock lock]; NSHashInsert(allCaches, (void*)self); [allCachesLock unlock]; } return self; } - (unsigned) lifetime { return my->lifetime; } - (unsigned) maxObjects { return my->maxObjects; } - (NSUInteger) maxSize { return my->maxSize; } - (NSString*) name { NSString *n; [my->lock lock]; n = [my->name retain]; [my->lock unlock]; return [n autorelease]; } - (id) objectForKey: (id)aKey { id object; GSCacheItem *item; unsigned when = GSTickerTimeTick(); [my->lock lock]; item = (GSCacheItem*)NSMapGet(my->contents, aKey); if (item == nil) { my->misses++; [my->lock unlock]; return nil; } if (item->when > 0 && item->when < when) { BOOL keep = NO; if (0 != my->replace) { GSCacheItem *orig = [item retain]; [my->lock unlock]; NS_DURING { keep = (*(my->replace))(my->delegate, @selector(shouldKeepItem:withKey:lifetime:after:), item->object, aKey, item->life, when - item->when); } NS_HANDLER { [my->lock unlock]; [localException raise]; } NS_ENDHANDLER [my->lock lock]; if (keep == YES) { GSCacheItem *current; /* Refetch in case delegate changed it. */ current = (GSCacheItem*)NSMapGet(my->contents, aKey); if (current == nil) { /* Delegate must have deleted the item even though * it returned YES to say we should keep it ... * we count this as a miss. */ my->misses++; [my->lock unlock]; [orig release]; return nil; } else if (orig == current) { /* Delegate told us to keep the original item so we * update its expiry time. */ item->when = when + item->life; item->warn = when + item->life / 2; } else { /* Delegate replaced the item with another and told * us to keep that one. */ item = current; } } [orig release]; } if (keep == NO) { removeItem(item, &my->first); my->currentObjects--; if (my->maxSize > 0) { my->currentSize -= item->size; } NSMapRemove(my->contents, (void*)item->key); my->misses++; [my->lock unlock]; return nil; // Lifetime expired. } } else if (item->warn > 0 && item->warn < when) { item->warn = 0; // Don't warn again. if (0 != my->refresh) { GSCacheItem *orig = [item retain]; GSCacheItem *current; [my->lock unlock]; NS_DURING { (*(my->refresh))(my->delegate, @selector(mayRefreshItem:withKey:lifetime:after:), item->object, aKey, item->life, when - item->when); } NS_HANDLER { [my->lock unlock]; [localException raise]; } NS_ENDHANDLER [my->lock lock]; /* Refetch in case delegate changed it. */ current = (GSCacheItem*)NSMapGet(my->contents, aKey); if (current == nil) { /* Delegate must have deleted the item! * So we count this as a miss. */ my->misses++; [my->lock unlock]; [orig release]; return nil; } else { item = current; } [orig release]; } } // Least recently used ... move to end of list. removeItem(item, &my->first); appendItem(item, &my->first); my->hits++; object = [item->object retain]; [my->lock unlock]; return [object autorelease]; } - (void) purge { if (my->contents != 0) { unsigned when = GSTickerTimeTick(); NSMapEnumerator e; GSCacheItem *i; id k; [my->lock lock]; e = NSEnumerateMapTable(my->contents); while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&i) != 0) { if (i->when > 0 && i->when < when) { removeItem(i, &my->first); my->currentObjects--; if (my->maxSize > 0) { my->currentSize -= i->size; } NSMapRemove(my->contents, (void*)i->key); } } NSEndMapTableEnumeration(&e); [my->lock unlock]; } } - (oneway void) release { /* We lock the table while checking, to prevent * another thread from grabbing this object while we are * checking it. * If we are going to deallocate the object, we first remove * it from the table so that no other thread will find it * and try to use it while it is being deallocated. */ [allCachesLock lock]; if (NSDecrementExtraRefCountWasZero(self)) { NSHashRemove(allCaches, (void*)self); [allCachesLock unlock]; [self dealloc]; } else { [allCachesLock unlock]; } } - (void) setDelegate: (id)anObject { [my->lock lock]; my->delegate = anObject; if ([my->delegate respondsToSelector: @selector(shouldKeepItem:withKey:lifetime:after:)]) { my->replace = (BOOL (*)(id,SEL,id,id,unsigned,unsigned)) [my->delegate methodForSelector: @selector(shouldKeepItem:withKey:lifetime:after:)]; } else { my->replace = 0; } if ([my->delegate respondsToSelector: @selector(mayRefreshItem:withKey:lifetime:after:)]) { my->refresh = (void (*)(id,SEL,id,id,unsigned,unsigned)) [my->delegate methodForSelector: @selector(mayRefreshItem:withKey:lifetime:after:)]; } else { my->refresh = 0; } [my->lock unlock]; } - (void) setLifetime: (unsigned)max { my->lifetime = max; } - (void) setMaxObjects: (unsigned)max { [my->lock lock]; my->maxObjects = max; if (my->currentObjects > my->maxObjects) { [self shrinkObjects: my->maxObjects andSize: my->maxSize]; } [my->lock unlock]; } - (void) setMaxSize: (NSUInteger)max { [my->lock lock]; if (max > 0 && my->maxSize == 0) { NSMapEnumerator e = NSEnumerateMapTable(my->contents); GSCacheItem *i; id k; NSUInteger size = 0; if (my->exclude == nil) { my->exclude = [NSMutableSet new]; } while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&i) != 0) { if (i->size == 0) { [my->exclude removeAllObjects]; i->size = [i->object sizeInBytes: my->exclude]; } if (i->size > max) { /* * Item in cache is too big for new size limit ... * Remove it. */ removeItem(i, &my->first); NSMapRemove(my->contents, (void*)i->key); my->currentObjects--; continue; } size += i->size; } NSEndMapTableEnumeration(&e); my->currentSize = size; } else if (max == 0) { my->currentSize = 0; } my->maxSize = max; if (my->currentSize > my->maxSize) { [self shrinkObjects: my->maxObjects andSize: my->maxSize]; } [my->lock unlock]; } - (void) setName: (NSString*)name { [my->lock lock]; [name retain]; [my->name release]; my->name = name; [my->lock unlock]; } - (void) setObject: (id)anObject forKey: (id)aKey { [self setObject: anObject forKey: aKey lifetime: my->lifetime]; } - (void) setObject: (id)anObject forKey: (id)aKey lifetime: (unsigned)lifetime { GSCacheItem *item; unsigned maxObjects; NSUInteger maxSize; unsigned addObjects = (anObject == nil ? 0 : 1); NSUInteger addSize = 0; [my->lock lock]; maxObjects = my->maxObjects; maxSize = my->maxSize; item = (GSCacheItem*)NSMapGet(my->contents, aKey); if (item != nil) { removeItem(item, &my->first); my->currentObjects--; if (my->maxSize > 0) { my->currentSize -= item->size; } NSMapRemove(my->contents, (void*)aKey); } if (addObjects > 0 && (maxSize > 0 || maxObjects > 0)) { if (maxSize > 0) { if (my->exclude == nil) { my->exclude = [NSMutableSet new]; } [my->exclude removeAllObjects]; addSize = [anObject sizeInBytes: my->exclude]; if (addSize > maxSize) { addObjects = 0; // Object too big to cache. } } } if (addObjects > 0) { /* * Make room for new object. */ [self shrinkObjects: maxObjects - addObjects andSize: maxSize - addSize]; item = [GSCacheItem newWithObject: anObject forKey: aKey]; if (lifetime > 0) { unsigned tick = GSTickerTimeTick(); item->when = tick + lifetime; item->warn = tick + lifetime / 2; } item->life = lifetime; item->size = addSize; NSMapInsert(my->contents, (void*)item->key, (void*)item); appendItem(item, &my->first); my->currentObjects += addObjects; my->currentSize += addSize; [item release]; } [my->lock unlock]; } - (void) setObject: (id)anObject forKey: (id)aKey until: (NSDate*)expires { NSTimeInterval i; i = (expires == nil) ? 0.0 : [expires timeIntervalSinceReferenceDate]; i -= GSTickerTimeNow(); if (i <= 0.0) { [self setObject: nil forKey: aKey]; // Already expired } else { unsigned limit; if (i > 2415919103.0) { limit = 0; // Limit in far future. } else { limit = (unsigned)i; } [self setObject: anObject forKey: aKey lifetime: limit]; } } - (void) shrinkObjects: (unsigned)objects andSize: (NSUInteger)size { NSUInteger newSize; unsigned newObjects; [my->lock lock]; newSize = [self currentSize]; newObjects = [self currentObjects]; if (newObjects > objects || (my->maxSize > 0 && newSize > size)) { [self purge]; newSize = [self currentSize]; newObjects = [self currentObjects]; while (newObjects > objects || (my->maxSize > 0 && newSize > size)) { GSCacheItem *item; item = my->first; removeItem(item, &my->first); newObjects--; if (my->maxSize > 0) { newSize -= item->size; } NSMapRemove(my->contents, (void*)item->key); } my->currentObjects = newObjects; my->currentSize = newSize; } [my->lock unlock]; } @end @implementation GSCache (Threading) + (void) _becomeThreaded: (NSNotification*)n { NSHashEnumerator e; GSCache *c; [[NSNotificationCenter defaultCenter] removeObserver: self name: NSWillBecomeMultiThreadedNotification object: nil]; allCachesLock = [NSRecursiveLock new]; e = NSEnumerateHashTable(allCaches); while ((c = (GSCache*)NSNextHashEnumeratorItem(&e)) != nil) { [c _createLock]; } NSEndHashTableEnumeration(&e); } - (void) _createLock { my->lock = [NSRecursiveLock new]; } @end @implementation NSArray (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { NSUInteger size = [super sizeInBytes: exclude]; if (size > 0) { NSUInteger count = [self count]; size += count*sizeof(void*); while (count-- > 0) { size += [[self objectAtIndex: count] sizeInBytes: exclude]; } } return size; } @end @implementation NSData (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { NSUInteger size = [super sizeInBytes: exclude]; if (size > 0) { size += [self length]; } return size; } @end @implementation NSDictionary (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { NSUInteger size = [super sizeInBytes: exclude]; if (size > 0) { NSUInteger count = [self count]; size += 3 * sizeof(void*) * count; if (count > 0) { NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSEnumerator *enumerator = [self keyEnumerator]; NSObject *k; while ((k = [enumerator nextObject]) != nil) { NSObject *o = [self objectForKey: k]; size += [k sizeInBytes: exclude] + [o sizeInBytes: exclude]; } [pool release]; } } return size; } @end @implementation NSObject (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { if ([exclude member: self] != nil) { return 0; } [exclude addObject: self]; return class_getInstanceSize(object_getClass(self)); } @end @implementation NSSet (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { NSUInteger size = [super sizeInBytes: exclude]; if (size > 0) { NSUInteger count = [self count]; size += 3 * sizeof(void*) * count; if (count > 0) { NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSEnumerator *enumerator = [self objectEnumerator]; NSObject *o; while ((o = [enumerator nextObject]) != nil) { size += [o sizeInBytes: exclude]; } [pool release]; } } return size; } @end @implementation NSString (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { if ([exclude member: self] != nil) { return 0; } else { return [super sizeInBytes: exclude] + sizeof(unichar) * [self length]; } } @end #if defined(GNUSTEP_BASE_LIBRARY) // FIXME ... this should be moved to base additions. #import #if GS_NONFRAGILE /* When using the non-fragile ABI, base does not expose the mime class * instance variables, so we use the raw data size as a rough approximation. * The actual memory usage will be larger than this of course. */ @implementation GSMimeDocument (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { return [[self rawMimeData] sizeInBytes: exclude]; } @end @implementation GSMimeHeader (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { return [[self rawMimeData] sizeInBytes: exclude]; } @end #else @implementation GSMimeDocument (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { NSUInteger size = [super sizeInBytes: exclude]; if (size > 0) { size += [content sizeInBytes: exclude] + [headers sizeInBytes: exclude]; } return size; } @end @implementation GSMimeHeader (GSCacheSizeInBytes) - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude { NSUInteger size = [super sizeInBytes: exclude]; if (size > 0) { size += [name sizeInBytes: exclude] + [value sizeInBytes: exclude] + [objects sizeInBytes: exclude] + [params sizeInBytes: exclude]; } return size; } @end #endif #endif Performance-0.5.0/GSLinkedList.m0000664000076500007650000002265012076312220016325 0ustar brains99brains99/** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: September 2010 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #import #import "GSLinkedList.h" @implementation GSListLink - (void) dealloc { NSAssert(nil == owner, NSInternalInconsistencyException); [item release]; [super dealloc]; } - (id) initCircular { if ((self = [super init]) != nil) { next = previous = self; } return self; } - (NSObject*) item { return item; } - (GSListLink*) next { if (next == self) { return nil; } return next; } - (GSLinkedList*) owner { return owner; } - (GSListLink*) previous { if (previous == self) { return nil; } return previous; } - (void) setItem: (NSObject*)anItem { id o = item; item = [anItem retain]; [o release]; } @end @implementation GSLinkedList - (void) append: (GSListLink*)link { if (nil == link) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] nil argument", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } if (self == link->owner) { if (link != tail) { GSLinkedListRemove(link, self); GSLinkedListInsertAfter(link, self, tail); } } else { if (nil != link->owner || nil != link->next || nil != link->previous) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] other link is still in a list", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } GSLinkedListInsertAfter(link, self, tail); [link retain]; } } - (NSUInteger) count { return count; } - (void) dealloc { [self empty]; [super dealloc]; } - (void) empty { GSListLink *link; while (nil != (link = head)) { head = link->next; link->owner = nil; link->next = link->previous = nil; [link release]; } tail = nil; count = 0; } - (GSListLink*) findEqual: (NSObject*)object from: (GSListLink*)start back: (BOOL)aFlag { if (nil != start && start->owner != self) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] start link is not in this list", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } return GSLinkedListFindEqual(object, self, start, aFlag); } - (GSListLink*) findIdentical: (NSObject*)object from: (GSListLink*)start back: (BOOL)aFlag { if (nil != start && start->owner != self) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] start link is not in this list", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } return GSLinkedListFindIdentical(object, self, start, aFlag); } - (GSListLink*) head { return head; } - (void) insert: (GSListLink*)link after: (GSListLink*)at { if (nil == link) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] nil link argument", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } if (nil == at) { at = tail; } if (at->owner != self) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] 'at' link is not in this list", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } if (at == link) { return; } if (link->owner == self) { GSLinkedListRemove(link, self); GSLinkedListInsertAfter(link, self, at); } else { if (nil != link->owner || nil != link->next || nil != link->previous) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] other link is still in a list", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } GSLinkedListInsertAfter(link, self, at); [link retain]; } } - (void) insert: (GSListLink*)link before: (GSListLink*)at { if (nil == link) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] nil link argument", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } if (nil == at) { at = head; } if (at->owner != self) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] 'at' link is not in this list", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } if (at == link) { return; } if (link->owner == self) { GSLinkedListRemove(link, self); GSLinkedListInsertBefore(link, self, at); } else { if (nil != link->owner || nil != link->next || nil != link->previous) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] other link is still in a list", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } GSLinkedListInsertBefore(link, self, at); [link retain]; } } - (void) removeLink: (GSListLink*)link { if (nil == link) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] nil link argument", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } if (link->owner != self) { [NSException raise: NSInvalidArgumentException format: @"[%@-%@] link is not in this list", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } GSLinkedListRemove(link, self); [link release]; } - (GSListLink*) tail { return tail; } @end GSListLink* GSLinkedListFindEqual(NSObject *object, GSLinkedList *list, GSListLink *from, BOOL back) { if (nil == from) { if (YES == back) { from = list->tail; } else { from = list->head; } } if (nil != object) { BOOL (*imp)(id, SEL, id); imp = (BOOL(*)(id,SEL,id))[object methodForSelector: @selector(isEqual:)]; if (YES == back) { while (nil != from) { if (YES == (*imp)(object, @selector(isEqual:), from->item)) { return from; } from = from->previous; } } else { while (nil != from) { if (YES == (*imp)(object, @selector(isEqual:), from->item)) { return from; } from = from->next; } } } return nil; } GSListLink* GSLinkedListFindIdentical(NSObject *object, GSLinkedList *list, GSListLink *from, BOOL back) { if (nil == from) { if (YES == back) { from = list->tail; } else { from = list->head; } } if (YES == back) { while (nil != from) { if (object == from->item) { return from; } from = from->previous; } } else { while (nil != from) { if (object == from->item) { return from; } from = from->next; } } return nil; } void GSLinkedListInsertBefore(GSListLink *link, GSLinkedList *list, GSListLink *at) { if (nil == list->head) { list->head = list->tail = link; } else { link->previous = at->previous; if (nil == link->previous) { list->head = link; } else { link->previous->next = link; } at->previous = link; link->next = at; } link->owner = list; list->count++; } void GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at) { if (nil == list->head) { list->head = list->tail = link; } else { link->next = at->next; if (nil == link->next) { list->tail = link; } else { link->next->previous = link; } at->next = link; link->previous = at; } link->owner = list; list->count++; } void GSLinkedListRemove(GSListLink *link, GSLinkedList *list) { if (list->head == link) { list->head = link->next; if (nil != list->head) { list->head->previous = nil; } } else { link->previous->next = link->next; } if (list->tail == link) { list->tail = link->previous; if (nil != list->tail) { list->tail->next = nil; } } else if (nil != link->next) { link->next->previous = link->previous; } link->next = link->previous = nil; link->owner = nil; list->count--; } extern void GSLinkedListMoveToHead(GSListLink *link, GSLinkedList *list) { if (link != list->head) { if (link == list->tail) { list->tail = link->previous; list->tail->next = nil; } else { link->next->previous = link->previous; link->previous->next = link->next; } link->next = list->head; link->previous = nil; list->head->previous = link; list->head = link; } } extern void GSLinkedListMoveToTail(GSListLink *link, GSLinkedList *list) { if (link != list->tail) { if (link == list->head) { list->head = link->next; list->head->previous = nil; } else { link->next->previous = link->previous; link->previous->next = link->next; } link->next = nil; link->previous = list->tail; list->tail->next = link; list->tail = link; } } Performance-0.5.0/COPYING.LIB0000664000076500007650000001673010672502772015332 0ustar brains99brains99 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. Performance-0.5.0/GSTicker.m0000664000076500007650000001657612103402666015523 0ustar brains99brains99/* -*-objc-*- */ /** Implementation of GSTicker for GNUStep Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: November 2005 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. $Date: 2013-02-03 06:37:42 +0000 (Sun, 03 Feb 2013) $ $Revision: 36058 $ */ #import #import #import #import #import #import #import #import "GSTicker.h" static Class NSDateClass = 0; static NSDate *startDate = nil; static SEL tiSel = 0; static NSTimeInterval (*tiImp)(Class,SEL) = 0; static volatile NSTimeInterval baseTime = 0; static volatile NSTimeInterval lastTime = 0; @interface GSTickerObservation : NSObject { @public id observer; id userInfo; } - (void) fire: (NSArray*)live; @end @implementation GSTickerObservation - (void) fire: (NSArray*)live { if ([live indexOfObjectIdenticalTo: self] != NSNotFound) { [observer newSecond: userInfo]; } } @end @interface GSTickerThread : NSObject { @public /* NB. We retain theTimer rather than depending on the run loop to do it. * This is because tis object is typically deallocated on thread exist, * so the thread's run loop may be deallocated before this object is * deallocated, and we want to be sure the timer is not already deallocated * when we invalidate it. */ NSTimer *theTimer; NSMutableArray *observers; unsigned last; } @end @implementation GSTickerThread - (void) dealloc { [theTimer invalidate]; [theTimer release]; theTimer = nil; [observers release]; observers = nil; [super dealloc]; } - (id) init { if (nil != (self = [super init])) { NSTimeInterval ti = GSTickerTimeNow(); observers = [NSMutableArray new]; theTimer = [[NSTimer scheduledTimerWithTimeInterval: ti - (int)ti target: [GSTicker class] selector: @selector(_tick:) userInfo: self repeats: NO] retain]; } return self; } @end @interface GSTicker (Private) + (void) _tick: (NSTimer*)t; @end inline NSTimeInterval GSTickerTimeLast() { return lastTime; } inline NSTimeInterval GSTickerTimeStart() { if (baseTime == 0) { [GSTicker class]; } return baseTime; } inline unsigned GSTickerTimeTick() { NSTimeInterval start = GSTickerTimeStart(); return (GSTickerTimeLast() - start) + 1; } NSTimeInterval GSTickerTimeNow() { if (baseTime == 0) { [GSTicker class]; } else { NSTimeInterval now; /* * If the clock has been reset so that time has gone backwards, * we adjust the baseTime so that lastTime >= baseTime is true. */ now = (*tiImp)(NSDateClass, tiSel); if (now < lastTime) { baseTime -= (lastTime - now); } lastTime = now; } return lastTime; } @implementation GSTicker + (void) initialize { if (0 == baseTime) { NSDateClass = [NSDate class]; tiSel = @selector(timeIntervalSinceReferenceDate); tiImp = (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel]; baseTime = lastTime = (*tiImp)(NSDateClass, tiSel); startDate = [[NSDateClass alloc] initWithTimeIntervalSinceReferenceDate: baseTime]; } } + (NSDate*) last { return [NSDateClass dateWithTimeIntervalSinceReferenceDate: GSTickerTimeLast()]; } + (void) newSecond: (id)userInfo { return; } + (NSDate*) now { return [NSDateClass dateWithTimeIntervalSinceReferenceDate: GSTickerTimeNow()]; } + (void) registerObserver: (id)anObject userInfo: (id)userInfo { GSTickerThread *tt; GSTickerObservation *to; unsigned count; tt = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSTickerThread"]; if (tt == nil) { tt = [GSTickerThread new]; [[[NSThread currentThread] threadDictionary] setObject: tt forKey: @"GSTickerThread"]; [tt release]; } count = [tt->observers count]; while (count-- > 0) { to = [tt->observers objectAtIndex: count]; if (to->observer == anObject) { return; // Already registered. } } to = [GSTickerObservation new]; to->observer = anObject; to->userInfo = userInfo; [tt->observers addObject: to]; [to release]; } + (NSDate*) start { if (startDate == nil) { startDate = [NSDateClass alloc]; startDate = [startDate initWithTimeIntervalSinceReferenceDate: GSTickerTimeStart()]; } return startDate; } + (unsigned) tick { return GSTickerTimeTick(); } + (void) unregisterObserver: (id)anObject { GSTickerThread *tt; tt = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSTickerThread"]; if (tt != nil) { GSTickerObservation *to; unsigned count = [tt->observers count]; while (count-- > 0) { to = [tt->observers objectAtIndex: count]; if (to->observer == anObject) { [tt->observers removeObjectAtIndex: count]; break; } } } } + (void) update { GSTickerTimeNow(); } - (NSDate*) last { return [NSDateClass dateWithTimeIntervalSinceReferenceDate: GSTickerTimeLast()]; } - (void) newSecond: (id)userInfo { return; } - (NSDate*) now { return [NSDateClass dateWithTimeIntervalSinceReferenceDate: GSTickerTimeNow()]; } - (NSDate*) start { return startDate; } - (unsigned) tick { return GSTickerTimeTick(); } - (void) update { GSTickerTimeNow(); } @end @implementation GSTicker (Private) + (void) _tick: (NSTimer*)t { GSTickerThread *tt = [t userInfo]; if (tt == nil) { tt = [[[NSThread currentThread] threadDictionary] objectForKey: @"GSTickerThread"]; } if (tt != nil && [tt->observers count] > 0) { NSTimeInterval ti; [tt->theTimer invalidate]; [tt->theTimer release]; tt->theTimer = nil; if ([tt->observers count] > 0) { NSArray *a; unsigned tick; GSTickerTimeNow(); tick = GSTickerTimeTick(); if (tick != tt->last) { tt->last = tick; a = [tt->observers copy]; NS_DURING { [a makeObjectsPerformSelector: @selector(fire:) withObject: tt->observers]; } NS_HANDLER { NSLog(@"Problem firing ticker observers: %@", localException); } NS_ENDHANDLER [a release]; } } ti = GSTickerTimeNow(); tt->theTimer = [[NSTimer scheduledTimerWithTimeInterval: ti - (int)ti target: self selector: @selector(_tick:) userInfo: tt repeats: NO] retain]; } else { [[[NSThread currentThread] threadDictionary] removeObjectForKey: @"GSTickerThread"]; } } @end Performance-0.5.0/GSThreadPool.m0000664000076500007650000002544312115113411016322 0ustar brains99brains99#import "GSLinkedList.h" #import "GSThreadPool.h" #import #import #import #import #import #import @class GSThreadPool; @interface GSOperation : GSListLink { @public SEL sel; NSObject *arg; } @end @implementation GSOperation - (void) dealloc { [arg release]; [super dealloc]; } @end @interface GSThreadLink : GSListLink { @public GSThreadPool *pool; // Not retained NSConditionLock *lock; GSOperation *op; } @end @implementation GSThreadLink - (void) dealloc { [lock release]; [super dealloc]; } - (id) init { if ((self = [super init]) != nil) { lock = [[NSConditionLock alloc] initWithCondition: 0]; } return self; } @end @interface GSThreadPool (Internal) - (void) _any; - (void) _dead: (GSThreadLink*)link; - (BOOL) _idle: (GSThreadLink*)link; - (BOOL) _more: (GSThreadLink*)link; - (void) _run: (GSThreadLink*)link; @end @implementation GSThreadPool static GSThreadPool *shared = nil; + (void) initialize { if ([GSThreadPool class] == self && nil == shared) { shared = [self new]; } } + (GSThreadPool*) sharedPool { return shared; } - (void) dealloc { GSThreadLink *link; if (self == shared) { [self retain]; [NSException raise: NSInternalInconsistencyException format: @"[GSThreadPool-dealloc] attempt to deallocate shared pool"]; } [poolLock lock]; [operations release]; operations = nil; [unused release]; unused = nil; if (nil != idle) { while (nil != (link = (GSThreadLink*)idle->head)) { GSLinkedListRemove(link, idle); [link->lock lock]; [link->lock unlockWithCondition: 1]; } [idle release]; idle = nil; } if (nil != live) { while (nil != (link = (GSThreadLink*)live->head)) { GSLinkedListRemove(link, live); link->pool = nil; } [live release]; live = nil; } [poolLock unlock]; [poolLock release]; [super dealloc]; } - (NSString*) description { NSString *result; [poolLock lock]; result = [NSString stringWithFormat: @"%@ queue: %"PRIuPTR"(%"PRIuPTR")" @" threads: %"PRIuPTR"(%"PRIuPTR")" @" active: %"PRIuPTR" processed: %"PRIuPTR"", [super description], operations->count, maxOperations, idle->count + live->count, maxThreads, live->count, processed]; [poolLock unlock]; return result; } - (BOOL) drain: (NSDate*)before { BOOL result = [self isEmpty]; while (NO == result && [before timeIntervalSinceNow] > 0.0) { #if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED<=MAC_OS_X_VERSION_10_4) NSDate *when; when = [[NSDate alloc] initWithTimeIntervalSinceNow: 0.1]; [NSThread sleepUntilDate: when]; [when release]; #else [NSThread sleepForTimeInterval: 0.1]; #endif result = [self isEmpty]; } return result; } - (NSUInteger) flush { NSUInteger counter; [poolLock lock]; counter = operations->count; [operations empty]; [poolLock unlock]; return counter; } - (id) init { if ((self = [super init]) != nil) { poolLock = [NSRecursiveLock new]; idle = [GSLinkedList new]; live = [GSLinkedList new]; operations = [GSLinkedList new]; unused = [GSLinkedList new]; [self setOperations: 100]; [self setThreads: 2]; } return self; } - (BOOL) isEmpty { return (0 == operations->count) ? YES : NO; } - (BOOL) isIdle { return (0 == live->count) ? YES : NO; } - (BOOL) isSuspended { return suspended; } - (NSUInteger) maxOperations { return maxOperations; } - (NSUInteger) maxThreads { return maxThreads; } - (void) resume { [poolLock lock]; if (YES == suspended) { suspended = NO; /* No longer suspended ... start as many operations as we have idle * threads available for. */ [self _any]; } [poolLock unlock]; } - (void) scheduleSelector: (SEL)aSelector onReceiver: (NSObject*)aReceiver withObject: (NSObject*)anArgument { if (0 == aSelector) { [NSException raise: NSInvalidArgumentException format: @"Null selector"]; } if (nil == aReceiver) { [NSException raise: NSInvalidArgumentException format: @"Nil receiver"]; } [poolLock lock]; if (operations->count < maxOperations && maxThreads > 0) { GSOperation *op = (GSOperation*)unused->head; if (nil == op) { op = [GSOperation new]; // Need a new one } else { GSLinkedListRemove(op, unused); // Re-use an old one } [op setItem: aReceiver]; op->sel = aSelector; op->arg = [anArgument retain]; GSLinkedListInsertAfter(op, operations, operations->tail); [self _any]; [poolLock unlock]; } else { NSAutoreleasePool *arp; [poolLock unlock]; NS_DURING { arp = [NSAutoreleasePool new]; [aReceiver performSelector: aSelector withObject: anArgument]; [arp release]; } NS_HANDLER { arp = [NSAutoreleasePool new]; NSLog(@"[%@-%@] %@", NSStringFromClass([aReceiver class]), NSStringFromSelector(aSelector), localException); [arp release]; } NS_ENDHANDLER } } - (void) setOperations: (NSUInteger)max { maxOperations = max; } - (void) setThreads: (NSUInteger)max { [poolLock lock]; if (max != maxThreads) { maxThreads = max; if (0 == maxThreads) { [poolLock unlock]; if (NO == [self drain: [NSDate dateWithTimeIntervalSinceNow: 30.0]]) { [self flush]; } [poolLock lock]; } while (maxThreads < idle->count + live->count && idle->count > 0) { GSThreadLink *link = (GSThreadLink*)idle->head; /* Remove thread link from the idle list, then start up the * thread using the condition lock ... the thread will see * that it has no operation to work with and will terminate * itsself and release the link. */ GSLinkedListRemove(link, idle); [link->lock lock]; [link->lock unlockWithCondition: 1]; } [self _any]; } [poolLock unlock]; } - (void) suspend { [poolLock lock]; suspended = YES; [poolLock unlock]; } @end @implementation GSThreadPool (Internal) /* This method expects the global lock to already be held. */ - (void) _any { if (NO == suspended) { GSOperation *op; while (nil != (op = (GSOperation*)operations->head)) { GSThreadLink *link = (GSThreadLink*)idle->head; if (nil == link) { if (maxThreads > idle->count + live->count) { NSThread *thread; /* Create a new link, add it to the idle list, and start the * thread which will work with it. */ link = [GSThreadLink new]; link->pool = self; GSLinkedListInsertAfter(link, idle, idle->tail); #if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED<=MAC_OS_X_VERSION_10_4) /* With the old thread API we can't get an NSThread object * until after the thread has started ... so we start the * thread and then wait for the new thread to have set the * link item up properly. */ [NSThread detachNewThreadSelector: @selector(_run:) toTarget: self withObject: link]; while (nil == link->item) { NSDate *when; when = [[NSDate alloc] initWithTimeIntervalSinceNow: 0.001]; [NSThread sleepUntilDate: when]; [when release]; } #else /* New thread API ... create thread object, set it in the * link, then start the thread. */ thread = [[NSThread alloc] initWithTarget: self selector: @selector(_run:) object: link]; [link setItem: thread]; [thread start]; [thread release]; // Retained by link #endif } else { break; // No idle thread to perform operation } } GSLinkedListRemove(op, operations); GSLinkedListRemove(link, idle); GSLinkedListInsertAfter(link, live, live->tail); link->op = op; [link->lock lock]; [link->lock unlockWithCondition: 1]; } } } - (void) _dead: (GSThreadLink*)link { [poolLock lock]; if (link->owner != nil) { GSLinkedListRemove(link, link->owner); } [poolLock unlock]; } /* Make the thread link idle ... returns YES on success, NO if the thread * should actually terminate instead. */ - (BOOL) _idle: (GSThreadLink*)link { BOOL madeIdle = YES; [poolLock lock]; if (link->owner != nil) { GSLinkedListRemove(link, link->owner); } if (idle->count + live->count > maxThreads) { madeIdle = NO; // Made dead instead } else { GSLinkedListInsertAfter(link, idle, idle->tail); } [poolLock unlock]; return madeIdle; } /* If there are more operations waiting for work, move the first one from the * operations queue into the supplied thread link.
* In any case, remove the old operation. */ - (BOOL) _more: (GSThreadLink*)link { GSOperation *op = link->op; BOOL more = NO; [poolLock lock]; processed++; if (unused->count < maxOperations) { if (nil != op->arg) { [op->arg release]; op->arg = nil; } [op setItem: nil]; GSLinkedListInsertAfter(op, unused, unused->tail); } else { [op release]; } link->op = (GSOperation*)operations->head; if (nil != link->op) { GSLinkedListRemove(link->op, operations); more = YES; } [poolLock unlock]; return more; } - (void) _run: (GSThreadLink*)link { NSAutoreleasePool *arp; #if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED<=MAC_OS_X_VERSION_10_4) /* With the older thread API we must set up the link item *after* the * thread starts. With the new API this is not needed as we cans set * things up and then start the thread. */ [link setItem: [NSThread currentThread]]; #endif for (;;) { GSOperation *op; [link->lock lockWhenCondition: 1]; //NSLog(@"locked"); op = link->op; if (nil == op) { //NSLog(@"nil op"); break; } else { [link->lock unlockWithCondition: 0]; //NSLog(@"unlock"); while (nil != op) { NS_DURING { arp = [NSAutoreleasePool new]; [op->item performSelector: op->sel withObject: op->arg]; [arp release]; } NS_HANDLER { arp = [NSAutoreleasePool new]; NSLog(@"[%@-%@] %@", NSStringFromClass([op->item class]), NSStringFromSelector(op->sel), localException); [arp release]; } NS_ENDHANDLER if (NO == [link->pool _more: link]) { //NSLog(@"no more"); op = nil; } else { //NSLog(@"more"); op = link->op; } } if (NO == [link->pool _idle: link]) // Make this idle { //NSLog(@"no idle"); break; // Thread should exit rather than be idle } } } arp = [NSAutoreleasePool new]; [link->pool _dead: link]; NSLog(@"Thread for %@ terminated.", self); [arp release]; [NSThread exit]; // Will release 'link' } @end Performance-0.5.0/GSFIFO.m0000664000076500007650000004011212236142444015006 0ustar brains99brains99/** Copyright (C) 2011 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: April 2011 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import "GSFIFO.h" #import #import #import #import #import #import #import #import #import @implementation GSFIFO static NSLock *classLock = nil; static NSMapTable *allFIFOs = nil; static NSArray *defaultBoundaries = nil; static Class NSDateClass = 0; static SEL tiSel = 0; static NSTimeInterval (*tiImp)(Class, SEL) = 0; #define NOW ((*tiImp)(NSDateClass, tiSel)) #define START if (boundsCount > 0) ti = NOW; #define ENDGET if (boundsCount > 0 && ti > 0.0) {\ ti = NOW - ti; getWaitTotal += ti; \ stats(ti, boundsCount, waitBoundaries, getWaitCounts); } #define ENDPUT if (boundsCount > 0 && ti > 0.0) {\ ti = NOW - ti; putWaitTotal += ti; \ stats(ti, boundsCount, waitBoundaries, putWaitCounts); } static void stats(NSTimeInterval ti, uint32_t max, NSTimeInterval *bounds, uint64_t *bands) { if (ti > bounds[max - 1]) { bands[max]++; } else { uint32_t min = 0; uint32_t pos = max / 2; while (max > min) { if (ti <= bounds[pos]) { max = pos; } else { min = pos + 1; } pos = (max + min)/2; } bands[pos]++; } } + (void) initialize { if (nil == defaultBoundaries) { classLock = [NSLock new]; allFIFOs = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonRetainedObjectMapValueCallBacks, 0); defaultBoundaries = [[NSArray alloc] initWithObjects: [NSNumber numberWithDouble: 0.1], [NSNumber numberWithDouble: 0.2], [NSNumber numberWithDouble: 0.5], [NSNumber numberWithDouble: 1.0], [NSNumber numberWithDouble: 2.0], [NSNumber numberWithDouble: 5.0], [NSNumber numberWithDouble: 10.0], [NSNumber numberWithDouble: 20.0], [NSNumber numberWithDouble: 50.0], nil]; NSDateClass = [NSDate class]; tiSel = @selector(timeIntervalSinceReferenceDate); tiImp = (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel]; } } + (NSString*) stats { NSMutableString *m = [NSMutableString stringWithCapacity: 1024]; NSString *k; GSFIFO *f; NSMapEnumerator e; [classLock lock]; e = NSEnumerateMapTable(allFIFOs); while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&f) != 0) { [m appendString: [f stats]]; } NSEndMapTableEnumeration(&e); [classLock unlock]; return m; } - (unsigned) _cooperatingGet: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block { NSTimeInterval ti; unsigned index; BOOL wasFull; [condition lock]; if (_head - _tail == 0) { emptyCount++; _getTryFailure++; if (NO == block) { [condition unlock]; return 0; } START if (0 == timeout) { while (_head - _tail == 0) { [condition wait]; } } else { NSDate *d; d = [[NSDateClass alloc] initWithTimeIntervalSinceNow: timeout / 1000.0f]; while (_head - _tail == 0) { if (NO == [condition waitUntilDate: d]) { [d release]; ENDGET [condition broadcast]; [condition unlock]; [NSException raise: NSGenericException format: @"Timeout waiting for new data in FIFO"]; } } [d release]; ENDGET } } else { _getTrySuccess++; } if (_head - _tail == _capacity) { wasFull = YES; } else { wasFull = NO; } for (index = 0; index < count && (_head - _tail) != 0; index++) { buf[index] = _items[_tail % _capacity]; _tail++; } if (YES == wasFull) { [condition broadcast]; } [condition unlock]; return index; } - (unsigned) _cooperatingPut: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block { NSTimeInterval ti; unsigned index; BOOL wasEmpty; [condition lock]; if (_head - _tail == _capacity) { _putTryFailure++; fullCount++; if (NO == block) { [condition unlock]; return 0; } START if (0 == timeout) { while (_head - _tail == _capacity) { [condition wait]; } } else { NSDate *d; d = [[NSDateClass alloc] initWithTimeIntervalSinceNow: timeout / 1000.0f]; while (_head - _tail == _capacity) { if (NO == [condition waitUntilDate: d]) { [d release]; ENDPUT [condition broadcast]; [condition unlock]; [NSException raise: NSGenericException format: @"Timeout waiting for space in FIFO"]; } } [d release]; } ENDPUT } else { _putTrySuccess++; } if (_head - _tail == 0) { wasEmpty = YES; } else { wasEmpty = NO; } for (index = 0; index < count && (_head - _tail < _capacity); index++) { _items[_head % _capacity] = buf[index]; _head++; } if (YES == wasEmpty) { [condition broadcast]; } [condition unlock]; return index; } - (void) dealloc { [classLock lock]; if (NSMapGet(allFIFOs, name) == self) { NSMapRemove(allFIFOs, name); } [classLock unlock]; [name release]; [condition release]; if (0 != _items) { NSZoneFree(NSDefaultMallocZone(), _items); } if (0 != waitBoundaries) { NSZoneFree(NSDefaultMallocZone(), waitBoundaries); } if (0 != getWaitCounts) { NSZoneFree(NSDefaultMallocZone(), getWaitCounts); } if (0 != putWaitCounts) { NSZoneFree(NSDefaultMallocZone(), putWaitCounts); } [super dealloc]; } - (NSUInteger) count { return (NSUInteger)(_head - _tail); } - (NSString*) description { return [NSString stringWithFormat: @"%@ (%@) capacity:%"PRIu32" lockless:%c" @" get:%"PRIu64" put:%"PRIu64" empty:%"PRIu64" full:%"PRIu64"", [super description], name, _capacity, ((nil == condition) ? 'Y' : 'N'), _tail, _head, emptyCount, fullCount]; } - (unsigned) get: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block { unsigned index; NSTimeInterval ti; NSTimeInterval sum; uint32_t old; uint32_t fib; if (0 == count) return 0; if (nil != condition) { return [self _cooperatingGet: buf count: count shouldBlock: block]; } if (nil == getThread) { getThread = [NSThread currentThread]; } if (_head > _tail) { for (index = 0; index < count && _head > _tail; index++) { buf[index] = _items[_tail % _capacity]; _tail++; _getTrySuccess++; } return index; } _getTryFailure++; emptyCount++; if (NO == block) { return 0; } START old = 0; fib = 1; sum = 0.0; while (_head <= _tail) { uint32_t tmp; NSTimeInterval dly; if (timeout > 0 && sum * 1000 > timeout) { ENDGET [NSException raise: NSGenericException format: @"Timeout waiting for new data in FIFO"]; } tmp = fib + old; old = fib; fib = tmp; if (granularity > 0 && fib > granularity) { fib = granularity; } dly = ((NSTimeInterval)fib) / 1000.0; [NSThread sleepForTimeInterval: dly]; sum += dly; } ENDGET for (index = 0; index < count && _head > _tail; index++) { buf[index] = _items[_tail % _capacity]; _tail++; } return index; } - (void*) get { void *item = 0; while (0 == [self get: &item count: 1 shouldBlock: YES]) ; return item; } - (id) initWithCapacity: (uint32_t)c granularity: (uint16_t)g timeout: (uint16_t)t multiProducer: (BOOL)mp multiConsumer: (BOOL)mc boundaries: (NSArray*)a name: (NSString*)n { if (c < 1 || c > 1000000) { [self release]; return nil; } _capacity = c; granularity = g; timeout = t; _items = (void*)NSAllocateCollectable(c * sizeof(void*), NSScannedOption); if (YES == mp || YES == mc) { condition = [NSCondition new]; } name = [n copy]; if (nil == a) { a = defaultBoundaries; } if ((c = [a count]) > 0) { NSTimeInterval l; NSNumber *n; waitBoundaries = (NSTimeInterval*)NSAllocateCollectable(c * sizeof(NSTimeInterval), 0); boundsCount = c++; getWaitCounts = (uint64_t*)NSAllocateCollectable(c * sizeof(uint64_t), 0); putWaitCounts = (uint64_t*)NSAllocateCollectable(c * sizeof(uint64_t), 0); n = [a lastObject]; if (NO == [n isKindOfClass: [NSNumber class]] || (l = [n doubleValue]) <= 0.0) { [self release]; [NSException raise: NSInvalidArgumentException format: @"Bad boundaries"]; } c = boundsCount; waitBoundaries[--c] = l; while (c-- > 0) { NSTimeInterval t; n = [a objectAtIndex: c]; if (NO == [n isKindOfClass: [NSNumber class]] || (t = [n doubleValue]) <= 0.0 || t >= l) { [self release]; [NSException raise: NSInvalidArgumentException format: @"Bad boundaries"]; } waitBoundaries[c] = t; l = t; } } [classLock lock]; if (nil != NSMapGet(allFIFOs, n)) { [classLock unlock]; [self release]; [NSException raise: NSInvalidArgumentException format: @"GSFIFO ... name (%@) already in use", n]; } NSMapInsert(allFIFOs, name, self); [classLock unlock]; return self; } - (id) initWithCapacity: (uint32_t)c name: (NSString*)n { NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; NSString *key; uint16_t g; uint16_t t; BOOL mc; BOOL mp; NSArray *b; key = [NSString stringWithFormat: @"GSFIFOCapacity%@", n]; if ([defs integerForKey: key] > 0) { c = [defs integerForKey: key]; } key = [NSString stringWithFormat: @"GSFIFOGranularity%@", n]; g = [defs integerForKey: key]; key = [NSString stringWithFormat: @"GSFIFOTimeout%@", n]; t = [defs integerForKey: key]; key = [NSString stringWithFormat: @"GSFIFOSingleConsumer%@", n]; mc = (YES == [defs boolForKey: key]) ? NO : YES; key = [NSString stringWithFormat: @"GSFIFOSingleProducer%@", n]; mp = (YES == [defs boolForKey: key]) ? NO : YES; key = [NSString stringWithFormat: @"GSFIFOBoundaries%@", n]; b = [defs arrayForKey: key]; return [self initWithCapacity: c granularity: g timeout: t multiProducer: mp multiConsumer: mc boundaries: b name: n]; } - (id) initWithName: (NSString*)n { return [self initWithCapacity: 10000 name: n]; } - (unsigned) put: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block { NSTimeInterval sum; unsigned index; NSTimeInterval ti = 0.0; uint32_t old; uint32_t fib; if (0 == count) { return 0; } if (nil != condition) { return [self _cooperatingPut: buf count: count shouldBlock: block]; } if (nil == putThread) { putThread = [NSThread currentThread]; } if (_head - _tail < _capacity) { for (index = 0; index < count && _head - _tail < _capacity; index++) { _items[_head % _capacity] = buf[index]; _head++; } _putTrySuccess++; return index; } _putTryFailure++; fullCount++; if (NO == block) { return 0; } START old = 0; fib = 1; sum = 0.0; while (_head - _tail >= _capacity) { uint32_t tmp; NSTimeInterval dly; if (timeout > 0 && sum * 1000 > timeout) { ENDPUT [NSException raise: NSGenericException format: @"Timeout waiting for space in FIFO"]; } tmp = fib + old; old = fib; fib = tmp; if (granularity > 0 && fib > granularity) { fib = granularity; } dly = ((NSTimeInterval)fib) / 1000.0; [NSThread sleepForTimeInterval: dly]; sum += dly; } ENDPUT for (index = 0; index < count && _head - _tail < _capacity; index++) { _items[_head % _capacity] = buf[index]; _head++; } return index; } - (void) put: (void*)item { while (0 == [self put: &item count: 1 shouldBlock: YES]) ; } - (void) _getStats: (NSMutableString*)s { [s appendFormat: @" empty:%"PRIu64" failures:%"PRIu64" successes:%"PRIu64"\n", emptyCount, _getTryFailure, _getTrySuccess]; if (boundsCount > 0) { unsigned i; [s appendFormat: @" blocked total:%g average:%g perfail:%g\n", getWaitTotal, (_getTryFailure+_getTrySuccess > 0) ? getWaitTotal / (_getTryFailure+_getTrySuccess) : 0.0, (_getTryFailure > 0) ? getWaitTotal / _getTryFailure : 0.0]; for (i = 0; i < boundsCount; i++) { [s appendFormat: @" up to %g: %"PRIu64"\n", waitBoundaries[i], getWaitCounts[i]]; } [s appendFormat: @" above %g: %"PRIu64"\n", waitBoundaries[boundsCount-1], getWaitCounts[boundsCount]]; } } - (void) _putStats: (NSMutableString*)s { [s appendFormat: @" full:%"PRIu64" failures:%"PRIu64" successes:%"PRIu64"\n", fullCount, _putTryFailure, _putTrySuccess]; if (boundsCount > 0) { unsigned i; [s appendFormat: @" blocked total:%g average:%g perfail:%g\n", putWaitTotal, (_putTryFailure+_putTrySuccess > 0) ? putWaitTotal / (_putTryFailure+_putTrySuccess) : 0.0, (_putTryFailure > 0) ? putWaitTotal / _putTryFailure : 0.0]; for (i = 0; i < boundsCount; i++) { [s appendFormat: @" up to %g: %"PRIu64"\n", waitBoundaries[i], putWaitCounts[i]]; } [s appendFormat: @" above %g: %"PRIu64"\n", waitBoundaries[boundsCount-1], putWaitCounts[boundsCount]]; } } - (NSString*) stats { NSMutableString *s = [NSMutableString stringWithCapacity: 100]; [s appendFormat: @"%@ (%@) capacity:%"PRIu32" lockless:%c\n", [super description], name, _capacity, ((nil == condition) ? 'Y' : 'N')]; if (nil != condition || [NSThread currentThread] == getThread) { [condition lock]; [self _getStats: s]; [condition unlock]; } if (nil != condition || [NSThread currentThread] == putThread) { [condition lock]; [self _putStats: s]; [condition unlock]; } return s; } - (NSString*) statsGet { NSMutableString *s = [NSMutableString stringWithCapacity: 100]; if (nil == condition) { if ([NSThread currentThread] != getThread) { if (nil == getThread) { getThread = [NSThread currentThread]; } else { [NSException raise: NSInternalInconsistencyException format: @"[%@-%@] called from wrong thread for %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), name]; } } } [condition lock]; [s appendFormat: @"%@ (%@)\n", [super description], name]; [self _getStats: s]; [condition unlock]; return s; } - (NSString*) statsPut { NSMutableString *s = [NSMutableString stringWithCapacity: 100]; if (nil == condition) { if ([NSThread currentThread] != putThread) { if (nil == putThread) { putThread = [NSThread currentThread]; } else { [NSException raise: NSInternalInconsistencyException format: @"[%@-%@] called from wrong thread for %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), name]; } } } [condition lock]; [s appendFormat: @"%@ (%@)\n", [super description], name]; [self _putStats: s]; [condition unlock]; return s; } - (void*) tryGet { void *item = nil; [self get: &item count: 1 shouldBlock: NO]; return item; } - (BOOL) tryPut: (void*)item { if (1 == [self put: &item count: 1 shouldBlock: NO]) { return YES; } return NO; } @end Performance-0.5.0/GSTicker.h0000664000076500007650000001175611572443100015506 0ustar brains99brains99/** Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: Nov 2005 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. $Date: 2011-06-04 15:50:40 +0100 (Sat, 04 Jun 2011) $ $Revision: 33243 $ */ #ifndef INCLUDED_GSTicker_H #define INCLUDED_GSTicker_H @class NSDate; /** * Returns the timestamp of the most recent call to GSTickerTimeNow(). */ extern NSTimeInterval GSTickerTimeLast(); /** * Convenience function to provide timing information quickly.
* This returns the current date/time, and stores the value for use * by the GSTickerTimeLast() function. */ extern NSTimeInterval GSTickerTimeNow(); /** * This returns the timestamp from which GSTicker was first used. */ extern NSTimeInterval GSTickerTimeStart(); /** * A convenience method to return the current clock 'tick' ... which is * the current second based on the time we started. This does not * check the current time, but relies on GSTickerTimeLast() returning an * up to date value (so if you need an accurate tick, you should ensure * that GSTickerTimeNow() is called at least once a second).
* The returned value is always greater than zero, and is basically * calculated as (GSTickerTimeLast() - GSTickerTimeStart() + 1).
* In the event that the system clock is reset into the past, the value * of GSTickerTimeStart() is automatically adjusted to ensure that the * result of a call to GSTickerTimeTick() is never less than the result * of any earlier call to the function. */ extern unsigned GSTickerTimeTick(); @protocol GSTicker /** * Sent to tell observers that the ticker has noticed that a new * second has occurred. The tick argument is the user information * provided in when registering the observer.
* This message is sent to each registered observer when the timeout * for the thread occurs. It is not guaranteed to be called every * second ... other processing in the thread may delay it. */ - (void) newSecond: (id)userInfo; @end /** * Wrapper round the more efficient ticker functions ... this class * provides convenience methods to get NSDate objects, and also * provides support for registering observers ofr second by second * ticks. */ @interface GSTicker : NSObject { } /** * Calls GSTickerTimeLast() and returns the result as an NSDate. */ + (NSDate*) last; /** * A dummy method ... does nothing, but allows the GSTicker class itsself * to act as an observer of regular timeouts.
* Thus, you may register the class as its own observer in order to set * up a timer to ensure that date/time information is updated at the * start of every second. */ + (void) newSecond: (id)userInfo; /** * Calls GSTickerTimeNow() and returns the result as an NSDate. */ + (NSDate*) now; /** * Registers an object to receive a [(GSTicker)-newSecond:] message * at the start of every second.
* Also starts a timer in the current thread (unless one is already * running) to notify registered objects of new seconds.
* The observer and the userInfo are retained by the ticker.
* Adding an observer more than once has no effect.
* NB. You must not add or remove observers inside the callback routine. */ + (void) registerObserver: (id)anObject userInfo: (id)userInfo; /** * Returns the result of GSTickerTimeStart() as an NSDate. */ + (NSDate*) start; /** * Calls GSTickerTimeTick() and returns the result. */ + (unsigned) tick; /** * Unregisters an observer previously set in the current thread using * the +registerObserver:userInfo: method.
* If all observers in a thread are removed, the timer for the thread * is cancelled at the start of the next second. */ + (void) unregisterObserver: (id)anObject; /** * Calls GSTickerTimeNow(); */ + (void) update; /** * Calls GSTickerTimeLast() and returns the result as an NSDate. */ - (NSDate*) last; /** * See +newSecond: */ - (void) newSecond: (id)userInfo; /** * Calls GSTickerTimeNow() and returns the result as an NSDate. */ - (NSDate*) now; /** * Returns the result of GSTickerTimeStart() as an NSDate. */ - (NSDate*) start; /** * Calls GSTickerTimeTick() and returns the result. */ - (unsigned) tick; /** * Calls GSTickerTimeNow(); */ - (void) update; @end #endif Performance-0.5.0/GSLinkedList.h0000664000076500007650000002054312076312220016317 0ustar brains99brains99#if !defined(INCLUDED_GSLINKEDLIST) #define INCLUDED_GSLINKEDLIST 1 /** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: September 2010 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4) typedef unsigned int NSUInteger; #endif @class GSLinkedList; /** GSListLink provides simple doubly linked list functionality to * avoid the need to constantly re-invent it (as the OpenStep/Cocoa * APIs do not provide this). The emphasis is on speed of operation * so instance variables are directly accessible and inline functions * are provided to manipulate them (without error cehcking), but you * can/should avoid these direct access features unless speed is * really critical.
* A list may either be 'normal' ... (where the head/tail ends of * the list have a nil pointer to the previous/next link) or 'circular' * in which case the list is not terminated.
* The GSListLink item carries a minimal payload of a single item which * is retained by the link.
* The GSListLink owner is an optional pointer to an object which 'owns' * the link ... a GSLinkedList instance may use this to check link * ownership when manipulating links. */ @interface GSListLink : NSObject { @public GSListLink *next; // Not retained GSListLink *previous; // Not retained GSLinkedList *owner; // Not retained NSObject *item; } /** Initialise the receiver as a link for a circular linked list. */ - (id) initCircular; /** Return the item in the link. */ - (NSObject*) item; /** Return the next item in the list containing the receiver, * or nil if there are no more items. */ - (GSListLink*) next; /** Return the list which owns the receiver or nil if the receiver is * not in a list. */ - (GSLinkedList*) owner; /** Return the previous item in the list containing the receiver, * or nil if there are no more items. */ - (GSListLink*) previous; /** Set an item value by retaining it and releasing any previous value. */ - (void) setItem: (NSObject*)anItem; @end /** Inserts link after at in a circular list.
* Arguments must not be nil and link must not be in a list * (ie its next and previous pointers must point to itsself). */ static inline void GSLinkCircularInsertAfter(GSListLink *link, GSListLink *at) { link->next = at->next; link->previous = at; link->next->previous = link; link->previous->next = link; } /** Inserts link before at in a circular list.
* Arguments must not be nil and link must not be in a list * (ie its next and previous pointers must point to itsself). */ static inline void GSLinkCircularInsertBefore(GSListLink *link, GSListLink *at) { link->next = at; link->previous = at->previous; link->next->previous = link; link->previous->next = link; } /** Removes link from any list it is in.
* The link argument must not be nil. */ static inline void GSLinkCircularRemove(GSListLink *link) { link->next->previous = link->previous; link->previous->next = link->next; link->next = link->previous = link; } /** Inserts link after at in a normal list.
* Arguments must not be nil and link must not be in a list * (ie its next and previous pointers must both be nil). */ static inline void GSLinkInsertAfter(GSListLink *link, GSListLink *at) { if (nil != at->next) { at->next->previous = link; } link->previous = at; at->next = link; } /** Inserts link before at in a normal list.
* Arguments must not be nil and link must not be in a list * (ie its next and previous pointers must both be nil). */ static inline void GSLinkInsertBefore(GSListLink *link, GSListLink *at) { if (nil != at->previous) { at->previous->next = link; } link->next = at; at->previous = link; } /** Removes link from the list it is in.
* The link argument must not be nil. */ static inline void GSLinkRemove(GSListLink *link) { if (nil != link->next) { link->next->previous = link->previous; } if (nil != link->previous) { link->previous->next = link->next; } link->next = link->previous = nil; } /** GSLinkedList manages a list of GSListLink objects. */ @interface GSLinkedList : NSObject { @public GSListLink *head; // First link in the list. GSListLink *tail; // Last link in the list. NSUInteger count; // Number of links in the list. } /** Appends link at the end of the linked list managed by the receiver.
* Retains the link. */ - (void) append: (GSListLink*)link; /** Returns the number of links in the list. */ - (NSUInteger) count; /** Remove all links from the list and release them all. */ - (void) empty; /** Searches the linked list returning the link containing object or nil * if it is not found.
* The comparison is performed using the [NSObject-isEqual:] method * of object.
* If start is nil then the whole list is searched.
* This method will not find links containing nil items. */ - (GSListLink*) findEqual: (NSObject*)object from: (GSListLink*)start back: (BOOL)aFlag; /** Searches the linked list returning the link containing object or nil * if it is not found.
* If start is nil then the whole list is searched.
* A direct pointer comparison is used to determine equality. */ - (GSListLink*) findIdentical: (NSObject*)object from: (GSListLink*)start back: (BOOL)aFlag; /** Returns the first link in the list. */ - (GSListLink*) head; /** Inserts other immediately after the receiver.
* Retains the link. */ - (void) insert: (GSListLink*)link after: (GSListLink*)at; /** Inserts other immediately before the receiver.
* Retains the link. */ - (void) insert: (GSListLink*)link before: (GSListLink*)at; /** Removes the link from the receiver.
* releases the link. */ - (void) removeLink: (GSListLink*)link; /** Returns the last link in the list. */ - (GSListLink*) tail; @end /** Searches from list to the end looking for the first link containing * object (as determined by using object's [NSObject-isEqual:] method).
* If from is nil the list is search from head or tail as appropriate * to the direction in which it is searched. */ extern GSListLink* GSLinkedListFindEqual(NSObject *object, GSLinkedList *list, GSListLink *from, BOOL back); /** Searches from list to the end looking for the first link containing * object (as determined by direct pointer comparison).
* If from is nil the list is search from head or tail as appropriate * to the direction in which it is searched. */ extern GSListLink* GSLinkedListFindIdentical(NSObject *object, GSLinkedList *list, GSListLink *from, BOOL back); /** Inserts link immediately after at.
* Updates the head, tail and count variables of list.
* Does not retain link. */ extern void GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at); /** Inserts link immediately before at.
* Updates the head, tail and count variables of list.
* Does not retain link. */ extern void GSLinkedListInsertBefore(GSListLink *link, GSLinkedList *list, GSListLink *at); /** Moves the link to the head of the list if it is not already there. */ extern void GSLinkedListMoveToHead(GSListLink *link, GSLinkedList *list); /** Moves the link to the tail of the list if it is not already there. */ extern void GSLinkedListMoveToTail(GSListLink *link, GSLinkedList *list); /** Removes link from the list.
* Updates the head, tail and count variables of list.
* Does not release link. */ extern void GSLinkedListRemove(GSListLink *link, GSLinkedList *list); #endif Performance-0.5.0/GNUmakefile.wrapper.objc.preamble0000664000076500007650000000014110377047451022112 0ustar brains99brains99ADDITIONAL_INCLUDE_DIRS += -I../../../Headers/ ADDITIONAL_LIB_DIRS += -L../../$(GNUSTEP_OBJ_DIR) Performance-0.5.0/GSSkipMutableArray.m0000664000076500007650000001706312205072442017507 0ustar brains99brains99/** Copyright (C) 2006 Free Software Foundation, Inc. Written by: Matt Rice Date: 2006 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #import #import #import #import #import "GSSkipMutableArray.h" #import "GSIndexedSkipList.h" #if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4) typedef unsigned int NSUInteger; #endif static Class abstractClass = 0; static Class concreteClass = 0; @interface GSSkipMutableArray : NSMutableArray @end @interface GSConcreteSkipArray : GSSkipMutableArray { GSISList l; } - (GSISList) _list; @end @implementation NSMutableArray (GSSkipMutableArray) + (NSMutableArray*) skipArray { return [GSSkipMutableArray array]; } @end @implementation GSSkipMutableArray + (id) allocWithZone: (NSZone*)z { if (self == abstractClass) { return [concreteClass allocWithZone: z]; } return [super allocWithZone: z]; } + (void) initialize { if (abstractClass == 0) { abstractClass = [GSSkipMutableArray class]; concreteClass = [GSConcreteSkipArray class]; } } @end @interface GSConcreteSkipArrayEnumerator : NSEnumerator { GSISLNode node; } @end @implementation GSConcreteSkipArrayEnumerator - (id) initWithArray: (NSArray *)arr { if (![arr isKindOfClass: [GSConcreteSkipArray class]]) { [[NSException exceptionWithName: NSInternalInconsistencyException reason: @"not a GSConcreteSkipArray" userInfo: nil] raise]; } self = [super init]; node = [(GSConcreteSkipArray *)arr _list]->header->forward[0].next; return self; } - (id) nextObject { id foo = node->value; if (node == GSISLNil) return nil; node = node->forward[0].next; return foo; } @end @implementation GSConcreteSkipArray - (GSISList) _list { return l; } - (void) _raiseRangeExceptionWithIndex: (NSUInteger)index from: (SEL)sel { NSDictionary *info; NSException *exception; NSString *reason; info = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: index], @"Index", [NSNumber numberWithUnsignedInt: l->count], @"Count", self, @"Array", nil, nil]; reason = [NSString stringWithFormat: @"Index %"PRIuPTR @" is out of range %d (in '%@')", index, l->count, NSStringFromSelector(sel)]; exception = [NSException exceptionWithName: NSRangeException reason: reason userInfo: info]; [exception raise]; } + (void) initialize { GSISLInitialize(); } - (id) initWithObjects: (const id[])objects count: (NSUInteger) count { int i; self = [super init]; if (!self) return nil; l = GSISLInitList([self zone]); for (i = 0; i < count; i++) { GSISLInsertItemAtIndex(l, [objects[i] retain], i); } return self; } - (id) init { self = [super init]; if (!self) return nil; l = GSISLInitList([self zone]); return self; } - (void) dealloc { GSISLNode p,q; p = l->header->forward[0].next; while (p != GSISLNil) { q = p->forward[0].next; [p->value release]; NSZoneFree(l->zone,p); p = q; } NSZoneFree(l->zone, l->header); NSZoneFree(l->zone, l); [super dealloc]; } - (void) insertObject: (id)object atIndex: (NSUInteger)index { if (index > l->count) { [self _raiseRangeExceptionWithIndex: index from: _cmd]; } GSISLInsertItemAtIndex(l, [object retain], index); } - (id) objectAtIndex: (NSUInteger)index { if (index >= l->count) { [self _raiseRangeExceptionWithIndex: index from: _cmd]; } return GSISLItemAtIndex(l, index); } - (void) removeObjectAtIndex: (NSUInteger)index { if (index >= l->count) { [self _raiseRangeExceptionWithIndex: index from: _cmd]; } [GSISLRemoveItemAtIndex(l, index) release]; } - (void) addObject: (id)obj { GSISLInsertItemAtIndex(l, [obj retain], l->count); } - (NSUInteger) count { return l->count; } - (void) replaceObjectAtIndex: (NSUInteger)index withObject: (id)obj { [GSISLReplaceItemAtIndex(l, [obj retain], index) release]; } - (NSEnumerator*) objectEnumerator { id e; e = [GSConcreteSkipArrayEnumerator allocWithZone: NSDefaultMallocZone()]; e = [e initWithArray: self]; return [e autorelease]; } /* returns an in an NSString suitable for running through graphviz, * with the graph named 'graphName' */ - (NSString *) _makeGraphOfInternalLayoutNamed: (NSString *)graphName { GSISLNode p; NSUInteger k, i; NSMutableDictionary *values; NSMutableArray *edges; NSMutableString *graph; NSArray *tmp; graph = [[NSMutableString alloc] initWithCapacity: 1024]; [graph appendString: [NSString stringWithFormat: @"digraph %@ {\n", graphName]]; [graph appendString: @"graph [rankdir = LR];\n"]; [graph appendString: @"node [shape = record];\n"]; values = [[NSMutableDictionary alloc] init]; edges = [[NSMutableArray alloc] init]; [values setObject: [NSMutableString stringWithFormat: @"\"%p\" [label = \"%p (NIL) |{ 0 | }", GSISLNil, GSISLNil] forKey: [NSString stringWithFormat: @"%p", GSISLNil]]; for (k = 0; k < l->level + 1; k++) { for (p = l->header; p != GSISLNil; p = p->forward[k].next) { NSString *value; NSMutableString *foo; value = [NSString stringWithFormat: @"%p", p]; foo = [values objectForKey: value]; if (foo == nil) { foo = [[NSMutableString alloc] init]; [foo appendString: [NSString stringWithFormat: @"\"%p\" [label = \"%p%@ |{ %i | }", p, p, p == l->header ? @"(HEADER)" : @"", k, p->forward[k].delta, k]]; if (p != GSISLNil) [edges addObject: [NSString stringWithFormat: @"\"%p\": forward%"PRIuPTR" -> \"%p\": delta%"PRIuPTR";\n", p, k, p->forward[k].next, p->forward[k].next == GSISLNil ? 0 : k]]; [values setObject: foo forKey: value]; [foo release]; } else { [foo appendString: [NSString stringWithFormat: @"|{ %i | }", k, p->forward[k].delta, k]]; if (p != GSISLNil) [edges addObject: [NSString stringWithFormat: @"\"%p\": forward%"PRIuPTR" -> \"%p\": delta%"PRIuPTR";\n", p, k, p->forward[k].next, p->forward[k].next == GSISLNil ? 0 : k]]; [values setObject: foo forKey: value]; } } } tmp = [values allKeys]; for (i = 0; i < [tmp count]; i++) { [graph appendString: [values objectForKey: [tmp objectAtIndex: i]]]; [graph appendString: @"\"];\n"]; } for (i = 0; i < [edges count]; i++) { [graph appendString: [edges objectAtIndex: i]]; } [graph appendString: @"}\n"]; [values release]; [edges release]; return [graph autorelease]; } @end Performance-0.5.0/GSFIFO.h0000664000076500007650000002567612203423157015020 0ustar brains99brains99#if !defined(INCLUDED_GSFIFO) #define INCLUDED_GSFIFO 1 /** Copyright (C) 2011 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: April 2011 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import @class NSArray; @class NSCondition; @class NSNumber; @class NSString; @class NSThread; /** GSFIFO manages a first-in-first-out queue of items.
* Items in the queue are NOT retained objects ... memory management * is not the job of this class and it is not intended to be treated * as a 'collection', rather its role is intended to be that of an * inter-thread coordination mechanism.
* Instances of the GSFIFO class are intended to support the producer-consumer * model of processing. The ideal example is that of a production line, * where you have a stream of items to be processed and while that processing * can be broken down into separate stages, they must be done in a particular * order. The FIFO is used as the link betwen those stages, ensuring that * the required ordering is maintained even when separate threads handle * each stage.
* Where there is a single producer and a single consumer thread, a fast * lock-free algorthm is used to get/pu items from the FIFO.
* To minimise the overheads of using the FIFO, we provide inline functions * to support the addition of items in the single producer thread case and to * support the removal of items in the single consumer thread case. When * operating that way, the overhead of using the FIFO is only a few CPU cycles * and it becomes reasonable to split sequentional processing into a long * series of small operations each handled by a separate thread (making * effective use of a multi-cpu machine).
* The FIFO may also be useful where you don't have a strictly sequential * process to manage, but some parts need to be sequential ... in these * cases it may make sense to have multiple consumers and/or producers. * In these cases, some locking is required and the use of the inline * functions is not allowed (you must call the -get and -put: methods.
* It is recommended that you create FIFOs using the -initWithName: method * so that you can easily use the NSUserDefaults system to adjust their * configurations to tests/tweak performance. */ @interface GSFIFO : NSObject { @public /* While the following instance variables are nominally public, they are in * fact only intended to be used by the provided inline functions ... you * should not access them directly in your own code. */ volatile uint64_t _head; volatile uint64_t _tail; uint64_t _getTryFailure; uint64_t _getTrySuccess; uint64_t _putTryFailure; uint64_t _putTrySuccess; void **_items; uint32_t _capacity; @private uint32_t boundsCount; uint16_t granularity; uint16_t timeout; uint64_t fullCount; // Total waits for full FIFO uint64_t emptyCount; // Total waits for empty FIFO NSCondition *condition; NSString *name; NSTimeInterval getWaitTotal; // Total time waiting for gets NSTimeInterval putWaitTotal; // Total time waiting for puts NSTimeInterval *waitBoundaries; // Stats boundaries uint64_t *getWaitCounts; // Waits for gets by time uint64_t *putWaitCounts; // Waits for puts by time NSThread *getThread; // Single consumer thread NSThread *putThread; // Single producer thread } /** Return statistics for all current GSFIFO instances.
* Statistics for FIFOs which are configued to be lock-free are empty * (listing the name only) except where we can safely obtain get or put * stats because the FIFOs consumer/producer thread is the same as the * current thread. */ + (NSString*) stats; /** Returns the approximate number of items in the FIFO. */ - (NSUInteger) count; /** Reads up to count items from the FIFO into buf. * If block is YES, this blocks if necessary until at least one item * is available, and raises an exception if the FIFO is configured * with a timeout and it is exceeded.
* Returns the number of items actually read. */ - (unsigned) get: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block; /** Gets the next item from the FIFO, blocking if necessary until an * item is available. Raises an exception if the FIFO is configured * with a timeout and it is exceeded.
* Implemented using -get:count:shouldBlock: */ - (void*) get; /** * Initialises the receiver with the specified capacity (buffer size).
* The capacity must lie in the range from one to a million, othewrwise * the receiver is deallocated and this method returns nil.
* If the granularity value is non-zero, it is treated as the maximum time * in milliseconds for which a -get or -put: operation will pause between * successive attempts.
* If the timeout value is non-zero, it is treated as the total time in * milliseconds for which a -get or -put: operation may block, and a * longer delay will cause those methods to raise an exception.
* If the multiProducer or multiConsumer flag is YES, the FIFO is * configured to support multiple producer/consumer threads using locking.
* The boundaries array is an ordered list of NSNumber objects containing * time intervals found boundaries of bands into which to categorise wait * time stats. Any wait whose duration is less than the interval specified * in the Nth element is counted in the stat's for the Nth band. * If this is nil, a default set of bundaries is used. If it is an empty * array then no time based stats are recorded.
* The name string is a unique identifier for the receiver and is used when * printing diagnostics and statistics. If an instance with the same name * already exists, the receiveris deallocated and an exception is raised. */ - (id) initWithCapacity: (uint32_t)c granularity: (uint16_t)g timeout: (uint16_t)t multiProducer: (BOOL)mp multiConsumer: (BOOL)mc boundaries: (NSArray*)a name: (NSString*)n; /** Initialises the receiver as a multi-producer, multi-consumer FIFO with * no timeout and with default stats gathering enabled.
* However, these values (including the supplied capacity) may be overridden * as specified in -initWithName: */ - (id) initWithCapacity: (uint32_t)c name: (NSString*)n; /** Initialises the receiver using the specified name and obtaining other * details from the NSUserDefaults system using defaults keys where 'NNN' * is the supplied name.
* The GSFIFOCapacityNNN default specifies the capacity for the FIFO, and * if missing a capacity of 1000 is assumed.
* The GSFIFOGranularityNNN integer is zero by default.
* The GSFIFOTimeoutNNN integer is zero by default.
* The GSFIFOSingleConsumerNNN boolean is NO by default.
* The GSFIFOSingleProducerNNN boolean is NO by default.
* The GSFIFOBoundariesNNN array is missing by default.
*/ - (id) initWithName: (NSString*)n; /** Writes up to count items from buf into the FIFO. * If block is YES, this blocks if necessary until at least one item * can be written, and raises an exception if the FIFO is configured * with a timeout and it is exceeded.
* Returns the number of items actually written. */ - (unsigned) put: (void**)buf count: (unsigned)count shouldBlock: (BOOL)block; /** Adds an item to the FIFO, blocking if necessary until there is * space in the buffer. Raises an exception if the FIFO is configured * with a timeout and it is exceeded.br /> * Implemented using -put:count:shouldBlock: */ - (void) put: (void*)item; /** Return any available statistics for the receiver.
*/ - (NSString*) stats; /** Return statistics on get operations for the receiver.
* NB. If the recever is not configured for multiple consumers, * this method may only be called from the single consumer thread. */ - (NSString*) statsGet; /** Return statistics on put operations for the receiver.
* NB. If the recever is not configured for multiple producers, * this method may only be called from the single producer thread. */ - (NSString*) statsPut; /** Checks the FIFO and returns the first available item or NULL if the * FIFO is empty.
* Implemented using -get:count:shouldBlock: */ - (void*) tryGet; /** Attempts to put an item into the FIFO, returning YES on success or NO * if the FIFO is full.
* Implemented using -put:count:shouldBlock: */ - (BOOL) tryPut: (void*)item; @end /** Function to efficiently get an item from a fast FIFO.
* Returns NULL if the FIFO is empty.
* Warning ... only for use if the FIFO is NOT configured for multiple * consumers. */ static inline void* GSGetFastNonBlockingFIFO(GSFIFO *receiver) { if (receiver->_head > receiver->_tail) { void *item; item = receiver->_items[receiver->_tail % receiver->_capacity]; receiver->_tail++; receiver->_getTrySuccess++; return item; } receiver->_getTryFailure++; return NULL; } /** Function to efficiently get an item from a fast FIFO, blocking if * necessary until an item is available or the timeout occurs.
* Warning ... only for use if the FIFO is NOT configured for multiple * consumers. */ static inline void* GSGetFastFIFO(GSFIFO *receiver) { void *item = GSGetFastNonBlockingFIFO(receiver); if (0 == item) { item = [receiver get]; } return item; } /** Function to efficiently put an item to a fast FIFO.
* Returns YES on success, NO on failure (FIFO is full).
* Warning ... only for use if the FIFO is NOT configured for multiple * producers. */ static inline BOOL GSPutFastNonBlockingFIFO(GSFIFO *receiver, void *item) { if (receiver->_head - receiver->_tail < receiver->_capacity) { receiver->_items[receiver->_head % receiver->_capacity] = item; receiver->_head++; receiver->_putTrySuccess++; return YES; } receiver->_putTryFailure++; return NO; } /** Function to efficiently put an item to a fast FIFO, blocking if * necessary until there is space in the FIFO or until the timeout * occurs.
* Warning ... only for use if the FIFO is NOT configured for multiple * producers. */ static inline void GSPutFastFIFO(GSFIFO *receiver, void *item) { if (NO == GSPutFastNonBlockingFIFO(receiver, item)) { [receiver put: item]; } } #endif Performance-0.5.0/GSIndexedSkipList.m0000664000076500007650000001751711533671302017342 0ustar brains99brains99/** Copyright (C) 2006 Free Software Foundation, Inc. Written by: Matt Rice Date: 2006 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #include #include #include #include #include #import "GSIndexedSkipList.h" /* This no longer seems to be needed/correct #if defined(__MINGW32__) #include // declares rand() #endif */ #define PrettyErr(x) do { fprintf(stderr, "%s:%i: %s\n",__FILE__, __LINE__, x); exit(EXIT_FAILURE); } while (0) GSISLNode GSISLNil; GSISLNode GSISLNewNodeOfLevel(int l, NSZone *zone) { GSISLNode ret = (GSISLNode) NSZoneMalloc(zone, sizeof(struct GSISLNode_t) + ((l) * sizeof(struct GSISLForward_t))); if (ret == NULL) { PrettyErr(strerror(errno)); } do { ret->forward[l].delta = 0; } while (--l >= 0); return ret; } void GSISLInitialize() { if (GSISLNil != NULL) return; GSISLNil = GSISLNewNodeOfLevel(0, NSDefaultMallocZone()); GSISLNil->forward[0].delta = UINT_MAX; GSISLNil->value = nil; GSISLNil->forward[0].next = NULL; } GSISList GSISLInitList(NSZone *zone) { GSISList l; int i; l = (GSISList)NSZoneMalloc(zone, sizeof(struct GSIndexedSkipList)); if (l == NULL) { PrettyErr(strerror(errno)); } l->zone = zone; l->level = 0; l->count = 0; l->header = GSISLNewNodeOfLevel(GSISLMaxNumberOfLevels, l->zone); l->header->value = nil; for (i=0; i < GSISLMaxNumberOfLevels; i++) { l->header->forward[0].delta = 0; l->header->forward[0].next = GSISLNil; #if GSISL_CACHE_OPT l->indexCache[i] = 0; l->nodeCache[i] = l->header; #endif } return(l); } void GSISLFreeList(GSISList l) { GSISLNode p,q; p = l->header; do { q = p->forward[0].next; NSZoneFree(l->zone,p); p = q; } while (p != GSISLNil); NSZoneFree(l->zone, l); }; int GSISLRandomLevel() { int level = 0; static int p = RAND_MAX / 4; while (rand() < p && level < GSISLMaxLevel) { level++; } return level; } void GSISLInsertItemAtIndex(GSISList l, GSISLValueType value, unsigned index) { int k, i; GSISLNode update[GSISLMaxNumberOfLevels]; unsigned updateIndexes[GSISLMaxNumberOfLevels]; GSISLNode p,q; unsigned depth; depth = 0; k = l->level; #if GSISL_CACHE_OPT if (l->indexCache[k] < index) { p = l->nodeCache[k]; depth = l->indexCache[k]; } else { p = l->header; } #else p = l->header; #endif do { while (q = p->forward[k].next, q != GSISLNil && depth + p->forward[k].delta < index + 1) { depth += p->forward[k].delta; p = q; } updateIndexes[k] = depth; update[k] = p; } while(--k >= 0); k = GSISLRandomLevel(); q = GSISLNewNodeOfLevel(k, l->zone); if (k > l->level) { /* we are creating a new level that looks like * header ---> new node ---> tail */ k = l->level; l->level++; #if GSISL_CACHE_OPT l->nodeCache[l->level] = l->header; l->indexCache[l->level] = 0; #endif l->header->forward[l->level].delta = index + 1; l->header->forward[l->level].next = q; q->forward[l->level].delta = 0; q->forward[l->level].next = GSISLNil; } else { /* if there are higher nodes than this nodes level. increment the deltas in the update, as we are inserting a node inbetween their starting point and their ending */ for (i = k + 1; i <= l->level; i++) { if (update[i]->forward[i].delta != 0) update[i]->forward[i].delta++; #if GSISL_CACHE_OPT l->nodeCache[i] = update[i]; l->indexCache[i] = updateIndexes[i]; #endif } } q->value = value; do { /* update from the nodes highest level down to level 0 * on all the levels already existing in the list */ p = update[k]; if (p->forward[k].delta) q->forward[k].delta = updateIndexes[k] + p->forward[k].delta - depth; p->forward[k].delta = depth + 1 - updateIndexes[k]; q->forward[k].next = p->forward[k].next; p->forward[k].next = q; #if GSISL_CACHE_OPT l->indexCache[k] = updateIndexes[k]; l->nodeCache[k] = update[k]; #endif } while(--k >= 0); l->count++; } GSISLValueType GSISLRemoveItemAtIndex(GSISList l, unsigned index) { int k,m; GSISLNode update[GSISLMaxNumberOfLevels]; unsigned updateIndexes[GSISLMaxNumberOfLevels]; GSISLNode p,q; unsigned depth = 0; GSISLValueType ret; k = m = l->level; #if GSISL_CACHE_OPT if (l->indexCache[k] < index) { p = l->nodeCache[k]; depth = l->indexCache[k]; } else { p = l->header; } #else p = l->header; #endif do { while (q = p->forward[k].next, q != GSISLNil && depth + p->forward[k].delta < index + 1) { depth += p->forward[k].delta; p = q; } update[k] = p; updateIndexes[k] = depth; } while(--k >= 0); for (k = 0; k <= m; k++) { p = update[k]; #if GSISL_CACHE_OPT l->indexCache[k] = updateIndexes[k]; l->nodeCache[k] = update[k]; #endif if (p->forward[k].next == q) { p->forward[k].delta = (q->forward[k].next == GSISLNil) ? 0 : p->forward[k].delta + q->forward[k].delta - 1; p->forward[k].next = q->forward[k].next; } else if (p->forward[k].next != GSISLNil) { p->forward[k].delta--; } else { p->forward[k].delta = 0; } } ret = q->value; NSZoneFree(l->zone,q); /* if header points to nil, decrement the list level */ while (l->header->forward[m].next == GSISLNil && m > 0 ) { l->header->forward[m].delta = 0; m--; } l->level = m; l->count--; return ret; } GSISLValueType GSISLItemAtIndex(GSISList l, unsigned index) { int k; unsigned depth = 0; GSISLNode p,q; k = l->level; #if GSISL_CACHE_OPT if (l->indexCache[k] < index) { p = l->nodeCache[k]; depth = l->indexCache[k]; } else { p = l->header; } #else p = l->header; #endif do { while (q = p->forward[k].next, q != GSISLNil && depth + p->forward[k].delta < index + 1) { depth += p->forward[k].delta; p = q; } #if GSISL_CACHE_OPT l->nodeCache[k] = p; l->indexCache[k] = depth; #endif } while(--k >= 0); return(q->value); } GSISLValueType GSISLReplaceItemAtIndex(GSISList l, GSISLValueType newVal, unsigned index) { int k; unsigned depth = 0; GSISLNode p,q; GSISLValueType ret; k = l->level; #if GSISL_CACHE_OPT if (l->indexCache[k] < index) { p = l->nodeCache[k]; depth = l->indexCache[k]; } else { p = l->header; } #else p = l->header; #endif do { while (q = p->forward[k].next, q != GSISLNil && depth + p->forward[k].delta < index + 1) { depth += p->forward[k].delta; p = q; } #if GSISL_CACHE_OPT l->indexCache[k] = depth; l->nodeCache[k] = p; #endif } while(--k >= 0); ret = q->value; q->value = newVal; return ret; } Performance-0.5.0/GSThroughput.h0000664000076500007650000002354111574677267016462 0ustar brains99brains99/** Copyright (C) 2005-2008 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: October 2005 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. $Date: 2011-06-11 15:41:59 +0100 (Sat, 11 Jun 2011) $ $Revision: 33278 $ */ #ifndef INCLUDED_GSThroughput_H #define INCLUDED_GSThroughput_H #import @class NSArray; @class NSString; extern NSString * const GSThroughputNotification; extern NSString * const GSThroughputCountKey; extern NSString * const GSThroughputMaximumKey; extern NSString * const GSThroughputMinimumKey; extern NSString * const GSThroughputTimeKey; extern NSString * const GSThroughputTotalKey; /** *

The GSThroughput class is used maintain statistics about the number * of events or the duration of operations in your software. *

*

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). You are responsible for ensuring * that a run loop runs in each thread in which you use an instance, so that * stats can be updated for that thread every second. *

*

You create an instance of the class for each event/operation that you * are interested in monitoring, and you call the -add: or -addDuration: * method to record events.
* For duration logging, you may also use the -startDuration: and * -endDuration methods to handle adding of the amount of time taken between * the two calls. *

*

To dump a record of the gathered statistics, you may call the * -description method of an instance or the class +description method * to dump statistics for all instances in the current thread.
* If you need to gather a record for all the threads you use, you must * generate a dump in each thread and combine the results. *

*

To be notified of statistics at the end of each minute, you may call * the -enableNotifications: method for an instance. The notifications are * generated in the thread that instance belongs to. *

*/ @interface GSThroughput : NSObject { void *_data; } /** * Return all the current throughput measuring objects in the current thread. * NB. This does not return instances from other threads. */ + (NSArray*) allInstances; /** * Return a report on all GSThroughput instances in the current thread...
* This calls the [GSThroughput-description] method of the individual instances * to get a report on each one.
* The results are ordered alphabetically by name of the instances (an * instance without a name is treated as having an empty string as a name). */ + (NSString*) description; /** * Instructs the monitoring system to use a timer at the start of each second * for keeping its idea of the current time up to date. This timer is used * to call the +tick method in the current thread.
* Passing a value of NO for aFlag will turn off the timer for the current * thread.
* For the timer to work, the thread's runloop must be running.
* Keeping the notion of the current time up to date is important for * instances configured to record stats broken down over a number of periods, * since the periodic breakdown must be adjusted each second. */ + (void) setTick: (BOOL)aFlag; /** * Updates the monitoring system's notion of the current time for all * instances associated with the current thread.
* This should be called at the start of each second (or more often) if * you want an accurate breakdown of monitoring by the second.
* If you don't want to call this yourself, you can call +setTick: to * have it called automatically.
* If you are not using any instances of the class configured to maintain * a breakdown of stats by periods, you do not need to call this method. */ + (void) tick; /** * Add to the count of the number of transactions for the receiver.
* You may use this method only if the receiver was initialised with * duration logging turned off. */ - (void) add: (unsigned)count; /** * Adds a record for multiple events of the specified * total duration.
* This is useful where you know a lot of similar events have completed * in a particular period of time, but can't afford to measure the * duration of the individual events because the timing overheads * would be too great.
* You may use this method only if the receiver was initialised with * duration logging turned on. */ - (void) add: (unsigned)count duration: (NSTimeInterval)length; /** * Adds a record for a single event of the specified duration.
* You may use this method only if the receiver was initialised with * duration logging turned on. */ - (void) addDuration: (NSTimeInterval)length; /** * Returns a string describing the status of the receiver.
* For an instance configured to maintain a periodic breakdown of stats, * this reports information for the current second, all seconds in the * current minute, all minutes in the current period, and all periods * in the configured number of periods.
* For an instance configured with no periodic breakdown, this produces * a short summary of the total count of events and, where durations are used, * the maximum, minimum and average duration of events. */ - (NSString*) description; /** Sets a flag to say whether the receiver will send GSThroughputNotification * at the end of each minute to provide information about statistics.
* The method returnes the previous setting. The initial setting is NO.
* The notification object is the reciever, and the user info dictionary * contains some or all of the following keys depending on how the receiver * was configured: * * GSThroughputCountKey * The number of events recorded (unsigned integer number) * GSThroughputMaximumKey * The maximum event duration (double floating point number) * GSThroughputMinimumKey * The minimum event duration (double floating point number) * or -1.0 if no events occurred during the minute. * GSThroughputTimeKey * The time of the start of the minute (an NSDate) * GSThroughputTotalKey * The sum of event durations (double floating point number) * */ - (BOOL) enableNotifications: (BOOL)flag; /** * Ends duration recording for the current event started by a matching * call to the -startDuration: method.
* Calls to this method without a matching call to -startDuration: are * quietly ignored. This is useful if you wish to time a function or * method by starting/ending timing before/after calling it, but also * want the function/method to be able to end timing of itsself before * it calls another function/method. */ - (void) endDuration; /** * Acts like -endDuration but records the duration as a total for * count events (if count is zero then this ends the interval started * by the corresponding -startDuration: call, but nothing is logged).
* This can be used when recording multiple events where the overhead of * timing each event individually would be too great. */ - (void) endDuration: (unsigned)count; /** * Initialises the receiver for duration logging (in the current thread only) * for fifteen minute periods over the last twentyfour hours. */ - (id) init; /** *

Initialises the receiver to maintain stats (for the current thread only) * over a particular time range, specifying whether duration statistics are * to be maintained, or just event/transaction counts. *

*

If the specified numberOfPeriods or minutesPerPeriod is zero, only a * running total is maintained rather than a per-second breakdown for the * current minute and per minute breakdown for the current period and * period breakdown for the number of periods. *

*

If all instances in a thread are initialised with numberOfPeriods or * minutesPerPeriod of zero, the +tick method does not need to be called and * +setTick: should not be used. *

*/ - (id) initWithDurations: (BOOL)aFlag forPeriods: (unsigned)numberOfPeriods ofLength: (unsigned)minutesPerPeriod; /** * Return the name of this instance (as set using -setName:).
* This is used in the -description method and for ordering instances * in the +description method. */ - (NSString*) name; /** * Sets the name of this instance. */ - (void) setName: (NSString*)name; /** * Starts recording the duration of an event. This must be followed by * a matching call to the -endDuration method.
* 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.
* You may use this method only if the receiver was initialised with * duration logging turned on.
* Use of this method if the reciever does not support duration logging * or if the method has already been called without a matching call to * -endDuration will cause an exception to be raised. */ - (void) startDuration: (NSString*)name; @end #endif Performance-0.5.0/GNUmakefile.preamble0000664000076500007650000000174510377047451017532 0ustar brains99brains99# # Makefile.preamble # # Project specific makefile variables, and additional # # Do not put any Makefile rules in this file, instead they should # be put into Makefile.postamble. # # # Flags dealing with compiling and linking # # Additional flags to pass to the preprocessor # ADDITIONAL_CPPFLAGS += # Additional flags to pass to the Objective-C compiler # ADDITIONAL_OBJCFLAGS += # Additional flags to pass to the C compiler # ADDITIONAL_CFLAGS += # Additional include directories the compiler should search # ADDITIONAL_INCLUDE_DIRS += # Additional LDFLAGS to pass to the linker # ADDITIONAL_LDFLAGS += # Additional library directories the linker should search # ADDITIONAL_LIB_DIRS += # Additional libraries when linking tools # ADDITIONAL_TOOL_LIBS += # Additional libraries when linking applications # ADDITIONAL_GUI_LIBS += # # Flags dealing with installing and uninstalling # # Additional directories to be created during installation # ADDITIONAL_INSTALL_DIRS += Performance-0.5.0/GSIndexedSkipList.h0000664000076500007650000000442310672502772017334 0ustar brains99brains99/** Copyright (C) 2006 Free Software Foundation, Inc. Written by: Matt Rice Date: 2006 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #define GSISLMaxNumberOfLevels 16 #define GSISLMaxLevel 15 /* * attempt at caching the previously looked up index * to reduce the search time required when wantedIndex > previousIndex * this didn't seem to provide any benefit, actually negatively impacting * performance though it was never thoroughly tested it */ #define GSISL_CACHE_OPT 0 typedef id GSISLValueType; typedef struct GSISLNode_t *GSISLNode; extern GSISLNode GSISLNil; struct GSISLForward_t { unsigned delta; GSISLNode next; }; struct GSISLNode_t { GSISLValueType value; struct GSISLForward_t forward[1]; }; typedef struct GSIndexedSkipList { int level; /* Maximum level of the list (1 more than the number of levels in the list) */ GSISLNode header; /* pointer to header */ unsigned count; NSZone *zone; #if GSISL_CACHE_OPT unsigned indexCache[GSISLMaxNumberOfLevels]; GSISLNode nodeCache[GSISLMaxNumberOfLevels]; #endif } * GSISList; void GSISLInitialize(); void GSISLFreeList(GSISList l); GSISList GSISLInitList(NSZone *zone); void GSISLInsertItemAtIndex(GSISList l, GSISLValueType value, unsigned index); GSISLValueType GSISLItemAtIndex(GSISList l, unsigned index); GSISLValueType GSISLRemoveItemAtIndex(GSISList l, unsigned index); GSISLValueType GSISLReplaceItemAtIndex(GSISList l, GSISLValueType newVal, unsigned index); Performance-0.5.0/GSSkipMutableArray.h0000664000076500007650000000600310672502772017504 0ustar brains99brains99/** Copyright (C) 2006 Free Software Foundation, Inc. Written by: Matt Rice Date: 2006 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import /**

An NSMutableArray category to provide an NSMutableArray instance which uses a skip list variant for it's underlying data structure.

While a skip list is typically sorted and represents a dictionary. the indexed skip list is sorted by index and maintains deltas to represent the distance between linked nodes.

The underlying data structure looks much like the figure below:

index -> HEAD 1 2 3 4 5 6 TAIL 5| ---------------------> # ------> # 3| -----------> 2 ------> # ------> # 1| -> 1 -> 1 -> 1 -> 1 -> 1 -> # -> #

Where the numbers represent how many indexes it is to the next node of the appropriate level. The bottom level always points to the next node.

Finding a specific index starts at the top level, until the current depth + the next nodes delta is larger than wanted index, then it goes down 1 level, and repeats until it finds the wanted index.

Addition and removal of indexes requires an update of the deltas of nodes which begin before, and end after the wanted index, these are the places where it goes down a level.

The rationale behind it was where a linked list based mutable array will quickly add and remove elements, it may perform poorly at accessing any random index (because it must traverse the entire list to get to the index).

While a C array based mutable array will perform good at random index access it may perform poorly at adding and removing indexes (because it must move all items after the altered index).

While a GSSkipMutableArray may not outperform a linked list or a C array based mutable array at their specific strengths, it attempts to not suffer from either of their weaknesses, at the cost of additional memory overhead.

*/ @interface NSMutableArray (GSSkipMutableArray) /** * Creates and returns an autoreleased NSMutableArray implemented as a * skip list for rapid insertion/deletion within very large arrays. */ + (NSMutableArray*) skipArray; @end Performance-0.5.0/GNUmakefile.postamble0000664000076500007650000000152610377047451017726 0ustar brains99brains99# # Makefile.postamble # # Project specific makefile rules # # Uncomment the targets you want. # The double colons (::) are important, do not make them single colons # otherwise the normal makefile rules will not be performed. # # Things to do before compiling # before-all:: # Things to do after compiling # after-all:: # Things to do before installing # before-install:: # Things to do after installing # after-install:: # Things to do before uninstalling # before-uninstall:: # Things to do after uninstalling # after-uninstall:: # Things to do before cleaning # before-clean:: # Things to do after cleaning # after-clean:: # Things to do before distcleaning # before-distclean:: # Things to do after distcleaning # after-distclean:: # Things to do before checking # before-check:: # Things to do after checking # after-check:: Performance-0.5.0/Performance.h0000664000076500007650000000227412326675743016311 0ustar brains99brains99/* JIGS import file for the GNUstep Performance Library. Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: Novb 2005 This file is part of the GNUstep Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. */ #import "GSCache.h" #import "GSFIFO.h" #import "GSIOThreadPool.h" #import "GSLinkedList.h" #import "GSThreadPool.h" #import "GSThroughput.h" #import "GSTicker.h" #import "GSSkipMutableArray.h" #import "GSUniqued.h" Performance-0.5.0/GSUniqued.m0000664000076500007650000001166312326675743015723 0ustar brains99brains99/** Copyright (C) 2014 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: April 2014 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #import #import #import #import #import #import #import "GSUniqued.h" static Class GSUniquedClass = Nil; static NSLock *uniquedObjectsLock; static IMP iLock; static IMP iUnlock; static NSHashTable *uniquedObjects; static NSLock *classLock; static NSMutableDictionary *classMap; /* Deallocate a uniqued object ... we must remove it from the uniqued * objects table and then call the real -dealloc method. */ static void uDealloc(id self, SEL _cmd) { Class c; IMP i; NSHashRemove(uniquedObjects, self); c = object_getClass(self); c = class_getSuperclass(c); i = class_getMethodImplementation(c, _cmd); (*i)(self, _cmd); } /* Release a uniqued object ... we must obtain a lock in case the uniqued * objects table has to be modified by removal of this instance on * deallocation. */ static void uRelease(id self, SEL _cmd) { Class c; IMP i; c = object_getClass(self); c = class_getSuperclass(c); i = class_getMethodImplementation(c, _cmd); (*iLock)(uniquedObjectsLock, @selector(lock)); (*i)(self, _cmd); (*iUnlock)(uniquedObjectsLock, @selector(unlock)); } @implementation GSUniqued + (void) initialize { if (Nil == GSUniquedClass) { classLock = [NSLock new]; classMap = [NSMutableDictionary new]; uniquedObjectsLock = [NSLock new]; iLock = [uniquedObjectsLock methodForSelector: @selector(lock)]; iUnlock = [uniquedObjectsLock methodForSelector: @selector(unlock)]; uniquedObjects = NSCreateHashTable( NSNonRetainedObjectHashCallBacks, 10000); GSUniquedClass = [GSUniqued class]; } } + (id) allocWithZone: (NSZone*)z { [NSException raise: NSInvalidArgumentException format: @"Attempt to allocate instance of GSUniqued"]; return nil; } + (id) copyUniqued: (id)anObject { NSObject *found; NSAssert(nil != anObject, NSInvalidArgumentException); (*iLock)(uniquedObjectsLock, @selector(lock)); found = [(NSObject*)NSHashGet(uniquedObjects, anObject) retain]; (*iUnlock)(uniquedObjectsLock, @selector(unlock)); if (nil == found) { NSObject *aCopy; Class c; Class u; aCopy = [anObject copyWithZone: NSDefaultMallocZone()]; c = object_getClass(aCopy); [classLock lock]; u = [classMap objectForKey: c]; if (Nil == u) { const char *cn = class_getName(c); char name[strlen(cn) + 20]; Method method; sprintf(name, "GSUniqued%s", cn); u = objc_allocateClassPair(c, name, 0); method = class_getInstanceMethod([NSObject class], @selector(dealloc)); class_addMethod(u, @selector(dealloc), (IMP)uDealloc, method_getTypeEncoding(method)); method = class_getInstanceMethod([NSObject class], @selector(release)); class_addMethod(u, @selector(release), (IMP)uRelease, method_getTypeEncoding(method)); objc_registerClassPair(u); [classMap setObject: u forKey: c]; } [classLock unlock]; (*iLock)(uniquedObjectsLock, @selector(lock)); found = [(NSObject*)NSHashGet(uniquedObjects, anObject) retain]; if (nil == found) { found = aCopy; #if defined(GNUSTEP) GSClassSwizzle(found, u); #else object_setClass(found, u); #endif NSHashInsert(uniquedObjects, found); } else { [aCopy release]; // Already uniqued by another thread } (*iUnlock)(uniquedObjectsLock, @selector(unlock)); } return found; } @end @implementation NSObject (GSUniqued) - (id) copyUniqued { if (Nil == GSUniquedClass) [GSUniqued class]; return [GSUniquedClass copyUniqued: (id)self]; } @end Performance-0.5.0/Performance.xcodeproj/0000775000076500007650000000000012332631460020110 5ustar brains99brains99Performance-0.5.0/Performance.xcodeproj/project.pbxproj0000664000076500007650000004171611467052070023177 0ustar brains99brains99// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 42; objects = { /* Begin PBXBuildFile section */ 85872EC11284CFC700B4601E /* GSCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB01284CFC700B4601E /* GSCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85872EC21284CFC700B4601E /* GSCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB11284CFC700B4601E /* GSCache.m */; }; 85872EC31284CFC700B4601E /* GSIndexedSkipList.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB21284CFC700B4601E /* GSIndexedSkipList.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85872EC41284CFC700B4601E /* GSIndexedSkipList.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB31284CFC700B4601E /* GSIndexedSkipList.m */; }; 85872EC51284CFC700B4601E /* GSIOThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB41284CFC700B4601E /* GSIOThreadPool.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85872EC61284CFC700B4601E /* GSIOThreadPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB51284CFC700B4601E /* GSIOThreadPool.m */; }; 85872EC71284CFC700B4601E /* GSLinkedList.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB61284CFC700B4601E /* GSLinkedList.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85872EC81284CFC700B4601E /* GSLinkedList.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB71284CFC700B4601E /* GSLinkedList.m */; }; 85872EC91284CFC700B4601E /* GSSkipMutableArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EB81284CFC700B4601E /* GSSkipMutableArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85872ECA1284CFC700B4601E /* GSSkipMutableArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EB91284CFC700B4601E /* GSSkipMutableArray.m */; }; 85872ECB1284CFC700B4601E /* GSThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EBA1284CFC700B4601E /* GSThreadPool.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85872ECC1284CFC700B4601E /* GSThreadPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EBB1284CFC700B4601E /* GSThreadPool.m */; }; 85872ECD1284CFC700B4601E /* GSThroughput.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EBC1284CFC700B4601E /* GSThroughput.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85872ECE1284CFC700B4601E /* GSThroughput.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EBD1284CFC700B4601E /* GSThroughput.m */; }; 85872ECF1284CFC700B4601E /* GSTicker.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EBE1284CFC700B4601E /* GSTicker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85872ED01284CFC700B4601E /* GSTicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 85872EBF1284CFC700B4601E /* GSTicker.m */; }; 85872ED11284CFC700B4601E /* Performance.h in Headers */ = {isa = PBXBuildFile; fileRef = 85872EC01284CFC700B4601E /* Performance.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 85872EB01284CFC700B4601E /* GSCache.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSCache.h; sourceTree = ""; }; 85872EB11284CFC700B4601E /* GSCache.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSCache.m; sourceTree = ""; }; 85872EB21284CFC700B4601E /* GSIndexedSkipList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSIndexedSkipList.h; sourceTree = ""; }; 85872EB31284CFC700B4601E /* GSIndexedSkipList.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSIndexedSkipList.m; sourceTree = ""; }; 85872EB41284CFC700B4601E /* GSIOThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSIOThreadPool.h; sourceTree = ""; }; 85872EB51284CFC700B4601E /* GSIOThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSIOThreadPool.m; sourceTree = ""; }; 85872EB61284CFC700B4601E /* GSLinkedList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSLinkedList.h; sourceTree = ""; }; 85872EB71284CFC700B4601E /* GSLinkedList.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSLinkedList.m; sourceTree = ""; }; 85872EB81284CFC700B4601E /* GSSkipMutableArray.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSSkipMutableArray.h; sourceTree = ""; }; 85872EB91284CFC700B4601E /* GSSkipMutableArray.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSSkipMutableArray.m; sourceTree = ""; }; 85872EBA1284CFC700B4601E /* GSThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSThreadPool.h; sourceTree = ""; }; 85872EBB1284CFC700B4601E /* GSThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSThreadPool.m; sourceTree = ""; }; 85872EBC1284CFC700B4601E /* GSThroughput.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSThroughput.h; sourceTree = ""; }; 85872EBD1284CFC700B4601E /* GSThroughput.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSThroughput.m; sourceTree = ""; }; 85872EBE1284CFC700B4601E /* GSTicker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GSTicker.h; sourceTree = ""; }; 85872EBF1284CFC700B4601E /* GSTicker.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GSTicker.m; sourceTree = ""; }; 85872EC01284CFC700B4601E /* Performance.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Performance.h; sourceTree = ""; }; 85B560B3128C6E47003BAF08 /* Performance.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Performance.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8DC2EF560486A6940098B216 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 034768DFFF38A50411DB9C8B /* Products */ = { isa = PBXGroup; children = ( 85B560B3128C6E47003BAF08 /* Performance.framework */, ); name = Products; sourceTree = ""; }; 0867D691FE84028FC02AAC07 /* performance */ = { isa = PBXGroup; children = ( 08FB77AEFE84172EC02AAC07 /* Classes */, 32C88DFF0371C24200C91783 /* Other Sources */, 089C1665FE841158C02AAC07 /* Resources */, 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */, 034768DFFF38A50411DB9C8B /* Products */, ); name = performance; sourceTree = ""; }; 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = { isa = PBXGroup; children = ( 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */, 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */, ); name = "External Frameworks and Libraries"; sourceTree = ""; }; 089C1665FE841158C02AAC07 /* Resources */ = { isa = PBXGroup; children = ( 8DC2EF5A0486A6940098B216 /* Info.plist */, 089C1666FE841158C02AAC07 /* InfoPlist.strings */, ); name = Resources; sourceTree = ""; }; 08FB77AEFE84172EC02AAC07 /* Classes */ = { isa = PBXGroup; children = ( 85872EB01284CFC700B4601E /* GSCache.h */, 85872EB11284CFC700B4601E /* GSCache.m */, 85872EB21284CFC700B4601E /* GSIndexedSkipList.h */, 85872EB31284CFC700B4601E /* GSIndexedSkipList.m */, 85872EB41284CFC700B4601E /* GSIOThreadPool.h */, 85872EB51284CFC700B4601E /* GSIOThreadPool.m */, 85872EB61284CFC700B4601E /* GSLinkedList.h */, 85872EB71284CFC700B4601E /* GSLinkedList.m */, 85872EB81284CFC700B4601E /* GSSkipMutableArray.h */, 85872EB91284CFC700B4601E /* GSSkipMutableArray.m */, 85872EBA1284CFC700B4601E /* GSThreadPool.h */, 85872EBB1284CFC700B4601E /* GSThreadPool.m */, 85872EBC1284CFC700B4601E /* GSThroughput.h */, 85872EBD1284CFC700B4601E /* GSThroughput.m */, 85872EBE1284CFC700B4601E /* GSTicker.h */, 85872EBF1284CFC700B4601E /* GSTicker.m */, 85872EC01284CFC700B4601E /* Performance.h */, ); name = Classes; sourceTree = ""; }; 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; sourceTree = ""; }; 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( 0867D6A5FE840307C02AAC07 /* AppKit.framework */, D2F7E79907B2D74100F64583 /* CoreData.framework */, 0867D69BFE84028FC02AAC07 /* Foundation.framework */, ); name = "Other Frameworks"; sourceTree = ""; }; 32C88DFF0371C24200C91783 /* Other Sources */ = { isa = PBXGroup; children = ( ); name = "Other Sources"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 8DC2EF500486A6940098B216 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 85872EC11284CFC700B4601E /* GSCache.h in Headers */, 85872EC31284CFC700B4601E /* GSIndexedSkipList.h in Headers */, 85872EC51284CFC700B4601E /* GSIOThreadPool.h in Headers */, 85872EC71284CFC700B4601E /* GSLinkedList.h in Headers */, 85872EC91284CFC700B4601E /* GSSkipMutableArray.h in Headers */, 85872ECB1284CFC700B4601E /* GSThreadPool.h in Headers */, 85872ECD1284CFC700B4601E /* GSThroughput.h in Headers */, 85872ECF1284CFC700B4601E /* GSTicker.h in Headers */, 85872ED11284CFC700B4601E /* Performance.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 8DC2EF4F0486A6940098B216 /* Performance */ = { isa = PBXNativeTarget; buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Performance" */; buildPhases = ( 8DC2EF500486A6940098B216 /* Headers */, 8DC2EF520486A6940098B216 /* Resources */, 8DC2EF540486A6940098B216 /* Sources */, 8DC2EF560486A6940098B216 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Performance; productInstallPath = "$(HOME)/Library/Frameworks"; productName = performance; productReference = 85B560B3128C6E47003BAF08 /* Performance.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Performance" */; hasScannedForEncodings = 1; mainGroup = 0867D691FE84028FC02AAC07 /* performance */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; targets = ( 8DC2EF4F0486A6940098B216 /* Performance */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 8DC2EF520486A6940098B216 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8DC2EF540486A6940098B216 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 85872EC21284CFC700B4601E /* GSCache.m in Sources */, 85872EC41284CFC700B4601E /* GSIndexedSkipList.m in Sources */, 85872EC61284CFC700B4601E /* GSIOThreadPool.m in Sources */, 85872EC81284CFC700B4601E /* GSLinkedList.m in Sources */, 85872ECA1284CFC700B4601E /* GSSkipMutableArray.m in Sources */, 85872ECC1284CFC700B4601E /* GSThreadPool.m in Sources */, 85872ECE1284CFC700B4601E /* GSThroughput.m in Sources */, 85872ED01284CFC700B4601E /* GSTicker.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 089C1666FE841158C02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 089C1667FE841158C02AAC07 /* English */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; GCC_CW_ASM_SYNTAX = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_CPP_EXCEPTIONS = YES; GCC_ENABLE_CPP_RTTI = YES; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_ENABLE_OBJC_EXCEPTIONS = NO; GCC_ENABLE_PASCAL_STRINGS = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Frameworks"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = YES; PRODUCT_NAME = Performance; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; name = Debug; }; 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = ( ppc, i386, ); DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = performance_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Frameworks"; PRODUCT_NAME = performance; WRAPPER_EXTENSION = framework; }; name = Release; }; 1DEB91B208733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Debug; }; 1DEB91B308733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Performance" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB91AE08733DA50010E9CD /* Debug */, 1DEB91AF08733DA50010E9CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Performance" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB91B208733DA50010E9CD /* Debug */, 1DEB91B308733DA50010E9CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 0867D690FE84028FC02AAC07 /* Project object */; } Performance-0.5.0/Performance.jigs0000664000076500007650000000111610377047451017001 0ustar brains99brains99{ /* -*-c-*- */ "prerequisite libraries" = ( "gnustep-base" ); types = { NSTimeInterval = double; }; classes = ( { "java name" = "gnu.gnustep.Performance.GSCache"; "objective-c name" = "GSCache"; "class methods" = ( "allInstances" ); "instance methods" = ( "currentObjects", "currentSize", "lifetime", "maxObjects", "maxSize", "name", "objectForKey:", "purge", "setLifetime:", "setMaxObjects:", "setMaxSize:", "setName:", "setObject:forKey:", "setObject:forKey:lifetime:", "shrinkObjects:andSize:" ); }, ); } Performance-0.5.0/GNUmakefile0000664000076500007650000000374712332631255015742 0ustar brains99brains99 ifeq ($(GNUSTEP_MAKEFILES),) GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null) ifeq ($(GNUSTEP_MAKEFILES),) $(warning ) $(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!) $(warning Perhaps gnustep-make is not properly installed,) $(warning so gnustep-config is not in your PATH.) $(warning ) $(warning Your PATH is currently $(PATH)) $(warning ) endif endif ifeq ($(GNUSTEP_MAKEFILES),) $(error You need to set GNUSTEP_MAKEFILES before compiling!) endif include $(GNUSTEP_MAKEFILES)/common.make -include config.make PACKAGE_NAME = Performance PACKAGE_VERSION = 0.5.0 Performance_INTERFACE_VERSION=0.5 SVN_BASE_URL = svn+ssh://svn.gna.org/svn/gnustep/libs SVN_MODULE_NAME = performance NEEDS_GUI = NO TEST_TOOL_NAME= LIBRARY_NAME=Performance DOCUMENT_NAME=Performance Performance_OBJC_FILES += \ GSCache.m \ GSFIFO.m \ GSIOThreadPool.m \ GSLinkedList.m \ GSThreadPool.m \ GSThroughput.m \ GSTicker.m \ GSIndexedSkipList.m \ GSSkipMutableArray.m \ GSUniqued.m \ Performance_HEADER_FILES += \ GSCache.h \ GSFIFO.h \ GSIOThreadPool.h \ GSLinkedList.h \ GSThreadPool.h \ GSThroughput.h \ GSTicker.h \ GSSkipMutableArray.h \ GSUniqued.h \ Performance_AGSDOC_FILES += \ GSCache.h \ GSFIFO.h \ GSIOThreadPool.h \ GSLinkedList.h \ GSThreadPool.h \ GSThroughput.h \ GSTicker.h \ GSSkipMutableArray.h \ GSUniqued.h \ # Optional Java wrappers for the library JAVA_WRAPPER_NAME = Performance Performance_HEADER_FILES_INSTALL_DIR = Performance -include GNUmakefile.preamble include $(GNUSTEP_MAKEFILES)/library.make # If JIGS is installed, automatically generate Java wrappers as well. # Because of the '-', should not complain if java-wrapper.make can't be # found ... simply skip generation of java wrappers in that case. -include $(GNUSTEP_MAKEFILES)/java-wrapper.make include $(GNUSTEP_MAKEFILES)/test-tool.make include $(GNUSTEP_MAKEFILES)/documentation.make -include GNUmakefile.postamble Performance-0.5.0/GSThreadPool.h0000664000076500007650000001076312104733015016322 0ustar brains99brains99#if !defined(INCLUDED_GSTHREADPOOL) #define INCLUDED_GSTHREADPOOL 1 /** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: September 2010 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import @class GSLinkedList; @class NSDate; @class NSLock; /** This class provides a thread pool for performing methods * of objects in parallel in other threads.
* This is similar to the NSOperationQueue class but is a * lightweight alternative designed to operate rather faster * though with slightly decreased functionality ... for instance * there is no dependency checking supported. */ @interface GSThreadPool : NSObject { NSRecursiveLock *poolLock; BOOL shutdown; BOOL suspended; NSUInteger maxThreads; GSLinkedList *idle; GSLinkedList *live; NSUInteger maxOperations; GSLinkedList *operations; GSLinkedList *unused; NSUInteger processed; } /** Returns an instance intended for sharing between sections of code which * wish to make use of threading by performing operations in other threads, * but which don't mind operations being interleaved with those belonging to * oither sections of code.
* Always returns the same instance whenever the method is called. */ + (GSThreadPool*) sharedPool; /** Waits until the pool of operations is empty or until the specified * timestamp. Returns YES if the pool was emptied, NO otherwise. */ - (BOOL) drain: (NSDate*)before; /** Removes all operations which have not yet started, returning a count * of the abandoned operations. */ - (NSUInteger) flush; /** Returns YES if no operations are waiting to be performed, NO otherwise. */ - (BOOL) isEmpty; /** Returns YES if NO operations are in progress, NO otherwise. */ - (BOOL) isIdle; /** Returns YES if startup of new operations is suspended, NO otherwise. */ - (BOOL) isSuspended; /** Returns the currently configured maximum number of operations which * may be scheduled at any one time. */ - (NSUInteger) maxOperations; /** Returns the currently configured maximum number of threads in the pool. */ - (NSUInteger) maxThreads; /** Reverses the effect of -suspend. */ - (void) resume; /** Adds the object to the queue for which operations should be performed.
* You may add an object more than once, but that may result in the operation * being performed simultaneously in more than one thread.
* If the pool is configured with zero threads or zero operations, * this method will simply perform the operation immediately.
* The operation will be performed in a context where there is an exception * handler set to trap exceptions, and an autorelease pool to deal with * autoreleased objects. */ - (void) scheduleSelector: (SEL)aSelector onReceiver: (NSObject*)aReceiver withObject: (NSObject*)anArgument; /** Specify the number of operations which may be waiting.
* Default is 100.
* Setting a value of zero ensures that operations are performed * immediately rather than being queued. */ - (void) setOperations: (NSUInteger)max; /** Specify the maximum number of threads in the pool (the actual number * used may be lower than this value).
* Default is 2.
* The pool creates threads on demand up to the specified limit (or a lower * limit if dictated by system resources) but will not destroy idle threads * unless the limit is subsequently released.
* Setting a value of zero means that operations are performed in the * main thread. In this case -drain: will be used (with a 30 second limit) * followed by -flush to ensure that the queue is emptied before the threads * are shut down. */ - (void) setThreads: (NSUInteger)max; /** Turns off startup of new operations. */ - (void) suspend; @end #endif Performance-0.5.0/ChangeLog0000664000076500007650000001740112326675743015447 0ustar brains99brains992014-04-26 Richard Frith-Macdonald * GSUniqued.h: * GSUniqued.m: * Performance.h: * GNUmakefile: New code to implement uniqued copies of objects for lowered memory footprint and faster collection lookups. 2013-11-05 Niels Grewe * GSFIFO.m: Fix calculation of the timeout for cooperating get/put. 2013-08-21 Richard Frith-Macdonald * GSFIFO.m: 64bit printf format changes * GSSkipMutableArray.m: 64bit printf format changes * GSCache.m: Fix mime size calculations for nonfragile abi. Change API to use NSUInteger for sizes * GNUmakefile: bump version fro new release 2013-06-25 Richard Frith-Macdonald * GNUmakefile: bump subminor version * GSIOThreadPool.h: * GSIOThreadPool.m: Add default for initialisation of shared pool, fix bug in setting new thread and add code for shrinking the pool size (previously setting it smaller did not actually cause old threads to exit). 2013-02-07 Sebastian Reitenbach * GSThreadPool.h, GSCache.m shutup clang warnings 2013-02-03 Richard Frith-Macdonald * GSTicker.m: Retain timer so that we can safely invalidate it even on thread exit when the runloop of the current thread may have been deallocated already. 2012-01-11 Niels Grewe * GSSkipMutableArray.m: Change -initWithObjects:count: declaration to match the superclass one in gnustep-base trunk. 2011-10-25 Richard Frith-Macdonald * GSFIFO.h: * GSFIFO.m: Add new method to create a named FIFO and configure it from the defaults system. * GNUmakefile: bump version to 0.3.2 2011-08-29 Richard Frith-Macdonald * GSCache.m: Try to fool clang/llvm to avoid useless warning/error. 2011-08-24 Richard Frith-Macdonald * GSFIFO.h: * GSFIFO.m: Use NSCondition to support multiple producer/consumer on FIFO. 2011-06-11 Richard Frith-Macdonald * GSFIFO.h: * GSFIFO.m: Add the ability to monitor statistics on how long the FIFO blocks on put and get operations. Add methods to get/put multiple items. Add method to get stats for all FIFOs. 2011-06-06 Richard Frith-Macdonald * GSCache.h: * GSCache.m: Add a new method to let the delegate know when a cache hit has occurred on an item which is nearning the end of its lifetime ... providing an opportunity for the delegate to refresh the cache before the actual expiry. Also fix minor memory leak. 2011-06-05 Richard Frith-Macdonald * GSFIFO.h: * GSFIFO.m: Use condition locks so that, if we have locking for both addition and removal, we can signal threads waiting at the other end. 2011-06-03 Richard Frith-Macdonald * GSTicker.m: Thread safety fixes ... make date storage volatile and ensure that only one thread gets to set the base timestamp. 2011-06-02 Richard Frith-Macdonald * GSFIFO.h: * GSFIFO.m: Make FIFO items be void* rather than id since we never send them any messages anyway. 2011-05-19 Richard Frith-Macdonald * GNUmakefile: Add FIFO * Performance.h: Add FIFO * GSFIFO.h: Add a FIFO class * GSFIFO.m: Add a FIFO class 2011-03-08 Richard Frith-Macdonald * GNUmakefile: Bump version number for next release. 2010-11-11 Riccardo Mottola * GSCache.m use always class_getInstanceSize() on GNUSTEP for all runtimes and define a compatibility macro for old macs * GSThreadPool.h * GSLinkedList.h * GSThreadPool.m * GSSkipMutableArray.m * GSIOThreadPool.h * GSIOThreadPool.m Complete includes and use typedef instead of #define 2010-11-11 Richard Frith-Macdonald * GSCache.m: Try to get defines right to get instance size depending on the runtime in use. 2010-11-10 Riccardo Mottola * GSLinkedList.h * GSSkipMutableArray.m * GSIOThreadPool.h Mac 10.4 compatibility definitions 2010-10-01 Richard Frith-Macdonald * GSIOThreadPool.h: * GSIOThreadPool.m: Add new methed class for pooling threads to handle I/O and timers. 2010-09-30 Richard Frith-Macdonald * GSThreadPool.h: * GSThreadPool.m: Add new methed to return a shared pool. 2010-09-29 Richard Frith-Macdonald * GSLinkedList.h: * GSLinkedList.m: Revise structure and API for greater flexibility. * GSThreadPool.h: * GSThreadPool.m: Modify for new linked list api. 2010-09-23 Richard Frith-Macdonald * performance/GSThreadPool.h: * performance/GSThreadPool.m: Track count of active threads and count of number of methods performed in the thread pool. Display via -description. 2010-09-22 Richard Frith-Macdonald * GSLinkedList.h: * GSLinkedList.m: Simple doubly linked list implementation * GSThreadPool.h: * GSThreadPool.m: Lightweight thread pool implementation * Performance.h: Include new headers * GNUmakefile: Build/install new source Add classes for a simple linked list and for a lightweight thread pool for rapid handoff of operations to other threads. 2008-12-12 Richard Frith-Macdonald * GSSkipMutableArray.m: Fix uninitialised variable. 2008-05-30 Richard Frith-Macdonald * GSThroughput.h: * GSThroughput.m: Add support for sending a notification of stats for the last minute so that rates by the minute can be logged. 2008-02-21 Richard Frith-Macdonald * GSCache.h: * GSCache.m: Improve handling of case when delegate asks for an item to be kept... changes the delegat callback methoid to include lifetime information. Make the cache thread-safe. 2008-01-31 Richard Frith-Macdonald * GSCache.h: * GSCache.m: Make cache keys 'id' rather than 'NSString*' as they can actually be any copyable object. 2007-12-08 Richard Frith-Macdonald * GNUmakefile: bump subminor version for release * GSThroughput.m: ([-description]) output information for periods from last cycle as well as current cycle. 2007-09-14 Richard Frith-Macdonald Update to LGPL3 2007-05-30 Richard Frith-Macdonald * GSTicker.m: Ensure tick is current before calling -newSecond: 2007-04-01 Richard Frith-Macdonald * GSCache.m: * GSThroughput.m: * GSSkipMutableArray.m: * GSTicker.m: Changes to avoid compiler warnings on MacOS-X Improvements to calculations for size of instances for cache. 2006-11-11 Matt Rice * GSMutableSkipArray.m: Fix bug when deallocating an empty list. 2006-11-11 Richard Frith-Macdonald * GSThroughput.h: New ([add:duration:]) method. * GSThroughput.m: Method to add multiple events with a total duration (eg where recording individual event times has too high an overhead). 2006-11-09 Richard Frith-Macdonald * GNUmakefile: update version * GSThroughput.m: Allow use to keep running totals of stats 2006-09-27 Richard Frith-Macdonald * GNUmakefile: * GSIndexedSkipList.m: * GSSkipMutableArray.h: * GSSkipMutableArray.m: Minor fix to (hopefully) build on windows. Hide implementation details. Add convenience method (to create skiplist) to NSMutableArray. Some reformatting for coding standards. * Performance.h: Ad new header for skip lists. 2006-09-27 Matt Rice * GSSkipMutableArray.[hm]: New NSMutableArray subclass. * GSIndexedSkipList.[hm]: Underlying C implementation. 2006-06-07 Richard Frith-Macdonald * GSCache.h: * GSCache.m: New convenience method to cache an item until a date. 2006-01-11 Nicola Pero * GNUmakefile.wrapper.objc.preamble: New file that fixes the "/usr/bin/ld: cannot find -lPerformance" error when linking Java wrappers. 2005-10-28 Richard Frith-Macdonald New Performance library (0.1) Performance-0.5.0/GSUniqued.h0000664000076500007650000000636512326675743015721 0ustar brains99brains99#if !defined(INCLUDED_GSUNIQUED) #define INCLUDED_GSUNIQUED 1 /** Copyright (C) 2014 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: April 2014 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import /** Class used to unique other objects.
*

The point of this class is to lower the memory footprint and speed * up comparisons (pointer equality) in cases where an application * stores multiple copies of the same object in various maps.
* Since uniquing is performed by storing an immutable copy of the * original object in a map until there are no further references * to that object, it's pointless to use this uniquing unless the * application would be storing at least two copies of the object.
* Also, since this is thread-safe there is a lock management * overhead wherever a uniqued object is released, so performance * gains are to be expected only if the uniqued object has a * relatively long lifetime and is tested for equality with other * instances frequently.
* In short, use with care; while uniquing can have a big performance * advantage for some programs, this is actually quite rare. *

*

The internal implementation of the uniquing works by taking * immutable copies of the objects to be uniqued, storing those copies * in a hash table, and swizzling their class pointers to a sub-class * which will automatically remove the instance from the hash table * before it is deallocated.
* Access to the hash table is protected by locks so that uniqued * objects may be used freely in multiple threads.
* The name of the subclass used is the name of the original class * with 'GSUniqued' added as a prefix. *

*/ @interface GSUniqued : NSObject /** This method returns a copy of its argument, uniqued so that other * such copies of equal objects will be the same instance.
* The argument must respond to -copyWithZone: by returning an instance * of class of immutable objects (ie where the -hash and -isEqual: * methods are stable for that instance). */ + (id) copyUniqued: (id)anObject; @end /** Category for uniquing any copyable object.
* NB. This must only be used by classes for which -copyWithZone: * produces an instance of an immutable class. */ @interface NSObject (GSUniqued) /** This method returns a copy of the receiver uniqued so that other * such copies of equal objects content will be the same instance. */ - (id) copyUniqued; @end #endif Performance-0.5.0/GSIOThreadPool.m0000664000076500007650000001342512162261422016557 0ustar brains99brains99/** Copyright (C) 2010 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: September 2010 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #import #import #import #import #import #import #import #import "GSIOThreadPool.h" @interface GSIOThread : NSThread { NSTimer *timer; @public NSUInteger count; } - (void) exit: (NSTimer*)t; - (void) run; - (void) terminate: (NSDate*)when; @end @implementation GSIOThread /* Force termination of this thread. */ - (void) exit: (NSTimer*)t { [NSThread exit]; } #if defined(GNUSTEP) || (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) - (id) init { self = [super initWithTarget: self selector: @selector(run) object: nil]; if (nil != self) { [self start]; } return self; } #endif /* Run the thread's main runloop until terminated. */ - (void) run { NSDate *when = [NSDate distantFuture]; NSTimeInterval delay = [when timeIntervalSinceNow]; timer = [NSTimer scheduledTimerWithTimeInterval: delay target: self selector: @selector(exit:) userInfo: nil repeats: NO]; [[NSRunLoop currentRunLoop] run]; } /* End execution of the thread by the specified date. */ - (void) terminate: (NSDate*)when { NSTimeInterval delay = [when timeIntervalSinceNow]; [timer invalidate]; if (delay > 0.0) { timer = [NSTimer scheduledTimerWithTimeInterval: delay target: self selector: @selector(exit:) userInfo: nil repeats: NO]; } else { timer = nil; [self exit: nil]; } } @end @implementation GSIOThreadPool static GSIOThreadPool *shared = nil; /* Return the thread with the lowest usage. */ static GSIOThread * best(NSMutableArray *a) { NSUInteger c = [a count]; NSUInteger l = NSNotFound; GSIOThread *t = nil; while (c-- > 0) { GSIOThread *o = [a objectAtIndex: c]; if (o->count < l) { t = o; l = o->count; } } return t; } + (void) initialize { if ([GSIOThreadPool class] == self && nil == shared) { NSInteger size; size = [[NSUserDefaults standardUserDefaults] integerForKey: @"GSIOThreadPoolSize"]; if (size < 0) { size = 0; } shared = [self new]; [shared setThreads: size]; } } + (GSIOThreadPool*) sharedPool { return shared; } - (NSThread*) acquireThread { GSIOThread *t; [poolLock lock]; if (0 == maxThreads) { [poolLock unlock]; return [NSThread mainThread]; } t = best(threads); if (nil == t || (t->count > 0 && [threads count] < maxThreads)) { t = [GSIOThread new]; [threads addObject: t]; [t release]; } t->count++; [poolLock unlock]; return t; } - (NSUInteger) countForThread: (NSThread*)aThread { NSUInteger count = 0; [poolLock lock]; if ([threads indexOfObjectIdenticalTo: aThread] != NSNotFound) { count = ((GSIOThread*)aThread)->count; } [poolLock unlock]; return count; } - (void) dealloc { #if defined(GNUSTEP) || (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) GSIOThread *thread; NSDate *when = [NSDate dateWithTimeIntervalSinceNow: timeout]; [poolLock lock]; while ((thread = [threads lastObject]) != nil) { [thread performSelector: @selector(terminate:) onThread: thread withObject: when waitUntilDone: NO]; [threads removeLastObject]; } [threads release]; [poolLock unlock]; [poolLock release]; #endif [super dealloc]; } - (id) init { #if defined(GNUSTEP) || (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) if ((self = [super init]) != nil) { poolLock = [NSLock new]; threads = [NSMutableArray new]; } #else [self release]; NSLog(@"WARNING, your OSX system is too old to use GSIOthreadPool"); return nil; #endif return self; } - (NSUInteger) maxThreads { return maxThreads; } - (void) setThreads: (NSUInteger)max { [poolLock lock]; maxThreads = max; [poolLock unlock]; } - (void) setTimeout: (NSTimeInterval)t { timeout = t; } - (NSTimeInterval) timeout { return timeout; } - (void) unacquireThread: (NSThread*)aThread { [poolLock lock]; if ([threads indexOfObjectIdenticalTo: aThread] != NSNotFound) { if (0 == ((GSIOThread*)aThread)->count) { [poolLock unlock]; [NSException raise: NSInternalInconsistencyException format: @"-unacquireThread: called too many times"]; } ((GSIOThread*)aThread)->count--; if (0 == ((GSIOThread*)aThread)->count && [threads count] > maxThreads) { [aThread retain]; [threads removeObjectIdenticalTo: aThread]; [aThread performSelector: @selector(terminate:) onThread: aThread withObject: [NSDate date] waitUntilDone: NO]; [aThread release]; } } [poolLock unlock]; } @end Performance-0.5.0/GSCache.h0000664000076500007650000002567712205077240015301 0ustar brains99brains99/** Copyright (C) 2005 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: October 2005 This file is part of the Performance Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. $Date: 2013-08-21 09:47:28 +0100 (Wed, 21 Aug 2013) $ $Revision: 36997 $ */ #ifndef INCLUDED_GSCache_H #define INCLUDED_GSCache_H #import @class NSArray; @class NSDate; @class NSDate; @class NSMutableSet; @class NSString; /** * The GSCache class is used to maintain a cache of objects in memory * for relatively rapid access.
* Typical usage might be to keep the results of a database query around * for a while in order to re-use them ... for instance when application * configuration is obtained from a database which might be updated while * the application is running.
* When the cache is full, old objects are removed to make room for new * ones on a least-recently-used basis.
* Cache sizes may be limited by the number of objects in the cache, * or by the memory used by the cache, or both. Calculation of the * size of items in the cache is relatively expensive, so caches are * only limited by number of objects in the default case.
* Objects stored in the cache may be given a limited lifetime, * in which case an attempt to fetch an expired object * from the cache will cause it to be removed from the cache instead * (subject to control by the delegate).
* Cache keys may be objects of any type as long as they are copyable * (and the copied keys are immutable) and implement the -hash and * -isEqual: methods such that any two keys can be tested for equality * and used as dictionary keys.
* NB. GSCache currently does not support subclassing ... use it as is * or extend it via categories, but do not try to add instance variables. */ @interface GSCache : NSObject { } /** * Return all the current cache instances... useful if you want to do * something to all cache instances in your process. */ + (NSArray*) allInstances; /** * Return a report on all GSCache instances ... calls the [GSCache-description] * method of the individual cache instances to get a report on each one. */ + (NSString*) description; /** * Return the count of objects currently in the cache. */ - (unsigned) currentObjects; /** * Return the total size of the objects currently in the cache. */ - (NSUInteger) currentSize; /** * Return the delegate object previously set using the -setDelegate: method. */ - (id) delegate; /** * Returns a string describing the status of the receiver for debug/reporting. */ - (NSString*) description; /** * Return the default lifetime for items set in the cache.
* A value of zero means that items are not purged based on lifetime. */ - (unsigned) lifetime; /** * Return the maximum number of items in the cache.
* A value of zero means there is no limit. */ - (unsigned) maxObjects; /** * Return the maximum tital size of items in the cache.
* A value of zero means there is no limit. */ - (NSUInteger) maxSize; /** * Return the name of this instance (as set using -setName:) */ - (NSString*) name; /** * Return the cached value for the specified key, or nil if there * is no value in the cache. */ - (id) objectForKey: (id)aKey; /** * Remove all items whose lifetimes have passed * (if lifetimes are in use for the cache).
*/ - (void) purge; /** * Sets the delegate for the receiver.
* The delegate object is not retained.
* If a delegate it set, it will be sent the messages in the * (GSCacheDelegate) protocol (if it implements them ... which * it does not need to do). */ - (void) setDelegate: (id)anObject; /** * Sets the lifetime (seconds) for items added to the cache. If this * is set to zero then items are not removed from the cache based on * lifetimes when the cache is full and an object is added, though * expired items are still removed when an attempt to retrieve * them is made. */ - (void) setLifetime: (unsigned)max; /** * Sets the maximum number of objects in the cache. If this is non-zero * then an attempt to set an object in a full cache will result in the * least recently used item in the cache being removed. */ - (void) setMaxObjects: (unsigned)max; /** * Sets the maximum total size for objects in the cache. If this is non-zero * then an attempt to set an object whose size would exceed the cache limit * will result in the least recently used items in the cache being removed. */ - (void) setMaxSize: (NSUInteger)max; /** * Sets the name of this instance. */ - (void) setName: (NSString*)name; /** * Sets (or replaces)the cached value for the specified key.
* The value of anObject may be nil to remove any cached object * for aKey. */ - (void) setObject: (id)anObject forKey: (id)aKey; /** * Sets (or replaces)the cached value for the specified key, giving * the value the specified lifetime (in seconds). A lifetime of zero * means that the item is not limited by lifetime.
* The value of anObject may be nil to remove any cached object * for aKey. */ - (void) setObject: (id)anObject forKey: (id)aKey lifetime: (unsigned)lifetime; /** * Sets (or replaces)the cached value for the specified key, giving * the value the specified expiry date. Calls -setObject:forKey:lifetime: * to do the real work ... this is just a convenience method to * handle working out the lifetime in seconds.
* If expires is nil or not in the future, this method simply removes the * cache entry for aKey. If it is many years in the future, the item is * set in the cache so that it is not limited by lifetime. */ - (void) setObject: (id)anObject forKey: (id)aKey until: (NSDate*)expires; /** * Called by -setObject:forKey:lifetime: to make space for a new * object in the cache (also when the cache is resized).
* This will, if a lifetime is set (see the -setLifetime: method) * first purge all expired objects from the cache, then * (if necessary) remove objects from the cache until the number * of objects and size of cache meet the limits specified.
* If the objects argument is zero then all objects are removed from * the cache.
* The size argument is used only if a maximum size is set * for the cache. */ - (void) shrinkObjects: (unsigned)objects andSize: (NSUInteger)size; @end /** * This protocol defines the messages which may be sent to a delegate * of a GSCache object. The messages are only sent if the delegate * actually implements them, so a delegate does not need to actually * conform to the protocol. */ @protocol GSCacheDelegate /** * Alerts the delegate to the fact that anObject, which was cached * using aKey and will expire delay seconds in the future has been * looked up now, and needs to be refreshed if it is not to expire * from the cache.
* This is called the first time an attempt is made to access the * cached value for aKey and the object is found in the cache but * more than half its lifetime has expired.
* The delegate method (if implemented) may replace the item in the * cache immediately, or do it later asynchronously, or may simply * take no action. */ - (void) mayRefreshItem: (id)anObject withKey: (id)aKey lifetime: (unsigned)lifetime after: (unsigned)delay; /** * Asks the delegate to decide whether anObject, which was cached * using aKey and expired delay seconds ago should still be retained * in the cache.
* This is called when an attempt is made to access the cached value * for aKey and the object is found in the cache but it is no longer * valid (has expired).
* If the method returns YES, then anObject will not be removed as it * normally would. This allows the delegate to change the cached item * or refresh it.
* For instance, the delegate could replace the object * in the cache before returning YES in order to update the cached value * when its lifetime has expired.
* Another possibility would be for the delegate to return YES (in order * to continue using the existing object) and queue an asynchronous * database query to update the cache later. In this case the expiry * time of the item will be reset relative to the current time, based * upon its original lifetime. */ - (BOOL) shouldKeepItem: (id)anObject withKey: (id)aKey lifetime: (unsigned)lifetime after: (unsigned)delay; @end /** *

This category declares the -sizeInBytes: method which is used by * [GSCache] to ask objects for their size when adding them to a cache * which has a limited size in bytes. *

*

The [NSObject] implementation of this method is intended to * provide a rough estimnate of object size which subclasses may refine * in order to provide more accurate sizing information.
* Subclasses should call the superclass implementation and use the * resulting size information (if it is zero then the object is in the * exclude set and the subclass implementation should also return zero) * as a starting point on which to add the sizes of any objects contained. *

* For example: * * - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude * { * NSUInteger size = [super sizeInBytes: exclude]; * if (size > 0) * { * size += [myInstanceVariable sizeInBytes: exclude]; * } * return size; * } * *

The performance library contains implementations giving reasonable size * estimates for several common classes: *

* * NSArray * NSData * NSDictionary * NSSet * NSString * GSMimeDocument (if built with the GNUstep base library) * *

The default ([NSObject]) implementation provides a reasonable size * for any class whose instance variables do not include other objects * or pointers to memory allocated on the heap. *

*/ @interface NSObject (GSCacheSizeInBytes) /** * If the receiver is a member of the exclude set, this method simply * returns zero. Otherwise, the receiver adds itsself to the exclude * set and returns its own size in bytes (the size of the memory used * to hold all the instance variables defined for the receiver's class * including all superclasses). */ - (NSUInteger) sizeInBytes: (NSMutableSet*)exclude; @end #endif