# 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();