1 /* 2 * Copyright (c) 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 26 import java.util.ArrayList; 27 import java.util.List; 28 import java.util.Objects; 29 import java.util.concurrent.Semaphore; 30 import java.util.concurrent.TimeUnit; 31 32 import org.testng.Assert; 33 import org.testng.TestNG; 34 import org.testng.annotations.Test; 35 import org.testng.annotations.BeforeSuite; 36 import org.testng.annotations.DataProvider; 37 38 import jdk.test.lib.Platform; 39 import jdk.test.lib.Utils; 40 41 import sun.misc.Signal; 42 import sun.misc.SignalHandler; 43 44 /* 45 * @test 46 * @library /test/lib 47 * @modules jdk.unsupported 48 * java.base/jdk.internal.misc 49 * @build jdk.test.lib.Utils 50 * jdk.test.lib.Asserts 51 * jdk.test.lib.JDKToolFinder 52 * jdk.test.lib.JDKToolLauncher 53 * jdk.test.lib.Platform 54 * jdk.test.lib.process.* 55 * @run testng/othervm -Xrs -DXrs=true SunMiscSignalTest 56 * @run testng/othervm SunMiscSignalTest 57 * @summary sun.misc.Signal test 58 */ 59 60 @Test 61 public class SunMiscSignalTest { 62 63 // Set to true to enable additional debug output 64 static boolean debug = true; 65 66 // True to test while running with -Xrs 67 static boolean RUNNING_WITH_Xrs = Boolean.getBoolean("Xrs"); 68 69 /** 70 * Print a debug message if enabled. 71 * 72 * @param format the format 73 * @param args the arguments 74 */ 75 static void printf(String format, Object... args) { 76 if (debug) { 77 System.out.printf(" " + format, args); 78 } 79 } 80 81 enum IsSupported {NO, YES} 82 83 enum CanRegister {NO, YES} 84 85 enum CanRaise {NO, YES} 86 87 enum Invoked {NO, YES} 88 89 enum RestrictedSignals {NORMAL, XRS} 90 91 @BeforeSuite 92 static void setup() { 93 System.out.printf("-Xrs: %s%n", RUNNING_WITH_Xrs); 94 } 95 96 // Provider of signals to be tested with variations for -Xrs and 97 // platform dependencies 98 // -Xrs restricted signals signals the VM will not handle SIGINT, SIGTERM, SIGHUP and others 99 @DataProvider(name = "supportedSignals") 100 static Object[][] supportedSignals() { 101 RestrictedSignals rs = RUNNING_WITH_Xrs ? RestrictedSignals.XRS : RestrictedSignals.NORMAL; 102 CanRegister registerXrs = RUNNING_WITH_Xrs ? CanRegister.NO : CanRegister.YES; 103 CanRaise raiseXrs = RUNNING_WITH_Xrs ? CanRaise.NO : CanRaise.YES; 104 Invoked invokedXrs = RUNNING_WITH_Xrs ? Invoked.NO : Invoked.YES; 105 106 Object[][] commonSignals = new Object[][]{ 107 {"INT", IsSupported.YES, registerXrs, raiseXrs, invokedXrs}, 108 {"TERM", IsSupported.YES, registerXrs, raiseXrs, invokedXrs}, 109 {"ABRT", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 110 }; 111 112 Object[][] posixSignals = { 113 {"HUP", IsSupported.YES, registerXrs, raiseXrs, invokedXrs}, 114 {"QUIT", IsSupported.YES, CanRegister.NO, CanRaise.NO, Invoked.NO}, 115 {"BUS", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 116 {"USR1", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 117 {"USR2", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 118 {"PIPE", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 119 {"ALRM", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 120 {"CHLD", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 121 {"CONT", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 122 {"TSTP", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 123 {"TTIN", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 124 {"TTOU", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 125 {"URG", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 126 {"XCPU", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 127 {"XFSZ", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 128 {"VTALRM", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 129 {"PROF", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 130 {"WINCH", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 131 {"IO", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 132 {"SYS", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs}, 133 }; 134 135 Object[][] windowsSignals = { 136 {"HUP", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 137 {"QUIT", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 138 {"BUS", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 139 {"USR1", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 140 {"USR2", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 141 {"PIPE", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 142 {"ALRM", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 143 {"CHLD", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 144 {"CONT", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 145 {"TSTP", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 146 {"TTIN", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 147 {"TTOU", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 148 {"URG", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 149 {"XCPU", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 150 {"XFSZ", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 151 {"VTALRM", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 152 {"PROF", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 153 {"WINCH", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 154 {"IO", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 155 {"SYS", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO}, 156 }; 157 158 return concatArrays(commonSignals, (Platform.isWindows() ? windowsSignals : posixSignals)); 159 } 160 161 // Provider of invalid signal names 162 @DataProvider(name = "invalidSunMiscSignalNames") 163 Object[][] invalidSunMiscSignalNames() { 164 return new Object[][]{ 165 {""}, 166 {"I"}, 167 {"SIG"}, 168 {"SIGabc"}, 169 {"SIGINT"}, // prefix not allowed 170 {"abc"}, 171 }; 172 } 173 174 static Object[][] concatArrays(Object[][]... arrays) { 175 int l = 0; 176 for (Object[][] a : arrays) { 177 l += a.length; 178 } 179 180 Object[][] newArray = new Object[l][]; 181 l = 0; 182 for (int i = 0; i < arrays.length; i++) { 183 System.arraycopy(arrays[i], 0, newArray, l, arrays[i].length); 184 l += arrays[i].length; 185 } 186 187 return newArray; 188 } 189 190 // Return true if the signal is one of the shutdown signals known to the VM 191 private static boolean isShutdownSignal(Signal signal) { 192 String name = signal.getName(); 193 return name.equals("INT") || name.equals("HUP") || name.equals("TERM"); 194 } 195 196 /** 197 * Quick verification of supported signals using sun.misc.Signal. 198 * 199 * @param name the signal name 200 * @throws InterruptedException would be an error if thrown 201 */ 202 @Test(dataProvider = "supportedSignals") 203 static void testSunMisc(String name, IsSupported supported, CanRegister register, 204 CanRaise raise, Invoked invoked) throws InterruptedException { 205 Handler h = new Handler(); 206 SignalHandler orig = null; 207 Signal signal = null; 208 try { 209 signal = new Signal(name); 210 Assert.assertEquals(supported, IsSupported.YES, "Unexpected support for " + name); 211 212 Assert.assertEquals(signal.getName(), name, "getName() mismatch, "); 213 214 Assert.assertEquals(signal.toString(), "SIG" + name, "toString() mismatch, "); 215 216 try { 217 orig = Signal.handle(signal, h); 218 printf("oldHandler: %s%n", orig); 219 Assert.assertEquals(CanRegister.YES, register, "Unexpected handle succeeded " + name); 220 try { 221 Signal.raise(signal); 222 Assert.assertEquals(CanRaise.YES, raise, "Unexpected raise success for " + name); 223 Invoked inv = h.semaphore().tryAcquire(Utils.adjustTimeout(100L), 224 TimeUnit.MILLISECONDS) ? Invoked.YES : Invoked.NO; 225 if (!isShutdownSignal(signal)) { 226 // Normal case 227 Assert.assertEquals(inv, invoked, "handler not invoked;"); 228 } else { 229 if (orig == SignalHandler.SIG_IGN) { 230 Assert.assertEquals(inv, Invoked.NO, "handler should not be invoked"); 231 } else { 232 Assert.assertEquals(inv, invoked, "handler not invoked;"); 233 } 234 } 235 } catch (IllegalArgumentException uoe3) { 236 Assert.assertNotEquals(CanRaise.YES, raise, "raise failed for " + name + 237 ": " + uoe3.getMessage()); 238 } 239 } catch (IllegalArgumentException uoe2) { 240 Assert.assertNotEquals(CanRegister.YES, register, "handle failed for: " + name + 241 ": " + uoe2.getMessage()); 242 } 243 } catch (IllegalArgumentException uoe) { 244 Assert.assertNotEquals(IsSupported.YES, supported, "Support missing for " + name + 245 ": " + uoe.getMessage()); 246 return; 247 } finally { 248 // Restore original signal handler 249 if (orig != null && signal != null) { 250 Signal.handle(signal, orig); 251 } 252 } 253 } 254 255 // Test Signal is equal to itself and not equals to others 256 @Test(dataProvider = "supportedSignals") 257 static void testEquals(String name, IsSupported supported, CanRegister register, 258 CanRaise raise, Invoked invoked) { 259 Object[][] data = supportedSignals(); 260 for (int i = 0; i < data.length; i++) { 261 IsSupported otherSupported = (IsSupported) data[i][1]; 262 if (supported == IsSupported.NO || otherSupported == IsSupported.NO) { 263 continue; 264 } 265 String otherName = (String) data[i][0]; 266 267 Signal sig1 = new Signal(name); 268 Signal sig2 = new Signal(otherName); 269 if (name.equals(otherName)) { 270 Assert.assertEquals(sig1, sig2, "Equals failed; "); 271 Assert.assertEquals(sig1.hashCode(), sig2.hashCode(), "HashCode wrong; "); 272 } else { 273 Assert.assertNotEquals(sig1, sig2, "NotEquals failed; "); 274 Assert.assertNotEquals(sig1.hashCode(), sig2.hashCode(), "HashCode wrong; "); 275 } 276 } 277 } 278 279 @Test(dataProvider = "invalidSunMiscSignalNames") 280 static void testSunMiscIAE(String name) { 281 try { 282 new Signal(name); 283 Assert.fail("Should have thrown IAE for signal: " + name); 284 } catch (IllegalArgumentException iae) { 285 Assert.assertEquals(iae.getMessage(), "Unknown signal: " + name, "getMessage() incorrect; "); 286 } 287 } 288 289 // Note: JDK 8 did not check/throw NPE, passing null resulted in a segv 290 @Test(expectedExceptions = NullPointerException.class) 291 static void nullSignal() { 292 new Signal(null); 293 } 294 295 // Test expected exception when raising a signal when no handler defined 296 @Test 297 static void testRaiseNoConsumer() { 298 Signal signal = new Signal("INT"); 299 SignalHandler orig = null; 300 try { 301 orig = Signal.handle(signal, SignalHandler.SIG_DFL); 302 printf("oldHandler: %s%n", orig); 303 if (orig == SignalHandler.SIG_IGN) { 304 // SIG_IGN for TERM means it cannot be handled 305 return; 306 } 307 Signal.raise(signal); 308 Assert.fail("Should have thrown IllegalArgumentException"); 309 } catch (IllegalArgumentException iae) { 310 printf("IAE message: %s%n", iae.getMessage()); 311 } finally { 312 // Restore original signal handler 313 if (orig != null && signal != null) { 314 Signal.handle(signal, orig); 315 } 316 } 317 } 318 319 /** 320 * The thread that runs the handler for sun.misc.Signal should be a 321 * Daemon thread. 322 */ 323 @Test 324 static void isDaemonThread() throws InterruptedException { 325 if (RUNNING_WITH_Xrs) { 326 return; 327 } 328 Handler handler = new Handler(); 329 Signal signal = new Signal("INT"); 330 SignalHandler orig = Signal.handle(signal, handler); 331 printf("oldHandler: %s%n", orig); 332 if (orig == SignalHandler.SIG_IGN) { 333 // SIG_IGN for INT means it cannot be handled 334 return; 335 } 336 337 Signal.raise(signal); 338 boolean handled = handler.semaphore() 339 .tryAcquire(Utils.adjustTimeout(100L), TimeUnit.MILLISECONDS); 340 if (!handled) { 341 // For debug try again 342 printf("Second try to see signal"); 343 handled = handler.semaphore() 344 .tryAcquire(Utils.adjustTimeout(2L), TimeUnit.SECONDS); 345 } 346 Assert.assertEquals(handled, !RUNNING_WITH_Xrs, 347 "raising s.m.Signal did not get a callback;"); 348 349 Assert.assertTrue(handler.wasDaemon(), "Thread.isDaemon running the handler; "); 350 } 351 352 // Check that trying to invoke SIG_DFL.handle throws UnsupportedOperationException. 353 @Test(expectedExceptions = UnsupportedOperationException.class) 354 static void cannotHandleSIGDFL() { 355 Signal signal = new Signal("INT"); 356 Assert.assertNotNull(SignalHandler.SIG_DFL, "SIG_DFL null; "); 357 SignalHandler.SIG_DFL.handle(signal); 358 } 359 360 // Check that trying to invoke SIG_IGN.handle throws UnsupportedOperationException. 361 @Test(expectedExceptions = UnsupportedOperationException.class) 362 static void cannotHandleSIGIGN() { 363 Signal signal = new Signal("INT"); 364 Assert.assertNotNull(SignalHandler.SIG_IGN, "SIG_IGN null; "); 365 SignalHandler.SIG_IGN.handle(signal); 366 } 367 368 // Check that setting a Signal handler returns the previous handler. 369 @Test() 370 static void checkLastHandler() { 371 if (RUNNING_WITH_Xrs) { 372 return; 373 } 374 Signal signal = new Signal("TERM"); 375 Handler h1 = new Handler(); 376 Handler h2 = new Handler(); 377 SignalHandler orig = Signal.handle(signal, h1); 378 if (orig == SignalHandler.SIG_IGN) { 379 // SIG_IGN for TERM means it cannot be handled 380 return; 381 } 382 383 try { 384 SignalHandler prev = Signal.handle(signal, h2); 385 Assert.assertSame(prev, h1, "prev handler mismatch"); 386 387 prev = Signal.handle(signal, h1); 388 Assert.assertSame(prev, h2, "prev handler mismatch"); 389 } finally { 390 if (orig != null && signal != null) { 391 Signal.handle(signal, orig); 392 } 393 } 394 } 395 396 /** 397 * Test Handler, a SignalHandler for Signal notifications. 398 * Signals a semaphore when invoked and records whether 399 * the thread calling the Handler was a daemon. 400 */ 401 static class Handler implements SignalHandler { 402 // A semaphore to check for accept being called 403 Semaphore sema = new Semaphore(0); 404 405 Boolean wasDaemon = null; 406 407 Semaphore semaphore() { 408 return sema; 409 } 410 411 synchronized Boolean wasDaemon() { 412 return wasDaemon; 413 } 414 415 /** 416 * Releases the semaphore when called as SignalHandler.handle. 417 * 418 * @param signal the Signal that occurred 419 */ 420 @Override 421 public void handle(Signal signal) { 422 synchronized (this) { 423 wasDaemon = Thread.currentThread().isDaemon(); 424 } 425 sema.release(); 426 printf("sun.misc.handle sig: %s, num: %d%n", signal.getName(), signal.getNumber()); 427 } 428 429 public String toString() { 430 return "Handler: sem: " + sema.getQueueLength() + 431 ", wasDaemon: " + Objects.toString(wasDaemon()); 432 } 433 } 434 435 // Main can be used to run the tests from the command line with only testng.jar. 436 @SuppressWarnings("raw_types") 437 @Test(enabled = false) 438 public static void main(String[] args) { 439 Class<?>[] testclass = {SunMiscSignalTest.class}; 440 TestNG testng = new TestNG(); 441 testng.setTestClasses(testclass); 442 testng.run(); 443 } 444 445 }