diff --git a/platform/platform-impl/src/com/intellij/execution/process/KillableColoredProcessHandler.java b/platform/platform-impl/src/com/intellij/execution/process/KillableColoredProcessHandler.java index 51e30cc7f21f..2d66edd75e05 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/KillableColoredProcessHandler.java +++ b/platform/platform-impl/src/com/intellij/execution/process/KillableColoredProcessHandler.java @@ -46,6 +46,10 @@ public class KillableColoredProcessHandler extends ColoredProcessHandler impleme super(process, commandLine); } + /** + * Sets whether the process will be terminated gracefully. + * @param shouldKillProcessSoftly true, if graceful process termination should be attempted first (i.e. soft kill) + */ public void setShouldKillProcessSoftly(boolean shouldKillProcessSoftly) { myShouldKillProcessSoftly = shouldKillProcessSoftly; } @@ -56,30 +60,42 @@ public class KillableColoredProcessHandler extends ColoredProcessHandler impleme * @return */ private boolean canKillProcessSoftly() { - // soft-kill works on Unix systems - return SystemInfo.isUnix && processCanBeKilledByOS(getProcess()); + if (processCanBeKilledByOS(myProcess)) { + if (SystemInfo.isWindows) { + // runnerw.exe can send Ctrl+C events to a wrapped process + return myProcess instanceof RunnerWinProcess; + } + else if (SystemInfo.isUnix) { + // 'kill -SIGINT ' will be executed + return true; + } + } + return false; } @Override protected void doDestroyProcess() { + boolean gracefulTerminationAttempted = false; if (canKillProcessSoftly() && shouldKillProcessSoftly()) { - // Unix: [soft-kill] at first send INT signal: - final Process process = getProcess(); - if (UnixProcessManager.sendSigIntToProcessTree(process)) { - return; + if (SystemInfo.isWindows) { + if (myProcess instanceof RunnerWinProcess) { + RunnerWinProcess runnerWinProcess = (RunnerWinProcess) myProcess; + runnerWinProcess.destroyGracefully(true); + gracefulTerminationAttempted = true; + } + } + else if (SystemInfo.isUnix) { + gracefulTerminationAttempted = UnixProcessManager.sendSigIntToProcessTree(myProcess); } } - - // if soft kill isn't supported - use default implementation - super.doDestroyProcess(); - // else IDE will suggest 'terminate dialog' - + if (!gracefulTerminationAttempted) { + // execute default process destroy + super.doDestroyProcess(); + } } /** - * This method should be overridden by children if the process shouldn't be killed softly (e.g. by kill -2) - * - * @return + * @return true, if graceful process termination should be attempted first */ protected boolean shouldKillProcessSoftly() { return myShouldKillProcessSoftly; @@ -92,7 +108,22 @@ public class KillableColoredProcessHandler extends ColoredProcessHandler impleme @Override public void killProcess() { - // kill -9 + // execute 'kill -SIGKILL ' on Unix killProcessTree(getProcess()); } + + @NotNull + public static KillableColoredProcessHandler create(@NotNull GeneralCommandLine commandLine) throws ExecutionException { + final Process process; + if (SystemInfo.isWindows) { + process = RunnerWinProcess.create(commandLine); + } + else { + process = commandLine.createProcess(); + } + return new KillableColoredProcessHandler(process, + commandLine.getCommandLineString(), + commandLine.getCharset()); + } + } diff --git a/platform/platform-impl/src/com/intellij/execution/process/RunnerMediator.java b/platform/platform-impl/src/com/intellij/execution/process/RunnerMediator.java index bc561c6d031c..f8e952c15467 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/RunnerMediator.java +++ b/platform/platform-impl/src/com/intellij/execution/process/RunnerMediator.java @@ -101,7 +101,7 @@ public class RunnerMediator { } } - private static void injectRunnerCommand(@NotNull GeneralCommandLine commandLine) { + static void injectRunnerCommand(@NotNull GeneralCommandLine commandLine) { final String path = getRunnerPath(); if (path != null) { commandLine.getParametersList().addAt(0, commandLine.getExePath()); @@ -121,7 +121,7 @@ public class RunnerMediator { * Destroys process tree: in case of windows via imitating ctrl+c, in case of unix via sending sig_int to every process in tree. * @param process to kill with all sub-processes. */ - private static boolean destroyProcess(@NotNull final Process process, final boolean softKill) { + static boolean destroyProcess(@NotNull final Process process, final boolean softKill) { try { if (SystemInfo.isWindows) { sendCtrlEventThroughStream(process, softKill ? C : BRK); diff --git a/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java b/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java new file mode 100644 index 000000000000..e2dd827b5018 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2013 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.execution.process; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.openapi.util.SystemInfo; +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; +import java.io.OutputStream; + +public class RunnerWinProcess extends Process { + + private final Process myOriginalProcess; + + private RunnerWinProcess(@NotNull Process originalProcess) { + myOriginalProcess = originalProcess; + } + + @Override + public OutputStream getOutputStream() { + return myOriginalProcess.getOutputStream(); + } + + @Override + public InputStream getInputStream() { + return myOriginalProcess.getInputStream(); + } + + @Override + public InputStream getErrorStream() { + return myOriginalProcess.getErrorStream(); + } + + @Override + public int waitFor() throws InterruptedException { + return myOriginalProcess.waitFor(); + } + + @Override + public int exitValue() { + return myOriginalProcess.exitValue(); + } + + @Override + public void destroy() { + myOriginalProcess.destroy(); + } + + public Process getOriginalProcess() { + return myOriginalProcess; + } + + /** + * Sends Ctrl+C or Ctrl+Break event to the process. + * @param softKill if true, Ctrl+C event will be sent (otherwise, Ctrl+Break) + */ + public void destroyGracefully(boolean softKill) { + RunnerMediator.destroyProcess(this, softKill); + } + + @NotNull + public static RunnerWinProcess create(@NotNull GeneralCommandLine commandLine) throws ExecutionException { + if (!SystemInfo.isWindows) { + throw new RuntimeException(RunnerWinProcess.class.getSimpleName() + " works on Windows only!"); + } + RunnerMediator.injectRunnerCommand(commandLine); + Process process = commandLine.createProcess(); + return new RunnerWinProcess(process); + } + +} diff --git a/platform/platform-impl/src/com/intellij/execution/process/impl/OSProcessManagerImpl.java b/platform/platform-impl/src/com/intellij/execution/process/impl/OSProcessManagerImpl.java index cf6b7668f569..e1848c7b1608 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/impl/OSProcessManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/execution/process/impl/OSProcessManagerImpl.java @@ -16,6 +16,7 @@ package com.intellij.execution.process.impl; import com.intellij.execution.process.OSProcessManager; +import com.intellij.execution.process.RunnerWinProcess; import com.intellij.execution.process.UnixProcessManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; @@ -42,12 +43,12 @@ public class OSProcessManagerImpl extends OSProcessManager { public boolean killProcessTree(@NotNull Process process) { if (SystemInfo.isWindows) { try { - new WinProcess(process).killRecursively(); + WinProcess winProcess = createWinProcess(process); + winProcess.killRecursively(); return true; } catch (Throwable e) { - LOG.info("Cannot kill process tree"); - LOG.info(e); + LOG.info("Cannot kill process tree", e); } } else if (SystemInfo.isUnix) { @@ -56,6 +57,15 @@ public class OSProcessManagerImpl extends OSProcessManager { return false; } + @NotNull + private static WinProcess createWinProcess(@NotNull Process process) { + if (process instanceof RunnerWinProcess) { + RunnerWinProcess runnerWinProcess = (RunnerWinProcess) process; + return new WinProcess(runnerWinProcess.getOriginalProcess()); + } + return new WinProcess(process); + } + @Override public List getCommandLinesOfRunningProcesses() { try {