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