| Index: editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/internal/model/delta/DeltaProcessingState.java
|
| diff --git a/editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/internal/model/delta/DeltaProcessingState.java b/editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/internal/model/delta/DeltaProcessingState.java
|
| index 6f90e67f143e96f5d6ff7c36a0b203c02481e33d..979725bf12dbabf269d69301c87494e6d2736572 100644
|
| --- a/editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/internal/model/delta/DeltaProcessingState.java
|
| +++ b/editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/internal/model/delta/DeltaProcessingState.java
|
| @@ -15,26 +15,26 @@ package com.google.dart.tools.core.internal.model.delta;
|
|
|
| import com.google.dart.tools.core.DartCore;
|
| import com.google.dart.tools.core.internal.model.DartModelManager;
|
| -import com.google.dart.tools.core.model.DartElement;
|
| -import com.google.dart.tools.core.model.DartModelException;
|
| -import com.google.dart.tools.core.model.DartProject;
|
| import com.google.dart.tools.core.model.ElementChangedListener;
|
|
|
| +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.ResourcesPlugin;
|
| import org.eclipse.core.runtime.CoreException;
|
| -import org.eclipse.core.runtime.ISafeRunnable;
|
| -import org.eclipse.core.runtime.SafeRunner;
|
|
|
| -import java.util.HashSet;
|
| +import java.util.concurrent.CountDownLatch;
|
| +import java.util.concurrent.ExecutorService;
|
| +import java.util.concurrent.Executors;
|
| +import java.util.concurrent.TimeUnit;
|
|
|
| /**
|
| * Instances of the class <code>DeltaProcessingState</code> keep the global states used during Dart
|
| * element delta processing.
|
| */
|
| public class DeltaProcessingState implements IResourceChangeListener {
|
| -
|
| /**
|
| * Collection of listeners for Dart element deltas. These listeners are notified from
|
| * {@link DeltaProcessor#fire(com.google.dart.tools.core.model.DartElementDelta, int)}.
|
| @@ -44,41 +44,28 @@ public class DeltaProcessingState implements IResourceChangeListener {
|
| private final static int INIT_ELEMENT_CHANGED_LISTENER_ARRAY_SIZE = 8;
|
| public ElementChangedListener[] elementChangedListeners = new ElementChangedListener[INIT_ELEMENT_CHANGED_LISTENER_ARRAY_SIZE];
|
| public int[] elementChangedListenerMasks = new int[INIT_ELEMENT_CHANGED_LISTENER_ARRAY_SIZE];
|
| - public int elementChangedListenerCount = 0;
|
|
|
| - /**
|
| - * Collection of pre Dart resource change listeners. These listeners are notified when
|
| - * {@link #resourceChanged(IResourceChangeEvent)} is called, just before when the
|
| - * {@link DeltaProcessor} is called.
|
| - * <p>
|
| - * A NPE occurs when the two arrays are different sizes, thus we enforce this with a private int.
|
| - */
|
| - private final static int INIT_PRE_RESOURCE_CHANGE_ARRAY_SIZE = 1;
|
| - private IResourceChangeListener[] preResourceChangeListeners = new IResourceChangeListener[INIT_PRE_RESOURCE_CHANGE_ARRAY_SIZE];
|
| - private int[] preResourceChangeEventMasks = new int[INIT_PRE_RESOURCE_CHANGE_ARRAY_SIZE];
|
| - private int preResourceChangeListenerCount = 0;
|
| + public int elementChangedListenerCount = 0;
|
|
|
| /**
|
| * The delta processor for the current thread.
|
| */
|
| private ThreadLocal<DeltaProcessor> deltaProcessors = new ThreadLocal<DeltaProcessor>();
|
|
|
| -// /**
|
| -// * Threads that are currently running initializeRoots()
|
| -// */
|
| -// private Set<Thread> initializingThreads = Collections.synchronizedSet(new HashSet<Thread>());
|
| + private ExecutorService threadPool;
|
|
|
| /**
|
| - * Workaround for bug 15168 circular errors not reported This is a cache of the projects before
|
| - * any project addition/deletion has started.
|
| + * Create a DeltaProcessingState instance. This constructor call should be paired with a call to
|
| + * dispose().
|
| */
|
| - private HashSet<String> dartProjectNamesCache;
|
| + public DeltaProcessingState() {
|
| + ResourcesPlugin.getWorkspace().addResourceChangeListener(
|
| + this,
|
| + IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE
|
| + | IResourceChangeEvent.PRE_CLOSE);
|
|
|
| - /**
|
| - * A list of DartElement used as a scope for external archives refresh during POST_CHANGE. This is
|
| - * null if no refresh is needed.
|
| - */
|
| - private HashSet<DartElement> externalElementsToRefresh;
|
| + threadPool = Executors.newSingleThreadExecutor();
|
| + }
|
|
|
| /**
|
| * Need to clone defensively the listener information, in case some listener is reacting to some
|
| @@ -127,67 +114,13 @@ public class DeltaProcessingState implements IResourceChangeListener {
|
| elementChangedListenerCount++;
|
| }
|
|
|
| - /**
|
| - * Adds the given element to the list of elements used as a scope for external jars refresh.
|
| - */
|
| - public synchronized void addForRefresh(DartElement externalElement) {
|
| - if (externalElementsToRefresh == null) {
|
| - externalElementsToRefresh = new HashSet<DartElement>();
|
| - }
|
| - externalElementsToRefresh.add(externalElement);
|
| - }
|
| + public void dispose() {
|
| + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
|
|
|
| - /**
|
| - * Adds a pre-resource change listener onto this class. The listeners are called from
|
| - * {@link DeltaProcessingState#resourceChanged(IResourceChangeEvent)}.
|
| - *
|
| - * @see #removePreResourceChangedListener(IResourceChangeListener)
|
| - */
|
| - public synchronized void addPreResourceChangedListener(IResourceChangeListener listener,
|
| - int eventMask) {
|
| - for (int i = 0; i < preResourceChangeListenerCount; i++) {
|
| - if (preResourceChangeListeners[i] == listener) {
|
| - preResourceChangeEventMasks[i] |= eventMask;
|
| - return;
|
| - }
|
| - }
|
| - // may need to grow, no need to clone, since iterators will have cached
|
| - // original arrays and max boundary and we only add to the end.
|
| - int length;
|
| - if ((length = preResourceChangeListeners.length) == preResourceChangeListenerCount) {
|
| - System.arraycopy(
|
| - preResourceChangeListeners,
|
| - 0,
|
| - preResourceChangeListeners = new IResourceChangeListener[length * 2],
|
| - 0,
|
| - length);
|
| - System.arraycopy(
|
| - preResourceChangeEventMasks,
|
| - 0,
|
| - preResourceChangeEventMasks = new int[length * 2],
|
| - 0,
|
| - length);
|
| - }
|
| - preResourceChangeListeners[preResourceChangeListenerCount] = listener;
|
| - preResourceChangeEventMasks[preResourceChangeListenerCount] = eventMask;
|
| - preResourceChangeListenerCount++;
|
| - }
|
| -
|
| - public void doNotUse() {
|
| - // reset the delta processor of the current thread to avoid to keep it in
|
| - // memory
|
| - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=269476
|
| - deltaProcessors.set(null);
|
| - }
|
| -
|
| - public DartProject findDartProject(String name) {
|
| - if (getOldDartProjectNames().contains(name)) {
|
| - return DartModelManager.getInstance().getDartModel().getDartProject(name);
|
| - }
|
| - return null;
|
| + threadPool.shutdown();
|
| }
|
|
|
| - public DeltaProcessor getDeltaProcessor() {
|
| + public IDeltaProcessor getDeltaProcessor() {
|
| DeltaProcessor deltaProcessor = deltaProcessors.get();
|
| if (deltaProcessor != null) {
|
| return deltaProcessor;
|
| @@ -198,24 +131,26 @@ public class DeltaProcessingState implements IResourceChangeListener {
|
| }
|
|
|
| /**
|
| - * Workaround for bug 15168 circular errors not reported Returns the list of Dart projects before
|
| - * resource delta processing has started.
|
| + * Block until the delta processor has processed all outstanding changes in the queue, or until
|
| + * timeoutMillis has been reached.
|
| + *
|
| + * @param timeoutMillis the maximum number of milliseconds to wait
|
| + * @throws InterruptedException
|
| */
|
| - public synchronized HashSet<String> getOldDartProjectNames() {
|
| - if (dartProjectNamesCache == null) {
|
| - HashSet<String> result = new HashSet<String>();
|
| - DartProject[] projects;
|
| - try {
|
| - projects = DartModelManager.getInstance().getDartModel().getDartProjects();
|
| - for (DartProject dartProject : projects) {
|
| - result.add(dartProject.getElementName());
|
| - }
|
| - } catch (DartModelException dme) {
|
| - return dartProjectNamesCache;
|
| + public void processAllChanges(long timeoutMillis) throws InterruptedException {
|
| + final CountDownLatch latch = new CountDownLatch(1);
|
| +
|
| + // We put a task in the queue and wait for it to be run. It is a single-threaded, non
|
| + // re-ordering queue, so we know that when our task has run, the other tasks in the queue
|
| + // already finished.
|
| + threadPool.execute(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + latch.countDown();
|
| }
|
| - return dartProjectNamesCache = result;
|
| - }
|
| - return dartProjectNamesCache;
|
| + });
|
| +
|
| + latch.await(timeoutMillis, TimeUnit.MILLISECONDS);
|
| }
|
|
|
| /**
|
| @@ -253,54 +188,6 @@ public class DeltaProcessingState implements IResourceChangeListener {
|
| }
|
| }
|
|
|
| - public synchronized HashSet<DartElement> removeExternalElementsToRefresh() {
|
| - HashSet<DartElement> result = externalElementsToRefresh;
|
| - externalElementsToRefresh = null;
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Removes the passed listener from the {@link #preResourceChangeListeners} array.
|
| - *
|
| - * @see #addPreResourceChangedListener(IResourceChangeListener, int)
|
| - */
|
| - public synchronized void removePreResourceChangedListener(IResourceChangeListener listener) {
|
| - for (int i = 0; i < preResourceChangeListenerCount; i++) {
|
| - if (preResourceChangeListeners[i] == listener) {
|
| - // need to clone defensively since we might be in the middle of listener
|
| - // notifications (#fire)
|
| - int length = preResourceChangeListeners.length;
|
| - IResourceChangeListener[] newListeners = new IResourceChangeListener[length];
|
| - int[] newEventMasks = new int[length];
|
| - System.arraycopy(preResourceChangeListeners, 0, newListeners, 0, i);
|
| - System.arraycopy(preResourceChangeEventMasks, 0, newEventMasks, 0, i);
|
| -
|
| - // copy trailing listeners
|
| - int trailingLength = preResourceChangeListenerCount - i - 1;
|
| - if (trailingLength > 0) {
|
| - System.arraycopy(preResourceChangeListeners, i + 1, newListeners, i, trailingLength);
|
| - System.arraycopy(preResourceChangeEventMasks, i + 1, newEventMasks, i, trailingLength);
|
| - }
|
| -
|
| - // update manager listener state (#fire need to iterate over original
|
| - // listeners through a local variable to hold onto
|
| - // the original ones)
|
| - preResourceChangeListeners = newListeners;
|
| - preResourceChangeEventMasks = newEventMasks;
|
| - preResourceChangeListenerCount--;
|
| - return;
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Calling this method resets the project names map cache by setting
|
| - * {@link #dartProjectNamesCache} to <code>null</code>.
|
| - */
|
| - public synchronized void resetOldDartProjectNames() {
|
| - dartProjectNamesCache = null;
|
| - }
|
| -
|
| /**
|
| * This is the only method from {@link IResourceChangeListener}, the workspace change listener
|
| * (this), is attached to the workspace in {@link DartModelManager#startup()}, which is called
|
| @@ -312,50 +199,82 @@ public class DeltaProcessingState implements IResourceChangeListener {
|
| */
|
| @Override
|
| public void resourceChanged(final IResourceChangeEvent event) {
|
| - // for each of the pre-resource change listeners, loop through and notify the listener of the change event
|
| - for (int i = 0; i < preResourceChangeListenerCount; i++) {
|
| - // wrap callbacks with Safe runnable for subsequent listeners to be called
|
| - // when some are causing grief
|
| - final IResourceChangeListener listener = preResourceChangeListeners[i];
|
| - if ((preResourceChangeEventMasks[i] & event.getType()) != 0) {
|
| - SafeRunner.run(new ISafeRunnable() {
|
| - @Override
|
| - public void handleException(Throwable exception) {
|
| - DartCore.logError(
|
| - "Exception occurred in listener of pre Dart resource change notification", exception); //$NON-NLS-1$
|
| + try {
|
| + if (event.getDelta() != null) {
|
| + IResourceDelta[] children = event.getDelta().getAffectedChildren();
|
| +
|
| + if (children.length > 0) {
|
| + IProject project = children[0].getResource().getProject();
|
| +
|
| + if (project.exists() && project.isOpen()) {
|
| + if (project.hasNature(DartCore.DART_PROJECT_NATURE)) {
|
| + processEvent(event);
|
| + }
|
| }
|
| + }
|
| + } else {
|
| + IProject project = event.getResource().getProject();
|
|
|
| - @Override
|
| - public void run() throws Exception {
|
| - listener.resourceChanged(event);
|
| + if (project.exists() && project.isOpen()) {
|
| + if (project.hasNature(DartCore.DART_PROJECT_NATURE)) {
|
| + processEvent(event);
|
| }
|
| - });
|
| + }
|
| }
|
| + } catch (CoreException e) {
|
| + DartCore.logError(e);
|
| }
|
| - try {
|
| - if (event.getDelta() != null) {
|
| - IResourceDelta[] children = event.getDelta().getAffectedChildren();
|
| - if (children.length > 0
|
| - && children[0].getResource().getProject().hasNature(DartCore.DART_PROJECT_NATURE)) {
|
| - getDeltaProcessor().resourceChanged(event);
|
| + }
|
| +
|
| + private boolean isDeleteEvent(int eventType, DeltaProcessorDelta delta) {
|
| + if (eventType == IResourceChangeEvent.PRE_DELETE || eventType == IResourceChangeEvent.PRE_CLOSE) {
|
| + return true;
|
| + }
|
| +
|
| + if (delta == null) {
|
| + return false;
|
| + } else {
|
| + return delta.isDeleteEvent();
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Given an IResourceChangeEvent, convert its information into a DeltaProcessorDelta object, and
|
| + * process that information in a separate thread.
|
| + *
|
| + * @param event
|
| + */
|
| + private void processEvent(IResourceChangeEvent event) {
|
| + final int eventType = event.getType();
|
| + final IResource resource = event.getResource();
|
| + final DeltaProcessorDelta delta = DeltaProcessorDelta.createFrom(event.getDelta());
|
| +
|
| + // Add the processing work to the end of the thread queue.
|
| + threadPool.submit(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + try {
|
| + DeltaProcessor deltaProcessor = ((DeltaProcessor) getDeltaProcessor());
|
| +
|
| + deltaProcessor.handleResourceChanged(eventType, resource, delta);
|
| + } catch (Throwable t) {
|
| + DartCore.logError(t);
|
| + } finally {
|
| + if (eventType == IResourceChangeEvent.POST_CHANGE) {
|
| + deltaProcessors.set(null);
|
| + }
|
| }
|
| - } else if (event.getResource().getProject().hasNature(DartCore.DART_PROJECT_NATURE)) {
|
| - getDeltaProcessor().resourceChanged(event);
|
| }
|
| - } catch (CoreException e) {
|
| - //ignore
|
| - } finally {
|
| - // TODO (jerome) see 47631, may want to get rid of following so as to
|
| - // reuse delta processor ?
|
| - if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
|
| - deltaProcessors.set(null);
|
| - } else {
|
| - // If we are going to reuse the delta processor of this thread, don't
|
| - // hang on to state
|
| - // that isn't meant to be reused.
|
| - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=273385
|
| - getDeltaProcessor().overridenEventType = -1;
|
| + });
|
| +
|
| + // wait on any delete events
|
| + if (isDeleteEvent(eventType, delta)) {
|
| + try {
|
| + processAllChanges(60 * 1000);
|
| + } catch (InterruptedException e) {
|
| +
|
| }
|
| }
|
| }
|
| +
|
| }
|
|
|