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