--- /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: + *

+ *

+ * 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; + } + } + } +}