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