Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 36358 - Classloading deadlock between two threads of same plugin
Summary: Classloading deadlock between two threads of same plugin
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Resources (show other bugs)
Version: 2.1   Edit
Hardware: PC Windows 2000
: P3 major (vote)
Target Milestone: 3.0   Edit
Assignee: Platform-Resources-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2003-04-10 12:07 EDT by John Arthorne CLA
Modified: 2003-05-09 10:40 EDT (History)
2 users (show)

See Also:


Attachments
deadlock.zip, Simplified plugin to reproduce problem (3.04 KB, application/octet-stream)
2003-04-10 14:44 EDT, John Arthorne CLA
no flags Details
Patch file on org.eclipse.core.runtime (2.24 KB, patch)
2003-04-10 14:52 EDT, John Arthorne CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description John Arthorne CLA 2003-04-10 12:07:15 EDT
Build: 2.1

A deadlock can occur if a Plugin.startup() method starts another thread, and
then both threads attempt to load classes with the same classloader.  Here is an
example stack trace:

Thread [Thread-1] (Suspended)
	FileWatcherPlugin.watchResource(IResource) line: 136
	FileWatcherPlugin.watchProjects() line: 112
	FileWatcherPlugin.startup() line: 78
	PluginDescriptor$1.run() line: 728
	InternalPlatform.run(ISafeRunnable) line: 889
	PluginDescriptor.internalDoPluginActivation() line: 740
	PluginDescriptor.doPluginActivation() line: 188
	PluginDescriptor.getPlugin() line: 301
	Workbench$15.run() line: 1325
	InternalPlatform.run(ISafeRunnable) line: 889
	Platform.run(ISafeRunnable) line: 413
	Workbench$14.run() line: 1334
	Thread.run() line: 534 [local variables unavailable]


Thread [Thread-2] (Suspended)
	PluginDescriptor.isPluginActivated() line: 758
	PluginClassLoader.internalFindClassParentsSelf(String, boolean,
DelegatingURLClassLoader, boolean) line: 164
	PluginClassLoader(DelegatingURLClassLoader).findClassParentsSelf(String,
boolean, DelegatingURLClassLoader, boolean) line: 485
	PluginClassLoader(DelegatingURLClassLoader).loadClass(String, boolean,
DelegatingURLClassLoader, Vector, boolean) line: 882
	PluginClassLoader(DelegatingURLClassLoader).loadClass(String, boolean) line: 862
	PluginClassLoader(ClassLoader).loadClass(String) line: 235 [local variables
unavailable]
	PluginClassLoader(ClassLoader).loadClassInternal(String) line: 302 [local
variables unavailable]
	ResourceUpdateQueue$UpdateTimerTask.run() line: 25
	TimerThread.mainLoop() line: 432 [local variables unavailable]
	TimerThread.run() line: 382 [local variables unavailable]


Thread-1 is trying to load a class, but the class loader is busy
Thread-2 is waiting on the PluginDescriptor lock, which is held by Thread-1
Comment 1 John Arthorne CLA 2003-04-10 12:42:33 EDT
I understand what is happening.

Usually, plugin activation happens as a side-effect of class loading.  In this
case, the plugin is being activated eagerly because it's a participant in the
startup extension point.  This means the flag

PluginClassLoader.pluginActivationInProgress = false

because the PluginClassLoader does not know that an activation is in progress.
So, when Thread-2 tries to load classes, it gets past this flag and calls
descriptor.isPluginActivated().  This method is synchronized, and Thread-1 holds
the lock.  When Thread-1 tries to do classloading, it cannot because Thread-2 is
in a classloading sync block.

I believe the solution should be to notify the PluginClassLoader that an
activation is in progress in the explicit activation case. I.e.,
PluginDescriptor should set the flag pluginActivationInProgress to true, so that
the class loader in Thread-2 does not block unnecessarily.

Matt, even after we fix this, I should advise you that your code is a bit
dangerous.  It would be safer if you fork that thread in the very last line of
your startup() method.  That would ensure there is no contention between that
thread's activity and the activity happening in your startup method.  Since
startup() already happens in a sync block, it would only take one more monitor
to get deadlock.  There is a comment in Plugin.startup() javadoc that mentions
this possibility.
Comment 2 John Arthorne CLA 2003-04-10 14:44:46 EDT
Created attachment 4540 [details]
deadlock.zip, Simplified plugin to reproduce problem

Attached is a plugin that reproduces the problem.  It implements the early
startup extension point.  In startup(), it simply forks a thread that tries to
load a class, and then tries to load another class.
Comment 3 John Arthorne CLA 2003-04-10 14:52:12 EDT
Created attachment 4542 [details]
Patch file on org.eclipse.core.runtime

This patch is a proposed fix.  It adds a method isActivationInProgress() to
PluginDescriptor.  The PluginClassLoader checks if activation is in progress
before attempting to activate the plugin:

if (pluginActivationInProgress || descriptor.isActivationInProgress() ||
descriptor.isPluginActivated()) {

Also, I've made the "activePending" field volatile.  This ensures the variable
will have the same value in multiple threads.
Comment 4 John Arthorne CLA 2003-05-09 10:40:55 EDT
Released the fix to HEAD