--- old/src/java.base/share/classes/sun/misc/Signal.java 2016-02-02 16:50:24.127993503 +0100 +++ new/src/java.base/share/classes/sun/misc/Signal.java 2016-02-02 16:50:24.029995374 +0100 @@ -24,7 +24,11 @@ */ package sun.misc; -import java.util.Hashtable; +import jdk.internal.misc.NativeSignal; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; /** * This class provides ANSI/ISO C signal support. A Java program can register @@ -72,15 +76,14 @@ * @since 1.2 */ public final class Signal { - private static Hashtable handlers = new Hashtable<>(4); - private static Hashtable signals = new Hashtable<>(4); - private int number; - private String name; + private static final Map handlers = new ConcurrentHashMap<>(4); + + private final java.util.Signal utSignal; /* Returns the signal number */ public int getNumber() { - return number; + return utSignal.number(); } /** @@ -90,7 +93,7 @@ * @see sun.misc.Signal#Signal(String name) */ public String getName() { - return name; + return utSignal.name(); } /** @@ -106,8 +109,7 @@ if (other == null || !(other instanceof Signal)) { return false; } - Signal other1 = (Signal)other; - return name.equals(other1.name) && (number == other1.number); + return utSignal == ((Signal) other).utSignal; } /** @@ -116,7 +118,7 @@ * @return a hash code value for this object. */ public int hashCode() { - return number; + return getNumber(); } /** @@ -126,7 +128,7 @@ * @return a string representation of the signal */ public String toString() { - return "SIG" + name; + return getName(); } /** @@ -137,11 +139,11 @@ * @see sun.misc.Signal#getName() */ public Signal(String name) { - number = findSignal(name); - this.name = name; - if (number < 0) { - throw new IllegalArgumentException("Unknown signal: " + name); - } + this(java.util.Signal.of("SIG" + name)); + } + + private Signal(java.util.Signal utSignal) { + this.utSignal = utSignal; } /** @@ -159,30 +161,21 @@ public static synchronized SignalHandler handle(Signal sig, SignalHandler handler) throws IllegalArgumentException { - long newH = (handler instanceof NativeSignalHandler) ? - ((NativeSignalHandler)handler).getHandler() : 2; - long oldH = handle0(sig.number, newH); - if (oldH == -1) { - throw new IllegalArgumentException - ("Signal already used by VM or OS: " + sig); - } - signals.put(sig.number, sig); - synchronized (handlers) { - SignalHandler oldHandler = handlers.get(sig); - handlers.remove(sig); - if (newH == 2) { - handlers.put(sig, handler); - } - if (oldH == 0) { - return SignalHandler.SIG_DFL; - } else if (oldH == 1) { - return SignalHandler.SIG_IGN; - } else if (oldH == 2) { - return oldHandler; - } else { - return new NativeSignalHandler(oldH); + + UtHandler newUtHandler = UtHandler.create(handler); + UtHandler oldUtHandler = handlers.put(sig.getNumber(), newUtHandler); + + if (oldUtHandler != null) { + if (!oldUtHandler.unregisterFrom(sig.utSignal)) { + throw new IllegalStateException( + "Can't unregister old sun.misc.SignalHandler as some new " + + "handler has been registered with java.util.Signal API."); } } + + newUtHandler.registerTo(sig.utSignal); + + return (oldUtHandler == null) ? null : oldUtHandler.signalHandler; } /** @@ -192,41 +185,71 @@ * @see sun.misc.Signal#handle(Signal sig, SignalHandler handler) */ public static void raise(Signal sig) throws IllegalArgumentException { - if (handlers.get(sig) == null) { + if (handlers.get(sig.getNumber()) == null) { throw new IllegalArgumentException("Unhandled signal: " + sig); } - raise0(sig.number); + sig.utSignal.raise(); } - /* Called by the VM to execute Java signal handlers. */ - private static void dispatch(final int number) { - final Signal sig = signals.get(number); - final SignalHandler handler = handlers.get(sig); - - Runnable runnable = new Runnable () { - public void run() { - // Don't bother to reset the priority. Signal handler will - // run at maximum priority inherited from the VM signal - // dispatch thread. - // Thread.currentThread().setPriority(Thread.NORM_PRIORITY); - handler.handle(sig); - } - }; - if (handler != null) { - new Thread(null, runnable, sig + " handler", 0, false).start(); + // abstraction of Java and Native handlers for java.util.Signal API + // with delegation to sun.misc.[SignalHandler|NativeSignalHandler] + private static abstract class UtHandler { + final SignalHandler signalHandler; + + static UtHandler create(SignalHandler signalHandler) { + return (signalHandler instanceof NativeSignalHandler) + ? new UtHandler.Native((NativeSignalHandler) signalHandler) + : new UtHandler.Java(signalHandler); } - } - /* Find the signal number, given a name. Returns -1 for unknown signals. */ - private static native int findSignal(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 - */ - private static native long handle0(int sig, long nativeH); - /* Raise a given signal number */ - private static native void raise0(int sig); + UtHandler(SignalHandler signalHandler) { + this.signalHandler = signalHandler; + } + + abstract void registerTo(java.util.Signal utSignal); + + abstract boolean unregisterFrom(java.util.Signal utSignal); + + private static class Java extends UtHandler + implements BiConsumer { + Java(SignalHandler signalHandler) { + super(signalHandler); + } + + @Override + public void accept(java.util.Signal uSignal, Runnable runnable) { + signalHandler.handle(new Signal(uSignal)); + } + + @Override + void registerTo(java.util.Signal utSignal) { + utSignal.register(this); + } + + @Override + boolean unregisterFrom(java.util.Signal utSignal) { + return utSignal.unregister(this); + } + } + + private static class Native extends UtHandler { + Native(NativeSignalHandler signalHandler) { + super(signalHandler); + } + + @Override + void registerTo(java.util.Signal utSignal) { + ((NativeSignal) utSignal).registerNative( + ((NativeSignalHandler) signalHandler).getHandler() + ); + } + + @Override + boolean unregisterFrom(java.util.Signal utSignal) { + return ((NativeSignal) utSignal).unregisterNative( + ((NativeSignalHandler) signalHandler).getHandler() + ); + } + } + } } --- /dev/null 2016-02-02 08:28:09.597457739 +0100 +++ new/src/java.base/share/classes/java/util/Signal.java 2016-02-02 16:50:24.323989761 +0100 @@ -0,0 +1,367 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.util; + +import jdk.internal.misc.NativeSignal; +import sun.misc.InnocuousThread; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Signal provides support for responding to and raising platform signals. + * Each signal has a current signal handler. + * The signals supported are implementation and operating system specific. + *

+ * {@link Signal#register(BiConsumer) Registering} a handler replaces the + * current handler with the provided handler. Provided handler receives, when + * invoked, a {@code Signal} instance being handler and a {@code Runnable} object + * representing the replaced handler, which may be invoked to chain the handling + * of the signal. + * {@link Signal#unregister(BiConsumer) Unregistering} a handler restores the + * signal handling to the state before registering the handler. + * The signal handler is initially registered for the signals + * {@code SIGINT}, {@code SIGTERM}, and {@code SIGHUP}. + * The system registered handler terminates the Java Runtime + * by invoking {@link System#exit System.exit} with a + * value of {@code 128 + the signal number}. Other signals are ignored. + * Handlers for signals that are unknown or reserved by the + * Java implementation can not be registered. + *

+ * Handling and raising signals are sensitive operations. Access to + * Signal instances is restricted via {@code RuntimePermission("handleSignal")} + * if a SecurityManager is installed. + *

+ * Common signal names that follow ANSI/ISO C conventions include: + *

    + *
  • SIGINT - Interrupt as if the user typed {@code Ctrl-C} + *
  • SIGTERM - A request for normal termination + *
  • SIGHUP - The process is no longer connected to a controlling terminal + *
+ *

+ * Signal objects are created based on their names. For example: + *

{@code + * Signal s = Signal.of("SIGINT"); + * }
+ * constructs a Signal for {@code SIGINT}, which is + * typically produced when the user presses {@code Ctrl-C}. + *

+ * To handle a signal a handler is registered for the Signal. + * For example, to respond to {@code Ctrl-C}: + *

{@code
+ * BiConsumer handler = (s, r) -> {
+ * ... // handle SIGINT
+ * ... // may invoke r.run() as part to chain previous handler
+ * };
+ * Signal.of("SIGINT").register(handler);
+ * }
+ *

+ * Signal is a value-based + * class; use of identity-sensitive operations (including reference equality + * ({@code ==}), identity hash code, or synchronization) on instances of + * {@code Signal} may have unpredictable results and should be avoided. + * The {@code equals} method should be used for comparisons. + *

+ * Unless otherwise noted, passing a {@code null} argument to any method in this + * class causes a {@link java.lang.NullPointerException NullPointerException} + * to be thrown. + *

+ * + * + * @implNote Signal handling is asynchronous to the native signal mechanisms. + * The implementation handles the native signal and signals a Java Thread to + * deliver the signal to the registered handler. + * Since the Java signal handler runs in a newly created thread, it may not be + * executed until some time after the native signal handler returns. + * + * @since 9 + */ +public class Signal { + + /** + * Returns the named Signal. + * + * @param name the name of the signal + * @return the named Signal + * @throws UnsupportedOperationException if the signal is unknown or reserved + * @throws SecurityException if a SecurityManager is installed and + * the {@code RuntimePermission("handleSignal")} is denied + */ + 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() { + @Override + public Signal apply(Integer number) { + return new Signal.NativeImpl(name, number); + } + } + ); + + SIGNAL_BY_NAME.putIfAbsent(name, signal); + + return signal; + } + + private static final ConcurrentMap SIGNAL_BY_NAME = new ConcurrentHashMap<>(4); + private static final ConcurrentMap SIGNAL_BY_NUMBER = new ConcurrentHashMap<>(4); + + private final String name; + private final int number; + volatile HandlerChain handlerChain; + long savedNativeHandler; + + Signal(String name, int number) { + this.name = name; + this.number = number; + } + + /** + * Returns the signal name. + * Signal names are implementation specific. + * + * @return the signal name + */ + public String name() { return name; } + + /** + * Returns the signal number. + * Signal numbers are implementation specific. + * + * @return the signal number + */ + public int number() { return number; } + + /** + * Raises the signal in the current process. + * A consumer must have been registered for the signal. + * @throws UnsupportedOperationException if handling of the signal is + * not supported by the implementation + */ + public void raise() { raise0(number); } + + /** + * Register a handler of the Signal. + * Given handler replaces current signal handler, but can + * invoke replaced handler by invoking the passed-in {@code Runnable} + * object. + * + * @param handler a signal handler + * @throws UnsupportedOperationException if handling of the signal is + * not supported by the implementation + */ + public synchronized void register(BiConsumer handler) { + HandlerChain oldChain = handlerChain; + handlerChain = new HandlerChain.Java(oldChain, handler); + if (oldChain == null) { + // set native to dispatch to Singnal.dispatch() + // and save top-level native handler + savedNativeHandler = handle1(2); + } else if (oldChain instanceof HandlerChain.Native) { + // set native to dispatch to Signal.dispatch() only + // (the old native handler is saved in oldChain) + handle1(2); + } + } + + /** + * Unregister a handler of the Signal. + * If given handler is the most recently registered handler for the signal, + * signal handling is restored to the state prior to registering given handler + * and the method returns {@code true}; otherwise the currently registered + * handler is left unchanged and {@code false} is returned. + * + * @param handler a signal handler; non-null + * @return {@code true} if given handler was the most recently registered handler + * and was unregistered, otherwise {@code false} + */ + public synchronized boolean unregister(BiConsumer handler) { + HandlerChain oldChain = handlerChain; + if (oldChain instanceof HandlerChain.Java && + ((HandlerChain.Java) oldChain).handler == handler) { + if (oldChain.next == null) { + // restore saved top-level native handler + handle1(savedNativeHandler); + } else if (oldChain.next instanceof HandlerChain.Native) { + // restore next native handler + handle1(((HandlerChain.Native) oldChain.next).nativeHandler); + } + handlerChain = oldChain.next; + return true; + } else { + return false; + } + } + + 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 abstract class HandlerChain implements Consumer { + final HandlerChain next; + + HandlerChain(HandlerChain next) { + this.next = next; + } + + @Override + public void accept(Signal signal) { + if (next != null) { + next.accept(signal); + } + } + + private static class Java extends HandlerChain { + final BiConsumer handler; + + Java(HandlerChain next, BiConsumer handler) { + super(next); + this.handler = handler; + } + + @Override + public void accept(Signal signal) { + handler.accept(signal, () -> super.accept(signal)); + } + } + + private static class Native extends HandlerChain { + final long nativeHandler; + + Native(HandlerChain next, long nativeHandler) { + super(next); + this.nativeHandler = nativeHandler; + } + + @Override + public void accept(Signal signal) { + throw new UnsupportedOperationException( + "Can't invoke native handler from Java"); + } + } + } + + private static final class NativeImpl extends Signal implements NativeSignal { + NativeImpl(String name, int number) { + super(name, number); + } + + @Override + public synchronized void registerNative(long nativeHandler) { + HandlerChain oldChain = handlerChain; + handlerChain = new HandlerChain.Native(oldChain, nativeHandler); + if (oldChain == null) { + // set native to given native handler + // and save top-level native handler + savedNativeHandler = handle1(nativeHandler); + } else { + // set native to dispatch to given native handler only + handle1(nativeHandler); + } + } + + @Override + public synchronized boolean unregisterNative(long nativeHandler) { + HandlerChain oldChain = handlerChain; + if (oldChain instanceof HandlerChain.Native && + ((HandlerChain.Native) oldChain).nativeHandler == nativeHandler) { + if (oldChain.next == null) { + // restore saved top-level native handler + handle1(savedNativeHandler); + } else if (oldChain.next instanceof HandlerChain.Native) { + // restore next native handler + handle1(((HandlerChain.Native) oldChain.next).nativeHandler); + } else { + assert oldChain.next instanceof HandlerChain.Java; + // restore native to dispatch to Java + handle1(2L); + } + handlerChain = oldChain.next; + return true; + } else { + return false; + } + } + } +} --- /dev/null 2016-02-02 08:28:09.597457739 +0100 +++ new/src/java.base/share/classes/jdk/internal/misc/NativeSignal.java 2016-02-02 16:50:24.641983690 +0100 @@ -0,0 +1,11 @@ +package jdk.internal.misc; + +/** + * Internal interface implemented by private extension of {@link java.util.Signal}. + */ +public interface NativeSignal { + + void registerNative(long nativeHandler); + + boolean unregisterNative(long nativeHandler); +}