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 16:14:21 UTC 2016


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).

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