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