Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Pazderski2020-02-07 12:25:27 +0000
committerPaul Pazderski2020-02-12 14:53:14 +0000
commit1174c3ba92868a02950e11aa138a63c5b3241fa8 (patch)
treea17cb8fd6c895417c30abdef66ab55d588745762
parent641acbdd37d0a0c9773b3c8f4a686efd557b3a81 (diff)
downloadeclipse.platform.debug-I20200212-1800.tar.gz
eclipse.platform.debug-I20200212-1800.tar.xz
eclipse.platform.debug-I20200212-1800.zip
Java 8 added a waitFor method with timeout for the Process class. Using this instead of the plain Thread.sleep can reduce waiting time in some situations from 500 ms down to 10 ms. For the case that Process is not a native process and has no optimized waitFor implementation the situation still improves since the terminated check is performed every 100 ms instead every 500 ms. Change-Id: I0fc54981b53615fbae14d46a6c82a9b85d64b035 Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
-rw-r--r--org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java7
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java2
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java47
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java96
4 files changed, 147 insertions, 5 deletions
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 8ee0a099d..1bfb08a1e 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
@@ -20,6 +20,7 @@ import java.nio.charset.UnsupportedCharsetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.PlatformObject;
@@ -219,7 +220,11 @@ public class RuntimeProcess extends PlatformObject implements IProcess {
} catch (IllegalThreadStateException ie) {
}
try {
- Thread.sleep(TIME_TO_WAIT_FOR_THREAD_DEATH);
+ if (process != null) {
+ process.waitFor(TIME_TO_WAIT_FOR_THREAD_DEATH, TimeUnit.MILLISECONDS);
+ } else {
+ Thread.sleep(TIME_TO_WAIT_FOR_THREAD_DEATH);
+ }
} catch (InterruptedException e) {
}
attempts++;
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
index 9e3efa615..32e1ed3c0 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
@@ -23,6 +23,7 @@ import org.eclipse.debug.tests.console.IOConsoleFixedWidthTests;
import org.eclipse.debug.tests.console.IOConsoleTests;
import org.eclipse.debug.tests.console.ProcessConsoleManagerTests;
import org.eclipse.debug.tests.console.ProcessConsoleTests;
+import org.eclipse.debug.tests.console.RuntimeProcessTests;
import org.eclipse.debug.tests.console.StreamsProxyTests;
import org.eclipse.debug.tests.console.TextConsoleViewerTest;
import org.eclipse.debug.tests.launching.AcceleratorSubstitutionTests;
@@ -123,6 +124,7 @@ public class AutomatedSuite extends TestSuite {
addTest(new TestSuite(ProcessConsoleTests.class));
addTest(new TestSuite(StreamsProxyTests.class));
addTest(new TestSuite(TextConsoleViewerTest.class));
+ addTest(new TestSuite(RuntimeProcessTests.class));
// Launch Groups
addTest(new TestSuite(LaunchGroupTests.class));
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java
index 7bd305e18..aa84545c0 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2019 Paul Pazderski and others.
+ * Copyright (c) 2019, 2020 Paul Pazderski and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -17,6 +17,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.debug.core.DebugPlugin;
@@ -57,6 +58,8 @@ public class MockProcess extends Process {
* </p>
*/
private long endTime;
+ /** The simulated exit code. */
+ private int exitCode = 0;
/**
* Create new silent mockup process which runs for a given amount of time.
@@ -171,7 +174,24 @@ public class MockProcess extends Process {
}
}
}
- return 0;
+ return exitCode;
+ }
+
+ @Override
+ public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException {
+ long remainingMs = unit.toMillis(timeout);
+ final long timeoutMs = System.currentTimeMillis() + remainingMs;
+ synchronized (waitForTerminationLock) {
+ while (!isTerminated() && remainingMs > 0) {
+ long waitTime = endTime == RUN_FOREVER ? Long.MAX_VALUE : endTime - System.currentTimeMillis();
+ waitTime = Math.min(waitTime, remainingMs);
+ if (waitTime > 0) {
+ waitForTerminationLock.wait(waitTime);
+ }
+ remainingMs = timeoutMs - System.currentTimeMillis();
+ }
+ }
+ return isTerminated();
}
@Override
@@ -180,13 +200,23 @@ public class MockProcess extends Process {
final String end = (endTime == RUN_FOREVER ? "never." : "in " + (endTime - System.currentTimeMillis()) + " ms.");
throw new IllegalThreadStateException("Mockup process terminates " + end);
}
- return 0;
+ return exitCode;
}
@Override
public void destroy() {
+ destroy(0);
+ }
+
+ /**
+ * Simulate a delay for the mockup process shutdown.
+ *
+ * @param delay amount of milliseconds must pass after destroy was called
+ * and before the mockup process goes in terminated state
+ */
+ public void destroy(int delay) {
synchronized (waitForTerminationLock) {
- endTime = System.currentTimeMillis();
+ endTime = System.currentTimeMillis() + delay;
waitForTerminationLock.notifyAll();
}
}
@@ -201,6 +231,15 @@ public class MockProcess extends Process {
}
/**
+ * Set the exit code returned once the process is finished.
+ *
+ * @param exitCode new exit code
+ */
+ public void setExitValue(int exitCode) {
+ this.exitCode = exitCode;
+ }
+
+ /**
* Create a {@link RuntimeProcess} which wraps this {@link MockProcess}.
* <p>
* Note: the process will only be connected to a minimal dummy launch
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
new file mode 100644
index 000000000..c3d631b10
--- /dev/null
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Paul Pazderski and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Paul Pazderski - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.tests.console;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.model.RuntimeProcess;
+import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
+
+public class RuntimeProcessTests extends AbstractDebugTest {
+
+ public RuntimeProcessTests() {
+ super(RuntimeProcessTests.class.getSimpleName());
+ }
+
+ public RuntimeProcessTests(String name) {
+ super(name);
+ }
+
+ /**
+ * Test behavior of {@link RuntimeProcess} if the wrapped process
+ * terminates.
+ */
+ public void testProcessTerminated() throws Exception {
+ AtomicInteger processTerminateEvents = new AtomicInteger();
+ DebugPlugin.getDefault().addDebugEventListener(new IDebugEventSetListener() {
+ @Override
+ public void handleDebugEvents(DebugEvent[] events) {
+ for (DebugEvent event : events) {
+ if (event.getKind() == DebugEvent.TERMINATE) {
+ processTerminateEvents.incrementAndGet();
+ }
+ }
+ }
+ });
+
+ MockProcess mockProcess = new MockProcess(MockProcess.RUN_FOREVER);
+ RuntimeProcess runtimeProcess = mockProcess.toRuntimeProcess();
+
+ assertFalse("RuntimeProcess already terminated.", runtimeProcess.isTerminated());
+ assertTrue(runtimeProcess.canTerminate());
+
+ mockProcess.setExitValue(1);
+ mockProcess.destroy();
+
+ TestUtil.waitWhile(p -> !p.isTerminated(), runtimeProcess, 1000, p -> "RuntimePocess not terminated.");
+ TestUtil.waitForJobs(getName(), 25, 500);
+ assertEquals("Wrong number of terminate events.", 1, processTerminateEvents.get());
+ assertEquals("RuntimeProcess reported wrong exit code.", 1, runtimeProcess.getExitValue());
+ }
+
+ /** Test {@link RuntimeProcess} terminating the wrapped process. */
+ public void testTerminateProcess() throws Exception {
+ AtomicInteger processTerminateEvents = new AtomicInteger();
+ DebugPlugin.getDefault().addDebugEventListener(new IDebugEventSetListener() {
+ @Override
+ public void handleDebugEvents(DebugEvent[] events) {
+ for (DebugEvent event : events) {
+ if (event.getKind() == DebugEvent.TERMINATE) {
+ processTerminateEvents.incrementAndGet();
+ }
+ }
+ }
+ });
+
+ MockProcess mockProcess = new MockProcess(MockProcess.RUN_FOREVER);
+ RuntimeProcess runtimeProcess = mockProcess.toRuntimeProcess();
+
+ assertFalse("RuntimeProcess already terminated.", runtimeProcess.isTerminated());
+ assertTrue(runtimeProcess.canTerminate());
+
+ mockProcess.setExitValue(1);
+ runtimeProcess.terminate();
+ assertFalse("RuntimeProcess failed to terminated wrapped process.", mockProcess.isAlive());
+
+ TestUtil.waitWhile(p -> !p.isTerminated(), runtimeProcess, 1000, p -> "RuntimePocess not terminated.");
+ TestUtil.waitForJobs(getName(), 25, 500);
+ assertEquals("Wrong number of terminate events.", 1, processTerminateEvents.get());
+ assertEquals("RuntimeProcess reported wrong exit code.", 1, runtimeProcess.getExitValue());
+ }
+}

Back to the top