From 1c747c6f757c0dfb238b9cb0cc87a085b68d8071 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 6 Jul 2009 16:33:37 +0400 Subject: [PATCH] TreeUi: bg loading tested and fixed --- .../ide/util/treeView/AbstractTreeBuilder.java | 33 ++- .../intellij/ide/util/treeView/AbstractTreeUi.java | 249 ++++++++++++++------- .../ide/util/treeView/UpdaterTreeState.java | 1 + .../src/com/intellij/openapi/util/SimpleTimer.java | 11 +- util/src/com/intellij/openapi/util/Condition.java | 15 ++ 5 files changed, 220 insertions(+), 89 deletions(-) diff --git a/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeBuilder.java b/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeBuilder.java index 7cc82dcc5a..f45fe942f4 100644 --- a/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeBuilder.java +++ b/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeBuilder.java @@ -20,6 +20,7 @@ import com.intellij.ide.projectView.PresentationData; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.Application; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.ActionCallback; import com.intellij.util.containers.HashSet; @@ -282,8 +283,9 @@ public class AbstractTreeBuilder implements Disposable { } protected void runOnYeildingDone(Runnable onDone) { - if (getTree().isShowing()) { - ApplicationManager.getApplication().invokeLater(onDone, ModalityState.stateForComponent(getTree())); + final Application app = ApplicationManager.getApplication(); + if (getTree().isShowing() && app != null) { + app.invokeLater(onDone, ModalityState.stateForComponent(getTree())); } else { UIUtil.invokeLaterIfNeeded(onDone); } @@ -297,6 +299,33 @@ public class AbstractTreeBuilder implements Disposable { return true; } + protected void runBackgroundLoading(final Runnable runnable) { + final Application app = ApplicationManager.getApplication(); + if (app != null) { + app.runReadAction(new Runnable() { + public void run() { + runnable.run(); + } + }); + } else { + runnable.run(); + } + } + + protected void updateAfterLoadedInBackground(Runnable runnable) { + final Application app = ApplicationManager.getApplication(); + if (app != null) { + if (getTree() != null && getTree().isVisible()) { + app.invokeLater(runnable, ModalityState.stateForComponent(getTree())); + } + else { + app.invokeLater(runnable); + } + } else { + UIUtil.invokeLaterIfNeeded(runnable); + } + } + public static class AbstractTreeNodeWrapper extends AbstractTreeNode { public AbstractTreeNodeWrapper() { super(null, null); diff --git a/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java b/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java index ce33afc028..c81d726547 100644 --- a/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java +++ b/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java @@ -62,7 +62,10 @@ class AbstractTreeUi { private final HashSet myUnbuiltNodes = new HashSet(); private TreeExpansionListener myExpansionListener; private MySelectionListener mySelectionListener; + private WorkerThread myWorker = null; + private final ArrayList myActiveWorkerTasks = new ArrayList(); + private ProgressIndicator myProgress; private static final int WAIT_CURSOR_DELAY = 100; private AbstractTreeNode TREE_NODE_WRAPPER; @@ -184,7 +187,8 @@ class AbstractTreeUi { tree.setPaintBusy(true); myBusyAlarm.cancelAllRequests(); myBusyAlarm.addRequest(myWaiterForReady, myWaitForReadyTime.asInteger()); - } else { + } + else { tree.setPaintBusy(false); } } @@ -270,6 +274,7 @@ class AbstractTreeUi { getUpdater().cancelAllRequests(); if (myWorker != null) { myWorker.dispose(true); + clearWorkerTasks(); } TREE_NODE_WRAPPER.setValue(null); if (myProgress != null) { @@ -496,7 +501,7 @@ class AbstractTreeUi { expand(getRootNode()); } if (!willUpdate) { - updateNodeChildren(getRootNode(), pass, null); + updateNodeChildren(getRootNode(), pass, null, false); } if (getRootNode().getChildCount() == 0) { myTreeModel.nodeChanged(getRootNode()); @@ -515,7 +520,8 @@ class AbstractTreeUi { final Application app = ApplicationManager.getApplication(); if (app.isDispatchThread()) { return getBuilder().updateNodeDescriptor(nodeDescriptor); - } else { + } + else { app.invokeLater(new Runnable() { public void run() { if (!isReleased()) { @@ -577,7 +583,7 @@ class AbstractTreeUi { setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate(); getBuilder().updateNode(node); - updateNodeChildren(node, pass, null); + updateNodeChildren(node, pass, null, false); } @NotNull @@ -618,7 +624,10 @@ class AbstractTreeUi { return getBuilder().getTreeStructureElement(descriptor); } - private void updateNodeChildren(final DefaultMutableTreeNode node, final TreeUpdatePass pass, @Nullable Object[] preloadedChildren) { + private void updateNodeChildren(final DefaultMutableTreeNode node, + final TreeUpdatePass pass, + @Nullable Object[] preloadedChildren, + boolean forcedNow) { getTreeStructure().commit(); final boolean wasExpanded = myTree.isExpanded(new TreePath(node.getPath())); final boolean wasLeaf = node.getChildCount() == 0; @@ -633,8 +642,9 @@ class AbstractTreeUi { return; } - if (getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor))) { - if (queueBackgroundUpdate(node, descriptor, pass)) return; + if (!forcedNow && getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor))) { + queueBackgroundUpdate(node, descriptor, pass); + return; } final Map elementToIndexMap = collectElementToIndexMap(descriptor, preloadedChildren); @@ -649,7 +659,7 @@ class AbstractTreeUi { if (canYield()) { removeLoadingNode(node); } - + ArrayList nodesToInsert = collectNodesToInsert(descriptor, elementToIndexMap); insertNodesInto(nodesToInsert, node); @@ -731,7 +741,8 @@ class AbstractTreeUi { final TreePath path = new TreePath(node.getPath()); if (getTree().isExpanded(path) || children.length == 0) { removeLoadingNode(node); - } else { + } + else { maybeYeild(new ActiveRunnable() { public ActionCallback run() { expand(element, null); @@ -803,7 +814,7 @@ class AbstractTreeUi { for (TreeNode aNodesToInsert : nodesToInsert) { DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)aNodesToInsert; addLoadingNode(childNode); - updateNodeChildren(childNode, pass, null); + updateNodeChildren(childNode, pass, null, false); } } @@ -883,7 +894,7 @@ class AbstractTreeUi { } public boolean isReady() { - return !isYeildingNow() && !hasSheduledUpdates() && !hasExpandedUnbuiltNodes(); + return !isYeildingNow() && !hasSheduledUpdates() && !hasExpandedUnbuiltNodes() && !isWorkerBusy(); } private void executeYeildingRequest(Runnable runnable, TreeUpdatePass pass) { @@ -929,16 +940,16 @@ class AbstractTreeUi { } private ActionCallback processAllChildren(final DefaultMutableTreeNode node, - final Map elementToIndexMap, - final TreeUpdatePass pass, - final ArrayList childNodes) { + final Map elementToIndexMap, + final TreeUpdatePass pass, + final ArrayList childNodes) { if (pass.isExpired()) return new ActionCallback.Rejected(); if (childNodes.size() == 0) return new ActionCallback.Done(); - + final ActionCallback result = new ActionCallback(childNodes.size()); for (TreeNode childNode1 : childNodes) { @@ -1106,7 +1117,7 @@ class AbstractTreeUi { } final Ref children = new Ref(); - Runnable updateRunnable = new Runnable() { + Runnable buildRunnable = new Runnable() { public void run() { if (isReleased()) return; @@ -1118,12 +1129,13 @@ class AbstractTreeUi { } }; - Runnable postRunnable = new Runnable() { + final DefaultMutableTreeNode[] nodeToProcessActions = new DefaultMutableTreeNode[1]; + Runnable updateRunnable = new Runnable() { public void run() { if (isReleased()) return; if (children.get() == null) return; - updateNodeChildren(node, pass, children.get()); + updateNodeChildren(node, pass, children.get(), true); myLoadingParents.remove(getElementFromDescriptor(descriptor)); @@ -1136,19 +1148,25 @@ class AbstractTreeUi { for (int i = 0; i < node.getChildCount(); i++) { TreeNode child = node.getChildAt(i); if (isLoadingNode(child)) { - if (TreeBuilderUtil.isNodeSelected(myTree, node)) { - addSelectionPath(new TreePath(myTreeModel.getPathToRoot(node)), true, Condition.FALSE); - } + //if (TreeBuilderUtil.isNodeSelected(myTree, node)) { + // addSelectionPath(new TreePath(myTreeModel.getPathToRoot(node)), true, Condition.FALSE); + //} removeIfLoading(child); i--; } } - processNodeActionsIfReady(node); + nodeToProcessActions[0] = node; } } }; - addTaskToWorker(updateRunnable, true, postRunnable); + addTaskToWorker(buildRunnable, true, updateRunnable, new Runnable() { + public void run() { + if (nodeToProcessActions[0] != null) { + processNodeActionsIfReady(nodeToProcessActions[0]); + } + } + }); return true; } @@ -1174,13 +1192,7 @@ class AbstractTreeUi { } } - if (!isUpdatingParent(node)) { - //if (myUpdaterState != null) { - // if (myUpdaterState.process(node, myTree)) { - // clearUpdaterState(); - // } - //} - + if (!isUpdatingParent(node) && !isWorkerBusy()) { final UpdaterTreeState state = myUpdaterState; if (myNodeActions.size() == 0 && state != null && !state.isProcessingNow()) { if (!state.restore()) { @@ -1190,6 +1202,7 @@ class AbstractTreeUi { } } + private void processSmartExpand(final DefaultMutableTreeNode node) { if (getBuilder().isSmartExpand() && node.getChildCount() == 1) { // "smart" expand TreeNode childNode = node.getChildAt(0); @@ -1237,10 +1250,10 @@ class AbstractTreeUi { } private ActionCallback processChildNode(final DefaultMutableTreeNode childNode, - final NodeDescriptor childDescr, - final DefaultMutableTreeNode node, - final Map elementToIndexMap, - TreeUpdatePass pass) { + final NodeDescriptor childDescr, + final DefaultMutableTreeNode node, + final Map elementToIndexMap, + TreeUpdatePass pass) { if (pass.isExpired()) { return new ActionCallback.Rejected(); @@ -1317,7 +1330,7 @@ class AbstractTreeUi { } else { elementToIndexMap.remove(getBuilder().getTreeStructureElement(childDescr)); - updateNodeChildren(childNode, pass, null); + updateNodeChildren(childNode, pass, null, false); } if (node.equals(getRootNode())) { @@ -1431,11 +1444,22 @@ class AbstractTreeUi { } private void addLoadingNode(final DefaultMutableTreeNode node) { + final boolean[] hasNoChildren = new boolean[1]; final NodeDescriptor descriptor = (NodeDescriptor)node.getUserObject(); if (!getBuilder().isAlwaysShowPlus(descriptor)) { + Runnable updateRunnable = new Runnable() { + public void run() { + if (isReleased()) return; + + if (hasNoChildren[0]) { + update(descriptor, false); + removeLoadingNode(node); + } + } + }; + if (getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor))) { - final boolean[] hasNoChildren = new boolean[1]; - Runnable runnable = new Runnable() { + Runnable buildRunnable = new Runnable() { public void run() { if (isReleased()) return; @@ -1447,26 +1471,11 @@ class AbstractTreeUi { hasNoChildren[0] = children.length == 0; } }; - - Runnable postRunnable = new Runnable() { + addTaskToWorker(buildRunnable, false, updateRunnable, new Runnable() { public void run() { - if (isReleased()) return; - - if (hasNoChildren[0]) { - update(descriptor, false); - // todo please check the fix - // removing loading node from the same node we've added it to, no need to look for it - removeLoadingNode(node); - //Object element = getElementFromDescriptor(descriptor); - //if (element != null) { - // DefaultMutableTreeNode node = getNodeForElement(element, false); - // removeLoadingNode(node); - //} - } + processNodeActionsIfReady(node); } - }; - - addTaskToWorker(runnable, false, postRunnable); + }); } else { Object[] children = getChildrenFor(getBuilder().getTreeStructureElement(descriptor)); @@ -1491,36 +1500,61 @@ class AbstractTreeUi { } } - protected void addTaskToWorker(final Runnable runnable, boolean first, final Runnable postRunnable) { - Runnable runnable1 = new Runnable() { + + protected void addTaskToWorker(@NotNull final Runnable bgReadActionRunnable, boolean first, @Nullable final Runnable edtPostRunnable, @Nullable final Runnable finilizeEdtRunnable) { + registerWorkerTask(bgReadActionRunnable); + + final Runnable pooledThreadWithProgressRunnable = new Runnable() { public void run() { - if (isReleased()) return; + if (isReleased()) { + return; + } - try { - Runnable runnable2 = new Runnable() { - public void run() { - if (isReleased()) return; - - final Application app = ApplicationManager.getApplication(); - if (app == null) return; - - app.runReadAction(runnable); - if (postRunnable != null) { - final JTree tree = myTree; - if (tree != null && tree.isVisible()) { - app.invokeLater(postRunnable, ModalityState.stateForComponent(tree)); - } - else { - app.invokeLater(postRunnable); - } + getBuilder().runBackgroundLoading(new Runnable() { + public void run() { + if (isReleased()) { + return; + } + + try { + bgReadActionRunnable.run(); + + if (edtPostRunnable != null) { + getBuilder().updateAfterLoadedInBackground(new Runnable() { + public void run() { + try { + edtPostRunnable.run(); + } + finally { + unregisterWorkerTask(bgReadActionRunnable, finilizeEdtRunnable); + } + } + }); + } else { + unregisterWorkerTask(bgReadActionRunnable, finilizeEdtRunnable); } } - }; + catch (ProcessCanceledException e) { + unregisterWorkerTask(bgReadActionRunnable, finilizeEdtRunnable); + } catch (Throwable t) { + unregisterWorkerTask(bgReadActionRunnable, finilizeEdtRunnable); + throw new RuntimeException(t); + } + } + }); + } + }; + + Runnable pooledThreadRunnable = new Runnable() { + public void run() { + if (isReleased()) return; + + try { if (myProgress != null) { - ProgressManager.getInstance().runProcess(runnable2, myProgress); + ProgressManager.getInstance().runProcess(pooledThreadWithProgressRunnable, myProgress); } else { - runnable2.run(); + pooledThreadWithProgressRunnable.run(); } } catch (ProcessCanceledException e) { @@ -1533,23 +1567,52 @@ class AbstractTreeUi { myWorker = new WorkerThread("AbstractTreeBuilder.Worker", 1); myWorker.start(); if (first) { - myWorker.addTaskFirst(runnable1); + myWorker.addTaskFirst(pooledThreadRunnable); } else { - myWorker.addTask(runnable1); + myWorker.addTask(pooledThreadRunnable); } myWorker.dispose(false); } else { if (first) { - myWorker.addTaskFirst(runnable1); + myWorker.addTaskFirst(pooledThreadRunnable); } else { - myWorker.addTask(runnable1); + myWorker.addTask(pooledThreadRunnable); } } } + private void registerWorkerTask(Runnable runnable) { + synchronized (myActiveWorkerTasks) { + myActiveWorkerTasks.add(runnable); + } + } + + private void unregisterWorkerTask(Runnable runnable, @Nullable Runnable finalizeRunnable) { + boolean wasRemoved; + synchronized (myActiveWorkerTasks) { + wasRemoved = myActiveWorkerTasks.remove(runnable); + } + + if (wasRemoved && finalizeRunnable != null) { + finalizeRunnable.run(); + } + } + + public boolean isWorkerBusy() { + synchronized (myActiveWorkerTasks) { + return myActiveWorkerTasks.size() > 0; + } + } + + private void clearWorkerTasks() { + synchronized (myActiveWorkerTasks) { + myActiveWorkerTasks.clear(); + } + } + private void updateNodeImageAndPosition(final DefaultMutableTreeNode node) { if (!(node.getUserObject() instanceof NodeDescriptor)) return; NodeDescriptor descriptor = (NodeDescriptor)node.getUserObject(); @@ -1845,7 +1908,8 @@ class AbstractTreeUi { if (!myYeildingDoneRunnables.contains(done)) { myYeildingDoneRunnables.add(done); } - } else { + } + else { done.run(); } } @@ -1911,11 +1975,18 @@ class AbstractTreeUi { final int expandIndex, @NotNull final Runnable onDone) { final Object element = getElementFor(toExpand); - if (element == null) return; + if (element == null) { + runDone(onDone); + return; + } addNodeAction(element, new NodeAction() { public void onReady(final DefaultMutableTreeNode node) { if (node.getChildCount() >= 0 && !myTree.isExpanded(new TreePath(node.getPath()))) { + final ArrayList list = new ArrayList(); + for (int i = 0; i < node.getChildCount(); i++) { + list.add(node.getChildAt(i)); + } expand(node); } @@ -2107,6 +2178,7 @@ class AbstractTreeUi { public void cancelBackgroundLoading() { if (myWorker != null) { myWorker.cancelTasks(); + clearWorkerTasks(); } myNodeActions.clear(); } @@ -2171,13 +2243,18 @@ class AbstractTreeUi { private class MyExpansionListener implements TreeExpansionListener { public void treeExpanded(TreeExpansionEvent event) { + dropUpdaterStateIfExternalChange(); TreePath path = event.getPath(); final DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); - if (!myUnbuiltNodes.contains(node)) return; + + if (!myUnbuiltNodes.contains(node)) { + return; + } myUnbuiltNodes.remove(node); + getBuilder().expandNodeChildren(node); runDone(new Runnable() { @@ -2217,7 +2294,7 @@ class AbstractTreeUi { runDone(new Runnable() { public void run() { if (isDisposed(node)) return; - + TreePath nodePath = new TreePath(node.getPath()); if (myTree.isExpanded(nodePath)) return; diff --git a/platform-api/src/com/intellij/ide/util/treeView/UpdaterTreeState.java b/platform-api/src/com/intellij/ide/util/treeView/UpdaterTreeState.java index 22ccc0c6e7..b8aa9bf911 100644 --- a/platform-api/src/com/intellij/ide/util/treeView/UpdaterTreeState.java +++ b/platform-api/src/com/intellij/ide/util/treeView/UpdaterTreeState.java @@ -182,6 +182,7 @@ public class UpdaterTreeState { final ActionCallback result = new ActionCallback(); final Set allSelected = myUi.getSelectedElements(); + Set toSelect = new HashSet(); for (Object each : adjusted.keySet()) { if (adjusted.get(each).value(each)) continue; diff --git a/platform-api/src/com/intellij/openapi/util/SimpleTimer.java b/platform-api/src/com/intellij/openapi/util/SimpleTimer.java index 6f995c74d1..0fbe7ed5be 100644 --- a/platform-api/src/com/intellij/openapi/util/SimpleTimer.java +++ b/platform-api/src/com/intellij/openapi/util/SimpleTimer.java @@ -5,9 +5,10 @@ import java.util.TimerTask; public class SimpleTimer { - private Timer ourTimer = new Timer("SimpleTimer", true); + private Timer ourTimer = new Timer(THREAD_NAME, true); private static SimpleTimer ourInstance = new SimpleTimer(); + private static final String THREAD_NAME = "SimpleTimer"; public static SimpleTimer getInstance() { return ourInstance; @@ -24,4 +25,12 @@ public class SimpleTimer { return task; } + public boolean isTimerThread() { + return isTimerThread(Thread.currentThread()); + } + + public boolean isTimerThread(Thread thread) { + return THREAD_NAME.equals(thread.getName()); + } + } \ No newline at end of file diff --git a/util/src/com/intellij/openapi/util/Condition.java b/util/src/com/intellij/openapi/util/Condition.java index 79d0b5cf19..0b00987a5c 100644 --- a/util/src/com/intellij/openapi/util/Condition.java +++ b/util/src/com/intellij/openapi/util/Condition.java @@ -25,16 +25,31 @@ public interface Condition { public boolean value(final Object object) { return object != null; } + + @Override + public String toString() { + return "Condition.NOT_NULL"; + } }; Condition TRUE = new Condition() { public boolean value(final Object object) { return true; } + + @Override + public String toString() { + return "Condition.TRUE"; + } }; Condition FALSE = new Condition() { public boolean value(final Object object) { return false; } + + @Override + public String toString() { + return "Condition.FALSE"; + } }; } -- 2.11.4.GIT