From 4e2f72cf9e808de8fe22f0ae1d1050fce443e5eb Mon Sep 17 00:00:00 2001 From: Irina Chernushina Date: Fri, 28 Aug 2009 19:28:09 +0400 Subject: [PATCH] CVS: do not hung when closing a project while invoke-and-wait in another thread and that thread is possibly waited for while closing --- .../cvsSupport2/cvsExecution/ModalityContext.java | 4 +- .../cvsExecution/CvsOperationExecutor.java | 4 +- .../cvsExecution/ModalityContextImpl.java | 6 ++- .../cvsSupport2/cvshandlers/UpdateHandler.java | 2 +- .../intellij/lifecycle/PeriodicalTasksCloser.java | 58 ++++++++++++++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/plugins/cvs2/cvs_util/src/com/intellij/cvsSupport2/cvsExecution/ModalityContext.java b/plugins/cvs2/cvs_util/src/com/intellij/cvsSupport2/cvsExecution/ModalityContext.java index 1dfa3cb99a..463ec64399 100644 --- a/plugins/cvs2/cvs_util/src/com/intellij/cvsSupport2/cvsExecution/ModalityContext.java +++ b/plugins/cvs2/cvs_util/src/com/intellij/cvsSupport2/cvsExecution/ModalityContext.java @@ -4,8 +4,10 @@ package com.intellij.cvsSupport2.cvsExecution; +import com.intellij.openapi.project.Project; + public interface ModalityContext { - void runInDispatchThread(Runnable action); + void runInDispatchThread(Runnable action, Project project); boolean isForTemporaryConfiguration(); } diff --git a/plugins/cvs2/source/com/intellij/cvsSupport2/cvsExecution/CvsOperationExecutor.java b/plugins/cvs2/source/com/intellij/cvsSupport2/cvsExecution/CvsOperationExecutor.java index 4297c811ce..2a0401c9d9 100644 --- a/plugins/cvs2/source/com/intellij/cvsSupport2/cvsExecution/CvsOperationExecutor.java +++ b/plugins/cvs2/source/com/intellij/cvsSupport2/cvsExecution/CvsOperationExecutor.java @@ -150,7 +150,7 @@ public class CvsOperationExecutor { if (doNotShowProgress()) { cvsAction.run(); - myExecutor.runInDispatchThread(finish); + myExecutor.runInDispatchThread(finish, myProject); } else { PerformInBackgroundOption backgroundOption = handler.getBackgroundOption(myProject); @@ -201,7 +201,7 @@ public class CvsOperationExecutor { } } - }); + }, myProject); } private static void setText(String text) { diff --git a/plugins/cvs2/source/com/intellij/cvsSupport2/cvsExecution/ModalityContextImpl.java b/plugins/cvs2/source/com/intellij/cvsSupport2/cvsExecution/ModalityContextImpl.java index 6d2f3d4297..5acb129977 100644 --- a/plugins/cvs2/source/com/intellij/cvsSupport2/cvsExecution/ModalityContextImpl.java +++ b/plugins/cvs2/source/com/intellij/cvsSupport2/cvsExecution/ModalityContextImpl.java @@ -1,10 +1,12 @@ package com.intellij.cvsSupport2.cvsExecution; +import com.intellij.lifecycle.PeriodicalTasksCloser; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; /** * author: lesya @@ -23,14 +25,14 @@ public class ModalityContextImpl implements ModalityContext { myIsForTemporaryConfiguration = forTemp; } - public void runInDispatchThread(Runnable action) { + public void runInDispatchThread(Runnable action, Project project) { Application application = ApplicationManager.getApplication(); if (application.isUnitTestMode() || application.isDispatchThread()) { action.run(); } else { ModalityState modalityState = getCurrentModalityState(); - application.invokeAndWait(action, modalityState); + PeriodicalTasksCloser.invokeAndWaitInterruptedWhenClosing(project, action, modalityState); } } diff --git a/plugins/cvs2/source/com/intellij/cvsSupport2/cvshandlers/UpdateHandler.java b/plugins/cvs2/source/com/intellij/cvsSupport2/cvshandlers/UpdateHandler.java index 18476fd05b..0c41b2671a 100644 --- a/plugins/cvs2/source/com/intellij/cvsSupport2/cvshandlers/UpdateHandler.java +++ b/plugins/cvs2/source/com/intellij/cvsSupport2/cvshandlers/UpdateHandler.java @@ -122,7 +122,7 @@ public class UpdateHandler extends CommandCvsHandler implements PostCvsActivity public void run() { new CorruptedProjectFilesDialog(myProject, myCorruptedFiles).show(); } - }); + }, myProject); } diff --git a/vcs-impl/src/com/intellij/lifecycle/PeriodicalTasksCloser.java b/vcs-impl/src/com/intellij/lifecycle/PeriodicalTasksCloser.java index 5540c421c3..964efbc658 100644 --- a/vcs-impl/src/com/intellij/lifecycle/PeriodicalTasksCloser.java +++ b/vcs-impl/src/com/intellij/lifecycle/PeriodicalTasksCloser.java @@ -1,5 +1,9 @@ package com.intellij.lifecycle; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; @@ -8,7 +12,11 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerListener; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.util.concurrency.Semaphore; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -92,4 +100,54 @@ public class PeriodicalTasksCloser implements ProjectManagerListener { return component; } } + + public static void invokeAndWaitInterruptedWhenClosing(final Project project, final Runnable runnable, final ModalityState modalityState) { + final Ref start = new Ref(Boolean.TRUE); + final Application application = ApplicationManager.getApplication(); + LOG.assertTrue(! application.isDispatchThread()); + + final Semaphore semaphore = new Semaphore(); + semaphore.down(); + Runnable runnable1 = new Runnable() { + public void run() { + try { + runnable.run(); + } + finally { + semaphore.up(); + } + } + + @NonNls + public String toString() { + return "PeriodicalTaskCloser's invoke and wait [" + runnable.toString() + "]"; + } + }; + LaterInvocator.invokeLater(runnable1, modalityState, new Condition() { + public boolean value(Object o) { + synchronized (start) { + return ! start.get(); + } + } + }); + + while (true) { + if (semaphore.waitFor(1000)) { + return; + } + final Ref fire = new Ref(); + synchronized (ourLock) { + final Boolean state = myStates.get(project); + if (! Boolean.TRUE.equals(state)) { + fire.set(Boolean.TRUE); + } + if (Boolean.TRUE.equals(fire.get())) { + synchronized (start) { + start.set(Boolean.FALSE); + return; + } + } + } + } + } } -- 2.11.4.GIT