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 hotspot-runtime-dev mailing list