key0-96/
key0-96/doc/key/
key0-96/doc/key/credits/
key0-96/doc/key/developers/
key0-96/doc/key/developers/resources/
key0-96/setup/caves/
key0-96/setup/help/
key0-96/setup/ruins/
key0-96/src/
key0-96/src/commands/
key0-96/src/events/
key0-96/src/hack/
key0-96/src/sql/
key0-96/src/swing/
key0-96/src/talker/forest/
key0-96/src/talker/objects/
key0-96/src/terminals/
/*
**               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 )
			{
			}
		}
	}
}