RFR 9: 8087286 Need a way to handle control-C and possibly some other signals
Chris Hegarty
chris.hegarty at oracle.com
Tue Feb 2 16:22:54 UTC 2016
On 2 Feb 2016, at 16:14, Peter Levart <peter.levart at gmail.com> wrote:
> Hi Roger,
>
> On 02/02/2016 04:16 PM, Roger Riggs wrote:
>> Hi Peter,
>>
>> On 2/2/2016 6:44 AM, Peter Levart wrote:
>>>
>> ...
>>>
>>> Also if this is to become public API, There's a chance users would want to add a handler to the chain of existing handlers or override them. So what about an API that allows registering/unregistering a default (non-native) handler and other handlers above it in a uniform way, like:
>> The problem with chaining, as in the current API, is that there is no way to know what the next
>> handler in the chain will do. If it is the default one for INT, TERM, HUP, it will call Shutdown and exit.
>> So without extra information and cooperation chaining is risky.
>> If the handler knows something about the other actors in the environment, it can coordinate with them directly.
>> For the use cases that have been raised for existing use of sun.misc.Signal, they are simple interactive
>> environments that want to give the appearance of being able to interrupt using control-c.
>>
>> I've been aiming for the simplest API that would support the current use cases.
>
> I noticed that sun.misc.Signal[Handler] is a critical API according to JEP 260, so it can't be removed in JDK9 yet.
Right. It must stay, pretty much, as is in JDK 9.
> I wanted to see if sun.misc.Signal[Handler] could be modified to use java.util.Signal
I originally though about doing something like this:
public interface SignalHandler extends Consumer<java.util.Signal> {
/**
* Handle the given signal
*
* @param sig a signal object
*/
public void handle(Signal sig);
+
+ @Override default void accept(java.util.Signal signal) {
+ handle(new Signal(signal));
+ }
+
+ static SignalHandler from(Consumer<java.util.Signal> h) {
+ return new SignalHandler() {
+ final Consumer<java.util.Signal> handler = h;
+ @Override
+ public void handle(Signal sig) {
+ accept(sig.signal());
+ }
+
+ @Override
+ public void accept(java.util.Signal signal) {
+ handler.accept(signal);
+ }
+ };
+ }
}
-Chris.
> directly while making java.util.Signal support chaining and restoring of previous handler on deregister. Here's what it looks like in code:
>
> http://cr.openjdk.java.net/~plevart/jdk9-dev/Signal/webrev.01/
>
> But if chaining is not desired, then at least restoring of previous handler could be implemented in a uniform way and for arbitrary registration depth (no need for register/registerDefault distinction).
>
> What do you think?
>
> Regards, Peter
>
>>
>> Thanks, Roger
>>
>>
>>>
>>>
>>> public final class Signal {
>>>
>>> private static final ConcurrentMap<String, Signal>
>>> SIGNAL_BY_NAME = new ConcurrentHashMap<>(4);
>>> private static final ConcurrentMap<Integer, Signal>
>>> SIGNAL_BY_NUMBER = new ConcurrentHashMap<>(4);
>>>
>>> public static Signal of(String name) {
>>> Signal signal = SIGNAL_BY_NAME.get(name);
>>> if (signal != null) {
>>> return signal;
>>> }
>>>
>>> int number;
>>> if (!name.startsWith("SIG") || name.length() <= 3 ||
>>> (number = findSignal0(name.substring(3))) < 0) {
>>> throw new UnsupportedOperationException("Unknown signal: " + name);
>>> }
>>>
>>> signal = SIGNAL_BY_NUMBER.computeIfAbsent(
>>> number,
>>> new Function<Integer, Signal>() {
>>> @Override
>>> public Signal apply(Integer number) {
>>> return new Signal(name, number);
>>> }
>>> }
>>> );
>>>
>>> SIGNAL_BY_NAME.putIfAbsent(name, signal);
>>>
>>> return signal;
>>> }
>>>
>>> private final String name;
>>> private final int number;
>>> private volatile HandlerChain handlerChain;
>>> private long savedNativeHandler;
>>>
>>> private Signal(String name, int number) {
>>> this.name = name;
>>> this.number = number;
>>> }
>>>
>>> public String name() { return name; }
>>>
>>> public int number() { return number; }
>>>
>>> public void raise() { raise0(number); }
>>>
>>> public void register(BiConsumer<Signal, Runnable> handler) {
>>> synchronized (this) {
>>> HandlerChain oldChain = handlerChain;
>>> handlerChain = new HandlerChain(handler, oldChain);
>>> if (oldChain == null) {
>>> // set native to dispatch to Singnal.dispatch()
>>> savedNativeHandler = handle1(2);
>>> }
>>> }
>>> }
>>>
>>> public boolean unregister(BiConsumer<Signal, Runnable> handler) {
>>> synchronized (this) {
>>> HandlerChain oldChain = handlerChain;
>>> if (oldChain != null && oldChain.handler == handler) {
>>> if (oldChain.next == null) {
>>> // restore saved native handler
>>> long oldNativeHandler = handle1(savedNativeHandler);
>>> assert oldNativeHandler == 2L;
>>> }
>>> handlerChain = oldChain.next;
>>> return true;
>>> } else {
>>> return false;
>>> }
>>> }
>>> }
>>>
>>> // following two should probably be hidden from public API
>>>
>>> public void nativeIgnore() {
>>> synchronized (this) {
>>> if (handlerChain == null) {
>>> handle1(1); // ignore signal
>>> } else {
>>> throw new IllegalStateException(
>>> "Can't ignore signal after handlers have already been registered.");
>>> }
>>> }
>>> }
>>>
>>> public void nativeDefault() {
>>> synchronized (this) {
>>> if (handlerChain == null) {
>>> handle1(0); // default native handler
>>> } else {
>>> throw new IllegalStateException(
>>> "Can't restore signal after handlers have already been registered.");
>>> }
>>> }
>>> }
>>>
>>> private long handle1(long nativeHandler) {
>>> long oldNativeHandler = handle0(number, nativeHandler);
>>> if (oldNativeHandler == -1L) {
>>> throw new UnsupportedOperationException(
>>> "Signal already used by VM or OS: " + name);
>>> }
>>> return oldNativeHandler;
>>> }
>>>
>>> /*
>>> * Called by the VM to execute Java signal handlers.
>>> */
>>> private static void dispatch(int number) {
>>> Signal signal = SIGNAL_BY_NUMBER.get(number);
>>> if (signal != null) {
>>> HandlerChain handlerChain = signal.handlerChain;
>>> if (handlerChain != null) {
>>> new InnocuousThread(() -> handlerChain.accept(signal))
>>> .start();
>>> }
>>> }
>>> }
>>>
>>> /**
>>> * Find the signal number, given a name.
>>> *
>>> * @param sigName the signal name
>>> * @return the signal number or -1 for unknown signals.
>>> */
>>> private static native int findSignal0(String sigName);
>>>
>>> /* Registers a native signal handler, and returns the old handler.
>>> * Handler values:
>>> * 0 default handler
>>> * 1 ignore the signal
>>> * 2 call back to Signal.dispatch
>>> * other arbitrary native signal handlers
>>> * @param nativeH the index or address of the new signal handler
>>> * @return the previous index or address
>>> */
>>> private static native long handle0(int sig, long nativeH);
>>>
>>> /*
>>> * Raise a given signal number.
>>> * @param sig the signal number to raise
>>> */
>>> private static native void raise0(int sig);
>>>
>>>
>>> private static class HandlerChain implements Consumer<Signal> {
>>> final BiConsumer<Signal, Runnable> handler;
>>> final HandlerChain next;
>>>
>>> HandlerChain(BiConsumer<Signal, Runnable> handler, HandlerChain next) {
>>> this.handler = handler;
>>> this.next = next;
>>> }
>>>
>>> @Override
>>> public void accept(Signal signal) {
>>> handler.accept(signal, () -> {
>>> if (next != null) {
>>> next.accept(signal);
>>> }
>>> });
>>> }
>>> }
>>> }
>>>
>>>
>>>
>>>
>>> Regards, Peter
>>>
>>>
>>> On 02/01/2016 05:02 PM, Roger Riggs wrote:
>>>> Please review an API addition to handle signals such as SIGINT, SIGHUP, and SIGTERM.
>>>> This JEP 260 motivated alternative to sun.misc.Signal supports the use case for
>>>> interactive applications that need to handle Control-C and other signals.
>>>>
>>>> The new java.util.Signal class provides a settable primary signal handler and a default
>>>> signal handler. The primary signal handler can be unregistered and handling is restored
>>>> to the default signal handler. System initialization registers default signal handlers
>>>> to terminate on SIGINT, SIGHUP, and SIGTERM. Use of the Signal API requires
>>>> a permission if a SecurityManager is set.
>>>>
>>>> The sun.misc.Signal implementation is modified to be layered on a common
>>>> thread and dispatch mechanism. The VM handling of native signals is not affected.
>>>> The command option to reduce signal use by the runtime with -Xrs is unmodified.
>>>>
>>>> The changes to hotspot are minimal to rename the hardcoded callback to the Java
>>>> Signal dispatcher.
>>>>
>>>> Please review and comment on the API and implementation.
>>>>
>>>> javadoc:
>>>> http://cr.openjdk.java.net/~rriggs/signal-doc/
>>>>
>>>> Webrev:
>>>> jdk: http://cr.openjdk.java.net/~rriggs/webrev-signal-8087286/
>>>> hotspot: http://cr.openjdk.java.net/~rriggs/webrev-hs-signal-8087286/
>>>>
>>>> Issue:
>>>> https://bugs.openjdk.java.net/browse/JDK-8087286
>>>>
>>>> JEP 260:
>>>> https://bugs.openjdk.java.net/browse/JDK-8132928
>>>>
>>>> Thanks, Roger
More information about the core-libs-dev
mailing list