1 /*
   2  * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.util;
  26 
  27 import jdk.internal.misc.NativeSignal;
  28 import sun.misc.InnocuousThread;
  29 
  30 import java.util.concurrent.ConcurrentHashMap;
  31 import java.util.concurrent.ConcurrentMap;
  32 import java.util.function.BiConsumer;
  33 import java.util.function.Consumer;
  34 import java.util.function.Function;
  35 
  36 /**
  37  * Signal provides support for responding to and raising platform signals.
  38  * Each signal has a current signal handler.
  39  * The signals supported are implementation and operating system specific.
  40  * <p>
  41  * {@link Signal#register(BiConsumer)  Registering} a handler replaces the
  42  * current handler with the provided handler. Provided handler receives, when
  43  * invoked, a {@code Signal} instance being handler and a {@code Runnable} object
  44  * representing the replaced handler, which may be invoked to chain the handling
  45  * of the signal.
  46  * {@link Signal#unregister(BiConsumer) Unregistering} a handler restores the
  47  * signal handling to the state before registering the handler.
  48  * The signal handler is initially registered for the signals
  49  * {@code SIGINT}, {@code SIGTERM}, and {@code SIGHUP}.
  50  * The system registered handler terminates the Java Runtime
  51  * by invoking {@link System#exit System.exit} with a
  52  * value of {@code 128 + the signal number}. Other signals are ignored.
  53  * Handlers for signals that are unknown or reserved by the
  54  * Java implementation can not be registered.
  55  * <p>
  56  * Handling and raising signals are sensitive operations.  Access to
  57  * Signal instances is restricted via {@code RuntimePermission("handleSignal")}
  58  * if a SecurityManager is installed.
  59  * <p>
  60  * Common signal names that follow ANSI/ISO C conventions include:
  61  * <ul>
  62  * <li>SIGINT - Interrupt as if the user typed {@code Ctrl-C}
  63  * <li>SIGTERM - A request for normal termination
  64  * <li>SIGHUP - The process is no longer connected to a controlling terminal
  65  * </ul>
  66  * <p>
  67  * Signal objects are created based on their names. For example:
  68  * <blockquote>{@code
  69  *     Signal s = Signal.of("SIGINT");
  70  * }</blockquote>
  71  * constructs a Signal for {@code SIGINT}, which is
  72  * typically produced when the user presses {@code Ctrl-C}.
  73  * <p>
  74  * To handle a signal a handler is registered for the Signal.
  75  * For example, to respond to {@code Ctrl-C}:
  76  * <pre>{@code
  77  * BiConsumer<Signal, Runnable> handler = (s, r) -> {
  78  * ... // handle SIGINT
  79  * ... // may invoke r.run() as part to chain previous handler
  80  * };
  81  * Signal.of("SIGINT").register(handler);
  82  * }</pre>
  83  * <p>
  84  * Signal is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>
  85  * class; use of identity-sensitive operations (including reference equality
  86  * ({@code ==}), identity hash code, or synchronization) on instances of
  87  * {@code Signal} may have unpredictable results and should be avoided.
  88  * The {@code equals} method should be used for comparisons.
  89  * <p>
  90  * Unless otherwise noted, passing a {@code null} argument to any method in this
  91  * class causes a {@link java.lang.NullPointerException NullPointerException}
  92  * to be thrown.
  93  * <p>
  94  *
  95  *
  96  * @implNote Signal handling is asynchronous to the native signal mechanisms.
  97  * The implementation handles the native signal and signals a Java Thread to
  98  * deliver the signal to the registered handler.
  99  * Since the Java signal handler runs in a newly created thread,  it may not be
 100  * executed until some time after the native signal handler returns.
 101  *
 102  * @since 9
 103  */
 104 public class Signal {
 105 
 106     /**
 107      * Returns the named Signal.
 108      *
 109      * @param name the name of the signal
 110      * @return the named Signal
 111      * @throws UnsupportedOperationException if the signal is unknown or reserved
 112      * @throws SecurityException        if a SecurityManager is installed and
 113      *                 the {@code RuntimePermission("handleSignal")} is denied
 114      */
 115     public static Signal of(String name) {
 116         Signal signal = SIGNAL_BY_NAME.get(name);
 117         if (signal != null) {
 118             return signal;
 119         }
 120 
 121         int number;
 122         if (!name.startsWith("SIG") || name.length() <= 3 ||
 123             (number = findSignal0(name.substring(3))) < 0) {
 124             throw new UnsupportedOperationException("Unknown signal: " + name);
 125         }
 126 
 127         signal = SIGNAL_BY_NUMBER.computeIfAbsent(
 128             number,
 129             new Function<Integer, Signal>() {
 130                 @Override
 131                 public Signal apply(Integer number) {
 132                     return new Signal.NativeImpl(name, number);
 133                 }
 134             }
 135         );
 136 
 137         SIGNAL_BY_NAME.putIfAbsent(name, signal);
 138 
 139         return signal;
 140     }
 141 
 142     private static final ConcurrentMap<String, Signal> SIGNAL_BY_NAME = new ConcurrentHashMap<>(4);
 143     private static final ConcurrentMap<Integer, Signal> SIGNAL_BY_NUMBER = new ConcurrentHashMap<>(4);
 144 
 145     private final String name;
 146     private final int number;
 147     volatile HandlerChain handlerChain;
 148     long savedNativeHandler;
 149 
 150     Signal(String name, int number) {
 151         this.name = name;
 152         this.number = number;
 153     }
 154 
 155     /**
 156      * Returns the signal name.
 157      * Signal names are implementation specific.
 158      *
 159      * @return the signal name
 160      */
 161     public String name() { return name; }
 162 
 163     /**
 164      * Returns the signal number.
 165      * Signal numbers are implementation specific.
 166      *
 167      * @return the signal number
 168      */
 169     public int number() { return number; }
 170 
 171     /**
 172      * Raises the signal in the current process.
 173      * A consumer must have been registered for the signal.
 174      * @throws UnsupportedOperationException if handling of the signal is
 175      *         not supported by the implementation
 176      */
 177     public void raise() { raise0(number); }
 178 
 179     /**
 180      * Register a handler of the Signal.
 181      * Given handler replaces current signal handler, but can
 182      * invoke replaced handler by invoking the passed-in {@code Runnable}
 183      * object.
 184      *
 185      * @param handler a signal handler
 186      * @throws UnsupportedOperationException if handling of the signal is
 187      *                                       not supported by the implementation
 188      */
 189     public synchronized void register(BiConsumer<Signal, Runnable> handler) {
 190         HandlerChain oldChain = handlerChain;
 191         handlerChain = new HandlerChain.Java(oldChain, handler);
 192         if (oldChain == null) {
 193             // set native to dispatch to Singnal.dispatch()
 194             // and save top-level native handler
 195             savedNativeHandler = handle1(2);
 196         } else if (oldChain instanceof HandlerChain.Native) {
 197             // set native to dispatch to Signal.dispatch() only
 198             // (the old native handler is saved in oldChain)
 199             handle1(2);
 200         }
 201     }
 202 
 203     /**
 204      * Unregister a handler of the Signal.
 205      * If given handler is the most recently registered handler for the signal,
 206      * signal handling is restored to the state prior to registering given handler
 207      * and the method returns {@code true}; otherwise the currently registered
 208      * handler is left unchanged and {@code false} is returned.
 209      *
 210      * @param handler a signal handler; non-null
 211      * @return {@code true} if given handler was the most recently registered handler
 212      *         and was unregistered, otherwise {@code false}
 213      */
 214     public synchronized boolean unregister(BiConsumer<Signal, Runnable> handler) {
 215         HandlerChain oldChain = handlerChain;
 216         if (oldChain instanceof HandlerChain.Java &&
 217             ((HandlerChain.Java) oldChain).handler == handler) {
 218             if (oldChain.next == null) {
 219                 // restore saved top-level native handler
 220                 handle1(savedNativeHandler);
 221             } else if (oldChain.next instanceof HandlerChain.Native) {
 222                 // restore next native handler
 223                 handle1(((HandlerChain.Native) oldChain.next).nativeHandler);
 224             }
 225             handlerChain = oldChain.next;
 226             return true;
 227         } else {
 228             return false;
 229         }
 230     }
 231 
 232     long handle1(long nativeHandler) {
 233         long oldNativeHandler = handle0(number, nativeHandler);
 234         if (oldNativeHandler == -1L) {
 235             throw new UnsupportedOperationException(
 236                 "Signal already used by VM or OS: " + name);
 237         }
 238         return oldNativeHandler;
 239     }
 240 
 241     /*
 242      * Called by the VM to execute Java signal handlers.
 243      */
 244     private static void dispatch(int number) {
 245         Signal signal = SIGNAL_BY_NUMBER.get(number);
 246         if (signal != null) {
 247             HandlerChain handlerChain = signal.handlerChain;
 248             if (handlerChain != null) {
 249                 new InnocuousThread(() -> handlerChain.accept(signal))
 250                     .start();
 251             }
 252         }
 253     }
 254 
 255     /**
 256      * Find the signal number, given a name.
 257      *
 258      * @param sigName the signal name
 259      * @return the signal number or -1 for unknown signals.
 260      */
 261     private static native int findSignal0(String sigName);
 262 
 263     /* Registers a native signal handler, and returns the old handler.
 264      * Handler values:
 265      *   0     default handler
 266      *   1     ignore the signal
 267      *   2     call back to Signal.dispatch
 268      *   other arbitrary native signal handlers
 269      * @param nativeH the index or address of the new signal handler
 270      * @return the previous index or address
 271      */
 272     private static native long handle0(int sig, long nativeH);
 273 
 274     /*
 275      * Raise a given signal number.
 276      * @param sig the signal number to raise
 277      */
 278     private static native void raise0(int sig);
 279 
 280 
 281     private static abstract class HandlerChain implements Consumer<Signal> {
 282         final HandlerChain next;
 283 
 284         HandlerChain(HandlerChain next) {
 285             this.next = next;
 286         }
 287 
 288         @Override
 289         public void accept(Signal signal) {
 290             if (next != null) {
 291                 next.accept(signal);
 292             }
 293         }
 294 
 295         private static class Java extends HandlerChain {
 296             final BiConsumer<Signal, Runnable> handler;
 297 
 298             Java(HandlerChain next, BiConsumer<Signal, Runnable> handler) {
 299                 super(next);
 300                 this.handler = handler;
 301             }
 302 
 303             @Override
 304             public void accept(Signal signal) {
 305                 handler.accept(signal, () -> super.accept(signal));
 306             }
 307         }
 308 
 309         private static class Native extends HandlerChain {
 310             final long nativeHandler;
 311 
 312             Native(HandlerChain next, long nativeHandler) {
 313                 super(next);
 314                 this.nativeHandler = nativeHandler;
 315             }
 316 
 317             @Override
 318             public void accept(Signal signal) {
 319                 throw new UnsupportedOperationException(
 320                     "Can't invoke native handler from Java");
 321             }
 322         }
 323     }
 324 
 325     private static final class NativeImpl extends Signal implements NativeSignal {
 326         NativeImpl(String name, int number) {
 327             super(name, number);
 328         }
 329 
 330         @Override
 331         public synchronized void registerNative(long nativeHandler) {
 332             HandlerChain oldChain = handlerChain;
 333             handlerChain = new HandlerChain.Native(oldChain, nativeHandler);
 334             if (oldChain == null) {
 335                 // set native to given native handler
 336                 // and save top-level native handler
 337                 savedNativeHandler = handle1(nativeHandler);
 338             } else {
 339                 // set native to dispatch to given native handler only
 340                 handle1(nativeHandler);
 341             }
 342         }
 343 
 344         @Override
 345         public synchronized boolean unregisterNative(long nativeHandler) {
 346             HandlerChain oldChain = handlerChain;
 347             if (oldChain instanceof HandlerChain.Native &&
 348                 ((HandlerChain.Native) oldChain).nativeHandler == nativeHandler) {
 349                 if (oldChain.next == null) {
 350                     // restore saved top-level native handler
 351                     handle1(savedNativeHandler);
 352                 } else if (oldChain.next instanceof HandlerChain.Native) {
 353                     // restore next native handler
 354                     handle1(((HandlerChain.Native) oldChain.next).nativeHandler);
 355                 } else {
 356                     assert oldChain.next instanceof HandlerChain.Java;
 357                     // restore native to dispatch to Java
 358                     handle1(2L);
 359                 }
 360                 handlerChain = oldChain.next;
 361                 return true;
 362             } else {
 363                 return false;
 364             }
 365         }
 366     }
 367 }