diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/ASTCache.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/ASTCache.java index 94927b9ac70..69c189d8098 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/ASTCache.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/ASTCache.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others. All rights reserved. + * Copyright (c) 2007, 2011 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 @@ -7,9 +7,12 @@ * Contributors: * Anton Leherbauer (Wind River Systems) - initial API and implementation * Markus Schorn (Wind River Systems) + * Sergey Prigogin (Google) ******************************************************************************/ package org.eclipse.cdt.internal.core.model; +import java.util.concurrent.Semaphore; + import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.index.IIndex; @@ -71,6 +74,11 @@ public class ASTCache { private ITranslationUnit fActiveTU; /** The cached AST if any */ private IASTTranslationUnit fAST; + /** + * The semaphore controlling exclusive access to the cached AST. + * null when fAST is null. + */ + private Semaphore fASTSemaphore; /** * The timestamp of the last index write access at the time * the AST got cached. A cached AST becomes invalid on any index @@ -212,13 +220,15 @@ public class ASTCache { } try { - IASTTranslationUnit ast= getAST(tUnit, index, wait, monitor); + IASTTranslationUnit ast= acquireSharedAST(tUnit, index, wait, monitor); ILanguage lang= (tUnit instanceof TranslationUnit) ? ((TranslationUnit) tUnit).getLanguageOfContext() : tUnit.getLanguage(); if (ast == null) { return astRunnable.runOnAST(lang, ast); } - synchronized (ast) { + try { return astRunnable.runOnAST(lang, ast); + } finally { + releaseSharedAST(ast); } } catch (CoreException e) { return e.getStatus(); @@ -227,6 +237,31 @@ public class ASTCache { } } + public IASTTranslationUnit acquireSharedAST(ITranslationUnit tUnit, IIndex index, boolean wait, + IProgressMonitor monitor) { + IASTTranslationUnit ast= getAST(tUnit, index, wait, monitor); + if (ast == null) + return null; + synchronized (fCacheMutex) { + if (ast == fAST) { + try { + fASTSemaphore.acquire(); + } catch (InterruptedException e) { + throw new OperationCanceledException(); + } + } + } + return ast; + } + + public void releaseSharedAST(IASTTranslationUnit ast) { + synchronized (fCacheMutex) { + if (ast == fAST) { + fASTSemaphore.release(); + } + } + } + /** * Caches the given AST for the given translation unit. * @@ -248,6 +283,7 @@ public class ASTCache { disposeAST(); fAST= ast; + fASTSemaphore= fAST == null ? null : new Semaphore(1); fLastWriteOnIndex= fAST == null ? 0 : fAST.getIndex().getLastWriteAccess(); // Signal AST change @@ -266,6 +302,7 @@ public class ASTCache { System.out.println(DEBUG_PREFIX + getThreadName() + "disposing AST: " + toString(fAST) + " for: " + toString(fActiveTU)); //$NON-NLS-1$ //$NON-NLS-2$ fAST= null; + fASTSemaphore= null; cache(null, null); } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java index 8b279e368c5..7a0eb580249 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * Anton Leherbauer (Wind River Systems) - Adapted for CDT * Markus Schorn (Wind River Systems) + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui.editor; @@ -28,13 +29,13 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.ITextEditor; 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.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. @@ -42,7 +43,6 @@ import org.eclipse.cdt.internal.core.model.ASTCache; * @since 4.0 */ public final class ASTProvider { - /** * Wait flag. */ @@ -329,16 +329,57 @@ public final class ASTProvider { ASTCache.ASTRunnable astRunnable) { Assert.isTrue(cElement instanceof ITranslationUnit); final ITranslationUnit tu = (ITranslationUnit) cElement; - if (!tu.isOpen()) + if (!canUseCache(tu, waitFlag)) return Status.CANCEL_STATUS; + return fCache.runOnAST(tu, waitFlag != WAIT_NO, monitor, astRunnable); + } + /** + * Returns a shared AST and locks it for exclusive access. An AST obtained from this + * method has to be released by calling {@link #releaseSharedAST(IASTTranslationUnit)}. + * Subsequent call to this method will block until the AST is released. + *

+ * The AST can be released by a thread other than the one that acquired it. + *

+ * An index lock must be held by the caller when calling this method. The index lock may + * not be released until the AST is released. + * + * @param tu The translation unit to get the AST for. + * @param index index with read lock held. + * @param waitFlag condition for waiting for the AST to be built. + * @param monitor a progress monitor, may be null. + * @return the shared AST, or null if the shared AST is not available. + */ + public final IASTTranslationUnit acquireSharedAST(ITranslationUnit tu, IIndex index, + WAIT_FLAG waitFlag, IProgressMonitor monitor) { + if (!canUseCache(tu, waitFlag)) + return null; + return fCache.acquireSharedAST(tu, index, waitFlag != WAIT_NO, monitor); + } + + /** + * Releases a shared AST previously acquired by calling + * {@link #acquireSharedAST(ITranslationUnit, IIndex, WAIT_FLAG, IProgressMonitor)}. + *

+ * Can be called by a thread other than the one that acquired the AST. + * + * @param ast the AST to release. + */ + public final void releaseSharedAST(IASTTranslationUnit ast) { + fCache.releaseSharedAST(ast); + } + + private synchronized boolean canUseCache(ITranslationUnit tu, WAIT_FLAG waitFlag) { final boolean isActive= fCache.isActiveElement(tu); + if (!tu.isOpen()) + return false; + if (waitFlag == WAIT_ACTIVE_ONLY && !isActive) { - return Status.CANCEL_STATUS; + return false; } if (isActive && updateModificationStamp()) { fCache.disposeAST(); } - return fCache.runOnAST(tu, waitFlag != WAIT_NO, monitor, astRunnable); + return true; } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/RefactoringASTCache.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/RefactoringASTCache.java index 833d436c70d..e0e6ad5d468 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/RefactoringASTCache.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/RefactoringASTCache.java @@ -16,9 +16,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.core.runtime.Status; import org.eclipse.ui.services.IDisposable; import org.eclipse.cdt.core.CCorePlugin; @@ -26,11 +24,9 @@ import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICProject; -import org.eclipse.cdt.core.model.ILanguage; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.ui.CUIPlugin; -import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable; import org.eclipse.cdt.internal.corext.util.CModelUtil; import org.eclipse.cdt.internal.ui.editor.ASTProvider; @@ -39,8 +35,8 @@ import org.eclipse.cdt.internal.ui.editor.ASTProvider; * Cache containing ASTs for the translation units participating in refactoring. * The cache object has to be disposed of after use. Failure to do so may cause * loss of index lock. - * - * This class is thread-safe. + *

+ * This class is not thread-safe. */ public class RefactoringASTCache implements IDisposable { private static final int PARSE_MODE = ITranslationUnit.AST_SKIP_ALL_HEADERS @@ -49,21 +45,24 @@ public class RefactoringASTCache implements IDisposable { | ITranslationUnit.AST_PARSE_INACTIVE_CODE; private final Map fASTCache; - private final Object astBuildMutex; private IIndex fIndex; + private IASTTranslationUnit fSharedAST; private boolean fDisposed; public RefactoringASTCache() { fASTCache = new ConcurrentHashMap(); - astBuildMutex = new Object(); } /** * Returns an AST for the given translation unit. The AST is built for the working * copy of the translation unit if such working copy exists. The returned AST is * a shared one whenever possible. - * NOTE: No references to the AST or its nodes can be kept after calling - * the {@link #dispose()} method. + *

+ * An AST returned by this method should not be accessed concurrently by multiple threads. + *

+ * NOTE: No references to the AST or its nodes can be kept after calling + * the {@link #dispose()} method. + * * @param tu The translation unit. * @param pm A progress monitor. * @return An AST, or null if the AST cannot be obtained. @@ -76,38 +75,31 @@ public class RefactoringASTCache implements IDisposable { throw new OperationCanceledException(); tu= CModelUtil.toWorkingCopy(tu); - IASTTranslationUnit ast; - ast= fASTCache.get(tu); - - if (ast == null) { - // Try to get a shared AST before creating our own. - final IASTTranslationUnit[] astHolder = new IASTTranslationUnit[1]; - ASTProvider.getASTProvider().runOnAST(tu, ASTProvider.WAIT_ACTIVE_ONLY, pm, new ASTRunnable() { - public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { - // Leaking of AST outside of runOnAST method is dangerous, but it does not cause - // harm here since the index remains locked for the duration of the AST life span. - astHolder[0] = ast; - return Status.OK_STATUS; - } - }); - ast = astHolder[0]; - - if (ast == null) { - synchronized (astBuildMutex) { - ast= fASTCache.get(tu); - if (ast == null) { - if (pm != null && pm.isCanceled()) - throw new OperationCanceledException(); - ast= tu.getAST(fIndex, PARSE_MODE); - fASTCache.put(tu, ast); - } - } - } - } + // Try to get a shared AST before creating our own. + IASTTranslationUnit ast= fASTCache.get(tu); + if (ast == null) { + if (fSharedAST != null && tu.equals(fSharedAST.getOriginatingTranslationUnit())) { + ast = fSharedAST; + } else { + ast = ASTProvider.getASTProvider().acquireSharedAST(tu, fIndex, + ASTProvider.WAIT_ACTIVE_ONLY, pm); + if (ast == null) { + if (pm != null && pm.isCanceled()) + throw new OperationCanceledException(); + ast= tu.getAST(fIndex, PARSE_MODE); + fASTCache.put(tu, ast); + } else { + if (fSharedAST != null) { + ASTProvider.getASTProvider().releaseSharedAST(fSharedAST); + } + fSharedAST = ast; + } + } + } if (pm != null) { pm.done(); } - return ast; + return ast; } /** @@ -115,7 +107,7 @@ public class RefactoringASTCache implements IDisposable { * * @return The index. */ - public synchronized IIndex getIndex() throws CoreException, OperationCanceledException { + public IIndex getIndex() throws CoreException, OperationCanceledException { Assert.isTrue(!fDisposed, "RefactoringASTCache is already disposed"); //$NON-NLS-1$ if (fIndex == null) { ICProject[] projects = CoreModel.getDefault().getCModel().getCProjects(); @@ -137,6 +129,9 @@ public class RefactoringASTCache implements IDisposable { public void dispose() { Assert.isTrue(!fDisposed, "RefactoringASTCache.dispose() called more than once"); //$NON-NLS-1$ fDisposed = true; + if (fSharedAST != null) { + ASTProvider.getASTProvider().releaseSharedAST(fSharedAST); + } if (fIndex != null) { fIndex.releaseReadLock(); }