On 2 Feb 2016, at 16:14, Peter Levart <peter.lev...@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