1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

Fix for 173872: ASTProvider locked up waiting on a notify()

This commit is contained in:
Anton Leherbauer 2007-03-21 14:45:58 +00:00
parent bea41263df
commit 2dfdde8c56
11 changed files with 685 additions and 389 deletions

View file

@ -0,0 +1,200 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. 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
*
* Contributors:
* Anton Leherbauer (Wind River Systems) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.model.tests;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.testplugin.CProjectHelper;
import org.eclipse.cdt.core.testplugin.CTestPlugin;
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
import org.eclipse.cdt.internal.core.model.ASTCache;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
/**
* Tests for the {@link ASTCache}.
*/
public class ASTCacheTests extends BaseTestCase {
private static int fgReconcilerCount;
public class MockReconciler extends Thread {
private ITranslationUnit fTU;
private ASTCache fCache;
public volatile boolean fStopped;
private IASTTranslationUnit fAST;
public MockReconciler(ITranslationUnit tu, ASTCache cache) {
super("MockReconciler-"+fgReconcilerCount++);
fTU= tu;
fCache= cache;
}
public void run() {
while (!fStopped) {
try {
Thread.sleep(500);
fCache.aboutToBeReconciled(fTU);
IASTTranslationUnit ast;
synchronized (this) {
notifyAll();
}
fAST= fCache.createAST(fTU, fIndex, null);
fCache.reconciled(fAST, fTU);
} catch (InterruptedException exc) {
fStopped= true;
break;
}
}
}
}
private ICProject fProject;
private ITranslationUnit fTU1;
private ITranslationUnit fTU2;
private IIndex fIndex;
public ASTCacheTests(String name) {
super(name);
}
// {source1.cpp}
// void foo1() {}
// void bar1() {}
// {source2.cpp}
// void foo2() {}
// void bar2() {}
public static Test suite() {
TestSuite suite= new TestSuite(ASTCacheTests.class);
return suite;
}
public void setUp() throws Exception {
super.setUp();
fProject= createProject("ASTCacheTest");
assertNotNull(fProject);
IFile file1= createFile(fProject.getProject(), "source1.cpp", readTaggedComment("source1.cpp"));
assertNotNull(file1);
fTU1= CProjectHelper.findTranslationUnit(fProject, file1.getName());
assertNotNull(fTU1);
IFile file2= createFile(fProject.getProject(), "source2.cpp", readTaggedComment("source2.cpp"));
assertNotNull(file2);
fTU2= CProjectHelper.findTranslationUnit(fProject, file2.getName());
assertNotNull(fTU2);
CCorePlugin.getIndexManager().joinIndexer(5000, new NullProgressMonitor());
fIndex= CCorePlugin.getIndexManager().getIndex(fProject);
fIndex.acquireReadLock();
}
public void tearDown() throws Exception {
if (fIndex != null) {
fIndex.releaseReadLock();
}
if (fProject != null) {
CProjectHelper.delete(fProject);
}
super.tearDown();
}
protected ICProject createProject(final String name) throws CoreException {
return CProjectHelper.createCProject(name, null, IPDOMManager.ID_FAST_INDEXER);
}
protected String readTaggedComment(String tag) throws Exception {
return TestSourceReader.readTaggedComment(CTestPlugin.getDefault().getBundle(), "model", getClass(), tag);
}
protected IFile createFile(IContainer container, String fileName, String contents) throws Exception {
return TestSourceReader.createFile(container, new Path(fileName), contents);
}
public void testASTCache() throws Exception {
checkActiveElement();
checkSingleThreadAccess();
checkAccessWithBackgroundReconciler();
}
private void checkActiveElement() throws Exception {
ASTCache cache= new ASTCache();
assertFalse(cache.isActiveElement(fTU1));
assertFalse(cache.isActiveElement(fTU2));
cache.setActiveElement(fTU1);
assertTrue(cache.isActiveElement(fTU1));
assertFalse(cache.isActiveElement(fTU2));
cache.setActiveElement(fTU2);
assertFalse(cache.isActiveElement(fTU1));
assertTrue(cache.isActiveElement(fTU2));
}
private void checkSingleThreadAccess() throws Exception {
ASTCache cache= new ASTCache();
cache.setActiveElement(fTU1);
IASTTranslationUnit ast;
ast= cache.getAST(fTU1, fIndex, false, null);
assertNull(ast);
IProgressMonitor npm= new NullProgressMonitor();
npm.setCanceled(true);
ast= cache.getAST(fTU1, fIndex, true, npm);
assertNull(ast);
npm.setCanceled(false);
ast= cache.getAST(fTU1, fIndex, true, npm);
assertNotNull(ast);
}
private void checkAccessWithBackgroundReconciler() throws Exception {
ASTCache cache= new ASTCache();
cache.setActiveElement(fTU1);
MockReconciler reconciler1= new MockReconciler(fTU1, cache);
MockReconciler reconciler2= null;
try {
assertFalse(cache.isReconciling(fTU1));
reconciler1.start();
synchronized (reconciler1) {
reconciler1.wait();
}
IASTTranslationUnit ast;
ast= cache.getAST(fTU1, fIndex, true, null);
assertNotNull(ast);
assertSame(ast, reconciler1.fAST);
// change active element
cache.setActiveElement(fTU2);
reconciler2= new MockReconciler(fTU2, cache);
reconciler2.start();
synchronized (reconciler2) {
reconciler2.wait();
}
ast= cache.getAST(fTU2, fIndex, true, null);
assertNotNull(ast);
assertSame(ast, reconciler2.fAST);
} finally {
reconciler1.fStopped= true;
reconciler1.join(1000);
if (reconciler2 != null) {
reconciler2.fStopped= true;
reconciler2.join(1000);
}
}
}
}

