From: Sasa M. <sa...@us...> - 2004-10-19 10:14:20
|
Update of /cvsroot/jrobin/src/org/jrobin/core In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14900/org/jrobin/core Modified Files: RrdBackend.java RrdDb.java RrdDbPool.java RrdFileBackend.java RrdNioBackend.java Log Message: Major improvement to NIO backend: it is now possible to discard MappedByteBuffer without invoking System.gc(). As a result, the code is much simpler, cleaner and safer. HACK: to discard MappedByteBuffer buffer, use: ((DirectBuffer) buffer).cleaner.clean(); Index: RrdDbPool.java =================================================================== RCS file: /cvsroot/jrobin/src/org/jrobin/core/RrdDbPool.java,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** RrdDbPool.java 27 Sep 2004 14:32:13 -0000 1.17 --- RrdDbPool.java 19 Oct 2004 10:14:03 -0000 1.18 *************** *** 107,125 **** // singleton pattern private static RrdDbPool ourInstance; ! static { ! Runtime.getRuntime().addShutdownHook(new Thread(CLOSING_THREAD_NAME) { ! public void run() { ! if(ourInstance != null) { ! try { ! ourInstance.close(); ! } ! catch (IOException e) { ! e.printStackTrace(); ! } ! } } ! }); ! } /** --- 107,122 ---- // singleton pattern private static RrdDbPool ourInstance; + private boolean closingOnExit = true; ! private Thread shutdownHook = new Thread(CLOSING_THREAD_NAME) { ! public void run() { ! try { ! close(); } ! catch (IOException e) { ! e.printStackTrace(); ! } ! } ! }; /** *************** *** 164,168 **** private RrdDbPool() { ! // just to satisfy the singleton pattern } --- 161,192 ---- private RrdDbPool() { ! setClosingOnExit(closingOnExit); ! } ! ! /** ! * Checks the exiting behaviour of RrdDbPool. ! * @return <code>True</code>, if all RRD files are to be closed ! * when application invokes <code>System.exit()</code>. ! * <code>False</code> otherwise. The default behaviour is <code>true</code> ! * (all RRD files will be closed on exit). ! */ ! public synchronized boolean isClosingOnExit() { ! return closingOnExit; ! } ! ! /** ! * Sets the exiting behaviour of RrdDbPool. ! * @param closingOnExit <code>True</code>, if all RRD files are to be closed ! * when application invokes <code>System.exit()</code>. ! * <code>False</code> otherwise. The default behaviour is <code>true</code> ! * (all RRD files will be closed on exit). ! */ ! public synchronized void setClosingOnExit(boolean closingOnExit) { ! Runtime runtime = Runtime.getRuntime(); ! runtime.removeShutdownHook(shutdownHook); ! if(closingOnExit) { ! runtime.addShutdownHook(shutdownHook); ! } ! this.closingOnExit = closingOnExit; } Index: RrdBackend.java =================================================================== RCS file: /cvsroot/jrobin/src/org/jrobin/core/RrdBackend.java,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** RrdBackend.java 18 Oct 2004 13:09:33 -0000 1.8 --- RrdBackend.java 19 Oct 2004 10:14:02 -0000 1.9 *************** *** 137,151 **** /** ! * Closes the underlying storage. Calls sync() implicitly. ! * In other words, you don't have to call sync() before close() in order to preserve ! * data cached in memory. * @throws IOException Thrown in case of I/O error */ public void close() throws IOException { - sync(); } /** ! * Method called by the framework immediatelly before RRD update operation starts. This method * does nothing, but can be overriden in subclasses. */ --- 137,148 ---- /** ! * Closes the underlying backend. * @throws IOException Thrown in case of I/O error */ public void close() throws IOException { } /** ! * Method called by the framework immediately before RRD update operation starts. This method * does nothing, but can be overriden in subclasses. */ *************** *** 154,158 **** /** ! * Method called by the framework immediatelly after RRD update operation is completed. This method * does nothing, but can be overriden in subclasses. */ --- 151,155 ---- /** ! * Method called by the framework immediately after RRD update operation is completed. This method * does nothing, but can be overriden in subclasses. */ *************** *** 161,165 **** /** ! * Method called by the framework immediatelly before RRD fetch operation starts. This method * does nothing, but can be overriden in subclasses. */ --- 158,162 ---- /** ! * Method called by the framework immediately before RRD fetch operation starts. This method * does nothing, but can be overriden in subclasses. */ *************** *** 168,172 **** /** ! * Method called by the framework immediatelly after RRD fetch operation is completed. This method * does nothing, but can be overriden in subclasses. */ --- 165,169 ---- /** ! * Method called by the framework immediately after RRD fetch operation is completed. This method * does nothing, but can be overriden in subclasses. */ *************** *** 175,179 **** /** ! * Method called by the framework immediatelly after RrdDb obejct is created. This method * does nothing, but can be overriden in subclasses. */ --- 172,176 ---- /** ! * Method called by the framework immediately after RrdDb obejct is created. This method * does nothing, but can be overriden in subclasses. */ *************** *** 188,192 **** * @throws IOException Thrown in case of I/O error */ ! public void sync() throws IOException { } --- 185,197 ---- * @throws IOException Thrown in case of I/O error */ ! protected void sync() throws IOException { ! } ! ! /** ! * Method called by the framework when a RrdDb file is about to be closed. This method ! * calls {@link #sync()} internally. ! */ ! public void beforeClose() throws IOException { ! sync(); } Index: RrdNioBackend.java =================================================================== RCS file: /cvsroot/jrobin/src/org/jrobin/core/RrdNioBackend.java,v retrieving revision 1.18 retrieving revision 1.19 diff -C2 -d -r1.18 -r1.19 *** RrdNioBackend.java 18 Oct 2004 13:09:33 -0000 1.18 --- RrdNioBackend.java 19 Oct 2004 10:14:03 -0000 1.19 *************** *** 27,34 **** import java.io.IOException; - import java.nio.channels.FileChannel; import java.nio.MappedByteBuffer; ! import java.util.TimerTask; import java.util.Timer; /** --- 27,35 ---- import java.io.IOException; import java.nio.MappedByteBuffer; ! import java.nio.channels.FileChannel; ! import sun.nio.ch.DirectBuffer; import java.util.Timer; + import java.util.TimerTask; /** *************** *** 37,73 **** */ public class RrdNioBackend extends RrdFileBackend { ! /** ! * Defines <code>System.gc()</code> usage policy for this backend.<p> ! * ! * NIO backend uses potentially large in-memory buffer to cache file data. ! * The buffer remains 'active' (by prohibiting file re-creation with the smaller file size) ! * as long as it is not garbage-collected. By forcing <code>System.gc()</code> call where ! * appropriate, this backend will free in-memory buffers sooner and file re-creation won't fail.<p> ! * ! * The constant is set to <b><code>true</code></b> initially and currently there is no ! * API to change it during runtime. ! * ! * Garbage collection will be forced only in some special circumstances. ! * It should not affect the speed of your application significantly.<p> ! */ ! public static final boolean SHOULD_GC = true; ! ! private static final Timer syncTimer = new Timer(true); private int syncMode; - MappedByteBuffer byteBuffer; private TimerTask syncTask; protected RrdNioBackend(String path, boolean readOnly, int lockMode, int syncMode, int syncPeriod) throws IOException { super(path, readOnly, lockMode); - map(readOnly); this.syncMode = syncMode; if(syncMode == RrdNioBackendFactory.SYNC_BACKGROUND && !readOnly) { ! createSyncTask(syncPeriod); } } ! private void map(boolean readOnly) throws IOException { long length = getLength(); if(length > 0) { --- 38,72 ---- */ public class RrdNioBackend extends RrdFileBackend { ! private static final Timer fileSyncTimer = new Timer(true); + private MappedByteBuffer byteBuffer; private int syncMode; private TimerTask syncTask; + /** + * Creates RrdFileBackend object for the given file path, backed by java.nio.* classes. + * @param path Path to a file + * @param readOnly True, if file should be open in a read-only mode. False otherwise + * @param lockMode Locking mode, as described in {@link RrdDb#getLockMode()} + * @param syncMode See {@link RrdNioBackendFactory#setSyncMode(int)} for explanation + * @param syncPeriod See {@link RrdNioBackendFactory#setSyncMode(int)} for explanation + * @throws IOException Thrown in case of I/O error + */ protected RrdNioBackend(String path, boolean readOnly, int lockMode, int syncMode, int syncPeriod) throws IOException { super(path, readOnly, lockMode); this.syncMode = syncMode; + mapFile(); if(syncMode == RrdNioBackendFactory.SYNC_BACKGROUND && !readOnly) { ! syncTask = new TimerTask() { ! public void run() { ! sync(); ! } ! }; ! fileSyncTimer.schedule(syncTask, syncPeriod * 1000L, syncPeriod * 1000L); } } ! private void mapFile() throws IOException { long length = getLength(); if(length > 0) { *************** *** 76,91 **** byteBuffer = channel.map(mapMode, 0, length); } - else { - byteBuffer = null; - } } ! private void createSyncTask(int syncPeriod) { ! syncTask = new TimerTask() { ! public void run() { ! sync(); ! } ! }; ! syncTimer.schedule(syncTask, syncPeriod * 1000L, syncPeriod * 1000L); } --- 75,85 ---- byteBuffer = channel.map(mapMode, 0, length); } } ! private void unmapFile() { ! if(byteBuffer != null) { ! ((DirectBuffer) byteBuffer).cleaner().clean(); ! byteBuffer = null; ! } } *************** *** 96,109 **** * @throws IOException Thrown in case of I/O error. */ ! protected void setLength(long newLength) throws IOException { ! if(newLength < getLength()) { ! // the file will be truncated ! if(SHOULD_GC) { ! byteBuffer = null; ! System.gc(); ! } ! } super.setLength(newLength); ! map(false); } --- 90,97 ---- * @throws IOException Thrown in case of I/O error. */ ! protected synchronized void setLength(long newLength) throws IOException { ! unmapFile(); super.setLength(newLength); ! mapFile(); } *************** *** 113,121 **** * @param b Bytes to be written. */ ! protected void write(long offset, byte[] b) { ! synchronized(byteBuffer) { byteBuffer.position((int)offset); byteBuffer.put(b); } } --- 101,112 ---- * @param b Bytes to be written. */ ! protected synchronized void write(long offset, byte[] b) throws IOException { ! if(byteBuffer != null) { byteBuffer.position((int)offset); byteBuffer.put(b); } + else { + throw new IOException("Write failed, file " + getPath() + " not mapped for I/O"); + } } *************** *** 125,133 **** * @param b Buffer which receives bytes read from the file. */ ! protected void read(long offset, byte[] b) { ! synchronized(byteBuffer) { byteBuffer.position((int)offset); byteBuffer.get(b); } } --- 116,127 ---- * @param b Buffer which receives bytes read from the file. */ ! protected synchronized void read(long offset, byte[] b) throws IOException { ! if(byteBuffer != null) { byteBuffer.position((int)offset); byteBuffer.get(b); } + else { + throw new IOException("Read failed, file " + getPath() + " not mapped for I/O"); + } } *************** *** 136,149 **** * @throws IOException Thrown in case of I/O error */ ! public void close() throws IOException { // cancel synchronization if(syncTask != null) { syncTask.cancel(); } ! // synchronize with the disk for the last time ! sync(); ! // release the buffer, make it eligible for GC as soon as possible ! byteBuffer = null; ! // close the underlying file super.close(); } --- 130,139 ---- * @throws IOException Thrown in case of I/O error */ ! public synchronized void close() throws IOException { // cancel synchronization if(syncTask != null) { syncTask.cancel(); } ! unmapFile(); super.close(); } *************** *** 151,164 **** /** * This method forces all data cached in memory but not yet stored in the file, ! * to be stored in it. RrdNioBackend uses (a lot of) memory to cache I/O data. ! * This method is automatically invoked when the {@link #close()} ! * method is called. In other words, you don't have to call sync() before you call close().<p> */ ! public void sync() { if(byteBuffer != null) { ! synchronized(byteBuffer) { ! // System.out.println("** SYNC **"); ! byteBuffer.force(); ! } } } --- 141,149 ---- /** * This method forces all data cached in memory but not yet stored in the file, ! * to be stored in it. */ ! protected synchronized void sync() { if(byteBuffer != null) { ! byteBuffer.force(); } } Index: RrdDb.java =================================================================== RCS file: /cvsroot/jrobin/src/org/jrobin/core/RrdDb.java,v retrieving revision 1.30 retrieving revision 1.31 diff -C2 -d -r1.30 -r1.31 *** RrdDb.java 21 Sep 2004 08:42:09 -0000 1.30 --- RrdDb.java 19 Oct 2004 10:14:02 -0000 1.31 *************** *** 421,426 **** public synchronized void close() throws IOException { if(!closed) { - backend.close(); closed = true; } } --- 421,427 ---- public synchronized void close() throws IOException { if(!closed) { closed = true; + backend.beforeClose(); + backend.close(); } } Index: RrdFileBackend.java =================================================================== RCS file: /cvsroot/jrobin/src/org/jrobin/core/RrdFileBackend.java,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** RrdFileBackend.java 18 Oct 2004 13:09:33 -0000 1.10 --- RrdFileBackend.java 19 Oct 2004 10:14:03 -0000 1.11 *************** *** 40,55 **** */ public class RrdFileBackend extends RrdBackend { ! static final long LOCK_DELAY = 100; // 0.1sec private static HashSet openFiles = new HashSet(); ! private boolean readOnly; ! private int lockMode; ! private boolean closed = false; protected RandomAccessFile file; protected FileChannel channel; protected FileLock fileLock; protected RrdFileBackend(String path, boolean readOnly, int lockMode) throws IOException { super(path); --- 40,66 ---- */ public class RrdFileBackend extends RrdBackend { ! private static final long LOCK_DELAY = 100; // 0.1sec private static HashSet openFiles = new HashSet(); ! /** read/write file status */ ! protected boolean readOnly; ! /** locking mode */ ! protected int lockMode; + /** radnom access file handle */ protected RandomAccessFile file; + /** file channel used to create locks */ protected FileChannel channel; + /** file lock */ protected FileLock fileLock; + /** + * Creates RrdFileBackend object for the given file path, backed by RandomAccessFile object. + * @param path Path to a file + * @param readOnly True, if file should be open in a read-only mode. False otherwise + * @param lockMode Locking mode, as described in {@link RrdDb#getLockMode()} + * @throws IOException Thrown in case of I/O error + */ protected RrdFileBackend(String path, boolean readOnly, int lockMode) throws IOException { super(path); *************** *** 112,122 **** */ public void close() throws IOException { ! if (!closed) { ! unregisterWriter(); ! unlockFile(); ! channel.close(); ! file.close(); ! closed = true; ! } } --- 123,130 ---- */ public void close() throws IOException { ! unregisterWriter(); ! unlockFile(); ! channel.close(); ! file.close(); } *************** *** 142,154 **** /** - * Closes the underlying RRD file if not already closed - * - * @throws IOException Thrown in case of I/O error - */ - protected void finalize() throws IOException { - close(); - } - - /** * Returns canonical path to the file on the disk. * --- 150,153 ---- |