RFR 9: 8087286 Need a way to handle control-C and possibly some other signals

Peter Levart peter.levart at gmail.com
Tue Feb 2 20:58:54 UTC 2016



On 02/02/2016 05:45 PM, Roger Riggs wrote:
> Hi Peter,
>
>
> On 2/2/2016 11:14 AM, Peter Levart 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. I wanted to see if 
>> sun.misc.Signal[Handler] could be modified to use java.util.Signal 
>> 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).
>
> A stack based model can work but still needs backup handlers for when 
> all the specified handlers to unregistered.
> I'm ok with submerging registerDefault into the implementation used 
> only by the system Termination class.
> I don't think the use cases call for a stack and it makes the API more 
> complex for register/unregister.
>
> I'd rather see a good simple j.u.Signal API that meets the 
> requirements without considering how to
> support the legacy s.m.Signal, if it needs a bit of cruft to support 
> it for a while that's ok.
> Someday we will want to rip out all that code and have a simple API.

Here's another attempt that removes the possibility to chain handlers, 
but keeps a stack of previous handlers so that unregistration returns to 
exactly the same state as before registration. j.l.System can therefore 
use plain register() method for the default handlers and nesting 
registrations/unregistrations are possible:

http://cr.openjdk.java.net/~plevart/jdk9-dev/Signal/webrev.02/

The API is practically the same as your's with the exception of the 
specification of unregister() which documents the restoration of 
previous state and does not mention any "default" handlers.

The implementation is much simpler now (counting the lines of 
NativeSignal interface + j.u.Singnal class vs. your SignalImpl + 
j.u.Signal results in 392 vs. 513)

What do you think?


Regards, Peter

>
> Roger
>
>
>>
>> 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