/*
** j###t ########## #### ####
** j###t ########## #### ####
** j###T "###L J###"
** ######P' ########## #########
** ######k, ########## T######T
** ####~###L ####
** #### q###L ########## .#####
** #### \###L ########## #####"
*/
package key;
import key.primitive.*;
import key.util.LinkedList;
import java.io.*;
import java.util.Enumeration;
import java.util.StringTokenizer;
/**
* The LatentCache is the final memory cache that is outside the
* CachedAtom mechanism of locks. It is used to 'transparently'
* cache things that can be easily rebuilt from the available
* information on demand.
*
* This class is used, for instance, by a ShortcutCollection. As a
* ShortcutCollection stores redundant information - all those Trie's
* and Hashtables are irrelevant and often unrequired, when it creates
* the Trie and Hashtable, it also add's itself to this latentcache.
* Every 5 minutes the latent cache checks back with the linkedtrie to
* see if the trie or hashtable has been used within those five minutes.
* If it has not been used at any time within those 5 minutes, the
* LatentlyCached atom is called to deallocate itself as it sees fit.
*
* This class provides an efficient mechanism for this behaviour
*/
public final class LatentCache extends Daemon
{
private static final long serialVersionUID = 887548235534240810L;
public static final AtomicElement[] ELEMENTS =
{
AtomicElement.construct( LatentCache.class, Duration.class, "sleepLength",
AtomicElement.PUBLIC_ACCESSORS,
"the delay between latency scans" ),
AtomicElement.construct( LatentCache.class, Paragraph.class, "entries",
AtomicElement.PUBLIC_ACCESSORS | AtomicElement.READ_ONLY |
AtomicElement.GENERATED,
"the entries in the latency cache" )
};
public static final AtomicStructure STRUCTURE = new AtomicStructure( Daemon.STRUCTURE, ELEMENTS );
transient LinkedList list;
public Duration sleepLength;
public Duration getSleepLength()
{
return( sleepLength );
}
public int countEntries()
{
return( list.count() );
}
public void setSleepLength( Duration d )
{
sleepLength = d;
// This is not compatible with the JDK1.2 for linux
//interrupt(); // this makes sure it comes into play quickly
}
public LatentCache()
{
super( false );
list = new LinkedList();
// default to 5 minutes
sleepLength = new Duration( 300000 );
setKey( "latencyCache" );
}
public Paragraph getEntries()
{
TableParagraph.Generator tp = new TableParagraph.Generator( columns );
for( Enumeration e = list.elements(); e.hasMoreElements(); )
{
String[] rowContents = new String[ 2 ];
LatentlyCached o = (LatentlyCached) e.nextElement();
if( o instanceof Atom )
rowContents[0] = ((Atom)o).getId();
else
rowContents[0] = o.toString();
rowContents[1] = o.modified() ? "true" : "false";
tp.appendRow( rowContents );
}
tp.setFooter( Integer.toString( list.count() ) + " entries" );
return( tp.getParagraph() );
}
public static final TableParagraph.Column[] columns =
{
new TableParagraph.Column( "id", 50 ),
new TableParagraph.Column( "modified", 8 )
};
public AtomicStructure getDeclaredStructure()
{
return( STRUCTURE );
}
public void loaded()
{
super.loaded();
list = new LinkedList();
}
// synchronizing these methods can cause deadlock
public void removeFromCache( LatentlyCached lc )
{
list.remove( lc );
}
// synchronizing these methods can cause deadlock
public void addToCache( LatentlyCached lc )
{
list.append( lc );
}
public synchronized void stop()
{
super.stop();
Log.debug( this, "stopping latentcache" );
for( Enumeration e = list.elements(); e.hasMoreElements(); )
{
LatentlyCached lc = (LatentlyCached) e.nextElement();
try
{
lc.deallocate();
removeFromCache( lc );
}
catch( Exception except )
{
// generally, it won't let you swap out a player
// that is still online - at this point in the
// shutdown, we don't particularly care - just
// ignore it.
Log.debug( this, except.toString() );
except.printStackTrace( System.out );
}
}
}
public synchronized void deallocateNow( LatentlyCached lc )
{
lc.resetModify();
if( !lc.modified() )
{
removeFromCache( lc );
try
{
lc.deallocate();
}
catch( Exception except )
{
Log.error( "during latency cache deallocation, ignoring", except );
}
}
}
public void run()
{
setPriority( Thread.MAX_PRIORITY - 1 );
while( true )
{
//System.out.println( "LC: begin loop " );
synchronized( this )
{
try
{
for( Enumeration e = list.elements(); e.hasMoreElements(); )
{
LatentlyCached lc = (LatentlyCached) e.nextElement();
//System.out.println( "LC: scanning " + lc.toString() );
if( lc.modified() )
lc.resetModify();
else
{
//System.out.println( "LC: deallocating " + lc.toString() );
try
{
lc.deallocate();
}
catch( Exception except )
{
Log.debug( this, except.toString() + " during latency cache deallocation, ignoring..." );
except.printStackTrace( System.out );
}
removeFromCache( lc );
}
}
}
catch( ThreadDeath e )
{
Log.debug( this, "Latency cache shutting down..." );
throw e;
}
catch( Throwable e )
{
Log.debug( this, e.toString() + " in latency cache, ignoring..." );
e.printStackTrace( System.out );
}
}
//System.out.println( "LC: begin gc " );
//Runtime rt = Runtime.getRuntime();
//setPriority( Thread.NORM_PRIORITY );
//Thread.yield();
//rt.runFinalization();
//rt.gc();
//System.out.println( "LC: begin sleep " );
try
{
//setPriority( Thread.MAX_PRIORITY - 1 );
Thread.sleep( sleepLength.getTime() ); // five minutes
}
catch( InterruptedException e )
{
}
}
}
}