--- /dev/null 2016-02-02 08:28:09.597457739 +0100 +++ new/src/java.base/share/classes/java/util/Signal.java 2016-02-02 22:57:28.860522302 +0100 @@ -0,0 +1,342 @@ +/* + * 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.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(Consumer) Registering} a handler replaces the + * current handler with the provided handler. + * {@link Signal#unregister(Consumer) 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
+ * Consumer handler = s -> {
+ * ... // handle SIGINT
+ * };
+ * Signal.of("SIGINT").register(handler);
+ * }
+ *

+ * Signal is an interned object. There is a single instance for a particular + * signal name. + *

+ * 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) { + Objects.requireNonNull(name, "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 HandlerNode handlerNode; + long topNativeHandler; + + 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. + * + * @param handler a signal handler + * @throws UnsupportedOperationException if handling of the signal is + * not supported by the implementation + */ + public synchronized void register(Consumer handler) { + Objects.requireNonNull(handler, "handler"); + HandlerNode oldNode = handlerNode; + HandlerNode newNode = new HandlerNode(oldNode, handler); + replace(oldNode, newNode); + } + + /** + * 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(Consumer handler) { + Objects.requireNonNull(handler, "handler"); + HandlerNode oldNode = handlerNode; + if (oldNode != null && !oldNode.isNative() && oldNode.handler == handler) { + replace(oldNode, oldNode.previous); + return true; + } else { + return false; + } + } + + void replace(HandlerNode oldNode, HandlerNode newNode) { + assert Thread.holdsLock(this); + if ((oldNode == null || oldNode.isNative()) && + (newNode != null && !newNode.isNative())) { + // when transitioning from native to java handler, we must + // set handlerNode before setting native handler to avoid + // possible race... + handlerNode = newNode; + long oldNativeHandler; + try { + oldNativeHandler = handle1(newNode.nativeHandler); + } catch (RuntimeException | Error e) { + // ...but must restore it if unsuccessful + handlerNode = oldNode; + throw e; + } + if (oldNode == null) { + // save top-level native handler if replacing the null oldNode + topNativeHandler = oldNativeHandler; + } + } else { + // when transitioning from java to native handler, we must + // set native handler before setting handlerNode to avoid + // possible race; when transitioning from one native handler to another + // or when transitioning from one java handler to another, + // the order is not important... + handle1(newNode == null + ? topNativeHandler // restore top-level native handler if + // replacing with null newNode + : newNode.nativeHandler); + handlerNode = newNode; + } + } + + 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) { + HandlerNode handlerNode = signal.handlerNode; + if (handlerNode != null) { + Consumer handler = handlerNode.handler; + if (handler != null) { + new InnocuousThread(() -> handler.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 final class HandlerNode { + final HandlerNode previous; + final Consumer handler; + final long nativeHandler; + + HandlerNode(HandlerNode previous, Consumer handler) { + this.previous = previous; + this.handler = handler; + this.nativeHandler = 2L; + } + + HandlerNode(HandlerNode previous, long nativeHandler) { + this.previous = previous; + this.handler = null; + this.nativeHandler = nativeHandler; + } + + boolean isNative() { + return handler == null; + } + } + + private static final class NativeImpl extends Signal implements NativeSignal { + NativeImpl(String name, int number) { + super(name, number); + } + + @Override + public synchronized void register(long nativeHandler) { + HandlerNode oldNode = handlerNode; + HandlerNode newNode = new HandlerNode(oldNode, nativeHandler); + replace(oldNode, newNode); + } + + @Override + public synchronized boolean unregister(long nativeHandler) { + HandlerNode oldNode = handlerNode; + if (oldNode != null && oldNode.isNative() && oldNode.nativeHandler == nativeHandler) { + replace(oldNode, oldNode.previous); + return true; + } else { + return false; + } + } + } +}