diff options
author | Hannes Wellmann | 2021-02-11 20:08:39 +0000 |
---|---|---|
committer | Sarika Sinha | 2021-02-22 11:01:21 +0000 |
commit | c239e1061cd3e02c6e3cb7f34b2be0be7fea7355 (patch) | |
tree | faa275a7f82e7b8458cc681fd00a363bfeb520d5 | |
parent | ee6cc06388c729e1abe892abd01fa0a2c271793c (diff) | |
download | eclipse.platform.debug-c239e1061cd3e02c6e3cb7f34b2be0be7fea7355.tar.gz eclipse.platform.debug-c239e1061cd3e02c6e3cb7f34b2be0be7fea7355.tar.xz eclipse.platform.debug-c239e1061cd3e02c6e3cb7f34b2be0be7fea7355.zip |
Bug 570480 - Make termination of descendants configurableY20210316-1310Y20210316-0510Y20210315-1000Y20210314-1000Y20210313-1000Y20210312-2250Y20210312-1250Y20210312-1000Y20210311-1000Y20210310-1000Y20210309-1000Y20210307-1000Y20210306-1000Y20210305-1000Y20210304-1000Y20210303-1000Y20210301-1000Y20210228-1000Y20210227-1000Y20210226-1000Y20210225-1000Y20210224-1000Y20210223-1050Y20210222-1000S4_19_0_RC2S4_19_0_RC1R4_19I20210303-1800I20210303-0600I20210302-1800I20210302-0930I20210302-0720I20210302-0600I20210301-1800I20210301-0600I20210228-1800I20210228-0600I20210227-1800I20210227-0600I20210226-2140I20210226-1800I20210226-0220I20210224-1800I20210224-0840I20210224-0600I20210223-1800I20210223-0650I20210223-0600I20210222-1800
Change-Id: I6f77f33c27028c06c4afb58eae9604eeab317866
Signed-off-by: Hannes Wellmann <wellmann.hannes1@gmx.net>
7 files changed, 101 insertions, 17 deletions
diff --git a/org.eclipse.debug.core/META-INF/MANIFEST.MF b/org.eclipse.debug.core/META-INF/MANIFEST.MF index 916303bb9..08fab6d01 100644 --- a/org.eclipse.debug.core/META-INF/MANIFEST.MF +++ b/org.eclipse.debug.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.debug.core; singleton:=true -Bundle-Version: 3.17.100.qualifier +Bundle-Version: 3.18.0.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.debug.core.DebugPlugin Bundle-Vendor: %providerName diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java index f35c70ed8..0a9b71229 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java @@ -356,6 +356,21 @@ public class DebugPlugin extends Plugin { public static final String ATTR_PATH = PI_DEBUG_CORE + ".ATTR_PATH"; //$NON-NLS-1$ /** + * Launch configuration attribute that designates whether or not the + * descendants of the {@link IProcess} associated to a launch of this + * configuration should be terminated if the main-process is terminated. The + * descendants (also called child- or sub-processes) of a operating system + * process are the processes started by that process. + * + * Value is a string representing a boolean - <code>true</code> or + * <code>false</code>. When unspecified, the default value is considered + * <code>true</code>. + * + * @since 3.18 + */ + public static final String ATTR_TERMINATE_DESCENDANTS = PI_DEBUG_CORE + ".TERMINATE_DESCENDANTS"; //$NON-NLS-1$ + + /** * The singleton debug plug-in instance. */ private static DebugPlugin fgDebugPlugin= null; diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java index bef44820a..98122a8d6 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2020 IBM Corporation and others. + * Copyright (c) 2000, 2021 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.Status; @@ -102,6 +103,11 @@ public class RuntimeProcess extends PlatformObject implements IProcess { private boolean fCaptureOutput = true; /** + * Whether the descendants of this process should be terminated too + */ + private boolean fTerminateDescendants = true; + + /** * Constructs a RuntimeProcess on the given system process * with the given name, adding this process to the given * launch. @@ -127,6 +133,15 @@ public class RuntimeProcess extends PlatformObject implements IProcess { String captureOutput = launch.getAttribute(DebugPlugin.ATTR_CAPTURE_OUTPUT); fCaptureOutput = !("false".equals(captureOutput)); //$NON-NLS-1$ + try { + ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); + if (launchConfiguration != null) { + fTerminateDescendants = launchConfiguration.getAttribute(DebugPlugin.ATTR_TERMINATE_DESCENDANTS, true); + } + } catch (CoreException e) { + DebugPlugin.log(e); + } + fStreamsProxy = createStreamsProxy(); fMonitor = new ProcessMonitorThread(); fMonitor.start(); @@ -209,12 +224,13 @@ public class RuntimeProcess extends PlatformObject implements IProcess { return; } - List<ProcessHandle> descendants; // only a snapshot! - try { - descendants = process.descendants().collect(Collectors.toList()); - } catch (UnsupportedOperationException e) { - // JVM may not support toHandle() -> assume no descendants - descendants = Collections.emptyList(); + List<ProcessHandle> descendants = Collections.emptyList(); + if (fTerminateDescendants) { + try { // List of descendants of process is only a snapshot! + descendants = process.descendants().collect(Collectors.toList()); + } catch (UnsupportedOperationException e) { + // JVM may not support toHandle() -> assume no descendants + } } process.destroy(); diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java index 79e4b68cd..bbcca06ea 100644 --- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 Paul Pazderski and others. + * Copyright (c) 2020, 2021 Paul Pazderski and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -22,6 +22,7 @@ import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.debug.core.DebugEvent; @@ -32,7 +33,6 @@ import org.eclipse.debug.internal.core.DebugCoreMessages; import org.eclipse.debug.tests.AbstractDebugTest; import org.eclipse.debug.tests.TestUtil; import org.junit.Test; -import org.junit.function.ThrowingRunnable; public class RuntimeProcessTests extends AbstractDebugTest { @@ -129,6 +129,31 @@ public class RuntimeProcessTests extends AbstractDebugTest { } /** + * Test {@link RuntimeProcess} terminating the wrapped process while not + * terminating its descendants. + */ + @Test + public void testTerminateProcessWithoutTerminatingDescendents() throws Exception { + + MockProcess childProcess = new MockProcess(MockProcess.RUN_FOREVER); + + MockProcess mockProcess = new MockProcess(MockProcess.RUN_FOREVER); + mockProcess.setHandle(new MockProcessHandle(mockProcess, List.of(childProcess))); + + RuntimeProcess runtimeProcess = mockProcess.toRuntimeProcess("MockProcess", Map.of(DebugPlugin.ATTR_TERMINATE_DESCENDANTS, false)); + + assertTrue("RuntimeProcess already terminated.", childProcess.isAlive()); + assertFalse("RuntimeProcess already terminated.", runtimeProcess.isTerminated()); + + runtimeProcess.terminate(); + + assertFalse("RuntimeProcess failed to terminate wrapped process.", mockProcess.isAlive()); + assertTrue("RuntimeProcess terminated child of wrapped process, unlike configured.", childProcess.isAlive()); + + TestUtil.waitWhile(p -> !p.isTerminated(), runtimeProcess, 1000, p -> "RuntimePocess not terminated."); + } + + /** * Test {@link RuntimeProcess} terminating the wrapped process which does * not support {@link Process#toHandle()}. */ @@ -140,7 +165,7 @@ public class RuntimeProcessTests extends AbstractDebugTest { // implementation is called which throws an // UnsupportedOperationException mockProcess.setHandle(null); - assertThrows(UnsupportedOperationException.class, () -> mockProcess.toHandle()); + assertThrows(UnsupportedOperationException.class, mockProcess::toHandle); RuntimeProcess runtimeProcess = mockProcess.toRuntimeProcess(); runtimeProcess.terminate(); // must not throw, even toHandle() does @@ -158,9 +183,8 @@ public class RuntimeProcessTests extends AbstractDebugTest { mockProcess.setTerminationDelay(6000); RuntimeProcess runtimeProcess = mockProcess.toRuntimeProcess(); - ThrowingRunnable termianteProcess = () -> runtimeProcess.terminate(); - DebugException timeoutException = assertThrows(DebugException.class, termianteProcess); + DebugException timeoutException = assertThrows(DebugException.class, runtimeProcess::terminate); assertThat(timeoutException.getMessage(), is(DebugCoreMessages.RuntimeProcess_terminate_failed)); } @@ -178,9 +202,8 @@ public class RuntimeProcessTests extends AbstractDebugTest { mockProcess.setHandle(new MockProcessHandle(mockProcess, List.of(childProcess))); RuntimeProcess runtimeProcess = mockProcess.toRuntimeProcess(); - ThrowingRunnable termianteProcess = () -> runtimeProcess.terminate(); - DebugException timeoutException = assertThrows(DebugException.class, termianteProcess); + DebugException timeoutException = assertThrows(DebugException.class, runtimeProcess::terminate); assertThat(timeoutException.getMessage(), is(DebugCoreMessages.RuntimeProcess_terminate_failed)); } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsMessages.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsMessages.java index d2ed415e3..84a080257 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsMessages.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsMessages.java @@ -63,6 +63,7 @@ public class LaunchConfigurationsMessages extends NLS { public static String CommonTab_AttributeLabel_AppendToFile; public static String CommonTab_AttributeLabel_LaunchInBackground; public static String CommonTab_AttributeLabel_FavoriteGroups; + public static String CommonTab_AttributeLabel_TerminateDescendants; public static String CompileErrorProjectPromptStatusHandler_0; public static String CompileErrorProjectPromptStatusHandler_1; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsMessages.properties b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsMessages.properties index a26a4d1cc..5a9456a68 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsMessages.properties +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsMessages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2019 IBM Corporation and others. +# Copyright (c) 2000, 2021 IBM Corporation and others. # # This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 @@ -55,6 +55,7 @@ CommonTab_AttributeLabel_CaptureInFile=Capture in file CommonTab_AttributeLabel_AppendToFile=Append to file CommonTab_AttributeLabel_LaunchInBackground=Launch in background CommonTab_AttributeLabel_FavoriteGroups=Favorite groups +CommonTab_AttributeLabel_TerminateDescendants=&Terminate child-processes if terminating the launched process CompileErrorPromptStatusHandler_0=Errors in Workspace CompileErrorPromptStatusHandler_1=Errors exist in a required project. Continue launch? diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/CommonTab.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/CommonTab.java index 5e34555e7..405867a5e 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/CommonTab.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/CommonTab.java @@ -124,6 +124,7 @@ public class CommonTab extends AbstractLaunchConfigurationTab { private Text fSharedLocationText; private Button fSharedLocationButton; private Button fLaunchInBackgroundButton; + private Button fTerminateDescendantsButton; private Button fDefaultEncodingButton; private Button fAltEncodingButton; private Combo fEncodingCombo; @@ -173,6 +174,7 @@ public class CommonTab extends AbstractLaunchConfigurationTab { createEncodingComponent(comp); createOutputCaptureComponent(comp); createLaunchInBackgroundComponent(comp); + createTerminateDescendantsButtonComponent(comp); } /** @@ -501,6 +503,22 @@ public class CommonTab extends AbstractLaunchConfigurationTab { } /** + * Creates the controls needed to edit the terminate descendants attribute of an + * external tool + * + * @param parent the composite to create the controls in + */ + private void createTerminateDescendantsButtonComponent(Composite parent) { + fTerminateDescendantsButton = createCheckButton(parent, + LaunchConfigurationsMessages.CommonTab_AttributeLabel_TerminateDescendants); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + data.horizontalSpan = 2; + fTerminateDescendantsButton.setLayoutData(data); + fTerminateDescendantsButton.setFont(parent.getFont()); + fTerminateDescendantsButton.addSelectionListener(widgetSelectedAdapter(e -> updateLaunchConfigurationDialog())); + } + + /** * handles the shared radio button being selected */ private void handleSharedRadioButtonSelected() { @@ -621,6 +639,9 @@ public class CommonTab extends AbstractLaunchConfigurationTab { updateLaunchInBackground(configuration); updateEncoding(configuration); updateConsoleOutput(configuration); + + boolean terminateDescendants = getAttribute(configuration, DebugPlugin.ATTR_TERMINATE_DESCENDANTS, true); + fTerminateDescendantsButton.setSelection(terminateDescendants); } /** @@ -934,7 +955,13 @@ public class CommonTab extends AbstractLaunchConfigurationTab { public void performApply(ILaunchConfigurationWorkingCopy configuration) { updateConfigFromLocalShared(configuration); updateConfigFromFavorites(configuration); - setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, configuration, fLaunchInBackgroundButton.getSelection(), true); + + boolean launchInBackground = fLaunchInBackgroundButton.getSelection(); + setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, configuration, launchInBackground, true); + + boolean terminateDescendants = fTerminateDescendantsButton.getSelection(); + setAttribute(DebugPlugin.ATTR_TERMINATE_DESCENDANTS, configuration, terminateDescendants, true); + String encoding = null; if(fAltEncodingButton.getSelection()) { encoding = fEncodingCombo.getText().trim(); @@ -1022,6 +1049,7 @@ public class CommonTab extends AbstractLaunchConfigurationTab { getAttributesLabelsForPrototype().put(IDebugUIConstants.ATTR_APPEND_TO_FILE, LaunchConfigurationsMessages.CommonTab_AttributeLabel_AppendToFile); getAttributesLabelsForPrototype().put(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, LaunchConfigurationsMessages.CommonTab_AttributeLabel_LaunchInBackground); getAttributesLabelsForPrototype().put(IDebugUIConstants.ATTR_FAVORITE_GROUPS, LaunchConfigurationsMessages.CommonTab_AttributeLabel_FavoriteGroups); + getAttributesLabelsForPrototype().put(DebugPlugin.ATTR_TERMINATE_DESCENDANTS, LaunchConfigurationsMessages.CommonTab_AttributeLabel_TerminateDescendants); } /** |