View file

@ -47,7 +47,7 @@ public class AllCoreTests {
suite.addTest(FailedDeclaratorsTest.suite());
suite.addTest(CPathEntryTest.suite());
suite.addTest(CConfigurationDescriptionReferenceTests.suite());
suite.addTest(ASTCacheTests.suite());
return suite;
}

View file

@ -35,3 +35,6 @@ org.eclipse.cdt.core/debug/indexer/problems=false
# Code formatter debugging
org.eclipse.cdt.core/debug/formatter=false
# ASTCache debugging
org.eclipse.cdt.core/debug/ASTCache=false

View file

@ -0,0 +1,427 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. 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
*
* Contributors: Anton Leherbauer (Wind River Systems) - initial API and
* implementation
******************************************************************************/
package org.eclipse.cdt.internal.core.model;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
/**
* Provides a shared AST of a single translation unit at a time.
*
* @since 4.0
*/
public class ASTCache {
/**
* Tells whether this class is in debug mode.
*/
private static final boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.core/debug/ASTCache")); //$NON-NLS-1$//$NON-NLS-2$
private static final String DEBUG_PREFIX= "[ASTCache] "; //$NON-NLS-1$
/** Full parse mode (no PDOM) */
public static int PARSE_MODE_FULL= 0;
/** Fast parse mode (use PDOM) */
public static int PARSE_MODE_FAST= ITranslationUnit.AST_SKIP_INDEXED_HEADERS;
/**
* Do something with an AST.
*
* @see #runOnAST(IASTTranslationUnit)
*/
public static interface ASTRunnable {
/**
* Do something with the given AST.
*
* @param ast the translation unit AST, may be <code>null</code>
* @return a status object
*/
IStatus runOnAST(IASTTranslationUnit ast);
}
private final int fParseMode;
private final Object fCacheMutex= new Object();
/** The active translation unit for which to cache the AST */
private ITranslationUnit fActiveTU;
/** The cached AST if any */
private IASTTranslationUnit fAST;
/**
* The timestamp of the last index write access at the time
* the AST got cached. A cached AST becomes invalid on any index
* write access afterwards.
*/
private long fLastWriteOnIndex;
/** Inidicates whether the AST is currenty being computed */
private volatile boolean fIsReconciling;
/**
* Create a new AST cache.
*/
public ASTCache() {
fParseMode= PARSE_MODE_FAST;
}
/**
* Returns a shared translation unit AST for the given
* translation unit.
* <p>
* Clients are not allowed to modify the AST and must
* hold a read lock prior to calling this method and continue
* to hold the lock as long as the AST is being used.
* </p>
*
* @param tUnit the translation unit
* @param index the index used to create the AST, needs to be read-locked
* @param wait if <code>true</code>, wait for AST to be computed (might compute a new AST)
* @param progressMonitor the progress monitor or <code>null</code>
* @return the AST or <code>null</code> if the AST is not available
*/
public IASTTranslationUnit getAST(ITranslationUnit tUnit, IIndex index, boolean wait, IProgressMonitor progressMonitor) {
if (tUnit == null)
return null;
while (true) {
if (progressMonitor != null && progressMonitor.isCanceled())
return null;
final boolean isActiveElement;
synchronized (fCacheMutex) {
isActiveElement= tUnit.equals(fActiveTU);
if (isActiveElement) {
if (fAST != null) {
// AST is cached
if (fLastWriteOnIndex < index.getLastWriteAccess()) {
// AST has been invalidated by index write access
disposeAST();
} else {
// cached AST is valid
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "returning cached AST:" + toString(fAST) + " for: " + tUnit.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$
return fAST;
}
}
// no cached AST
if (!wait) {
// no AST, no wait - we are done
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "returning null (WAIT_NO) for: " + tUnit.getElementName()); //$NON-NLS-1$
return null;
}
}
// no cached AST, but wait
if (isActiveElement && isReconciling(tUnit)) {
try {
final ICElement activeElement= fActiveTU;
// Wait for AST
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "waiting for AST for: " + tUnit.getElementName()); //$NON-NLS-1$
fCacheMutex.wait();
// Check whether active element is still valid (
synchronized (this) {
if (activeElement == fActiveTU && fAST != null) {
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "...got AST for: " + tUnit.getElementName()); //$NON-NLS-1$
return fAST;
}
}
// try again
continue;
} catch (InterruptedException e) {
return null; // thread has been interrupted don't compute AST
}
} else if (!wait) {
return null;
}
}
if (isActiveElement)
aboutToBeReconciled(tUnit);
if (DEBUG)
System.err.println(DEBUG_PREFIX + getThreadName() + "creating AST for " + tUnit.getElementName()); //$NON-NLS-1$
IASTTranslationUnit ast= null;
try {
ast= createAST(tUnit, index, progressMonitor);
if (progressMonitor != null && progressMonitor.isCanceled())
ast= null;
else if (DEBUG && ast != null)
System.err.println(DEBUG_PREFIX + getThreadName() + "created AST for: " + tUnit.getElementName()); //$NON-NLS-1$
} finally {
if (isActiveElement) {
if (fAST != null) {
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "Ignore created AST for " + tUnit.getElementName() + "- AST from reconciler is newer"); //$NON-NLS-1$ //$NON-NLS-2$
// other reconciler was faster, still need to trigger notify
reconciled(fAST, tUnit);
} else
reconciled(ast, tUnit);
}
}
return ast;
}
}
/**
* Executes {@link ASTRunnable#runOnAST(IASTTranslationUnit)} with the AST
* provided by this cache for the given translation unit. Handles acquiring
* and releasing the index read-lock for the client.
*
* @param tUnit
* the translation unit
* @param wait
* <code>true</code> if the AST should be computed or waited
* upon in case it is not yet available
* @param monitor a progress monitor, may be <code>null</code>
* @param astRunnable the runnable taking the AST
* @return the status returned by the ASTRunnable
*/
public IStatus runOnAST(ITranslationUnit tUnit, boolean wait, IProgressMonitor monitor,
ASTRunnable astRunnable) {
IIndex index;
try {
index = CCorePlugin.getIndexManager().getIndex(tUnit.getCProject());
index.acquireReadLock();
} catch (CoreException e) {
return e.getStatus();
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
}
try {
IASTTranslationUnit ast= getAST(tUnit, index, wait, monitor);
return astRunnable.runOnAST(ast);
}
finally {
index.releaseReadLock();
}
}
/**
* Caches the given AST for the given translation unit.
*
* @param ast the AST
* @param tUnit the translation unit
*/
private void cache(IASTTranslationUnit ast, ITranslationUnit tUnit) {
synchronized (fCacheMutex) {
if (fActiveTU != null && !fActiveTU.equals(tUnit)) {
if (DEBUG && tUnit != null) // don't report call from disposeAST()
System.out.println(DEBUG_PREFIX + getThreadName() + "don't cache AST for inactive: " + toString(tUnit)); //$NON-NLS-1$
return;
}
if (DEBUG && (tUnit != null || ast != null)) // don't report call from disposeAST()
System.out.println(DEBUG_PREFIX + getThreadName() + "caching AST: " + toString(ast) + " for: " + toString(tUnit)); //$NON-NLS-1$ //$NON-NLS-2$
if (fAST != null)
disposeAST();
fAST= ast;
fLastWriteOnIndex= fAST == null ? 0 : fAST.getIndex().getLastWriteAccess();
// Signal AST change
fCacheMutex.notifyAll();
}
}
/**
* Disposes the cached AST.
*/
public void disposeAST() {
synchronized (fCacheMutex) {
if (fAST == null)
return;
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "disposing AST: " + toString(fAST) + " for: " + toString(fActiveTU)); //$NON-NLS-1$ //$NON-NLS-2$
fAST= null;
cache(null, null);
}
}
/**
* Creates a new translation unit AST.
*
* @param tUnit the C element for which to create the AST
* @param index for AST generation, needs to be read-locked.
* @param progressMonitor the progress monitor
* @return AST
*/
public IASTTranslationUnit createAST(final ITranslationUnit tUnit, final IIndex index, final IProgressMonitor progressMonitor) {
if (progressMonitor != null && progressMonitor.isCanceled())
return null;
final IASTTranslationUnit root[]= new IASTTranslationUnit[1];
SafeRunner.run(new ISafeRunnable() {
public void run() throws CoreException {
try {
if (progressMonitor != null && progressMonitor.isCanceled()) {
root[0]= null;
} else {
root[0]= tUnit.getAST(index, fParseMode);
}
} catch (OperationCanceledException ex) {
root[0]= null;
}
}
public void handleException(Throwable ex) {
IStatus status= new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, IStatus.OK, "Error in CDT Core during AST creation", ex); //$NON-NLS-1$
CCorePlugin.getDefault().getLog().log(status);
}
});
return root[0];
}
/**
* Set the given translation unit as active element to cache an AST for.
*
* @param tUnit
*/
public void setActiveElement(ITranslationUnit tUnit) {
if (tUnit == fActiveTU) {
return;
}
fIsReconciling= false;
synchronized (fCacheMutex) {
fActiveTU= tUnit;
cache(null, tUnit);
}
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "active element is: " + toString(tUnit)); //$NON-NLS-1$
}
/**
* Check whether the given translation unit is the active element of this cache.
*
* @param tUnit
* @return <code>true</code>, if this cache manages the given translation unit
*/
public boolean isActiveElement(ITranslationUnit tUnit) {
synchronized (fCacheMutex) {
return fActiveTU != null && fActiveTU.equals(tUnit);
}
}
/**
* Informs that reconciling (computation of the AST) for the given element
* is about to be started.
*
* @param tUnit the C element
* @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#aboutToBeReconciled()
*/
public void aboutToBeReconciled(ITranslationUnit tUnit) {
if (tUnit == null)
return;
if (fActiveTU == null || !fActiveTU.equals(tUnit)) {
return;
}
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "about to reconcile: " + toString(tUnit)); //$NON-NLS-1$
fIsReconciling= true;
cache(null, tUnit);
}
/**
* Informs that reconciling of the AST of the given translation unit has finished.
*
* @param ast
* @param tUnit
*/
public void reconciled(IASTTranslationUnit ast, ITranslationUnit tUnit) {
synchronized (fCacheMutex) {
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "reconciled: " + toString(tUnit) + ", AST: " + toString(ast)); //$NON-NLS-1$ //$NON-NLS-2$
fIsReconciling= false;
if (tUnit == null || !tUnit.equals(fActiveTU)) {
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + " ignoring AST of out-dated element"); //$NON-NLS-1$
// Signal - threads might wait for wrong element
fCacheMutex.notifyAll();
return;
}
cache(ast, tUnit);
}
}
/**
* Tells whether the given C element is the one
* reported as currently being reconciled.
*
* @param tUnit the C element
* @return <code>true</code> if reported as currently being reconciled
*/
public boolean isReconciling(ITranslationUnit tUnit) {
synchronized (fCacheMutex) {
if (fActiveTU == null || tUnit == null) {
return false;
}
return fIsReconciling && (fActiveTU.equals(tUnit));
}
}
private static String getThreadName() {
String name= Thread.currentThread().getName();
if (name != null)
return name + ": "; //$NON-NLS-1$
else
return Thread.currentThread().toString() + ": "; //$NON-NLS-1$
}
/**
* Returns a string for the given C element used for debugging.
*
* @param cElement the translation unit AST
* @return a string used for debugging
*/
private static String toString(ICElement cElement) {
if (cElement == null)
return "null"; //$NON-NLS-1$
else
return cElement.getElementName();
}
/**
* Returns a string for the given AST used for debugging.
*
* @param ast the translation unit AST
* @return a string used for debugging
*/
private static String toString(IASTTranslationUnit ast) {
if (ast == null)
return "null"; //$NON-NLS-1$
IASTNode[] nodes= ast.getDeclarations();
if (nodes != null && nodes.length > 0)
return nodes[0].getRawSignature();
else
return "AST without any declaration"; //$NON-NLS-1$
}
}

