# HG changeset patch
# Parent c6ee961ab61fa8588f8b30a5022db487724b2447
8087286: Need a way to handle control-C and possibly some other signals
Summary: ProcessEvents support raising and listening for events between processes
diff --git a/make/mapfiles/libjava/mapfile-vers b/make/mapfiles/libjava/mapfile-vers
--- a/make/mapfiles/libjava/mapfile-vers
+++ b/make/mapfiles/libjava/mapfile-vers
@@ -170,6 +170,7 @@ SUNWprivate_1.1 {
Java_java_lang_ProcessHandleImpl_initNative;
Java_java_lang_ProcessHandleImpl_isAlive0;
Java_java_lang_ProcessHandleImpl_parent0;
+ Java_java_lang_ProcessHandleImpl_raise0;
Java_java_lang_ProcessHandleImpl_waitForProcessExit0;
Java_java_lang_ProcessHandleImpl_00024Info_initIDs;
Java_java_lang_ProcessHandleImpl_00024Info_info0;
diff --git a/src/java.base/share/classes/java/lang/ProcessHandle.java b/src/java.base/share/classes/java/lang/ProcessHandle.java
--- a/src/java.base/share/classes/java/lang/ProcessHandle.java
+++ b/src/java.base/share/classes/java/lang/ProcessHandle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,17 @@ package java.lang;
import java.time.Duration;
import java.time.Instant;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ForkJoinPool;
+import java.util.function.Consumer;
import java.util.stream.Stream;
+import jdk.internal.misc.Signal;
+
/**
* ProcessHandle identifies and provides control of native processes. Each
* individual process can be monitored for liveness, list its children,
@@ -37,6 +44,8 @@ import java.util.stream.Stream;
* By comparison, {@link java.lang.Process Process} instances were started
* by the current process and additionally provide access to the process
* input, output, and error streams.
+ * Process events (signals) can be raised in native processes and
+ * event notifications received.
*
* The native process ID is an identification number that the
* operating system assigns to the process.
@@ -71,6 +80,19 @@ import java.util.stream.Stream;
* The ability to control processes is also restricted by the native system,
* ProcessHandle provides no more access to, or control over, the native process
* than would be allowed by a native application.
+ *
+ * {@link ProcessEvent Process events} can be {@link #raise(ProcessEvent) raised}
+ * in a target process. Process events are implemented using operating
+ * system specific mechanisms. For example, a process can be interrupted
+ * on Unix by raising {@link ProcessEvent#INTERRUPT INTERRUPT} that sends
+ * {@code SIGINT} signal.
+ * {@link #registerEvent(ProcessEvent, Consumer, ExecutorService) Registering} for a process
+ * event allows the current process to be notified when an external event occurs.
+ * Registering for process events requires the
+ * {@link RuntimePermission RuntimePermission("manageProcess")} permission,
+ * if a SecurityManager is present.
+ * Not all process events are supported on all platforms and are not available
+ * if used by the Java runtime.
*
* @implSpec
* In the case where ProcessHandles cannot be supported then the factory
@@ -112,10 +134,10 @@ public interface ProcessHandle extends C
* @param pid a native process ID
* @return an {@code Optional} of the PID for the process;
* the {@code Optional} is empty if the process does not exist
+ * @throws UnsupportedOperationException if the implementation
+ * does not support this operation
* @throws SecurityException if a security manager has been installed and
* it denies RuntimePermission("manageProcess")
- * @throws UnsupportedOperationException if the implementation
- * does not support this operation
*/
public static Optional of(long pid) {
return ProcessHandleImpl.get(pid);
@@ -381,6 +403,18 @@ public interface ProcessHandle extends C
boolean isAlive();
/**
+ * Raises the event in the process.
+ *
+ * @param event the event to raise in the process
+ * @return {@code true} if the event was raised successfully,
+ * otherwise {@code false}
+ *
+ * @throws UnsupportedOperationException if the event is
+ * not supported on the current platform
+ */
+ boolean raise(ProcessEvent event);
+
+ /**
* Returns a hash code value for this ProcessHandle.
* The hashcode value follows the general contract for {@link Object#hashCode()}.
* The value is a function of the {@link #getPid getPid()} value and
@@ -435,4 +469,177 @@ public interface ProcessHandle extends C
@Override
int compareTo(ProcessHandle other);
+ /**
+ * Registers the consumer in the current process to be notified when a
+ * {@link ProcessEvent ProcessEvent} occurs.
+ * The {@code consumer} and {@code executorService} replace any previous
+ * consumer and executor service for the event.
+ * When the low level event occurs the consumer is executed asynchronously
+ * using the {@link ExecutorService#submit(Runnable)}.
+ * @apiNote
+ * The application can select a dedicated ExecutorService such as
+ * {@link Executors#newSingleThreadExecutor()} or a shared ExecutorService
+ * such as {@link ForkJoinPool#commonPool()}, or an other suitable
+ * {@link ExecutorService}.
+ *
+ * @param event the event to register
+ * @param newConsumer the consumer to be invoked when the event occurs
+ * @param executorService an {@link ExecutorService} used to deliver
+ * the event notifications
+ *
+ * @throws NullPointerException if {@code event}, {@code consumer},
+ * or {@code executorService} is null
+ * @throws UnsupportedOperationException if the event is
+ * not supported on the current platform
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("processEvent")
+ */
+ static void registerEvent(ProcessEvent event,
+ Consumer newConsumer,
+ ExecutorService executorService) {
+ Objects.requireNonNull(event, "event");
+ event.register(newConsumer, executorService);
+ }
+
+ /**
+ * Unregister the consumer in the current process to be notified
+ * when {@link ProcessEvent ProcessEvent}s occur.
+ * The consumer previously registered is removed, if any, and
+ * the default processing for the process event is restored.
+ *
+ * @param event the event to unregister
+ *
+ * @throws NullPointerException if {@code event} is null
+ * @throws UnsupportedOperationException if the event is
+ * not supported on the current platform
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("processEvent")
+ */
+ static void unregisterEvent(ProcessEvent event) {
+ event.unregister();
+ }
+
+ /**
+ * Events that can be raised to a process and registered for notification
+ * in the current process.
+ */
+ enum ProcessEvent {
+
+ /**
+ * An interrupt event.
+ * @implSpec
+ * Implemented on signal "INT".
+ */
+ INTERRUPT("INT"),
+ /**
+ * A terminate event.
+ * @implSpec
+ * Implemented on signal "TERM".
+ */
+ TERMINATE("TERM"),
+ /**
+ * A hangup event.
+ * @implSpec
+ * Implemented on signal "HUP".
+ * @apiNote Windows does not have a {code HANGUP} notification.
+ */
+ HANGUP("HUP"),
+ /**
+ * A USER1 event.
+ * @implSpec
+ * Implemented on signal "USR1".
+ */
+ USER1("USR1"),
+ /**
+ * A USER2 event.
+ * @implSpec
+ * Implemented on signal "USR2".
+ */
+ USER2("USR2");
+
+ private final Signal signal;
+
+ // The current consumer or {@code null} if none.
+ private Consumer consumer;
+
+ // The original Signal handler to be restored on unregister.
+ private Signal.Handler origHandler;
+
+ /**
+ * Constructor of a ProcessEvent.
+ * @param signalName The corresponding low level Signal name.
+ */
+ private ProcessEvent(String signalName) {
+ this.consumer = null;
+ this.origHandler = null;
+ Signal sig = null;
+ try {
+ sig = new Signal(signalName);
+ } catch (IllegalArgumentException iae) {
+ // Signal not supported, for example, HUP on Windows
+ }
+ this.signal = sig;
+ }
+
+ /**
+ * Returns the low level signal corresponding to this event.
+ * @return the Signal
+ */
+ Signal signal() {
+ return signal;
+ }
+
+ /**
+ * Registers a consumer to be notified when this {@link ProcessEvent}s occur.
+ * The consumer replaces any previous consumer for this process event.
+ * The notifications are asynchronous with the low level event signaling.
+ * The consumer is executed using the {@link ForkJoinPool#commonPool()}.
+ *
+ * @param newConsumer the consumer to be invoked when the event occurs; non-null
+ * @param executorService an {@link ExecutorService} used to deliver
+ * the event notifications; non-null
+ *
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("processEvents")
+ */
+ synchronized void register(Consumer newConsumer,
+ ExecutorService executorService) {
+ Objects.requireNonNull(newConsumer, "newConsumer");
+ Objects.requireNonNull(executorService, "executorService");
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("processEvents"));
+ }
+
+ Signal.Handler handler = (s) -> {
+ executorService.execute(() -> newConsumer.accept(this));
+ };
+ Signal.Handler oldH = Signal.handle(signal, handler);
+ if (oldH == Signal.Handler.SIG_IGN) {
+ throw new UnsupportedOperationException("Event support not available: " + this.name());
+ }
+ if (origHandler == null) {
+ origHandler = oldH;
+ }
+ consumer = newConsumer; // record for debugging
+ }
+
+ /**
+ * Unregister to be notified when {@link ProcessEvent}s occur.
+ * The consumer, if any, previously registered is removed and
+ * the default processing for the process event is restored.
+ *
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("processEvents")
+ */
+ synchronized void unregister() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("processEvents"));
+ }
+
+ Signal.handle(signal, origHandler != null ? origHandler : Signal.Handler.SIG_DFL);
+ }
+ }
+
}
diff --git a/src/java.base/share/classes/java/lang/ProcessHandleImpl.java b/src/java.base/share/classes/java/lang/ProcessHandleImpl.java
--- a/src/java.base/share/classes/java/lang/ProcessHandleImpl.java
+++ b/src/java.base/share/classes/java/lang/ProcessHandleImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,7 @@
*/
package java.lang;
+import java.io.UncheckedIOException;
import java.security.PrivilegedAction;
import java.time.Duration;
import java.time.Instant;
@@ -39,6 +40,7 @@ import java.util.concurrent.SynchronousQ
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@@ -361,6 +363,28 @@ final class ProcessHandleImpl implements
*/
private static native long isAlive0(long pid);
+ /**
+ * Raises the event in the process.
+ * @param event the event to raise in the process
+ * @return {@code true} if the event was raised successfully,
+ * otherwise {@code false}
+ */
+ @Override
+ public boolean raise(ProcessEvent event) {
+ return raise0(pid, startTime, event.signal().getNumber());
+ }
+
+ /**
+ * Raise the signal in the process.
+ * The process is signaled only if its start time matches the known start time.
+ *
+ * @param pid process id of the process
+ * @param startTime the start time of the process
+ * @param signum the signal number
+ * @return true if the process was signaled without error; false otherwise
+ */
+ private static native boolean raise0(long pid, long startTime, int signum);
+
@Override
public Stream children() {
// The native OS code selects based on matching the requested parent pid.
diff --git a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
--- a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
+++ b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
@@ -320,6 +320,27 @@ Java_java_lang_ProcessHandleImpl_destroy
}
/*
+ * Class: java_lang_ProcessHandleImpl
+ * Method: destroy0
+ * Signature: (JJZ)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_java_lang_ProcessHandleImpl_raise0(JNIEnv *env,
+ jobject obj,
+ jlong jpid,
+ jlong startTime,
+ jint signum) {
+ pid_t pid = (pid_t) jpid;
+ jlong start = Java_java_lang_ProcessHandleImpl_isAlive0(env, obj, jpid);
+
+ if (start == startTime || start == 0 || startTime == 0) {
+ return (kill(pid, signum) < 0) ? JNI_FALSE : JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
+}
+
+/*
* Returns the children of the requested pid and optionally each parent and
* start time.
* Accumulates any process who parent pid matches.
diff --git a/test/sun/misc/SunMiscSignalTest.java b/test/java/lang/ProcessHandle/ProcessEventTest.java
copy from test/sun/misc/SunMiscSignalTest.java
copy to test/java/lang/ProcessHandle/ProcessEventTest.java
--- a/test/sun/misc/SunMiscSignalTest.java
+++ b/test/java/lang/ProcessHandle/ProcessEventTest.java
@@ -23,11 +23,12 @@
* questions.
*/
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
+import java.lang.ProcessHandle.ProcessEvent;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
import org.testng.Assert;
import org.testng.TestNG;
@@ -38,22 +39,17 @@ import org.testng.annotations.DataProvid
import jdk.test.lib.Platform;
import jdk.test.lib.Utils;
-import sun.misc.Signal;
-import sun.misc.SignalHandler;
-
/*
* @test
* @library /test/lib/share/classes
- * @modules jdk.unsupported
- * java.base/jdk.internal.misc
* @build jdk.test.lib.Platform jdk.test.lib.Utils
- * @run testng/othervm -Xrs -DXrs=true SunMiscSignalTest
- * @run testng/othervm SunMiscSignalTest
- * @summary sun.misc.Signal test
+ * @run testng/othervm ProcessEventTest
+ * @run testng/othervm -Xrs -DXrs=true ProcessEventTest
+ * @summary ProcessEvent test
*/
@Test
-public class SunMiscSignalTest {
+public class ProcessEventTest {
// Set to true to enable additional debug output
static boolean debug = true;
@@ -61,6 +57,9 @@ public class SunMiscSignalTest {
// True to test while running with -Xrs
static boolean RUNNING_WITH_Xrs = Boolean.getBoolean("Xrs");
+ // The executor service to delivery notifications
+ static ExecutorService executorService = Executors.newSingleThreadExecutor();
+
/**
* Print a debug message if enabled.
*
@@ -73,8 +72,6 @@ public class SunMiscSignalTest {
}
}
- enum IsSupported {NO, YES}
-
enum CanRegister {NO, YES}
enum CanRaise {NO, YES}
@@ -93,79 +90,30 @@ public class SunMiscSignalTest {
// -Xrs restricted signals signals the VM will not handle SIGINT, SIGTERM, SIGHUP and others
@DataProvider(name = "supportedSignals")
static Object[][] supportedSignals() {
- RestrictedSignals rs = RUNNING_WITH_Xrs ? RestrictedSignals.XRS : RestrictedSignals.NORMAL;
CanRegister registerXrs = RUNNING_WITH_Xrs ? CanRegister.NO : CanRegister.YES;
CanRaise raiseXrs = RUNNING_WITH_Xrs ? CanRaise.NO : CanRaise.YES;
Invoked invokedXrs = RUNNING_WITH_Xrs ? Invoked.NO : Invoked.YES;
Object[][] commonSignals = new Object[][]{
- {"INT", IsSupported.YES, registerXrs, raiseXrs, invokedXrs},
- {"TERM", IsSupported.YES, registerXrs, raiseXrs, invokedXrs},
- {"ABRT", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
+ {ProcessEvent.INTERRUPT, registerXrs, raiseXrs, invokedXrs},
+ {ProcessEvent.TERMINATE, registerXrs, raiseXrs, invokedXrs},
};
Object[][] posixSignals = {
- {"HUP", IsSupported.YES, registerXrs, raiseXrs, invokedXrs},
- {"QUIT", IsSupported.YES, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"BUS", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"USR1", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"USR2", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"PIPE", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"ALRM", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"CHLD", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"CONT", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"TSTP", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"TTIN", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"TTOU", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"URG", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"XCPU", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"XFSZ", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"VTALRM", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"PROF", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"WINCH", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"IO", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
- {"SYS", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
+ {ProcessEvent.HANGUP, registerXrs, raiseXrs, invokedXrs},
+ {ProcessEvent.USER1, CanRegister.YES, CanRaise.YES, invokedXrs},
+ {ProcessEvent.USER2, CanRegister.YES, CanRaise.YES, invokedXrs},
};
Object[][] windowsSignals = {
- {"HUP", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"QUIT", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"BUS", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"USR1", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"USR2", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"PIPE", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"ALRM", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"CHLD", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"CONT", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"TSTP", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"TTIN", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"TTOU", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"URG", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"XCPU", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"XFSZ", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"VTALRM", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"PROF", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"WINCH", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"IO", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
- {"SYS", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
+ {ProcessEvent.HANGUP, CanRegister.NO, CanRaise.NO, Invoked.NO},
+ {ProcessEvent.USER1, CanRegister.NO, CanRaise.NO, Invoked.NO},
+ {ProcessEvent.USER2, CanRegister.NO, CanRaise.NO, Invoked.NO},
};
return concatArrays(commonSignals, (Platform.isWindows() ? windowsSignals : posixSignals));
}
- // Provider of invalid signal names
- @DataProvider(name = "invalidSunMiscSignalNames")
- Object[][] invalidSunMiscSignalNames() {
- return new Object[][]{
- {""},
- {"I"},
- {"SIG"},
- {"SIGabc"},
- {"SIGINT"}, // prefix not allowed
- {"abc"},
- };
- }
-
static Object[][] concatArrays(Object[][]... arrays) {
int l = 0;
for (Object[][] a : arrays) {
@@ -183,208 +131,39 @@ public class SunMiscSignalTest {
}
// Return true if the signal is one of the shutdown signals known to the VM
- private static boolean isShutdownSignal(Signal signal) {
- String name = signal.getName();
- return name.equals("INT") || name.equals("HUP") || name.equals("TERM");
+ private static boolean isShutdownSignal(ProcessEvent event) {
+ return event == ProcessEvent.INTERRUPT || event == ProcessEvent.HANGUP;
}
/**
- * Quick verification of supported signals using sun.misc.Signal.
+ * Quick verification of supported signals using ProcessEvent
*
- * @param name the signal name
* @throws InterruptedException would be an error if thrown
*/
@Test(dataProvider = "supportedSignals")
- static void testSunMisc(String name, IsSupported supported, CanRegister register,
+ static void testSunMisc(ProcessEvent event, CanRegister register,
CanRaise raise, Invoked invoked) throws InterruptedException {
- Handler h = new Handler();
- SignalHandler orig = null;
- Signal signal = null;
+ Handler handler = new Handler();
+ ProcessHandle self = ProcessHandle.current();
try {
- signal = new Signal(name);
- Assert.assertEquals(supported, IsSupported.YES, "Unexpected support for " + name);
- Assert.assertEquals(signal.getName(), name, "getName() mismatch, ");
+ ProcessHandle.registerEvent(event, handler, executorService);
+ Assert.assertEquals(CanRegister.YES, register, "Unexpected registerEvent succeeded " + event);
+ try {
+ self.raise(event);
+ Assert.assertEquals(CanRaise.YES, raise, "Unexpected raise success for " + event);
+ Invoked inv = handler.semaphore().tryAcquire(Utils.adjustTimeout(100L),
+ TimeUnit.MILLISECONDS) ? Invoked.YES : Invoked.NO;
+ Assert.assertEquals(inv, invoked, "handler not invoked;");
+ } catch (IllegalArgumentException uoe3) {
+ Assert.assertNotEquals(CanRaise.YES, raise, "raise failed for " + event +
+ ": " + uoe3.getMessage());
+ }
- Assert.assertEquals(signal.toString(), "SIG" + name, "toString() mismatch, ");
-
- try {
- orig = Signal.handle(signal, h);
- printf("oldHandler: %s%n", orig);
- Assert.assertEquals(CanRegister.YES, register, "Unexpected handle succeeded " + name);
- try {
- Signal.raise(signal);
- Assert.assertEquals(CanRaise.YES, raise, "Unexpected raise success for " + name);
- Invoked inv = h.semaphore().tryAcquire(Utils.adjustTimeout(100L),
- TimeUnit.MILLISECONDS) ? Invoked.YES : Invoked.NO;
- if (!isShutdownSignal(signal)) {
- // Normal case
- Assert.assertEquals(inv, invoked, "handler not invoked;");
- } else {
- if (orig == SignalHandler.SIG_IGN) {
- Assert.assertEquals(inv, Invoked.NO, "handler should not be invoked");
- } else {
- Assert.assertEquals(inv, invoked, "handler not invoked;");
- }
- }
- } catch (IllegalArgumentException uoe3) {
- Assert.assertNotEquals(CanRaise.YES, raise, "raise failed for " + name +
- ": " + uoe3.getMessage());
- }
- } catch (IllegalArgumentException uoe2) {
- Assert.assertNotEquals(CanRegister.YES, register, "handle failed for: " + name +
- ": " + uoe2.getMessage());
- }
} catch (IllegalArgumentException uoe) {
- Assert.assertNotEquals(IsSupported.YES, supported, "Support missing for " + name +
+ Assert.assertNotEquals(CanRegister.YES, register, "Support missing for " + event +
": " + uoe.getMessage());
return;
- } finally {
- // Restore original signal handler
- if (orig != null && signal != null) {
- Signal.handle(signal, orig);
- }
- }
- }
-
- // Test Signal is equal to itself and not equals to others
- @Test(dataProvider = "supportedSignals")
- static void testEquals(String name, IsSupported supported, CanRegister register,
- CanRaise raise, Invoked invoked) {
- Object[][] data = supportedSignals();
- for (int i = 0; i < data.length; i++) {
- IsSupported otherSupported = (IsSupported) data[i][1];
- if (supported == IsSupported.NO || otherSupported == IsSupported.NO) {
- continue;
- }
- String otherName = (String) data[i][0];
-
- Signal sig1 = new Signal(name);
- Signal sig2 = new Signal(otherName);
- if (name.equals(otherName)) {
- Assert.assertEquals(sig1, sig2, "Equals failed; ");
- Assert.assertEquals(sig1.hashCode(), sig2.hashCode(), "HashCode wrong; ");
- } else {
- Assert.assertNotEquals(sig1, sig2, "NotEquals failed; ");
- Assert.assertNotEquals(sig1.hashCode(), sig2.hashCode(), "HashCode wrong; ");
- }
- }
- }
-
- @Test(dataProvider = "invalidSunMiscSignalNames")
- static void testSunMiscIAE(String name) {
- try {
- new Signal(name);
- Assert.fail("Should have thrown IAE for signal: " + name);
- } catch (IllegalArgumentException iae) {
- Assert.assertEquals(iae.getMessage(), "Unknown signal: " + name, "getMessage() incorrect; ");
- }
- }
-
- // Note: JDK 8 did not check/throw NPE, passing null resulted in a segv
- @Test(expectedExceptions = NullPointerException.class)
- static void nullSignal() {
- new Signal(null);
- }
-
- // Test expected exception when raising a signal when no handler defined
- @Test
- static void testRaiseNoConsumer() {
- Signal signal = new Signal("INT");
- SignalHandler orig = null;
- try {
- orig = Signal.handle(signal, SignalHandler.SIG_DFL);
- printf("oldHandler: %s%n", orig);
- if (orig == SignalHandler.SIG_IGN) {
- // SIG_IGN for TERM means it cannot be handled
- return;
- }
- Signal.raise(signal);
- Assert.fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException iae) {
- printf("IAE message: %s%n", iae.getMessage());
- } finally {
- // Restore original signal handler
- if (orig != null && signal != null) {
- Signal.handle(signal, orig);
- }
- }
- }
-
- /**
- * The thread that runs the handler for sun.misc.Signal should be a
- * Daemon thread.
- */
- @Test
- static void isDaemonThread() throws InterruptedException {
- if (RUNNING_WITH_Xrs) {
- return;
- }
- Handler handler = new Handler();
- Signal signal = new Signal("INT");
- SignalHandler orig = Signal.handle(signal, handler);
- printf("oldHandler: %s%n", orig);
- if (orig == SignalHandler.SIG_IGN) {
- // SIG_IGN for INT means it cannot be handled
- return;
- }
-
- Signal.raise(signal);
- boolean handled = handler.semaphore()
- .tryAcquire(Utils.adjustTimeout(100L), TimeUnit.MILLISECONDS);
- if (!handled) {
- // For debug try again
- printf("Second try to see signal");
- handled = handler.semaphore()
- .tryAcquire(Utils.adjustTimeout(2L), TimeUnit.SECONDS);
- }
- Assert.assertEquals(handled, !RUNNING_WITH_Xrs,
- "raising s.m.Signal did not get a callback;");
-
- Assert.assertTrue(handler.wasDaemon(), "Thread.isDaemon running the handler; ");
- }
-
- // Check that trying to invoke SIG_DFL.handle throws UnsupportedOperationException.
- @Test(expectedExceptions = UnsupportedOperationException.class)
- static void cannotHandleSIGDFL() {
- Signal signal = new Signal("INT");
- Assert.assertNotNull(SignalHandler.SIG_DFL, "SIG_DFL null; ");
- SignalHandler.SIG_DFL.handle(signal);
- }
-
- // Check that trying to invoke SIG_IGN.handle throws UnsupportedOperationException.
- @Test(expectedExceptions = UnsupportedOperationException.class)
- static void cannotHandleSIGIGN() {
- Signal signal = new Signal("INT");
- Assert.assertNotNull(SignalHandler.SIG_IGN, "SIG_IGN null; ");
- SignalHandler.SIG_IGN.handle(signal);
- }
-
- // Check that setting a Signal handler returns the previous handler.
- @Test()
- static void checkLastHandler() {
- if (RUNNING_WITH_Xrs) {
- return;
- }
- Signal signal = new Signal("TERM");
- Handler h1 = new Handler();
- Handler h2 = new Handler();
- SignalHandler orig = Signal.handle(signal, h1);
- if (orig == SignalHandler.SIG_IGN) {
- // SIG_IGN for TERM means it cannot be handled
- return;
- }
-
- try {
- SignalHandler prev = Signal.handle(signal, h2);
- Assert.assertSame(prev, h1, "prev handler mismatch");
-
- prev = Signal.handle(signal, h1);
- Assert.assertSame(prev, h2, "prev handler mismatch");
- } finally {
- if (orig != null && signal != null) {
- Signal.handle(signal, orig);
- }
}
}
@@ -393,37 +172,27 @@ public class SunMiscSignalTest {
* Signals a semaphore when invoked and records whether
* the thread calling the Handler was a daemon.
*/
- static class Handler implements SignalHandler {
+ static class Handler implements Consumer {
// A semaphore to check for accept being called
Semaphore sema = new Semaphore(0);
- Boolean wasDaemon = null;
-
Semaphore semaphore() {
return sema;
}
- synchronized Boolean wasDaemon() {
- return wasDaemon;
- }
-
/**
* Releases the semaphore when called as SignalHandler.handle.
*
- * @param signal the Signal that occurred
+ * @param event that occurred
*/
@Override
- public void handle(Signal signal) {
- synchronized (this) {
- wasDaemon = Thread.currentThread().isDaemon();
- }
+ public void accept(ProcessEvent event) {
sema.release();
- printf("sun.misc.handle sig: %s, num: %d%n", signal.getName(), signal.getNumber());
+ printf("event: %s%n", event);
}
public String toString() {
- return "Handler: sem: " + sema.getQueueLength() +
- ", wasDaemon: " + Objects.toString(wasDaemon());
+ return "Handler: sem: " + sema.getQueueLength();
}
}
@@ -431,7 +200,7 @@ public class SunMiscSignalTest {
@SuppressWarnings("raw_types")
@Test(enabled = false)
public static void main(String[] args) {
- Class>[] testclass = {SunMiscSignalTest.class};
+ Class>[] testclass = {ProcessEventTest.class};
TestNG testng = new TestNG();
testng.setTestClasses(testclass);
testng.run();