Skip to main content
summaryrefslogtreecommitdiffstats
path: root/qt
diff options
context:
space:
mode:
authorDavid Kaspar2014-03-12 12:59:12 -0400
committerDoug Schaefer2014-03-12 20:50:01 -0400
commit73a912f95b0462e470a4d9b0dc5b0770bce8dd58 (patch)
tree58ffc3fe0a4fea1ef64e9cfb1d51a82eccf19e05 /qt
parente62ed2c8b70a67ac881c897535740d66c514ed92 (diff)
downloadorg.eclipse.cdt-73a912f95b0462e470a4d9b0dc5b0770bce8dd58.tar.gz
org.eclipse.cdt-73a912f95b0462e470a4d9b0dc5b0770bce8dd58.tar.xz
org.eclipse.cdt-73a912f95b0462e470a4d9b0dc5b0770bce8dd58.zip
Bug 429488: Fix for deadlock in QMakeProjectInfo.updateActiveConfiguration
To prevent a deadlock between two the workspace and QMakeProjectInfo.sync locks, QMakeProjectInfo class has been rewritten to not call any method under sync-lock except for IQMakeEnv.init/destroy method. All methods should allow calling under workspace lock. Added a new IQMakeEnv2 interface to provide an explicit init method. Original QMakeProjectInfo has been split to QMakeProjectInfo and QMakeProjectInfoManager. This change is fully backward compatible. Change-Id: I880f22dd9bd999af1d3f47e4a3c4c0ab216b4ad2 Signed-off-by: David Kaspar <dkaspar@blackberry.com> Reviewed-on: https://git.eclipse.org/r/23270 Tested-by: Hudson CI Reviewed-by: Andrew Eidsness <eclipse@jfront.com> Reviewed-by: Doug Schaefer <dschaefer@qnx.com> IP-Clean: Doug Schaefer <dschaefer@qnx.com>
Diffstat (limited to 'qt')
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java19
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java7
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java425
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfoManager.java224
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java24
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java7
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv2.java26
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java2
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java4
9 files changed, 420 insertions, 318 deletions
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java
index 63f6b4aedf..e24476deed 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java
@@ -39,8 +39,6 @@ public final class QMakeEnvProviderDescriptor implements Comparable<QMakeEnvProv
private final AtomicReference<Boolean> evaluation = new AtomicReference<Boolean>();
private final Expression enablementExpression;
- private IQMakeEnvProvider provider;
-
QMakeEnvProviderDescriptor(IConfigurationElement element) {
this.element = element;
@@ -99,17 +97,12 @@ public final class QMakeEnvProviderDescriptor implements Comparable<QMakeEnvProv
if (! matches(controller)) {
return null;
}
- if (provider == null) {
- synchronized (this) {
- if (provider == null) {
- try {
- provider = (IQMakeEnvProvider) element.createExecutableExtension(ATTR_CLASS);
- } catch (CoreException e) {
- QtPlugin.log("Error in class attribute of " + id, e); //$NON-NLS-1$
- return null;
- }
- }
- }
+ IQMakeEnvProvider provider;
+ try {
+ provider = (IQMakeEnvProvider) element.createExecutableExtension(ATTR_CLASS);
+ } catch (CoreException e) {
+ QtPlugin.log("Error in class attribute of " + id, e); //$NON-NLS-1$
+ return null;
}
return provider.createEnv(controller);
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java
index f1758ef934..8b4490922d 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java
@@ -16,6 +16,7 @@ import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.cdt.qt.core.index.IQMakeEnv;
+import org.eclipse.cdt.qt.core.index.IQMakeEnv2;
import org.eclipse.cdt.qt.core.index.IQMakeEnvProvider;
import org.eclipse.cdt.qt.core.index.QMakeEnvInfo;
import org.eclipse.core.resources.IFile;
@@ -77,7 +78,7 @@ public final class QMakeEnvProviderManager {
* Represents a fallback IQMakeEnvProvider that is used for a project that has QtNature
* while there is no registered IQMakeEnvProvider that would provide any IQMakeEnv.
*/
- private static class ConfigurationQMakeEnv implements IQMakeEnv {
+ private static class ConfigurationQMakeEnv implements IQMakeEnv2 {
private static final String PRO_FILE_SUFFIX = ".pro"; //$NON-NLS-1$
private static final String ENV_VAR_QMAKE = "QMAKE"; //$NON-NLS-1$
@@ -89,6 +90,10 @@ public final class QMakeEnvProviderManager {
}
@Override
+ public void init() {
+ }
+
+ @Override
public void destroy() {
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java
index 305c29ccb2..c3cfa866ac 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java
@@ -10,7 +10,6 @@ package org.eclipse.cdt.internal.qt.core.index;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -19,157 +18,90 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.cdt.core.model.CoreModel;
-import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
-import org.eclipse.cdt.core.settings.model.ICDescriptionDelta;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
-import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener;
import org.eclipse.cdt.qt.core.index.IQMakeEnv;
-import org.eclipse.cdt.qt.core.index.IQMakeEnvProvider;
+import org.eclipse.cdt.qt.core.index.IQMakeEnv2;
+import org.eclipse.cdt.qt.core.index.IQMakeEnvProvider.IController;
+import org.eclipse.cdt.qt.core.index.IQMakeInfo;
import org.eclipse.cdt.qt.core.index.IQMakeProjectInfo;
import org.eclipse.cdt.qt.core.index.IQMakeProjectInfoListener;
import org.eclipse.cdt.qt.core.index.QMakeEnvInfo;
-import org.eclipse.cdt.qt.core.index.IQMakeInfo;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IResourceChangeEvent;
-import org.eclipse.core.resources.IResourceChangeListener;
-import org.eclipse.core.resources.IResourceDelta;
-import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
/**
* Represents a QMake project information that is based on an activate project configuration of a specified related IProject.
* Allows to resolve actual information and listen its change.
- * Manages life-cycle of all QMakeProjectInfo instances.
*/
-public final class QMakeProjectInfo implements IQMakeProjectInfo, ICProjectDescriptionListener {
-
- private static final RCListener LISTENER = new RCListener();
- // sync object for CACHE field
- private static final Object SYNC = new Object();
- // a map of all QMakeProjectInfo instances
- private static final Map<IProject,QMakeProjectInfo> CACHE = new HashMap<IProject,QMakeProjectInfo>();
-
- // called by QtPlugin activator to setup this class
- public static final void start() {
- ResourcesPlugin.getWorkspace().addResourceChangeListener(LISTENER, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE);
- }
+public final class QMakeProjectInfo implements IQMakeProjectInfo {
- // called by QtPlugin activator to clean up this class
- public static final void stop() {
- ResourcesPlugin.getWorkspace().removeResourceChangeListener(LISTENER);
- List<QMakeProjectInfo> infos;
- synchronized (SYNC) {
- infos = new ArrayList<QMakeProjectInfo>(CACHE.values());
- CACHE.clear();
- }
- for (QMakeProjectInfo info : infos) {
- if (info != null) {
- info.destroy();
- }
- }
- }
-
- /**
- * Returns a QMakeProjectInfo for an active project configuration of a specified project.
- *
- * @param project the project
- * @return the QMakeProjectInfo; or null if the project does not have QtNature
- */
- public static QMakeProjectInfo getQMakeProjectInfoFor(IProject project) {
- QMakeProjectInfo info;
- synchronized (SYNC) {
- info = CACHE.get(project);
- if (info != null) {
- return info;
- }
- info = new QMakeProjectInfo(project);
- CACHE.put(project,info);
- }
- info.updateActiveConfiguration();
- return info;
- }
+ private final State STATE_FREEZE = new State();
+ private final State STATE_INVALID = new State();
- // removes the project from the CACHE
- private static void removeProjectFromCache(IResource project) {
- QMakeProjectInfo info;
- synchronized (SYNC) {
- info = CACHE.remove(project);
- }
- if (info != null) {
- info.destroy();
- }
- }
+ // listeners
+ private final List<IQMakeProjectInfoListener> listeners = new CopyOnWriteArrayList<IQMakeProjectInfoListener>();
private final IProject project;
- // sync object for all mutable fields
- private final Object sync = new Object();
- // true if this instance still registered in CACHE
- private boolean live = true;
- // a set of sensitive files that might affects actual QMake information
- private final SensitiveSet sensitiveFilePathSet = new SensitiveSet();
- // an active project configuration
- private ControllerImpl activeController;
- // the last calculated QMake info; null if not calculated
- private IQMakeInfo qmakeInfo = null;
- // listeners
- private final List<IQMakeProjectInfoListener> listeners = new CopyOnWriteArrayList<IQMakeProjectInfoListener>();
+ private final Object stateSync = new Object();
- private QMakeProjectInfo(IProject project) {
+ // represents a current state of QMakeProjectInfo
+ private State state = STATE_INVALID;
+
+ QMakeProjectInfo(IProject project) {
this.project = project;
- CoreModel.getDefault().addCProjectDescriptionListener(this, ICDescriptionDelta.ACTIVE_CFG);
}
- // called from removeProjectFromCache only
- private void destroy() {
- synchronized (sync) {
- if (! live) {
- return;
- }
- live = false;
- CoreModel.getDefault().removeCProjectDescriptionListener(this);
- setActiveConfiguration(null);
- qmakeInfo = QMakeInfo.INVALID;
- }
+ void destroy() {
+ setState(STATE_FREEZE);
}
- // must not be called under synchronized (SYNC) or synchronized (sync)
- private void updateActiveConfiguration() {
- synchronized (sync) {
- if (! live) {
+ // must not be called under any QMake-related sync-lock, except for workspace lock
+ private void updateStateFrom(State fromState) {
+ synchronized (stateSync) {
+ if (state != fromState) {
return;
}
- ICProjectDescription projectDescription = CoreModel.getDefault().getProjectDescriptionManager().getProjectDescription(project);
- setActiveConfiguration(projectDescription != null ? projectDescription.getActiveConfiguration() : null);
- qmakeInfo = null;
}
- notifyListeners();
+ updateState();
}
- // called under synchronized (sync)
- private void setActiveConfiguration(ICConfigurationDescription configuration) {
- ControllerImpl previous = activeController;
- activeController = configuration != null ? new ControllerImpl(configuration) : null;
- if (previous != null) {
- previous.destroy();
- }
+ // must not be called under any QMake-related sync-lock, except for workspace lock
+ // we are running outside of synchronized block to prevent deadlock involving workspace lock
+ // this means that theoretically there might be multiple thread calculating the same results but only the last one wins
+ void updateState() {
+ // note that getProjectDescription might acquire workspace lock
+ ICProjectDescription projectDescription = CoreModel.getDefault().getProjectDescriptionManager().getProjectDescription(project);
+ ICConfigurationDescription configuration = projectDescription != null ? projectDescription.getActiveConfiguration() : null;
+ setState(configuration != null ? new State(configuration) : STATE_INVALID);
}
- // called on active project configuration change
- @Override
- public void handleEvent(CProjectDescriptionEvent event) {
- if (event.getProject() != project) {
- return;
+ private void setState(State newState) {
+ State oldState = null;
+ synchronized (stateSync) {
+ if (newState == null || state == newState) {
+ return;
+ }
+ if (state == STATE_FREEZE) {
+ newState.destroyBeforeInit();
+ return;
+ }
+ oldState = state;
+ state = newState;
+ if (oldState != null) {
+ oldState.destroy();
+ }
+ newState.init();
+ }
+ for (IQMakeProjectInfoListener listener : listeners) {
+ listener.qmakeInfoChanged();
}
- updateActiveConfiguration();
}
@Override
@@ -182,60 +114,14 @@ public final class QMakeProjectInfo implements IQMakeProjectInfo, ICProjectDescr
listeners.remove(listener);
}
- private IProject getProject() {
+ IProject getProject() {
return project;
}
- // calculates (if does not exist) and returns actual QMake info
@Override
public IQMakeInfo getActualInfo() {
- synchronized (sync) {
- if (! live) {
- return QMakeInfo.INVALID;
- }
- if (qmakeInfo == null) {
- fetchQMakeInfo();
- }
- return qmakeInfo;
- }
- }
-
- // calculates actual QMake info
- private void fetchQMakeInfo() {
- // retrieves IQMakeEnvInfo from IQMakeEnv
- IQMakeEnv qmakeEnv = activeController != null ? activeController.getQMakeEnv() : null;
- QMakeEnvInfo qmakeEnvInfo = qmakeEnv != null ? qmakeEnv.getQMakeEnvInfo() : null;
-
- // retrieves .pro file path
- String proFilePath = toFilePath(qmakeEnvInfo != null ? qmakeEnvInfo.getProFile() : null);
- // retrieves qmake executable path
- String qmakeFilePath = qmakeEnvInfo != null ? qmakeEnvInfo.getQMakeFilePath() : null;
- // retries environment
- List<String> envList = new ArrayList<String>();
- Map<String, String> envMap = qmakeEnvInfo != null ? qmakeEnvInfo.getEnvironment() : Collections.<String,String>emptyMap();
- for (Map.Entry<String,String> entry : envMap.entrySet()) {
- envList.add(entry.getKey() + "=" + entry.getValue());
- }
-
- // calculates actual QMake info
- qmakeInfo = QMakeInfo.create(proFilePath, qmakeFilePath, envList.toArray(new String[envList.size()]));
-
- // calculates a new set of sensitive file paths
- sensitiveFilePathSet.clear();
- Set<IFile> envSensFiles = qmakeEnvInfo != null ? qmakeEnvInfo.getSensitiveFiles() : Collections.<IFile>emptySet();
- for (IFile sensitiveFile : envSensFiles) {
- if (sensitiveFile != null) {
- sensitiveFilePathSet.addSensitiveFile(sensitiveFile);
- }
- }
- if (proFilePath != null) {
- sensitiveFilePathSet.addSensitiveFile(proFilePath);
- }
- List<String> sensitiveFiles = qmakeInfo.getInvolvedQMakeFiles();
- if (sensitiveFiles != null) {
- for (String sensitiveFile : sensitiveFiles) {
- sensitiveFilePathSet.addSensitiveFile(sensitiveFile);
- }
+ synchronized (stateSync) {
+ return state.getQMakeInfo();
}
}
@@ -257,174 +143,115 @@ public final class QMakeProjectInfo implements IQMakeProjectInfo, ICProjectDescr
}
// checks if any of the specified files is a sensitive file
- private boolean containsAnySensitiveFile(Set<IPath> files) {
- synchronized (sync) {
- if (live) {
- for (Iterator<IPath> iterator = files.iterator(); iterator.hasNext();) {
- IPath path = iterator.next();
- if (sensitiveFilePathSet.contains(path)) {
- return true;
- }
- }
- }
- return false;
- }
- }
-
- // resets actual QMake info and notifies all listeners that QMake information has changes
- private void scheduleFetchQMakeInfo() {
- synchronized (sync) {
- if (! live) {
- return;
- }
- qmakeInfo = null;
- }
- notifyListeners();
- }
-
- private void notifyListeners() {
- for (IQMakeProjectInfoListener listener : listeners) {
- listener.qmakeInfoChanged();
+ boolean containsAnySensitiveFile(Set<IPath> files) {
+ synchronized (stateSync) {
+ return state.containsAnySensitiveFile(files);
}
}
/**
- * Represents a project configuration.
+ * Represents a state of QMakeInfo for a specific QMakeProjectInfo.
*/
- private final class ControllerImpl implements IQMakeEnvProvider.IController {
+ private final class State implements IController {
+ // an active project configuration
private final ICConfigurationDescription configuration;
+ // an active project qmake env
private final IQMakeEnv qmakeEnv;
+ // the last calculated QMake info; null if not calculated
+ private final IQMakeInfo qmakeInfo;
+ // a set of sensitive files that might affects actual QMake information
+ private final SensitiveSet sensitiveFilePathSet;
+
+ State() {
+ configuration = null;
+ qmakeEnv = null;
+ qmakeInfo = QMakeInfo.INVALID;
+ sensitiveFilePathSet = new SensitiveSet();
+ }
- public ControllerImpl(ICConfigurationDescription configuration) {
+ State(ICConfigurationDescription configuration) {
this.configuration = configuration;
// qmakeEnv created from registry of qmakeEnvProvider extensions
this.qmakeEnv = QMakeEnvProviderManager.getInstance().createEnv(this);
- }
-
- public void destroy() {
- qmakeEnv.destroy();
- }
-
- public IQMakeEnv getQMakeEnv() {
- return qmakeEnv;
- }
- @Override
- public ICConfigurationDescription getConfiguration() {
- return configuration;
- }
-
- @Override
- public void scheduleUpdate() {
- scheduleFetchQMakeInfo();
- }
+ // retrieves IQMakeEnvInfo from IQMakeEnv
+ QMakeEnvInfo qmakeEnvInfo = qmakeEnv.getQMakeEnvInfo();
+
+ // retrieves .pro file path
+ String proFilePath = toFilePath(qmakeEnvInfo != null ? qmakeEnvInfo.getProFile() : null);
+ // retrieves qmake executable path
+ String qmakeFilePath = qmakeEnvInfo != null ? qmakeEnvInfo.getQMakeFilePath() : null;
+ // retries environment
+ List<String> envList = new ArrayList<String>();
+ Map<String, String> envMap = qmakeEnvInfo != null ? qmakeEnvInfo.getEnvironment() : Collections.<String,String>emptyMap();
+ for (Map.Entry<String,String> entry : envMap.entrySet()) {
+ envList.add(entry.getKey() + "=" + entry.getValue());
+ }
- }
+ // calculates actual QMake info
+ qmakeInfo = QMakeInfo.create(proFilePath, qmakeFilePath, envList.toArray(new String[envList.size()]));
- /**
- * Listens on Eclipse file system changes.
- */
- private static final class RCListener implements IResourceChangeListener {
-
- @Override
- public void resourceChanged(IResourceChangeEvent event) {
- RDVisitor visitor = new RDVisitor();
-
- // collect project to delete and changed files
- switch (event.getType()) {
- case IResourceChangeEvent.PRE_CLOSE:
- case IResourceChangeEvent.PRE_DELETE:
- IResource project = event.getResource();
- if (project != null && project.getType() == IResource.PROJECT) {
- visitor.addProjectToDelete(project);
+ // calculates a new set of sensitive file paths
+ sensitiveFilePathSet = new SensitiveSet();
+ Set<IFile> envSensFiles = qmakeEnvInfo != null ? qmakeEnvInfo.getSensitiveFiles() : Collections.<IFile>emptySet();
+ for (IFile sensitiveFile : envSensFiles) {
+ if (sensitiveFile != null) {
+ sensitiveFilePathSet.addSensitiveFile(sensitiveFile);
}
- break;
- case IResourceChangeEvent.POST_CHANGE:
- IResourceDelta delta = event.getDelta();
- if (delta != null) {
- try {
- delta.accept(visitor);
- } catch (CoreException e) {
- // empty
- }
+ }
+ if (proFilePath != null) {
+ sensitiveFilePathSet.addSensitiveFile(proFilePath);
+ }
+ List<String> sensitiveFiles = qmakeInfo.getInvolvedQMakeFiles();
+ if (sensitiveFiles != null) {
+ for (String sensitiveFile : sensitiveFiles) {
+ sensitiveFilePathSet.addSensitiveFile(sensitiveFile);
}
- break;
}
-
- // process collected data
- visitor.process();
}
- }
-
- private static final class RDVisitor implements IResourceDeltaVisitor {
-
- private final Set<IResource> projectsToDelete = new HashSet<IResource>();
- private final Set<IResource> projectsToUpdate = new HashSet<IResource>();
- private final Set<IPath> changedFiles = new HashSet<IPath>();
-
- @Override
- public boolean visit(IResourceDelta delta) throws CoreException {
- IResource resource = delta.getResource();
- if (resource != null) {
- switch (resource.getType()) {
- case IResource.FILE:
- addChangedFile(resource);
- return false;
- case IResource.PROJECT:
- switch (delta.getKind()) {
- case IResourceDelta.CHANGED:
- if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
- addProjectToUpdate(resource);
- }
- return true;
- case IResourceDelta.REMOVED:
- addProjectToDelete(resource);
- return false;
- }
- break;
+ private boolean containsAnySensitiveFile(Set<IPath> files) {
+ for (Iterator<IPath> iterator = files.iterator(); iterator.hasNext();) {
+ IPath path = iterator.next();
+ if (sensitiveFilePathSet.contains(path)) {
+ return true;
}
}
- return true;
+ return false;
}
- private void addProjectToUpdate(IResource project) {
- projectsToUpdate.add(project);
+ @Override
+ public ICConfigurationDescription getConfiguration() {
+ return configuration;
}
- private void addProjectToDelete(IResource project) {
- projectsToDelete.add(project);
+ public void destroyBeforeInit() {
+ // see IQMakeEnv JavaDoc for details
+ if (qmakeEnv != null && ! (qmakeEnv instanceof IQMakeEnv2)) {
+ qmakeEnv.destroy();
+ }
}
- private void addChangedFile(IResource file) {
- IPath fullPath = file.getFullPath();
- if (fullPath != null) {
- changedFiles.add(fullPath);
+ public void init() {
+ if (qmakeEnv instanceof IQMakeEnv2) {
+ ((IQMakeEnv2) qmakeEnv).init();
}
}
- public void process() {
- // removing projects from CACHE
- for(IResource project : projectsToDelete) {
- removeProjectFromCache(project);
+ public void destroy() {
+ if (qmakeEnv != null) {
+ qmakeEnv.destroy();
}
+ }
- List<QMakeProjectInfo> infos;
- synchronized (SYNC) {
- infos = new ArrayList<QMakeProjectInfo>(CACHE.values());
- }
- for (QMakeProjectInfo info : infos) {
- // checking if any project description change affects QMakeProjectInfo
- if (projectsToUpdate.contains(info.getProject())) {
- info.updateActiveConfiguration();
- }
- // checking if any of the changed files affects QMakeProjectInfo
- if (info.containsAnySensitiveFile(changedFiles)) {
- // if so then scheduling update
- info.scheduleFetchQMakeInfo();
- }
- }
+ @Override
+ public void scheduleUpdate() {
+ updateStateFrom(this);
+ }
+
+ IQMakeInfo getQMakeInfo() {
+ return qmakeInfo;
}
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfoManager.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfoManager.java
new file mode 100644
index 0000000000..4553bcbfcd
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfoManager.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2014 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.internal.qt.core.index;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.cdt.core.model.CoreModel;
+import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent;
+import org.eclipse.cdt.core.settings.model.ICDescriptionDelta;
+import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Represents a management of QMakeProjectInfo instances and manages life-cycle of all QMakeProjectInfo instances.
+ */
+public class QMakeProjectInfoManager {
+
+ private static final PDListener PD_LISTENER = new PDListener();
+ private static final RCListener RC_LISTENER = new RCListener();
+
+ // sync object for CACHE field
+ private static final Object CACHE_SYNC = new Object();
+ // a list of all QMakeProjectInfo instances
+ private static Map<IProject,QMakeProjectInfo> CACHE;
+
+ // called by QtPlugin activator to setup this class
+ public static final void start() {
+ synchronized (CACHE_SYNC) {
+ CACHE = new HashMap<IProject,QMakeProjectInfo>();
+ }
+ CoreModel.getDefault().addCProjectDescriptionListener(PD_LISTENER, ICDescriptionDelta.ACTIVE_CFG);
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(RC_LISTENER, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE);
+ }
+
+ // called by QtPlugin activator to clean up this class
+ public static final void stop() {
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(RC_LISTENER);
+ CoreModel.getDefault().removeCProjectDescriptionListener(PD_LISTENER);
+ List<QMakeProjectInfo> infos;
+ synchronized (CACHE_SYNC) {
+ infos = new ArrayList<QMakeProjectInfo>(CACHE.values());
+ CACHE = null;
+ }
+ for (QMakeProjectInfo info : infos) {
+ if (info != null) {
+ info.destroy();
+ }
+ }
+ }
+
+ /**
+ * Returns a QMakeProjectInfo for an active project configuration of a specified project.
+ *
+ * @param project the project
+ * @return the QMakeProjectInfo; or null if the project does not have QtNature
+ */
+ public static QMakeProjectInfo getQMakeProjectInfoFor(IProject project) {
+ return getQMakeProjectInfoFor(project, true);
+ }
+
+ private static QMakeProjectInfo getQMakeProjectInfoFor(IProject project, boolean create) {
+ QMakeProjectInfo info;
+ synchronized (CACHE_SYNC) {
+ info = CACHE.get(project);
+ if (info != null) {
+ return info;
+ }
+ if (! create) {
+ // do not create, just return null
+ return null;
+ }
+ info = new QMakeProjectInfo(project);
+ CACHE.put(project, info);
+ }
+ info.updateState();
+ return info;
+ }
+
+ // removes the project from the CACHE
+ private static void removeProjectFromCache(IResource project) {
+ QMakeProjectInfo info;
+ synchronized (CACHE_SYNC) {
+ info = CACHE.remove(project);
+ }
+ if (info != null) {
+ info.destroy();
+ }
+ }
+
+ private static final class PDListener implements ICProjectDescriptionListener {
+
+ // called on active project configuration change
+ @Override
+ public void handleEvent(CProjectDescriptionEvent event) {
+ QMakeProjectInfo info = getQMakeProjectInfoFor(event.getProject(), false);
+ if (info != null) {
+ info.updateState();
+ }
+ }
+
+ }
+
+ /**
+ * Listens on Eclipse file system changes.
+ */
+ private static final class RCListener implements IResourceChangeListener {
+
+ @Override
+ public void resourceChanged(IResourceChangeEvent event) {
+ RDVisitor visitor = new RDVisitor();
+
+ // collect project to delete and changed files
+ switch (event.getType()) {
+ case IResourceChangeEvent.PRE_CLOSE:
+ case IResourceChangeEvent.PRE_DELETE:
+ IResource project = event.getResource();
+ if (project != null && project.getType() == IResource.PROJECT) {
+ visitor.addProjectToDelete(project);
+ }
+ break;
+ case IResourceChangeEvent.POST_CHANGE:
+ IResourceDelta delta = event.getDelta();
+ if (delta != null) {
+ try {
+ delta.accept(visitor);
+ } catch (CoreException e) {
+ // empty
+ }
+ }
+ break;
+ }
+
+ // process collected data
+ visitor.process();
+ }
+
+ }
+
+ private static final class RDVisitor implements IResourceDeltaVisitor {
+
+ private final Set<IResource> projectsToDelete = new HashSet<IResource>();
+ private final Set<IResource> projectsToUpdate = new HashSet<IResource>();
+ private final Set<IPath> changedFiles = new HashSet<IPath>();
+
+ @Override
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ IResource resource = delta.getResource();
+ if (resource != null) {
+ switch (resource.getType()) {
+ case IResource.FILE:
+ addChangedFile(resource);
+ return false;
+ case IResource.PROJECT:
+ switch (delta.getKind()) {
+ case IResourceDelta.CHANGED:
+ if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
+ addProjectToUpdate(resource);
+ }
+ return true;
+ case IResourceDelta.REMOVED:
+ addProjectToDelete(resource);
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ private void addProjectToUpdate(IResource project) {
+ projectsToUpdate.add(project);
+ }
+
+ private void addProjectToDelete(IResource project) {
+ projectsToDelete.add(project);
+ }
+
+ private void addChangedFile(IResource file) {
+ IPath fullPath = file.getFullPath();
+ if (fullPath != null) {
+ changedFiles.add(fullPath);
+ }
+ }
+
+ public void process() {
+ // removing projects from CACHE
+ for(IResource project : projectsToDelete) {
+ removeProjectFromCache(project);
+ }
+
+ List<QMakeProjectInfo> infos;
+ synchronized (CACHE_SYNC) {
+ infos = new ArrayList<QMakeProjectInfo>(CACHE.values());
+ }
+ for (QMakeProjectInfo info : infos) {
+ // checking if any project description change or any of the changed files affect QMakeProjectInfo
+ if (projectsToUpdate.contains(info.getProject()) || info.containsAnySensitiveFile(changedFiles)) {
+ // if so then updating
+ info.updateState();
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
index 7649685cd2..130ec7a1e3 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
@@ -7,14 +7,18 @@
*/
package org.eclipse.cdt.qt.core;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import org.eclipse.cdt.core.model.CModelException;
-import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfo;
+import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfoManager;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
public class QtPlugin extends Plugin {
@@ -47,13 +51,27 @@ public class QtPlugin extends Plugin {
@Override
public void start(BundleContext context) throws Exception {
+ // have to wait for STARTED event because
+ // cannot access CoreModel.getDefault().addCProjectDescriptionListener()
+ // since the CoreModel is not completely initialized at this time
+ BundleListener bundleListener = new BundleListener() {
+ final AtomicBoolean initStarted = new AtomicBoolean(false);
+ @Override
+ public void bundleChanged(BundleEvent bundleEvent) {
+ if (bundleEvent.getType() == BundleEvent.STARTED) {
+ if (!initStarted.getAndSet(true)) {
+ QMakeProjectInfoManager.start();
+ }
+ }
+ }
+ };
+ context.addBundleListener(bundleListener);
super.start(context);
- QMakeProjectInfo.start();
}
@Override
public void stop(BundleContext context) throws Exception {
- QMakeProjectInfo.stop();
+ QMakeProjectInfoManager.stop();
super.stop(context);
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java
index 574d3fb8c6..adafb0680c 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java
@@ -9,11 +9,18 @@ package org.eclipse.cdt.qt.core.index;
/**
* Represents a QMake environment. It is usually created by IQMakeEnvProvider.createEnv() method for a specific project configuration.
+ *
+ * Note that IQMakeEnv has destroy method only and it is expected that the instance is already initialized in the constructor.
+ * This means that it may happen that IQMakeEnv instance is created and destroyed immediately.
+ *
+ * See IQMakeEnv2 interface if you want to get an explicit notification when IQMakeEnv gets really used. In that case, the instance initialization
+ * needs to be done in init method completely - not in the constructor.
*/
public interface IQMakeEnv {
/**
* Notifies that this environment is no longer used.
+ * This method should not use any workspace-lock or sync-locks that might call QMake-related structures.
*/
void destroy();
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv2.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv2.java
new file mode 100644
index 0000000000..9dc8839141
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv2.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.core.index;
+
+/**
+ * Represents a QMake environment similarly to IQMakeEnv but it has an explicit init method which is called to notify
+ * that the IQMakeEnv is used for active listening on change of QMakeEnvInfo and therefore it should initialize
+ * its listener for such changes.
+ *
+ * Note that it is expected that IQMakeEnv2 does complete initialization in init method - not in the constructor
+ * i.e. the IQMakeEnv2 instance should start listening on possible changes of its IQMakeEnvInfo.
+ */
+public interface IQMakeEnv2 extends IQMakeEnv {
+
+ /**
+ * Notifies that this IQMakeEnv is used.
+ * This method should not use any workspace-lock or sync-locks that might call QMake-related structures.
+ */
+ void init();
+
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java
index 2dd5053240..deb851665d 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java
@@ -41,6 +41,8 @@ public interface IQMakeEnvProvider {
/**
* Request the controller to schedule a new qmake run to retrieve new QMake information.
* This method should be called when there is any change in IQMakeEnv that might affect resulting IQMakeEnvInfo.
+ *
+ * Note that calculation of new QMakeInfo is done immediately if this controller is still active and used.
*/
void scheduleUpdate();
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java
index 758b7135ff..4337a6baed 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java
@@ -7,7 +7,7 @@
*/
package org.eclipse.cdt.qt.core.index;
-import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfo;
+import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfoManager;
import org.eclipse.core.resources.IProject;
/**
@@ -27,7 +27,7 @@ public final class QMakeProjectInfoFactory {
* in the specified project.
*/
public static IQMakeProjectInfo getForActiveConfigurationIn(IProject project) {
- return QMakeProjectInfo.getQMakeProjectInfoFor(project);
+ return QMakeProjectInfoManager.getQMakeProjectInfoFor(project);
}
}

Back to the top