View file

@ -131,7 +131,7 @@ public class SemanticHighlightingTest extends AbstractSemanticHighlightingTest {
createPosition(118, 4, 15),
};
Position[] actual= getSemanticHighlightingPositions();
System.out.println(toString(actual));
if (PRINT_POSITIONS) System.out.println(toString(actual));
assertEqualPositions(expected, actual);
}

View file

@ -14,14 +14,8 @@
package org.eclipse.cdt.internal.ui.editor;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchPart;
@ -29,21 +23,19 @@ import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.IPositionConverter;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.core.model.ASTCache;
/**
* Provides a shared AST for clients. The shared AST is
* the AST of the active CEditor's input element.
* Cloned from JDT.
*
* @since 4.0
*/
@ -105,11 +97,6 @@ public final class ASTProvider {
/** Fast parse mode (use PDOM) */
public static int PARSE_MODE_FAST= ITranslationUnit.AST_SKIP_INDEXED_HEADERS;
/**
* Tells whether this class is in debug mode.
*/
private static final boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.ui/debug/ASTProvider")); //$NON-NLS-1$//$NON-NLS-2$
/**
* Internal activation listener.
*/
@ -136,9 +123,6 @@ public final class ASTProvider {
*/
public void partClosed(IWorkbenchPartReference ref) {
if (isActiveEditor(ref)) {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "closed active editor: " + ref.getTitle()); //$NON-NLS-1$ //$NON-NLS-2$
activeEditorChanged(null);
}
}
@ -199,9 +183,6 @@ public final class ASTProvider {
*/
public void windowClosed(IWorkbenchWindow window) {
if (fActiveEditor != null && fActiveEditor.getSite() != null && window == fActiveEditor.getSite().getWorkbenchWindow()) {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "closed active editor: " + fActiveEditor.getTitle()); //$NON-NLS-1$ //$NON-NLS-2$
activeEditorChanged(null);
}
window.getPartService().removePartListener(this);
@ -232,23 +213,10 @@ public final class ASTProvider {
}
}
private static final String DEBUG_PREFIX= "ASTProvider > "; //$NON-NLS-1$
private ICElement fReconcilingCElement;
private ICElement fActiveCElement;
private IPositionConverter fActivePositionConverter;
private IASTTranslationUnit fAST;
private ASTCache fCache= new ASTCache();
private ActivationListener fActivationListener;
private Object fReconcileLock= new Object();
private Object fWaitLock= new Object();
private boolean fIsReconciling;
private IWorkbenchPart fActiveEditor;
protected int fParseMode= PARSE_MODE_FAST;
private long fLastWriteOnIndex= -1;
/**
* Returns the C plug-in's AST provider.
*
@ -282,41 +250,14 @@ public final class ASTProvider {
}
private void activeEditorChanged(IWorkbenchPart editor) {
ICElement cElement= null;
if (editor instanceof CEditor) {
cElement= ((CEditor)editor).getInputCElement();
}
synchronized (this) {
fActiveEditor= editor;
fActiveCElement= cElement;
cache(null, null, cElement);
fCache.setActiveElement((ITranslationUnit)cElement);
}
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "active editor is: " + toString(cElement)); //$NON-NLS-1$ //$NON-NLS-2$
synchronized (fReconcileLock) {
if (fIsReconciling && (fReconcilingCElement == null || !fReconcilingCElement.equals(cElement))) {
fIsReconciling= false;
fReconcilingCElement= null;
} else if (cElement == null) {
fIsReconciling= false;
fReconcilingCElement= null;
}
}
}
/**
* Returns whether the given translation unit AST is
* cached by this AST provided.
*
* @param ast the translation unit AST
* @return <code>true</code> if the given AST is the cached one
*/
public boolean isCached(IASTTranslationUnit ast) {
return ast != null && fAST == ast;
}
/**
@ -327,7 +268,7 @@ public final class ASTProvider {
* @return <code>true</code> if the given translation unit is the active one
*/
public boolean isActive(ITranslationUnit tu) {
return tu != null && tu.equals(fActiveCElement);
return fCache.isActiveElement(tu);
}
/**
@ -337,95 +278,10 @@ public final class ASTProvider {
* @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#aboutToBeReconciled()
*/
void aboutToBeReconciled(ICElement cElement) {
if (cElement == null)
return;
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "about to reconcile: " + toString(cElement)); //$NON-NLS-1$ //$NON-NLS-2$
synchronized (fReconcileLock) {
fIsReconciling= true;
fReconcilingCElement= cElement;
}
cache(null, null, cElement);
}
/**
* Disposes the cached AST.
*/
private synchronized void disposeAST() {
if (fAST == null)
return;
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "disposing AST: " + toString(fAST) + " for: " + toString(fActiveCElement)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
fAST= null;
cache(null, null, null);
}
/**
* Returns a string for the given C element used for debugging.
*
* @param cElement the translation unit AST
* @return a string used for debugging
*/
private String toString(ICElement cElement) {
if (cElement == null)
return "null"; //$NON-NLS-1$
else
return cElement.getElementName();
}
/**
* Returns a string for the given AST used for debugging.
*
* @param ast the translation unit AST
* @return a string used for debugging
*/
private String toString(IASTTranslationUnit ast) {
if (ast == null)
return "null"; //$NON-NLS-1$
IASTNode[] nodes= ast.getDeclarations();
if (nodes != null && nodes.length > 0)
return nodes[0].getRawSignature();
else
return "AST without any declaration"; //$NON-NLS-1$
}
/**
* Caches the given translation unit AST for the given C element.
*
* @param ast
* @param cElement
*/
private synchronized void cache(IASTTranslationUnit ast, IPositionConverter converter, ICElement cElement) {
if (fActiveCElement != null && !fActiveCElement.equals(cElement)) {
if (DEBUG && cElement != null) // don't report call from disposeAST()
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "don't cache AST for inactive: " + toString(cElement)); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
if (DEBUG && (cElement != null || ast != null)) // don't report call from disposeAST()
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "caching AST: " + toString(ast) + " for: " + toString(cElement)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
if (fAST != null)
disposeAST();
fAST= ast;
fLastWriteOnIndex= fAST == null ? 0 : fAST.getIndex().getLastWriteAccess();
fActivePositionConverter= converter;
// Signal AST change
synchronized (fWaitLock) {
fWaitLock.notifyAll();
}
Assert.isTrue(cElement instanceof ITranslationUnit);
fCache.aboutToBeReconciled((ITranslationUnit)cElement);
}
/**
@ -445,248 +301,48 @@ public final class ASTProvider {
public IASTTranslationUnit getAST(ICElement cElement, IIndex index, WAIT_FLAG waitFlag, IProgressMonitor progressMonitor) {
if (cElement == null)
return null;
Assert.isTrue(cElement instanceof ITranslationUnit);
if (progressMonitor != null && progressMonitor.isCanceled())
return null;
boolean isActiveElement;
synchronized (this) {
isActiveElement= cElement.equals(fActiveCElement);
if (isActiveElement) {
if (fAST != null) {
if (fLastWriteOnIndex < index.getLastWriteAccess()) {
disposeAST();
}
else {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "returning cached AST:" + toString(fAST) + " for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return fAST;
}
}
if (waitFlag == WAIT_NO) {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "returning null (WAIT_NO) for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
}
}
if (isActiveElement && isReconciling(cElement)) {
try {
final ICElement activeElement= fReconcilingCElement;
// Wait for AST
synchronized (fWaitLock) {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "waiting for AST for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$
// don't wait forever, notify might have happened already
fWaitLock.wait(1000);
}
// Check whether active element is still valid
synchronized (this) {
if (activeElement == fActiveCElement && fAST != null) {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "...got AST for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$
return fAST;
}
}
return getAST(cElement, index, waitFlag, progressMonitor);
} catch (InterruptedException e) {
return null; // thread has been interrupted don't compute AST
}
} else if (waitFlag == WAIT_NO || (waitFlag == WAIT_ACTIVE_ONLY && !(isActiveElement && fAST == null)))
return null;
if (isActiveElement)
aboutToBeReconciled(cElement);
if (DEBUG)
System.err.println(getThreadName() + " - " + DEBUG_PREFIX + "creating AST for " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$
IASTTranslationUnit ast= null;
try {
ast= createAST(cElement, index, progressMonitor);
if (progressMonitor != null && progressMonitor.isCanceled())
ast= null;
else if (DEBUG && ast != null)
System.err.println(getThreadName() + " - " + DEBUG_PREFIX + "created AST for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$
} finally {
if (isActiveElement) {
if (fAST != null) {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "Ignore created AST for " + cElement.getElementName() + "- AST from reconciler is newer"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
reconciled(fAST, fActivePositionConverter, cElement, null);
} else
reconciled(ast, null, cElement, null);
}
}
return ast;
return fCache.getAST((ITranslationUnit)cElement, index, waitFlag != WAIT_NO, progressMonitor);
}
/**
* Tells whether the given C element is the one
* reported as currently being reconciled.
*
* @param cElement the C element
* @return <code>true</code> if reported as currently being reconciled
*/
private boolean isReconciling(ICElement cElement) {
synchronized (fReconcileLock) {
return cElement != null && cElement.equals(fReconcilingCElement) && fIsReconciling;
}
}
/**
* Creates a new translation unit AST.
*
* @param cElement the C element for which to create the AST
* @param index for AST generation, needs to be read-locked.
* @param progressMonitor the progress monitor
* @return AST
*/
IASTTranslationUnit createAST(ICElement cElement, final IIndex index, final IProgressMonitor progressMonitor) {
if (!hasSource(cElement))
return null;
if (progressMonitor != null && progressMonitor.isCanceled())
return null;
if (!(cElement instanceof ITranslationUnit))
return null;
final ITranslationUnit tu= (ITranslationUnit)cElement;
final IASTTranslationUnit root[]= new IASTTranslationUnit[1];
SafeRunner.run(new ISafeRunnable() {
public void run() throws CoreException {
try {
if (progressMonitor != null && progressMonitor.isCanceled()) {
root[0]= null;
} else {
root[0]= tu.getAST(index, fParseMode);
}
} catch (OperationCanceledException ex) {
root[0]= null;
}
}
public void handleException(Throwable ex) {
IStatus status= new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, IStatus.OK, "Error in CDT Core during AST creation", ex); //$NON-NLS-1$
CUIPlugin.getDefault().getLog().log(status);
}
});
return root[0];
}
/**
* Checks whether the given C element has accessible source.
*
* @param cElement the C element to test
* @return <code>true</code> if the element has source
*/
private boolean hasSource(ICElement cElement) {
if (cElement == null || !cElement.exists())
return false;
try {
return cElement instanceof ISourceReference /* && ((ISourceReference)cElement).getSource() != null */;
} catch (Exception ex) {
IStatus status= new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, IStatus.OK, "Error in CDT Core during AST creation", ex); //$NON-NLS-1$
CUIPlugin.getDefault().getLog().log(status);
}
return false;
}
/**
* Disposes this AST provider.
*/
public void dispose() {
if (fActivationListener != null) {
// Dispose activation listener
PlatformUI.getWorkbench().removeWindowListener(fActivationListener);
fActivationListener= null;
}
disposeAST();
synchronized (fWaitLock) {
fWaitLock.notifyAll();
}
fCache.disposeAST();
}
/*
* @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled()
*/
void reconciled(IASTTranslationUnit ast, IPositionConverter converter, ICElement cElement, IProgressMonitor progressMonitor) {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "reconciled: " + toString(cElement) + ", AST: " + toString(ast)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
synchronized (fReconcileLock) {
fIsReconciling= progressMonitor != null && progressMonitor.isCanceled();
if (cElement == null || !cElement.equals(fReconcilingCElement)) {
if (DEBUG)
System.out.println(getThreadName() + " - " + DEBUG_PREFIX + " ignoring AST of out-dated editor"); //$NON-NLS-1$ //$NON-NLS-2$
// Signal - threads might wait for wrong element
synchronized (fWaitLock) {
fWaitLock.notifyAll();
}
return;
}
cache(ast, converter, cElement);
}
}
private static String getThreadName() {
String name= Thread.currentThread().getName();
if (name != null)
return name;
else
return Thread.currentThread().toString();
}
/**
* @param element
* @return the position converter for the AST of the active element or <code>null</code>
*/
public IPositionConverter getActivePositionConverter(ICElement element) {
if (fActiveCElement == element) {
return fActivePositionConverter;
}
return null;
if (cElement == null)
return;
Assert.isTrue(cElement instanceof ITranslationUnit);
fCache.reconciled(ast, (ITranslationUnit)cElement);
}
public IStatus runOnAST(ICElement cElement, WAIT_FLAG waitFlag, IProgressMonitor monitor,
ASTRunnable astRunnable) {
IIndex index;
try {
index = CCorePlugin.getIndexManager().getIndex(cElement.getCProject());
index.acquireReadLock();
} catch (CoreException e) {
return e.getStatus();
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
}
try {
IASTTranslationUnit ast= getAST(cElement, index, waitFlag, monitor);
return astRunnable.runOnAST(ast);
}
finally {
index.releaseReadLock();
}
ASTCache.ASTRunnable astRunnable) {
Assert.isTrue(cElement instanceof ITranslationUnit);
return fCache.runOnAST((ITranslationUnit)cElement, waitFlag != WAIT_NO, monitor, astRunnable);
}
/**
* @param cElement
* @param index
* @param monitor
* @return an AST or <code>null</code>, if no AST could be computed
*/
public IASTTranslationUnit createAST(ICElement cElement, IIndex index, IProgressMonitor monitor) {
Assert.isTrue(cElement instanceof ITranslationUnit);
return fCache.createAST((ITranslationUnit)cElement, index, monitor);
}
}

