From bbf7893fea034397382d2b1b2075b3612e2b14f1 Mon Sep 17 00:00:00 2001 From: irengrig Date: Thu, 19 Aug 2010 14:52:42 +0400 Subject: [PATCH] IDEA-57639 Stuck on Open Project, Cancel Button does not work (add file watch requests in pooled thread, not synchronously during initialization) --- .../FileWatchRequestModifier.java | 64 +++++++++ .../FileWatchRequestsManager.java | 51 +++++++ .../vcs/impl/projectlevelman/NewMappings.java | 131 +++--------------- 3 files changed, 137 insertions(+), 109 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/FileWatchRequestModifier.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/FileWatchRequestsManager.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/FileWatchRequestModifier.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/FileWatchRequestModifier.java new file mode 100644 index 000000000000..801d3b6f7271 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/FileWatchRequestModifier.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.impl.projectlevelman; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.VcsDirectoryMapping; +import com.intellij.openapi.vfs.LocalFileSystem; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** +* @author irengrig +*/ +public class FileWatchRequestModifier implements Runnable { + private final Project myProject; + private final NewMappings myNewMappings; + private final Map myDirectoryMappingWatches; + private final LocalFileSystem myLfs; + + public FileWatchRequestModifier(final Project project, final NewMappings newMappings, final LocalFileSystem localFileSystem) { + myLfs = localFileSystem; + myProject = project; + myNewMappings = newMappings; + myDirectoryMappingWatches = new HashMap(); + } + + @Override + public void run() { + if ((! myProject.isInitialized()) || myProject.isDisposed()) return; + final List copy = myNewMappings.getDirectoryMappings(); + + final List added = new LinkedList(copy); + added.removeAll(myDirectoryMappingWatches.keySet()); + + final List deleted = new LinkedList(myDirectoryMappingWatches.keySet()); + deleted.removeAll(copy); + + for (VcsDirectoryMapping mapping : added) { + if (mapping.isDefaultMapping()) continue; + final LocalFileSystem.WatchRequest watchRequest = myLfs.addRootToWatch(mapping.getDirectory(), true); + myDirectoryMappingWatches.put(mapping, watchRequest); + } + for (VcsDirectoryMapping mapping : deleted) { + if (mapping.isDefaultMapping()) continue; + myLfs.removeWatchedRoot(myDirectoryMappingWatches.remove(mapping)); + } + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/FileWatchRequestsManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/FileWatchRequestsManager.java new file mode 100644 index 000000000000..e40bf33bd5f4 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/FileWatchRequestsManager.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.impl.projectlevelman; + +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.RequestsMerger; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.util.Consumer; + +/** +* @author irengrig +*/ +public class FileWatchRequestsManager { + private final RequestsMerger myMerger; + private final Project myProject; + + public FileWatchRequestsManager(final Project project, final NewMappings newMappings, final LocalFileSystem localFileSystem) { + myProject = project; + myMerger = new RequestsMerger(new FileWatchRequestModifier(project, newMappings, localFileSystem), new Consumer() { + @Override + public void consume(Runnable runnable) { + if ((! myProject.isInitialized()) || myProject.isDisposed()) return; + final Application application = ApplicationManager.getApplication(); + if (application.isUnitTestMode()) { + runnable.run(); + } else { + application.executeOnPooledThread(runnable); + } + } + }); + } + + public void ping() { + myMerger.request(); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/NewMappings.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/NewMappings.java index 93094c047b8e..ec14906e73dc 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/NewMappings.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/NewMappings.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.impl.projectlevelman; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; @@ -46,8 +47,8 @@ public class NewMappings { private final Map> myVcsToPaths; private AbstractVcs[] myActiveVcses; private VcsDirectoryMapping[] mySortedMappings; - private final Map myDirectoryMappingWatches; - + private FileWatchRequestsManager myFileWatchRequestsManager; + private final DefaultVcsRootPolicy myDefaultVcsRootPolicy; private final EventDispatcher myEventDispatcher; private final FileStatusManager myFileStatusManager; @@ -61,7 +62,7 @@ public class NewMappings { myFileStatusManager = fileStatusManager; myLock = new Object(); myVcsToPaths = new HashMap>(); - myDirectoryMappingWatches = new HashMap(); + myFileWatchRequestsManager = new FileWatchRequestsManager(myProject, this, LocalFileSystem.getInstance()); myDefaultVcsRootPolicy = DefaultVcsRootPolicy.getInstance(project); myActiveVcses = new AbstractVcs[0]; myEventDispatcher = eventDispatcher; @@ -80,6 +81,12 @@ public class NewMappings { }); } + // for tests + public void setFileWatchRequestsManager(FileWatchRequestsManager fileWatchRequestsManager) { + assert ApplicationManager.getApplication().isUnitTestMode(); + myFileWatchRequestsManager = fileWatchRequestsManager; + } + public AbstractVcs[] getActiveVcses() { synchronized (myLock) { final AbstractVcs[] result = new AbstractVcs[myActiveVcses.length]; @@ -111,7 +118,6 @@ public class NewMappings { } } - final LocalFileSystem.WatchRequest request = addWatchRequest(newMapping); final Ref switched = new Ref(Boolean.FALSE); keepActiveVcs(new Runnable() { @@ -122,18 +128,10 @@ public class NewMappings { final List newList = listForVcsFromMap(newMapping.getVcs()); newList.add(newMapping); sortedMappingsByMap(); - - if (request != null) { - myDirectoryMappingWatches.put(newMapping, request); - } } } }); - if (switched.get().booleanValue() && (request != null)) { - LocalFileSystem.getInstance().removeWatchedRoot(request); - } - mappingsChanged(); } @@ -173,6 +171,7 @@ public class NewMappings { public void mappingsChanged() { myEventDispatcher.getMulticaster().directoryMappingChanged(); myFileStatusManager.fileStatusesChanged(); + myFileWatchRequestsManager.ping(); } @Modification @@ -180,17 +179,16 @@ public class NewMappings { LOG.debug("setDirectoryMappings, size: " + items.size()); MySetMappingsPreProcessor setMappingsPreProcessor = new MySetMappingsPreProcessor(items); setMappingsPreProcessor.invoke(); - final List itemsCopy = setMappingsPreProcessor.getItemsCopy(); - final Map requests = setMappingsPreProcessor.getRequests(); - final Collection toRemove = new ArrayList(); + final List itemsCopy; + if (items.isEmpty()) { + itemsCopy = Collections.singletonList(new VcsDirectoryMapping("", "")); + } else { + itemsCopy = items; + } keepActiveVcs(new Runnable() { public void run() { - // a copy! - toRemove.addAll(myDirectoryMappingWatches.values()); - myDirectoryMappingWatches.clear(); - myVcsToPaths.clear(); for (VcsDirectoryMapping mapping : itemsCopy) { listForVcsFromMap(mapping.getVcs()).add(mapping); @@ -199,11 +197,6 @@ public class NewMappings { } }); - // do not remove what added and is active - toRemove.removeAll(requests.values()); - // tracked by request object reference so OK to first add and then remove - LocalFileSystem.getInstance().removeWatchedRoots(toRemove); - mappingsChanged(); } @@ -293,21 +286,14 @@ public class NewMappings { // if vcses were not mapped, there's nothing to clear if ((myActiveVcses == null) || (myActiveVcses.length == 0)) return; - final Collection toRemove = new ArrayList(); keepActiveVcs(new Runnable() { public void run() { - // a copy! - toRemove.addAll(myDirectoryMappingWatches.values()); - myDirectoryMappingWatches.clear(); - myVcsToPaths.clear(); myActiveVcses = new AbstractVcs[0]; mySortedMappings = new VcsDirectoryMapping[0]; } }); - if (! toRemove.isEmpty()) { - LocalFileSystem.getInstance().removeWatchedRoots(toRemove); - } + myFileWatchRequestsManager.ping(); } public List getDirectoryMappings() { @@ -324,11 +310,10 @@ public class NewMappings { } public void cleanupMappings() { - final List watchRequestList; synchronized (myLock) { - watchRequestList = removeRedundantMappings(); + removeRedundantMappings(); } - LocalFileSystem.getInstance().removeWatchedRoots(watchRequestList); + myFileWatchRequestsManager.ping(); } @Nullable @@ -349,70 +334,23 @@ public class NewMappings { @Modification public void removeDirectoryMapping(final VcsDirectoryMapping mapping) { LOG.debug("remove mapping: " + mapping.getDirectory()); - final Ref request = new Ref(); keepActiveVcs(new Runnable() { public void run() { if (removeVcsFromMap(mapping, mapping.getVcs())) { sortedMappingsByMap(); - request.set(myDirectoryMappingWatches.remove(mapping)); } } }); - if (! request.isNull()) { - LocalFileSystem.getInstance().removeWatchedRoot(request.get()); - } - mappingsChanged(); } - private class MyMappingsFilter extends AbstractFilterChildren { - private final List myRemovedRequests; - - private MyMappingsFilter() { - myRemovedRequests = new ArrayList(); - } - - protected void sortAscending(List vcsDirectoryMappings) { - // todo ordering is actually here - Collections.sort(vcsDirectoryMappings, MyMappingsComparator.getInstance()); - } - - @Override - protected void onRemove(final VcsDirectoryMapping vcsDirectoryMapping) { - final LocalFileSystem.WatchRequest request = myDirectoryMappingWatches.remove(vcsDirectoryMapping); - if (request != null) { - myRemovedRequests.add(request); - } - } - - protected boolean isAncestor(VcsDirectoryMapping parent, VcsDirectoryMapping child) { - if (! parent.getVcs().equals(child.getVcs())) return false; - - final String parentPath = parent.systemIndependentPath(); - final String fixedParentPath = (parentPath.endsWith("/")) ? parentPath : (parentPath + "/"); - - if (child.systemIndependentPath().length() < fixedParentPath.length()) { - return child.systemIndependentPath().equals(parentPath); - } - return child.systemIndependentPath().startsWith(fixedParentPath); - } - - public List getRemovedRequests() { - return myRemovedRequests; - } - } - // todo area for optimization - private List removeRedundantMappings() { - final Set>> entries = myVcsToPaths.entrySet(); - + private void removeRedundantMappings() { final LocalFileSystem lfs = LocalFileSystem.getInstance(); final AllVcsesI allVcses = AllVcses.getInstance(myProject); - final List removedRequests = new LinkedList(); - for (Iterator iterator = myVcsToPaths.keySet().iterator(); iterator.hasNext();) { final String vcsName = iterator.next(); final List mappings = myVcsToPaths.get(vcsName); @@ -456,9 +394,6 @@ public class NewMappings { // to calculate what had been removed mappings.removeAll(filteredMappings); - for (VcsDirectoryMapping mapping : mappings) { - removedRequests.add(myDirectoryMappingWatches.remove(mapping)); - } if (filteredMappings.isEmpty()) { iterator.remove(); @@ -469,7 +404,6 @@ public class NewMappings { } sortedMappingsByMap(); - return removedRequests; } private boolean trySwitchVcs(final String path, final String activeVcsName) { @@ -525,14 +459,6 @@ public class NewMappings { return newList; } - @Nullable - private static LocalFileSystem.WatchRequest addWatchRequest(final VcsDirectoryMapping mapping) { - if (! mapping.isDefaultMapping()) { - return LocalFileSystem.getInstance().addRootToWatch(mapping.getDirectory(), true); - } - return null; - } - private static class MyMappingsComparator implements Comparator { private static final MyMappingsComparator ourInstance = new MyMappingsComparator(); @@ -628,7 +554,6 @@ public class NewMappings { private static class MySetMappingsPreProcessor { private final List myItems; private List myItemsCopy; - private Map myRequests; public MySetMappingsPreProcessor(final List items) { myItems = items; @@ -638,23 +563,10 @@ public class NewMappings { return myItemsCopy; } - public Map getRequests() { - return myRequests; - } - public void invoke() { if (myItems.isEmpty()) { myItemsCopy = Collections.singletonList(new VcsDirectoryMapping("", "")); - myRequests = Collections.emptyMap(); } else { - myRequests = new HashMap(); - - for (VcsDirectoryMapping item : myItems) { - final LocalFileSystem.WatchRequest request = addWatchRequest(item); - if (request != null) { - myRequests.put(item, request); - } - } myItemsCopy = myItems; } } @@ -662,4 +574,5 @@ public class NewMappings { private @interface Modification { } + }