diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.java index 138bc95372fe..7a9bf72b8fdd 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.java @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * @author max - */ package com.intellij.openapi.vfs.newvfs; import com.intellij.openapi.application.ModalityState; @@ -28,6 +24,9 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; +/** + * @author max + */ public abstract class RefreshQueue { public static RefreshQueue getInstance() { return ServiceManager.getService(RefreshQueue.class); @@ -71,6 +70,8 @@ public abstract class RefreshQueue { public abstract void processSingleEvent(@NotNull VFileEvent event); + public abstract void cancelSession(long id); + @NotNull protected ModalityState getDefaultModalityState() { return ModalityState.NON_MODAL; diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshSession.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshSession.java index dcfd68e3955b..78f0daaa7cc7 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshSession.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshSession.java @@ -25,13 +25,17 @@ import java.util.Collection; * @author max */ public abstract class RefreshSession { + public long getId() { + return 0; + } + public abstract boolean isAsynchronous(); public abstract void addFile(@NotNull VirtualFile file); public abstract void addAllFiles(@NotNull Collection files); - public void addAllFiles(@NotNull VirtualFile[] files) { + public void addAllFiles(@NotNull VirtualFile... files) { addAllFiles(Arrays.asList(files)); } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshQueueImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshQueueImpl.java index 2cabf0369902..a7d611208fee 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshQueueImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshQueueImpl.java @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * @author max - */ package com.intellij.openapi.vfs.newvfs; import com.intellij.openapi.application.Application; @@ -30,17 +26,22 @@ import com.intellij.openapi.vfs.VfsBundle; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.io.storage.HeavyProcessLatch; +import gnu.trove.TLongObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.concurrent.ExecutorService; +/** + * @author max + */ public class RefreshQueueImpl extends RefreshQueue { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.RefreshQueueImpl"); private final ExecutorService myQueue = ConcurrencyUtil.newSingleThreadExecutor("FS Synchronizer"); private final ProgressIndicator myRefreshIndicator = new RefreshProgress(VfsBundle.message("file.synchronize.progress")); + private final TLongObjectHashMap mySessions = new TLongObjectHashMap(); public void execute(@NotNull RefreshSessionImpl session) { if (session.isAsynchronous()) { @@ -51,8 +52,14 @@ public class RefreshQueueImpl extends RefreshQueue { final Application application = ApplicationManager.getApplication(); boolean isEDT = application.isDispatchThread(); if (isEDT) { - session.scan(); - final boolean hasWriteAction = application.isWriteAccessAllowed(); + try { + updateSessionMap(session, true); + session.scan(); + } + finally { + updateSessionMap(session, false); + } + boolean hasWriteAction = application.isWriteAccessAllowed(); session.fireEvents(hasWriteAction); } else { @@ -75,9 +82,11 @@ public class RefreshQueueImpl extends RefreshQueue { myRefreshIndicator.start(); HeavyProcessLatch.INSTANCE.processStarted(); try { + updateSessionMap(session, true); session.scan(); } finally { + updateSessionMap(session, false); HeavyProcessLatch.INSTANCE.processFinished(); myRefreshIndicator.stop(); } @@ -96,6 +105,31 @@ public class RefreshQueueImpl extends RefreshQueue { }); } + private void updateSessionMap(RefreshSession session, boolean add) { + long id = session.getId(); + if (id != 0) { + synchronized (mySessions) { + if (add) { + mySessions.put(id, session); + } + else { + mySessions.remove(id); + } + } + } + } + + @Override + public void cancelSession(long id) { + RefreshSession session; + synchronized (mySessions) { + session = mySessions.get(id); + } + if (session instanceof RefreshSessionImpl) { + ((RefreshSessionImpl)session).cancel(); + } + } + @Override public RefreshSession createSession(boolean async, boolean recursively, @Nullable Runnable finishRunnable, @NotNull ModalityState state) { return new RefreshSessionImpl(async, recursively, finishRunnable, state); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshSessionImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshSessionImpl.java index 5100401459be..5ade281f5b19 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshSessionImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshSessionImpl.java @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * @author max - */ package com.intellij.openapi.vfs.newvfs; import com.intellij.openapi.application.ApplicationManager; @@ -37,10 +33,17 @@ import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +/** + * @author max + */ public class RefreshSessionImpl extends RefreshSession { private static final Logger LOG = Logger.getInstance(RefreshSession.class); + private static final AtomicLong ID_COUNTER = new AtomicLong(0); + + private final long myId = ID_COUNTER.incrementAndGet(); private final boolean myIsAsync; private final boolean myIsRecursive; private final Runnable myFinishRunnable; @@ -50,6 +53,8 @@ public class RefreshSessionImpl extends RefreshSession { private List myWorkQueue = new ArrayList(); private List myEvents = new ArrayList(); private volatile boolean iHaveEventsToFire; + private volatile RefreshWorker myWorker = null; + private volatile boolean myCancelled = false; public RefreshSessionImpl(final boolean isAsync, final boolean recursively, final Runnable finishRunnable) { this(isAsync, recursively, finishRunnable, ModalityState.NON_MODAL); @@ -70,6 +75,11 @@ public class RefreshSessionImpl extends RefreshSession { myModalityState = ModalityState.NON_MODAL; } + @Override + public long getId() { + return myId; + } + @Override public void addAllFiles(@NotNull Collection files) { for (VirtualFile file : files) { @@ -101,7 +111,7 @@ public class RefreshSessionImpl extends RefreshSession { public void scan() { List workQueue = myWorkQueue; myWorkQueue = new ArrayList(); - boolean hasEventsToFire = myFinishRunnable != null || !myEvents.isEmpty(); + boolean haveEventsToFire = myFinishRunnable != null || !myEvents.isEmpty(); if (!workQueue.isEmpty()) { LocalFileSystemImpl fs = (LocalFileSystemImpl)LocalFileSystem.getInstance(); @@ -109,12 +119,14 @@ public class RefreshSessionImpl extends RefreshSession { FileWatcher watcher = fs.getFileWatcher(); for (VirtualFile file : workQueue) { + if (myCancelled) break; + NewVirtualFile nvf = (NewVirtualFile)file; if (!myIsRecursive && (!myIsAsync || !watcher.isWatched(nvf))) { // We're unable to definitely refresh synchronously by means of file watcher. nvf.markDirty(); } - RefreshWorker worker = new RefreshWorker(file, myIsRecursive); + RefreshWorker worker = myWorker = new RefreshWorker(file, myIsRecursive); long t = LOG.isDebugEnabled() ? System.currentTimeMillis() : 0; worker.scan(); List events = worker.getEvents(); @@ -123,10 +135,21 @@ public class RefreshSessionImpl extends RefreshSession { LOG.debug(file + " scanned in " + t + " ms, events: " + events); } myEvents.addAll(events); - if (!events.isEmpty()) hasEventsToFire = true; + if (!events.isEmpty()) haveEventsToFire = true; } } - iHaveEventsToFire = hasEventsToFire; + + myWorker = null; + iHaveEventsToFire = haveEventsToFire; + } + + public void cancel() { + myCancelled = true; + + RefreshWorker worker = myWorker; + if (worker != null) { + worker.cancel(); + } } public void fireEvents(boolean hasWriteAction) { diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java index c1ae59481dc1..66d8167112b4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java @@ -48,12 +48,17 @@ public class RefreshWorker { private final boolean myIsRecursive; private final Queue myRefreshQueue = new Queue(100); private final List myEvents = new ArrayList(); + private volatile boolean myCancelled = false; public RefreshWorker(final VirtualFile refreshRoot, final boolean isRecursive) { myIsRecursive = isRecursive; myRefreshQueue.addLast(refreshRoot); } + public void cancel() { + myCancelled = true; + } + public void scan() { final NewVirtualFile root = (NewVirtualFile)myRefreshQueue.peekFirst(); final boolean rootDirty = root.isDirty(); @@ -77,7 +82,8 @@ public class RefreshWorker { final PersistentFS persistence = PersistentFS.getInstance(); - while (!myRefreshQueue.isEmpty()) { + main: + while (!myRefreshQueue.isEmpty() && !myCancelled) { final VirtualFileSystemEntry file = (VirtualFileSystemEntry)myRefreshQueue.pullFirst(); final boolean fileDirty = file.isDirty(); debug(LOG, "file=%s dirty=%b", file, fileDirty); @@ -114,6 +120,7 @@ public class RefreshWorker { } for (String name : newNames) { + if (myCancelled) break main; final FileAttributes childAttributes = fs.getAttributes(new FakeVirtualFile(file, name)); if (childAttributes != null) { scheduleCreation(file, name, childAttributes.isDirectory()); @@ -124,6 +131,7 @@ public class RefreshWorker { } for (VirtualFile child : file.getChildren()) { + if (myCancelled) break main; if (!deletedNames.contains(child.getName())) { final FileAttributes childAttributes = fs.getAttributes(child); if (childAttributes != null) { @@ -140,6 +148,7 @@ public class RefreshWorker { final Collection cachedChildren = file.getCachedChildren(); debug(LOG, "cached=%s", cachedChildren); for (VirtualFile child : cachedChildren) { + if (myCancelled) break main; final FileAttributes childAttributes = fs.getAttributes(child); if (childAttributes != null) { checkAndScheduleChildRefresh(file, child, childAttributes); @@ -152,6 +161,7 @@ public class RefreshWorker { final List names = dir.getSuspiciousNames(); debug(LOG, "suspicious=%s", names); for (String name : names) { + if (myCancelled) break main; if (name.isEmpty()) continue; final VirtualFile fake = new FakeVirtualFile(file, name);