diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index 2d45b34a79a2..c9e54fc23499 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -86,11 +86,13 @@ import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import javax.swing.*; import java.io.IOException; import java.net.UnknownHostException; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -151,6 +153,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb private final Alarm myStatusUpdateAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, myDisposable); private final ThreadBlockedMonitor myThreadBlockedMonitor = new ThreadBlockedMonitor(this, myDisposable); + private ThreadGroup myThreadGroupForJDI; protected DebugProcessImpl(Project project) { myProject = project; @@ -288,6 +291,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb checkVirtualMachineVersion(vm); myVirtualMachineProxy = new VirtualMachineProxyImpl(this, vm); + myThreadGroupForJDI = ReflectionUtil.getField(vm.getClass(), vm, ThreadGroup.class, "threadGroupForJDI"); if (!StringUtil.isEmpty(ourTrace)) { int mask = 0; @@ -2222,4 +2226,27 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb public DebuggerSession getSession() { return mySession; } + + @TestOnly + public void awaitJDIThreadTermination(long timeout, @NotNull TimeUnit unit) { + ThreadGroup threadGroupForJDI = myThreadGroupForJDI; + if (threadGroupForJDI == null) { + return; + } + long start = System.currentTimeMillis(); + Thread[] threads = new Thread[1]; + while (System.currentTimeMillis() < start + unit.toMillis(timeout)) { + int n = threadGroupForJDI.enumerate(threads); + if (n == 0) { + break; + } + try { + long timeLeft = start + unit.toMillis(timeout) - System.currentTimeMillis(); + threads[0].join(timeLeft); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } } diff --git a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java index 68426748e81a..8a3ceba5cfd2 100644 --- a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java +++ b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java @@ -41,6 +41,7 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.ui.classFilter.ClassFilter; import com.intellij.util.IJSwingUtilities; +import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; import com.intellij.util.TimeoutUtil; import com.intellij.util.lang.CompoundRuntimeException; @@ -52,19 +53,20 @@ import javax.swing.*; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; +import java.util.concurrent.TimeUnit; public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCase { - private DebugProcessListener myPauseScriptListener = null; + private DebugProcessListener myPauseScriptListener; private final List myScriptRunnables = new ArrayList(); private final SynchronizationBasedSemaphore myScriptRunnablesSema = new SynchronizationBasedSemaphore(); protected static final int RATHER_LATER_INVOKES_N = 10; - public DebugProcessImpl myDebugProcess = null; + public DebugProcessImpl myDebugProcess; private final List myException = new SmartList(); private static class InvokeRatherLaterRequest { private final DebuggerCommandImpl myDebuggerCommand; private final DebugProcessImpl myDebugProcess; - int invokesN = 0; + int invokesN; public InvokeRatherLaterRequest(DebuggerCommandImpl debuggerCommand, DebugProcessImpl debugProcess) { myDebuggerCommand = debuggerCommand; @@ -113,6 +115,10 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa @Override protected void tearDown() throws Exception { + DebugProcessImpl debugProcess = ObjectUtils.chooseNotNull(getDebugProcess(), myDebugProcess); + if (debugProcess != null) { + debugProcess.awaitJDIThreadTermination(100, TimeUnit.SECONDS); + } try { super.tearDown(); } diff --git a/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java b/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java index 58c82ae05dad..f84cb57e7807 100644 --- a/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java +++ b/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java @@ -79,7 +79,6 @@ public class ThreadTracker { wellKnownOffenders.add("IDEA Test Case Thread"); wellKnownOffenders.add("Image Fetcher "); wellKnownOffenders.add("Java2D Disposer"); - wellKnownOffenders.add("JDI Target VM Interface"); wellKnownOffenders.add("JobScheduler FJ pool "); wellKnownOffenders.add("JPS thread pool"); wellKnownOffenders.add("Keep-Alive-Timer");