Bug 359535 - Deadlock on startup when using Spring OSGi
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/loader/ClasspathManager.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/loader/ClasspathManager.java
index da6943d..2a3d0b2 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/loader/ClasspathManager.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/loader/ClasspathManager.java
@@ -47,6 +47,7 @@
private final static String PROP_CLASSLOADER_LOCK = "osgi.classloader.lock"; //$NON-NLS-1$
private final static String VALUE_CLASSNAME_LOCK = "classname"; //$NON-NLS-1$
private final static boolean LOCK_CLASSNAME = VALUE_CLASSNAME_LOCK.equals(FrameworkProperties.getProperty(PROP_CLASSLOADER_LOCK));
+ private final static Class<?>[] NULL_CLASS_RESULT = new Class[2];
private final BaseData data;
private final String[] classpath;
@@ -452,10 +453,10 @@
try {
for (int i = 0; i < hooks.length; i++)
hooks[i].preFindLocalClass(classname, this);
- if (LOCK_CLASSNAME || isParallelClassLoader)
- result = findLocalClass_LockClassName(classname, hooks);
- else
- result = findLocalClass_LockClassLoader(classname, hooks);
+ result = findLoadedClass(classname);
+ if (result != null)
+ return result;
+ result = findLocalClassImpl(classname, hooks);
return result;
} finally {
for (int i = 0; i < hooks.length; i++)
@@ -463,28 +464,23 @@
}
}
- private Class<?> findLocalClass_LockClassName(String classname, ClassLoadingStatsHook[] hooks) throws ClassNotFoundException {
- boolean initialLock = lockClassName(classname);
- try {
- return findLocalClassImpl(classname, hooks);
- } finally {
- if (initialLock)
- unlockClassName(classname);
+ private Class<?> findLoadedClass(String classname) {
+ if (LOCK_CLASSNAME || isParallelClassLoader) {
+ boolean initialLock = lockClassName(classname);
+ try {
+ return classloader.publicFindLoaded(classname);
+ } finally {
+ if (initialLock)
+ unlockClassName(classname);
+ }
}
- }
-
- private Class<?> findLocalClass_LockClassLoader(String classname, ClassLoadingStatsHook[] hooks) throws ClassNotFoundException {
synchronized (classloader) {
- return findLocalClassImpl(classname, hooks);
+ return classloader.publicFindLoaded(classname);
}
}
private Class<?> findLocalClassImpl(String classname, ClassLoadingStatsHook[] hooks) throws ClassNotFoundException {
- // must call findLoadedClass here even if it was called earlier,
- // the findLoadedClass and defineClass calls must be atomic
- Class<?> result = classloader.publicFindLoaded(classname);
- if (result != null)
- return result;
+ Class<?> result = null;
for (int i = 0; i < entries.length; i++) {
if (entries[i] != null) {
result = findClassImpl(classname, entries[i], hooks);
@@ -504,7 +500,7 @@
throw new ClassNotFoundException(classname);
}
- private boolean lockClassName(String classname) throws ClassNotFoundException {
+ private boolean lockClassName(String classname) {
synchronized (classNameLocks) {
Object lockingThread = classNameLocks.get(classname);
Thread current = Thread.currentThread();
@@ -520,7 +516,7 @@
lockingThread = classNameLocks.get(classname);
} catch (InterruptedException e) {
current.interrupt();
- throw new ClassNotFoundException(classname, e);
+ throw (LinkageError) new LinkageError(classname).initCause(e);
}
}
}
@@ -535,7 +531,7 @@
private Class<?> findClassImpl(String name, ClasspathEntry classpathEntry, ClassLoadingStatsHook[] hooks) {
if (Debug.DEBUG_LOADER)
- Debug.println("BundleClassLoader[" + classpathEntry.getBundleFile() + "].findClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
+ Debug.println("BundleClassLoader[" + classpathEntry.getBundleFile() + "].findClassImpl(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
String filename = name.replace('.', '/').concat(".class"); //$NON-NLS-1$
BundleEntry entry = classpathEntry.getBundleFile().getEntry(filename);
if (entry == null)
@@ -549,7 +545,6 @@
Debug.println(" IOException reading " + filename + " from " + classpathEntry.getBundleFile()); //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
-
if (Debug.DEBUG_LOADER) {
Debug.println(" read " + classbytes.length + " bytes from " + classpathEntry.getBundleFile() + "/" + filename); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Debug.println(" defining class " + name); //$NON-NLS-1$
@@ -590,19 +585,45 @@
private Class<?> defineClass(String name, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClassLoadingStatsHook[] statsHooks) {
ClassLoadingHook[] hooks = data.getAdaptor().getHookRegistry().getClassLoadingHooks();
byte[] modifiedBytes = classbytes;
- Class<?> result = null;
+ // The result holds two Class objects.
+ // The first slot to either a pre loaded class or the newly defined class.
+ // The second slot is only set to a newly defined class object if it was successfully defined
+ Class<?>[] result = NULL_CLASS_RESULT;
try {
for (int i = 0; i < hooks.length; i++) {
modifiedBytes = hooks[i].processClass(name, classbytes, classpathEntry, entry, this);
if (modifiedBytes != null)
classbytes = modifiedBytes;
}
-
- result = classloader.defineClass(name, classbytes, classpathEntry, entry);
+ if (LOCK_CLASSNAME || isParallelClassLoader) {
+ boolean initialLock = lockClassName(name);
+ try {
+ result = defineClassHoldingLock(name, classbytes, classpathEntry, entry);
+ } finally {
+ if (initialLock)
+ unlockClassName(name);
+ }
+ } else {
+ synchronized (classloader) {
+ result = defineClassHoldingLock(name, classbytes, classpathEntry, entry);
+ }
+ }
} finally {
for (int i = 0; i < statsHooks.length; i++)
- statsHooks[i].recordClassDefine(name, result, classbytes, classpathEntry, entry, this);
+ // only pass the newly defined class to the hook
+ statsHooks[i].recordClassDefine(name, result[1], classbytes, classpathEntry, entry, this);
}
+ // return either the pre-loaded class or the newly defined class
+ return result[0];
+ }
+
+ private Class<?>[] defineClassHoldingLock(String name, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry) {
+ Class<?>[] result = new Class[2];
+ // must call findLoadedClass here even if it was called earlier,
+ // the findLoadedClass and defineClass calls must be atomic
+ result[0] = classloader.publicFindLoaded(name);
+ if (result[0] == null)
+ result[0] = result[1] = classloader.defineClass(name, classbytes, classpathEntry, entry);
return result;
}