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.Consumer; 33 import java.util.function.Function; 34 35 /** 36 * Signal provides support for responding to and raising platform signals. 37 * Each signal has a current signal handler. 38 * The signals supported are implementation and operating system specific. 39 * <p> 40 * {@link Signal#register(Consumer) Registering} a handler replaces the 41 * current handler with the provided handler. 42 * {@link Signal#unregister(Consumer) Unregistering} a handler restores the 43 * signal handling to the state before registering the handler. 44 * The signal handler is initially registered for the signals 45 * {@code SIGINT}, {@code SIGTERM}, and {@code SIGHUP}. 46 * The system registered handler terminates the Java Runtime 47 * by invoking {@link System#exit System.exit} with a 48 * value of {@code 128 + the signal number}. Other signals are ignored. 49 * Handlers for signals that are unknown or reserved by the 50 * Java implementation can not be registered. 51 * <p> 52 * Handling and raising signals are sensitive operations. Access to 53 * Signal instances is restricted via {@code RuntimePermission("handleSignal")} 54 * if a SecurityManager is installed. 55 * <p> 56 * Common signal names that follow ANSI/ISO C conventions include: 57 * <ul> 58 * <li>SIGINT - Interrupt as if the user typed {@code Ctrl-C} 59 * <li>SIGTERM - A request for normal termination 60 * <li>SIGHUP - The process is no longer connected to a controlling terminal 61 * </ul> 62 * <p> 63 * Signal objects are created based on their names. For example: 64 * <blockquote>{@code 65 * Signal s = Signal.of("SIGINT"); 66 * }</blockquote> 67 * constructs a Signal for {@code SIGINT}, which is 68 * typically produced when the user presses {@code Ctrl-C}. 69 * <p> 70 * To handle a signal a handler is registered for the Signal. 71 * For example, to respond to {@code Ctrl-C}: 72 * <pre>{@code 73 * Consumer<Signal> handler = s -> { 74 * ... // handle SIGINT 75 * }; 76 * Signal.of("SIGINT").register(handler); 77 * }</pre> 78 * <p> 79 * Signal is an interned object. There is a single instance for a particular 80 * signal name. 81 * <p> 82 * Unless otherwise noted, passing a {@code null} argument to any method in this 83 * class causes a {@link java.lang.NullPointerException NullPointerException} 84 * to be thrown. 85 * <p> 86 * 87 * 88 * @implNote Signal handling is asynchronous to the native signal mechanisms. 89 * The implementation handles the native signal and signals a Java Thread to 90 * deliver the signal to the registered handler. 91 * Since the Java signal handler runs in a newly created thread, it may not be 92 * executed until some time after the native signal handler returns. 93 * 94 * @since 9 95 */ 96 public class Signal { 97 98 /** 99 * Returns the named Signal. 100 * 101 * @param name the name of the signal 102 * @return the named Signal 103 * @throws UnsupportedOperationException if the signal is unknown or reserved 104 * @throws SecurityException if a SecurityManager is installed and 105 * the {@code RuntimePermission("handleSignal")} is denied 106 */ 107 public static Signal of(String name) { 108 Objects.requireNonNull(name, "name"); 109 110 Signal signal = SIGNAL_BY_NAME.get(name); 111 if (signal != null) { 112 return signal; 113 } 114 115 int number; 116 if (!name.startsWith("SIG") || name.length() <= 3 || 117 (number = findSignal0(name.substring(3))) < 0) { 118 throw new UnsupportedOperationException("Unknown signal: " + name); 119 } 120 121 signal = SIGNAL_BY_NUMBER.computeIfAbsent( 122 number, 123 new Function<Integer, Signal>() { 124 @Override 125 public Signal apply(Integer number) { 126 return new Signal.NativeImpl(name, number); 127 } 128 } 129 ); 130 131 SIGNAL_BY_NAME.putIfAbsent(name, signal); 132 133 return signal; 134 } 135 136 private static final ConcurrentMap<String, Signal> SIGNAL_BY_NAME = new ConcurrentHashMap<>(4); 137 private static final ConcurrentMap<Integer, Signal> SIGNAL_BY_NUMBER = new ConcurrentHashMap<>(4); 138 139 private final String name; 140 private final int number; 141 volatile HandlerNode handlerNode; 142 long topNativeHandler; 143 144 Signal(String name, int number) { 145 this.name = name; 146 this.number = number; 147 } 148 149 /** 150 * Returns the signal name. 151 * Signal names are implementation specific. 152 * 153 * @return the signal name 154 */ 155 public String name() { return name; } 156 157 /** 158 * Returns the signal number. 159 * Signal numbers are implementation specific. 160 * 161 * @return the signal number 162 */ 163 public int number() { return number; } 164 165 /** 166 * Raises the signal in the current process. 167 * A consumer must have been registered for the signal. 168 * @throws UnsupportedOperationException if handling of the signal is 169 * not supported by the implementation 170 */ 171 public void raise() { raise0(number); } 172 173 /** 174 * Register a handler of the Signal. 175 * Given handler replaces current signal handler. 176 * 177 * @param handler a signal handler 178 * @throws UnsupportedOperationException if handling of the signal is 179 * not supported by the implementation 180 */ 181 public synchronized void register(Consumer<Signal> handler) { 182 Objects.requireNonNull(handler, "handler"); 183 HandlerNode oldNode = handlerNode; 184 HandlerNode newNode = new HandlerNode(oldNode, handler); 185 replace(oldNode, newNode); 186 } 187 188 /** 189 * Unregister a handler of the Signal. 190 * If given handler is the most recently registered handler for the signal, 191 * signal handling is restored to the state prior to registering given handler 192 * and the method returns {@code true}; otherwise the currently registered 193 * handler is left unchanged and {@code false} is returned. 194 * 195 * @param handler a signal handler; non-null 196 * @return {@code true} if given handler was the most recently registered handler 197 * and was unregistered, otherwise {@code false} 198 */ 199 public synchronized boolean unregister(Consumer<Signal> handler) { 200 Objects.requireNonNull(handler, "handler"); 201 HandlerNode oldNode = handlerNode; 202 if (oldNode != null && !oldNode.isNative() && oldNode.handler == handler) { 203 replace(oldNode, oldNode.previous); 204 return true; 205 } else { 206 return false; 207 } 208 } 209 210 void replace(HandlerNode oldNode, HandlerNode newNode) { 211 assert Thread.holdsLock(this); 212 if ((oldNode == null || oldNode.isNative()) && 213 (newNode != null && !newNode.isNative())) { 214 // when transitioning from native to java handler, we must 215 // set handlerNode before setting native handler to avoid 216 // possible race... 217 handlerNode = newNode; 218 long oldNativeHandler; 219 try { 220 oldNativeHandler = handle1(newNode.nativeHandler); 221 } catch (RuntimeException | Error e) { 222 // ...but must restore it if unsuccessful 223 handlerNode = oldNode; 224 throw e; 225 } 226 if (oldNode == null) { 227 // save top-level native handler if replacing the null oldNode 228 topNativeHandler = oldNativeHandler; 229 } 230 } else { 231 // when transitioning from java to native handler, we must 232 // set native handler before setting handlerNode to avoid 233 // possible race; when transitioning from one native handler to another 234 // or when transitioning from one java handler to another, 235 // the order is not important... 236 handle1(newNode == null 237 ? topNativeHandler // restore top-level native handler if 238 // replacing with null newNode 239 : newNode.nativeHandler); 240 handlerNode = newNode; 241 } 242 } 243 244 long handle1(long nativeHandler) { 245 long oldNativeHandler = handle0(number, nativeHandler); 246 if (oldNativeHandler == -1L) { 247 throw new UnsupportedOperationException( 248 "Signal already used by VM or OS: " + name); 249 } 250 return oldNativeHandler; 251 } 252 253 /* 254 * Called by the VM to execute Java signal handlers. 255 */ 256 private static void dispatch(int number) { 257 Signal signal = SIGNAL_BY_NUMBER.get(number); 258 if (signal != null) { 259 HandlerNode handlerNode = signal.handlerNode; 260 if (handlerNode != null) { 261 Consumer<Signal> handler = handlerNode.handler; 262 if (handler != null) { 263 new InnocuousThread(() -> handler.accept(signal)) 264 .start(); 265 } 266 } 267 } 268 } 269 270 /** 271 * Find the signal number, given a name. 272 * 273 * @param sigName the signal name 274 * @return the signal number or -1 for unknown signals. 275 */ 276 private static native int findSignal0(String sigName); 277 278 /** 279 * Registers a native signal handler, and returns the old handler. 280 * Handler values: 281 * 0 default handler 282 * 1 ignore the signal 283 * 2 call back to Signal.dispatch 284 * other arbitrary native signal handlers 285 * @param nativeH the index or address of the new signal handler 286 * @return the previous index or address 287 */ 288 private static native long handle0(int sig, long nativeH); 289 290 /** 291 * Raise a given signal number. 292 * @param sig the signal number to raise 293 */ 294 private static native void raise0(int sig); 295 296 297 private static final class HandlerNode { 298 final HandlerNode previous; 299 final Consumer<Signal> handler; 300 final long nativeHandler; 301 302 HandlerNode(HandlerNode previous, Consumer<Signal> handler) { 303 this.previous = previous; 304 this.handler = handler; 305 this.nativeHandler = 2L; 306 } 307 308 HandlerNode(HandlerNode previous, long nativeHandler) { 309 this.previous = previous; 310 this.handler = null; 311 this.nativeHandler = nativeHandler; 312 } 313 314 boolean isNative() { 315 return handler == null; 316 } 317 } 318 319 private static final class NativeImpl extends Signal implements NativeSignal { 320 NativeImpl(String name, int number) { 321 super(name, number); 322 } 323 324 @Override 325 public synchronized void register(long nativeHandler) { 326 HandlerNode oldNode = handlerNode; 327 HandlerNode newNode = new HandlerNode(oldNode, nativeHandler); 328 replace(oldNode, newNode); 329 } 330 331 @Override 332 public synchronized boolean unregister(long nativeHandler) { 333 HandlerNode oldNode = handlerNode; 334 if (oldNode != null && oldNode.isNative() && oldNode.nativeHandler == nativeHandler) { 335 replace(oldNode, oldNode.previous); 336 return true; 337 } else { 338 return false; 339 } 340 } 341 } 342 }