View file

@ -40,8 +40,9 @@ import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.core.model.ASTCache;
import org.eclipse.cdt.internal.ui.LineBackgroundPainter;
import org.eclipse.cdt.internal.ui.editor.ASTProvider.ASTRunnable;
import org.eclipse.cdt.internal.ui.text.ICReconcilingListener;
/**
@ -101,7 +102,7 @@ public class InactiveCodeHighlighting implements ICReconcilingListener {
IStatus result = Status.OK_STATUS;
if (fTranslationUnit != null) {
final ASTProvider astProvider= CUIPlugin.getDefault().getASTProvider();
result= astProvider.runOnAST(fTranslationUnit, ASTProvider.WAIT_YES, monitor, new ASTRunnable() {
result= astProvider.runOnAST(fTranslationUnit, ASTProvider.WAIT_YES, monitor, new ASTCache.ASTRunnable() {
public IStatus runOnAST(IASTTranslationUnit ast) {
reconciled(ast, null, monitor);
return Status.OK_STATUS;

View file

@ -43,6 +43,7 @@ import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.IInclude;
import org.eclipse.cdt.core.parser.ExtendedScannerInfo;
import org.eclipse.cdt.core.parser.IExtendedScannerInfo;
import org.eclipse.cdt.core.parser.IScannerInfo;
@ -96,17 +97,22 @@ public class OpenIncludeAction extends Action {
if (info == null) {
info = provider.getScannerInformation(proj);
}
if (info != null) {
// search in system includes
String[] includePaths = info.getIncludePaths();
findFile(includePaths, includeName, filesFound);
boolean isSystemInclude = include instanceof IInclude
&& ((IInclude) include).isStandard();
// search in user includes
if (!isSystemInclude && info != null) {
IExtendedScannerInfo scanInfo = new ExtendedScannerInfo(info);
String[] localIncludePaths = scanInfo.getLocalIncludePath();
findFile(localIncludePaths, includeName, filesFound);
}
if (filesFound.size() == 0) {
// search in local includes
if (info != null) {
IExtendedScannerInfo scanInfo = new ExtendedScannerInfo(info);
String[] localIncludePaths = scanInfo.getLocalIncludePath();
findFile(localIncludePaths, includeName, filesFound);
// search in system includes
String[] includePaths = info.getIncludePaths();
findFile(includePaths, includeName, filesFound);
}
if (filesFound.size() == 0) {

View file

@ -45,7 +45,8 @@ import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.ui.editor.ASTProvider.ASTRunnable;
import org.eclipse.cdt.internal.core.model.ASTCache;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightedPosition;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightingStyle;
import org.eclipse.cdt.internal.ui.text.ICReconcilingListener;
@ -513,7 +514,7 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener {
final Job me= this;
ASTProvider astProvider= CUIPlugin.getDefault().getASTProvider();
IStatus status= astProvider.runOnAST(element, ASTProvider.WAIT_YES, monitor, new ASTRunnable() {
IStatus status= astProvider.runOnAST(element, ASTProvider.WAIT_YES, monitor, new ASTCache.ASTRunnable() {
public IStatus runOnAST(IASTTranslationUnit ast) {
reconciled(ast, null, monitor);
synchronized (fJobLock) {

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2005, 2006 IBM Corporation and others.
* Copyright (c) 2005, 2007 IBM Corporation 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
@ -140,6 +140,7 @@ public class CReconcilingStrategy implements IReconcilingStrategy, IReconcilingS
index.acquireReadLock();
} catch (InterruptedException exc) {
ast= null;
index= null;
}
}
}

View file

@ -72,9 +72,10 @@ import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider;
import org.eclipse.cdt.internal.core.model.ASTCache;
import org.eclipse.cdt.internal.ui.editor.ASTProvider;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.editor.ASTProvider.ASTRunnable;
import org.eclipse.cdt.internal.ui.text.DocumentCharacterIterator;
import org.eclipse.cdt.internal.ui.text.ICReconcilingListener;
@ -1093,11 +1094,11 @@ public class DefaultCFoldingStructureProvider implements ICFoldingStructureProvi
IASTTranslationUnit ast= ctx.getAST();
if (ast == null) {
final ASTProvider astProvider= CUIPlugin.getDefault().getASTProvider();
IStatus status= astProvider.runOnAST(getInputElement(), ASTProvider.WAIT_ACTIVE_ONLY, null, new ASTRunnable() {
IStatus status= astProvider.runOnAST(getInputElement(), ASTProvider.WAIT_ACTIVE_ONLY, null, new ASTCache.ASTRunnable() {
public IStatus runOnAST(IASTTranslationUnit ast) {
if (ast != null) {
ctx.fAST= ast;
ctx.fASTPositionConverter= astProvider.getActivePositionConverter(getInputElement());
ctx.fASTPositionConverter= null;
fInitialASTReconcile= false;
computeFoldingStructure(ast, ctx);
}