Bug 579459 - fix ConcurrentModificationException getActiveModels
Synchronize all public method which (indirectly) use fEntries
Change-Id: I23a26cd69d9aed12309b8d7094c74aa00fb14323
Reviewed-on: https://git.eclipse.org/r/c/pde/eclipse.pde.ui/+/192298
Tested-by: PDE Bot <pde-bot@eclipse.org>
Reviewed-by: Andrey Loskutov <loskutov@gmx.de>
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PluginModelManager.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PluginModelManager.java
index 8cd46e9..5b713b2 100644
--- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PluginModelManager.java
+++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PluginModelManager.java
@@ -191,7 +191,15 @@
private final WorkspacePluginModelManager fWorkspaceManager; // keeps track of changes in the workspace
private PDEState fState; // keeps the combined view of the target and workspace
+ /**
+ * only access synchronized with fEntriesSynchronizer
+ **/
private Map<String, LocalModelEntry> fEntries; // a master table keyed by plugin ID and the value is a ModelEntry
+ /**
+ * used to synchronize all public methods which (indirectly) use fEntries
+ **/
+ private final Object fEntriesSynchronizer = new Object();
+
private ArrayList<IPluginModelListener> fListeners; // a list of listeners interested in changes to the plug-in models
private ArrayList<IStateDeltaListener> fStateListeners; // a list of listeners interested in changes to the PDE/resolver State
private boolean fCancelled = false;
@@ -232,6 +240,12 @@
*/
@Override
public void modelsChanged(IModelProviderEvent e) {
+ synchronized (fEntriesSynchronizer) {
+ modelsChangedSynchronized(e);
+ }
+ }
+
+ private void modelsChangedSynchronized(IModelProviderEvent e) {
PluginModelDelta delta = new PluginModelDelta();
// Removes from the master table and the state all workspace plug-ins that have been
@@ -487,7 +501,9 @@
* <code>false</code> otherwise.
*/
public boolean isEmpty() {
- return getEntryTable().isEmpty();
+ synchronized (fEntriesSynchronizer) {
+ return getEntryTable().isEmpty();
+ }
}
/**
@@ -498,7 +514,9 @@
* <code>false</code> otherwise.
*/
public boolean isInitialized() {
- return fEntries != null;
+ synchronized (fEntriesSynchronizer) {
+ return fEntries != null;
+ }
}
/**
@@ -518,8 +536,10 @@
* Clears all existing models and recreates them
*/
public void targetReloaded(IProgressMonitor monitor) {
- fEntries = null;
- initializeTable(monitor);
+ synchronized (fEntriesSynchronizer) {
+ fEntries = null;
+ initializeTable(monitor);
+ }
}
/**
@@ -534,14 +554,8 @@
return fEntries;
}
- /**
- *
- * This method must be synchronized so that only one thread
- * initializes the table, and the rest would block until
- * the table is initialized.
- *
- */
- private synchronized void initializeTable(IProgressMonitor monitor) {
+ /** Has to be called synchronized with fEntriesSynchronizer **/
+ private void initializeTable(IProgressMonitor monitor) {
if (fEntries != null) {
return;
}
@@ -567,7 +581,7 @@
long startTime = System.currentTimeMillis();
// Cannot assign to fEntries here - will create a race condition with isInitialized()
- Map<String, LocalModelEntry> entries = Collections.synchronizedMap(new TreeMap<String, LocalModelEntry>());
+ Map<String, LocalModelEntry> entries = new TreeMap<>();
fCancelled = false;
ITargetDefinition unresolvedRepoBasedtarget = null;
@@ -732,11 +746,11 @@
*
* @param model the workspace model
*/
- private synchronized void addWorkspaceBundleToState(IPluginModelBase model) {
+ private void addWorkspaceBundleToState(IPluginModelBase model) {
addWorkspaceBundleToState(fEntries, model);
}
- private synchronized void addWorkspaceBundleToState(Map<String, LocalModelEntry> entries, IPluginModelBase model) {
+ private void addWorkspaceBundleToState(Map<String, LocalModelEntry> entries, IPluginModelBase model) {
String id = model.getPluginBase().getId();
if (id == null) {
return;
@@ -1009,10 +1023,12 @@
* @return a model entry containing all workspace and target plug-ins by the given ID
*/
public ModelEntry findEntry(String id) {
- if ("system.bundle".equals(id)) { //$NON-NLS-1$
- id = getSystemBundleId();
+ synchronized (fEntriesSynchronizer) {
+ if ("system.bundle".equals(id)) { //$NON-NLS-1$
+ id = getSystemBundleId();
+ }
+ return id == null ? null : (ModelEntry) getEntryTable().get(id);
}
- return id == null ? null : (ModelEntry) getEntryTable().get(id);
}
/**
@@ -1050,8 +1066,10 @@
* is not a plug-in project
*/
public IPluginModelBase findModel(IProject project) {
- initializeTable(null);
- return fWorkspaceManager.getModel(project);
+ synchronized (fEntriesSynchronizer) {
+ initializeTable(null);
+ return fWorkspaceManager.getModel(project);
+ }
}
/**
@@ -1102,19 +1120,21 @@
* (possibly) fragments that are checked on the Target Platform preference page.
*/
public IPluginModelBase[] getActiveModels(boolean includeFragments) {
- int size = getEntryTable().size();
- ArrayList<IPluginModelBase> result = new ArrayList<>(size);
- Iterator<LocalModelEntry> iter = getEntryTable().values().iterator();
- while (iter.hasNext()) {
- ModelEntry entry = iter.next();
- IPluginModelBase[] models = entry.getActiveModels();
- for (IPluginModelBase model : models) {
- if (model instanceof IPluginModel || includeFragments) {
- result.add(model);
+ synchronized (fEntriesSynchronizer) {
+ int size = getEntryTable().size();
+ ArrayList<IPluginModelBase> result = new ArrayList<>(size);
+ Iterator<LocalModelEntry> iter = getEntryTable().values().iterator();
+ while (iter.hasNext()) {
+ ModelEntry entry = iter.next();
+ IPluginModelBase[] models = entry.getActiveModels();
+ for (IPluginModelBase model : models) {
+ if (model instanceof IPluginModel || includeFragments) {
+ result.add(model);
+ }
}
}
+ return result.toArray(new IPluginModelBase[result.size()]);
}
- return result.toArray(new IPluginModelBase[result.size()]);
}
/**
@@ -1154,19 +1174,22 @@
* checked on the Target Platform preference page.
*/
public IPluginModelBase[] getAllModels(boolean includeFragments) {
- int size = getEntryTable().size();
- ArrayList<IPluginModelBase> result = new ArrayList<>(size);
- Iterator<LocalModelEntry> iter = getEntryTable().values().iterator();
- while (iter.hasNext()) {
- ModelEntry entry = iter.next();
- IPluginModelBase[] models = entry.hasWorkspaceModels() ? entry.getWorkspaceModels() : entry.getExternalModels();
- for (IPluginModelBase model : models) {
- if (model instanceof IPluginModel || includeFragments) {
- result.add(model);
+ synchronized (fEntriesSynchronizer) {
+ int size = getEntryTable().size();
+ ArrayList<IPluginModelBase> result = new ArrayList<>(size);
+ Iterator<LocalModelEntry> iter = getEntryTable().values().iterator();
+ while (iter.hasNext()) {
+ ModelEntry entry = iter.next();
+ IPluginModelBase[] models = entry.hasWorkspaceModels() ? entry.getWorkspaceModels()
+ : entry.getExternalModels();
+ for (IPluginModelBase model : models) {
+ if (model instanceof IPluginModel || includeFragments) {
+ result.add(model);
+ }
}
}
+ return result.toArray(new IPluginModelBase[result.size()]);
}
- return result.toArray(new IPluginModelBase[result.size()]);
}
/**
@@ -1175,8 +1198,10 @@
* @return all plug-ins in the target platform
*/
public IPluginModelBase[] getExternalModels() {
- initializeTable(null);
- return fExternalManager.getAllModels();
+ synchronized (fEntriesSynchronizer) {
+ initializeTable(null);
+ return fExternalManager.getAllModels();
+ }
}
/**
@@ -1185,8 +1210,10 @@
* @return all plug-in models in the workspace
*/
public IPluginModelBase[] getWorkspaceModels() {
- initializeTable(null);
- return fWorkspaceManager.getPluginModels();
+ synchronized (fEntriesSynchronizer) {
+ initializeTable(null);
+ return fWorkspaceManager.getPluginModels();
+ }
}
/**
@@ -1195,8 +1222,10 @@
* @return the model manager that keeps track of plug-ins in the target platform
*/
public ExternalModelManager getExternalModelManager() {
- initializeTable(null);
- return fExternalManager;
+ synchronized (fEntriesSynchronizer) {
+ initializeTable(null);
+ return fExternalManager;
+ }
}
/**
@@ -1204,8 +1233,10 @@
* that form the current PDE state
*/
public PDEState getState() {
- initializeTable(null);
- return fState;
+ synchronized (fEntriesSynchronizer) {
+ initializeTable(null);
+ return fState;
+ }
}
/**