You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2009/06/26 14:16:13 UTC

svn commit: r788670 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/services/monitor/ testing/org/apache/derbyTesting/functionTests/tests/engine/

Author: kahatlen
Date: Fri Jun 26 12:16:13 2009
New Revision: 788670

URL: http://svn.apache.org/viewvc?rev=788670&view=rev
Log:
DERBY-2074: NullPointerException when two threads load sort factory concurrently

Added a flag in ModuleInstance that tells whether the module is fully
booted. This flag is checked when we go through the list of modules,
so that we can avoid using partly booted instances.

Added:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/ModuleLoadingTest.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/ModuleInstance.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/TopService.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/ModuleInstance.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/ModuleInstance.java?rev=788670&r1=788669&r2=788670&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/ModuleInstance.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/ModuleInstance.java Fri Jun 26 12:16:13 2009
@@ -56,6 +56,9 @@
 	*/
 	protected Object	service;
 
+    /** Flag that tells whether booting of the module has completed. */
+    private boolean booted;
+
 	/*
 	** Constructor
 	*/
@@ -111,4 +114,20 @@
 	protected Object getInstance() {
 		return instance;
 	}
+
+    /**
+     * Set a flag that indicates that booting of the module has completed.
+     */
+    synchronized void setBooted() {
+        booted = true;
+    }
+
+    /**
+     * Check whether booting of the module has completed.
+     * @return {@code true} if the module has been booted, or {@code false}
+     * otherwise
+     */
+    synchronized boolean isBooted() {
+        return booted;
+    }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/TopService.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/TopService.java?rev=788670&r1=788669&r2=788670&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/TopService.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/TopService.java Fri Jun 26 12:16:13 2009
@@ -255,6 +255,16 @@
 			for (int i = 0; i < moduleInstances.size(); i++) {
 				ModuleInstance module = (ModuleInstance) moduleInstances.elementAt(i);
 
+                // DERBY-2074: The module has not been properly booted, so we
+                // cannot yet determine whether or not this is a module we can
+                // use. Assume that we cannot use it and continue looking. We
+                // may end up booting the module twice if the assumption
+                // doesn't hold, but we'll detect and resolve that later when
+                // we call addToProtocol().
+                if (!module.isBooted()) {
+                    continue;
+                }
+
 				if (!module.isTypeAndName((PersistentService) null, key.getFactoryInterface(), key.getIdentifier()))
 					continue;
 
@@ -294,6 +304,8 @@
 			throw se;
 		}
 
+        module.setBooted();
+
 		synchronized (this) {
 
 

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/ModuleLoadingTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/ModuleLoadingTest.java?rev=788670&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/ModuleLoadingTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/ModuleLoadingTest.java Fri Jun 26 12:16:13 2009
@@ -0,0 +1,138 @@
+/*
+ * Derby - Class org.apache.derbyTesting.functionTests.tests.engine.ModuleLoadingTest
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.derbyTesting.functionTests.tests.engine;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.util.ArrayList;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.JDBC;
+import org.apache.derbyTesting.junit.TestConfiguration;
+
+/**
+ * This class contains tests for correct loading (booting) of modules
+ * and factories.
+ */
+public class ModuleLoadingTest extends BaseJDBCTestCase {
+    public ModuleLoadingTest(String name) {
+        super(name);
+    }
+
+    public static Test suite() {
+        TestSuite ts = new TestSuite();
+
+        // Run concurrentLoadingOfSortFactory in a separate database so that
+        // the sort factory isn't already loaded.
+        ts.addTest(TestConfiguration.singleUseDatabaseDecorator(
+                new ModuleLoadingTest("concurrentLoadingOfSortFactory")));
+
+        return ts;
+    }
+
+    /**
+     * Test case for DERBY-2074. When multiple threads tried to load
+     * ExternalSortFactory concurrently, we sometimes got a
+     * NullPointerException.
+     */
+    public void concurrentLoadingOfSortFactory() throws Throwable {
+        // number of concurrent threads
+        final int numThreads = 10;
+
+        // Helper object to make it easier to refer to ModuleLoadingTest.this
+        // from within the nested Runnable class. Used for synchronization
+        // between the threads.
+        final Object me = this;
+
+        // Flag that tells the threads whether they're allowed to start.
+        final boolean[] go = new boolean[1];
+        // Active threads count.
+        final int[] activeThreads = new int[1];
+        // List of exceptions/throwables thrown by the forked threads.
+        final ArrayList exceptions = new ArrayList();
+
+        Thread[] threads = new Thread[numThreads];
+
+        // Start the threads.
+        for (int i = 0; i < numThreads; i++) {
+            final Connection c = openDefaultConnection();
+            // Prepare a statement that ends up calling
+            // DistinctScalarAggregateResultSet.loadSorter().
+            final PreparedStatement ps = c.prepareStatement(
+                    "select count(distinct tablename) from sys.systables");
+            threads[i] = new Thread(new Runnable() {
+                public void run() {
+                    try {
+                        _run();
+                    } catch (Throwable t) {
+                        synchronized (me) {
+                            exceptions.add(t);
+                        }
+                    }
+                }
+                private void _run() throws Exception {
+                    synchronized (me) {
+                        // Notify the main thread that we're ready to execute.
+                        activeThreads[0]++;
+                        me.notifyAll();
+
+                        // Wait for the main thread to notify us that we
+                        // should go ahead.
+                        while (!go[0]) {
+                            me.wait();
+                        }
+                    }
+                    // executeQuery() below used to get occational NPEs before
+                    // DERBY-2074.
+                    JDBC.assertDrainResults(ps.executeQuery());
+                    ps.close();
+                    c.close();
+                }
+            });
+            threads[i].start();
+        }
+
+        // We want all threads to execute the statement at the same time,
+        // so wait for all threads to be ready before giving them the GO
+        // signal.
+        synchronized (me) {
+            while (activeThreads[0] < numThreads && exceptions.isEmpty()) {
+                me.wait();
+            }
+
+            // All threads are active, or at least one of the threads have
+            // failed, so tell the threads to stop waiting.
+            go[0] = true;
+            me.notifyAll();
+        }
+
+        // The threads have been started, now wait for them to finish.
+        for (int i = 0; i < numThreads; i++) {
+            threads[i].join();
+        }
+
+        // At least one of the threads failed. Re-throw the first error
+        // reported.
+        if (!exceptions.isEmpty()) {
+            throw (Throwable) exceptions.get(0);
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/ModuleLoadingTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java?rev=788670&r1=788669&r2=788670&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java Fri Jun 26 12:16:13 2009
@@ -47,6 +47,7 @@
         TestSuite suite = new TestSuite("engine");
 
         suite.addTest(ErrorStreamTest.suite());
+        suite.addTest(ModuleLoadingTest.suite());
 
         return suite;
     }