Author: toad
Date: 2008-07-29 19:42:49 +0000 (Tue, 29 Jul 2008)
New Revision: 21491

Modified:
   branches/db4o/freenet/src/freenet/node/NodeClientCore.java
   branches/db4o/freenet/src/freenet/node/NodeInitException.java
Log:
Protect the integrity of the database on an OOM by shutting down the database 
thread, and then shutting down completely.

Modified: branches/db4o/freenet/src/freenet/node/NodeClientCore.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/NodeClientCore.java  2008-07-29 
18:52:00 UTC (rev 21490)
+++ branches/db4o/freenet/src/freenet/node/NodeClientCore.java  2008-07-29 
19:42:49 UTC (rev 21491)
@@ -59,6 +59,8 @@
 import freenet.support.Base64;
 import freenet.support.Executor;
 import freenet.support.Logger;
+import freenet.support.OOMHandler;
+import freenet.support.OOMHook;
 import freenet.support.PrioritizedSerialExecutor;
 import freenet.support.SerialExecutor;
 import freenet.support.SimpleFieldSet;
@@ -79,7 +81,7 @@
 /**
  * The connection between the node and the client layer.
  */
-public class NodeClientCore implements Persistable, DBJobRunner {
+public class NodeClientCore implements Persistable, DBJobRunner, OOMHook {

        private static boolean logMINOR;
        public final USKManager uskManager;
@@ -382,6 +384,7 @@
                }

                fecQueue.init(RequestStarter.NUMBER_OF_PRIORITY_CLASSES, 
FEC_QUEUE_CACHE_SIZE, clientContext.jobRunner, node.executor, clientContext);
+               OOMHandler.addOOMHook(this);
        }

        private static String l10n(String key) {
@@ -1189,6 +1192,8 @@
                        this.clientDatabaseExecutor.execute(new 
DBJobWrapper(job), priority, ""+job);
        }

+       private boolean killedDatabase = false;
+       
        class DBJobWrapper implements Runnable {

                DBJobWrapper(DBJob job) {
@@ -1201,10 +1206,24 @@
                public void run() {

                        try {
+                               synchronized(NodeClientCore.this) {
+                                       if(killedDatabase) {
+                                               Logger.error(this, "Database 
killed already, not running job");
+                                               return;
+                                       }
+                               }
                                if(job == null) throw new 
NullPointerException();
                                if(node == null) throw new 
NullPointerException();
                                job.run(node.db, clientContext);
-                               node.db.commit();
+                               boolean killed;
+                               synchronized(NodeClientCore.this) {
+                                       killed = killedDatabase;
+                               }
+                               if(killed) {
+                                       node.db.rollback();
+                                       return;
+                               } else
+                                       node.db.commit();
                                if(Logger.shouldLog(Logger.MINOR, this)) 
Logger.minor(this, "COMMITTED");
                                LinkedList toFree = 
persistentTempBucketFactory.grabBucketsToFree();
                                for(Iterator i=toFree.iterator();i.hasNext();) {
@@ -1216,7 +1235,21 @@
                                        }
                                }
                        } catch (Throwable t) {
-                               Logger.error(this, "Failed to run database job 
"+job+" : caught "+t, t);
+                               if(t instanceof OutOfMemoryError) {
+                                       synchronized(NodeClientCore.this) {
+                                               killedDatabase = true;
+                                       }
+                                       OOMHandler.handleOOM((OutOfMemoryError) 
t);
+                               } else {
+                                       Logger.error(this, "Failed to run 
database job "+job+" : caught "+t, t);
+                               }
+                               boolean killed;
+                               synchronized(NodeClientCore.this) {
+                                       killed = killedDatabase;
+                               }
+                               if(killed) {
+                                       node.db.rollback();
+                               }                               
                        }
                }

@@ -1239,4 +1272,16 @@
        public int getQueueSize(int priority) {
                return clientDatabaseExecutor.getQueueSize(priority);
        }
+
+       public void handleLowMemory() throws Exception {
+               // Ignore
+       }
+
+       public void handleOutOfMemory() throws Exception {
+               synchronized(this) {
+                       killedDatabase = true;
+               }
+               System.err.println("Out of memory: Emergency shutdown to 
protect database integrity in progress...");
+               
System.exit(NodeInitException.EXIT_OUT_OF_MEMORY_PROTECTING_DATABASE);
+       }
 }

Modified: branches/db4o/freenet/src/freenet/node/NodeInitException.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/NodeInitException.java       
2008-07-29 18:52:00 UTC (rev 21490)
+++ branches/db4o/freenet/src/freenet/node/NodeInitException.java       
2008-07-29 19:42:49 UTC (rev 21491)
@@ -36,6 +36,7 @@
        public static final int EXIT_STORE_FILE_NOT_FOUND = 1;
        public static final int EXIT_NODE_UPPER_LIMIT = 1024;
        public static final int EXIT_BROKE_WRAPPER_CONF = 28;
+       public static final int EXIT_OUT_OF_MEMORY_PROTECTING_DATABASE = 29;
        private static final long serialVersionUID = -1;

        NodeInitException(int exitCode, String msg) {


Reply via email to