mirror of
https://github.com/gnustep/libs-performance.git
synced 2025-02-15 08:00:52 +00:00
Support cache refresh via delegate.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@33253 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
dde5501c2b
commit
71d2b71d96
3 changed files with 127 additions and 9 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2011-06-06 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* 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 <rfm@gnu.org>
|
||||
|
||||
* GSFIFO.h:
|
||||
|
|
25
GSCache.h
25
GSCache.h
|
@ -132,8 +132,9 @@
|
|||
/**
|
||||
* Sets the delegate for the receiver.<br />
|
||||
* The delegate object is not retained.<br />
|
||||
* If a delegate it set, it must implement the methods in the
|
||||
* (GSCacheDelegate) protocol.
|
||||
* 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;
|
||||
|
||||
|
@ -213,10 +214,28 @@
|
|||
|
||||
/**
|
||||
* This protocol defines the messages which may be sent to a delegate
|
||||
* of a GSCache object.
|
||||
* 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.<br />
|
||||
* 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.<br />
|
||||
* 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
|
||||
|
|
101
GSCache.m
101
GSCache.m
|
@ -64,6 +64,7 @@
|
|||
GSCacheItem *next;
|
||||
GSCacheItem *prev;
|
||||
unsigned life;
|
||||
unsigned warn;
|
||||
unsigned when;
|
||||
unsigned size;
|
||||
id key;
|
||||
|
@ -98,6 +99,8 @@ static NSLock *allCachesLock = nil;
|
|||
|
||||
typedef struct {
|
||||
id delegate;
|
||||
void (*refresh)(id, SEL, id, id, unsigned, unsigned);
|
||||
BOOL (*replace)(id, SEL, id, id, unsigned, unsigned);
|
||||
unsigned currentObjects;
|
||||
unsigned currentSize;
|
||||
unsigned lifetime;
|
||||
|
@ -339,15 +342,26 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
|
|||
{
|
||||
BOOL keep = NO;
|
||||
|
||||
if (my->delegate != nil)
|
||||
if (0 != my->replace)
|
||||
{
|
||||
GSCacheItem *orig = [item retain];
|
||||
|
||||
[my->lock unlock];
|
||||
keep = [my->delegate shouldKeepItem: item->object
|
||||
withKey: aKey
|
||||
lifetime: item->life
|
||||
after: when - item->when];
|
||||
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)
|
||||
{
|
||||
|
@ -364,6 +378,7 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
|
|||
*/
|
||||
my->misses++;
|
||||
[my->lock unlock];
|
||||
[orig release];
|
||||
return nil;
|
||||
}
|
||||
else if (orig == current)
|
||||
|
@ -372,6 +387,7 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
|
|||
* update its expiry time.
|
||||
*/
|
||||
item->when = when + item->life;
|
||||
item->warn = when + item->life / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -398,6 +414,52 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
|
|||
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);
|
||||
|
@ -461,7 +523,31 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
|
|||
|
||||
- (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
|
||||
|
@ -597,7 +683,10 @@ static void removeItem(GSCacheItem *item, GSCacheItem **first)
|
|||
item = [GSCacheItem newWithObject: anObject forKey: aKey];
|
||||
if (lifetime > 0)
|
||||
{
|
||||
item->when = GSTickerTimeTick() + lifetime;
|
||||
unsigned tick = GSTickerTimeTick();
|
||||
|
||||
item->when = tick + lifetime;
|
||||
item->warn = tick + lifetime / 2;
|
||||
}
|
||||
item->life = lifetime;
|
||||
item->size = addSize;
|
||||
|
|
Loading…
Reference in a new issue