diff --git a/src/java.se/share/data/jdwp/jdwp.spec b/src/java.se/share/data/jdwp/jdwp.spec index be0bf8d39d1..a7d489b2813 100644 --- a/src/java.se/share/data/jdwp/jdwp.spec +++ b/src/java.se/share/data/jdwp/jdwp.spec @@ -2004,10 +2004,11 @@ JDWP "Java(tm) Debug Wire Protocol" (Command Stop=10 "Stops the thread with an asynchronous exception. " "
" - "The target VM may not support, or may only provide limited support, for " - "this command when the thread is a virtual thread. It may, for example, " - "only support this command when the virtual thread is suspended at a " - "breakpoint or singlestep event." + "This command may be used to send an asynchronous " + "exception to a virtual thread when it is suspended at an event. " + "An implementation may support sending an asynchronous exception " + "to a suspended virtual thread in other cases." + (Out (threadObject thread "The thread object ID. ") (object throwable "Asynchronous exception. This object must " @@ -2018,8 +2019,10 @@ JDWP "Java(tm) Debug Wire Protocol" (ErrorSet (Error INVALID_THREAD "The thread is null, not a valid thread, or the thread " "is not alive.") - (Error NOT_IMPLEMENTED "The thread is a virtual thread and the target " - "VM does not support the command on virtual threads.") + (Error THREAD_NOT_SUSPENDED "The thread is a virtual thread and was not suspended.") + (Error OPAQUE_FRAME "The thread is a suspended virtual thread and the implementation " + "was unable to throw an asynchronous exception " + "from the thread's current frame.") (Error INVALID_OBJECT "If thread is not a known ID or the asynchronous " "exception has been garbage collected.") (Error VM_DEAD) @@ -3166,7 +3169,7 @@ JDWP "Java(tm) Debug Wire Protocol" "call stack.") (Constant OPAQUE_FRAME =32 "Information about the frame is not available " "(e.g. native frame) or the target VM is unable " - "to perform an operation on the frame.") + "to perform an operation on the thread's current frame.") (Constant NOT_CURRENT_FRAME =33 "Operation can only be performed on current frame.") (Constant TYPE_MISMATCH =34 "The variable is not an appropriate type for " "the function used.") diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/ThreadReference.java b/src/jdk.jdi/share/classes/com/sun/jdi/ThreadReference.java index a49fe4c547c..a201e7ed137 100644 --- a/src/jdk.jdi/share/classes/com/sun/jdi/ThreadReference.java +++ b/src/jdk.jdi/share/classes/com/sun/jdi/ThreadReference.java @@ -116,18 +116,20 @@ public interface ThreadReference extends ObjectReference { * A debugger thread in the target VM will stop this thread * with the given {@link java.lang.Throwable} object. *
- * The target VM may not support, or may only provide limited support,
- * for stopping a virtual thread with an asynchronous exception. It may,
- * for example, only support this operation when the virtual thread is
- * suspended at a breakpoint or singlestep event.
+ * This method may be used to send an asynchronous
+ * exception to a virtual thread when it is suspended at an event.
+ * An implementation may support sending an asynchronous exception
+ * to a suspended virtual thread in other cases.
+
*
* @param throwable the asynchronous exception to throw
* @throws InvalidTypeException if throwable is not
* an instance of java.lang.Throwable in the target VM
- * @throws IllegalThreadStateException if the thread has terminated
- * @throws UnsupportedOperationException if the thread is a virtual
- * thread and the target VM does not support this operation on
- * virtual threads
+ * @throws IllegalThreadStateException if the thread has terminated,
+ * or if the thread is a virtual thread and was not suspended
+ * @throws OpaqueFrameException if the thread is a suspended
+ * virtual thread and the implementation was unable to throw an
+ * asynchronous exception from the thread's current frame
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only
* @see VirtualMachine#canBeModified()
*/
diff --git a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/Commands.java b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/Commands.java
index eca6753506b..f23db6056bb 100644
--- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/Commands.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/Commands.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -719,9 +719,13 @@ class Commands {
} catch (InvalidTypeException e) {
MessageOutput.println("Invalid exception object");
} catch (IllegalThreadStateException its) {
- MessageOutput.println("Illegal thread state");
- } catch (UnsupportedOperationException uoe) {
- MessageOutput.println("Operation is not supported on the target VM");
+ if (!thread.isSuspended() && thread.isVirtual()) {
+ MessageOutput.println("Illegal thread state (virtual thread not suspended)");
+ } else {
+ MessageOutput.println("Illegal thread state");
+ }
+ } catch (OpaqueFrameException ope) {
+ MessageOutput.println("Operation is not supported on the current frame");
}
} else {
MessageOutput.println("Expression must evaluate to an object");
diff --git a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/TTYResources.java b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/TTYResources.java
index bbbeb6b12ab..dec3261a81f 100644
--- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/TTYResources.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/TTYResources.java
@@ -142,6 +142,7 @@ public class TTYResources extends java.util.ListResourceBundle {
{"Illegal Argument Exception", "Illegal Argument Exception"},
{"Illegal connector argument", "Illegal connector argument: {0}"},
{"Illegal thread state", "Illegal thread state"},
+ {"Illegal thread state (virtual thread not suspended)", "Illegal thread state (virtual thread not suspended)"},
{"implementor:", "implementor: {0}"},
{"implements:", "implements: {0}"},
{"Initializing progname", "Initializing {0} ..."},
@@ -244,7 +245,7 @@ public class TTYResources extends java.util.ListResourceBundle {
{"Not waiting for a monitor", " Not waiting for a monitor"},
{"Nothing suspended.", "Nothing suspended."},
{"object description and id", "({0}){1}"},
- {"Operation is not supported on the target VM", "Operation is not supported on the target VM"},
+ {"Operation is not supported on the current frame", "Operation is not supported on the current frame"},
{"operation not yet supported", "operation not yet supported"},
{"Owned by:", " Owned by: {0}, entry count: {1,number,integer}"},
{"Owned monitor:", " Owned monitor: {0}"},
diff --git a/src/jdk.jdi/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java b/src/jdk.jdi/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java
index d81f450c6d1..79e11e32751 100644
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java
@@ -273,7 +273,18 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
JDWP.ThreadReference.Stop.process(vm, this,
(ObjectReferenceImpl)throwable);
} catch (JDWPException exc) {
- throw exc.toJDIException();
+ switch (exc.errorCode()) {
+ case JDWP.Error.OPAQUE_FRAME:
+ assert isVirtual(); // can only happen with virtual threads
+ throw new OpaqueFrameException();
+ case JDWP.Error.THREAD_NOT_SUSPENDED:
+ assert isVirtual(); // can only happen with virtual threads
+ throw new IllegalThreadStateException("virtual thread not suspended");
+ case JDWP.Error.INVALID_THREAD:
+ throw new IllegalThreadStateException("thread has terminated");
+ default:
+ throw exc.toJDIException();
+ }
}
}
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002.java
index 153100b53b4..55788e323b1 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,16 +35,21 @@ import nsk.share.jpda.*;
import nsk.share.jdi.*;
/**
- * The test checks that the JDI method:
- * com.sun.jdi.ThreadReference.stop()
- * properly throws InvalidTypeException - if specified
- * throwable is not an instance of java.lang.Throwable in the
- * target VM.
+ * The test checks that the JDI method:com.sun.jdi.ThreadReference.stop()
+ * behaves properly in various situations. It consists of 5 subtests.
*
- * Debugger part of the test tries to stop debuggee thread
- * through the JDI method using as a parameter an object
- * reference of the main debuggee class stop002t itself
- * which is not Throwable.
+ * TEST #1: Tests that stop() properly throws InvalidTypeException if
+ * specified throwable is not an instance of java.lang.Throwable in the target VM.
+ * + * TEST #2: Verify that stop() works when suspended at a breakpoint. + * + * TEST #3: Verify that stop() works when not suspended in a loop. For virtual threads + * we expect an IncompatibleThreadStateException. + * + * TEST #4: Verify that stop() works when suspended in a loop. + * + * TEST #5: Verify that stop() works when suspended in Thread.sleep(). For virtual + * threads we expect an OpaqueFrameException. */ public class stop002 { static final String DEBUGGEE_CLASS = @@ -54,12 +59,15 @@ public class stop002 { static final String DEBUGGEE_THRNAME = "stop002tThr"; // debuggee local var used to find needed non-throwable object - static final String DEBUGGEE_LOCALVAR = "stop002tNonThrowable"; - // debuggee field used to indicate that testing is over - static final String DEBUGGEE_FIELD = "stopLooping"; + static final String DEBUGGEE_NON_THROWABLE_VAR= "stop002tNonThrowable"; + // debuggee local var used to find needed throwable object + static final String DEBUGGEE_THROWABLE_VAR = "stop002tThrowable"; + // debuggee fields used to indicate to exit infinite loops + static final String DEBUGGEE_STOP_LOOP1_FIELD = "stopLooping1"; + static final String DEBUGGEE_STOP_LOOP2_FIELD = "stopLooping2"; // debuggee source line where it should be stopped - static final int DEBUGGEE_STOPATLINE = 69; + static final int DEBUGGEE_STOPATLINE = 88; static final int DELAY = 500; // in milliseconds @@ -67,12 +75,15 @@ public class stop002 { static final String COMMAND_GO = "go"; static final String COMMAND_QUIT = "quit"; + static final boolean vthreadMode = "Virtual".equals(System.getProperty("main.wrapper")); + private ArgumentHandler argHandler; private Log log; private IOPipe pipe; private Debugee debuggee; private VirtualMachine vm; private BreakpointRequest BPreq; + private ReferenceType mainClass; private volatile int tot_res = Consts.TEST_PASSED; private volatile boolean gotEvent = false; @@ -101,68 +112,180 @@ public class stop002 { return quitDebuggee(); } - ThreadReference thrRef = null; - if ((thrRef = - debuggee.threadByName(DEBUGGEE_THRNAME)) == null) { + ThreadReference thrRef = debuggee.threadByName(DEBUGGEE_THRNAME); + if (thrRef == null) { log.complain("TEST FAILURE: method Debugee.threadByName() returned null for debuggee thread " + DEBUGGEE_THRNAME); tot_res = Consts.TEST_FAILED; return quitDebuggee(); } - Field doExit = null; + Field stopLoop1 = null; + Field stopLoop2 = null; ObjectReference objRef = null; + ObjectReference throwableRef = null; try { // debuggee main class - ReferenceType rType = debuggee.classByName(DEBUGGEE_CLASS); + mainClass = debuggee.classByName(DEBUGGEE_CLASS); - suspendAtBP(rType, DEBUGGEE_STOPATLINE); - objRef = findObjRef(thrRef, DEBUGGEE_LOCALVAR); + suspendAtBP(mainClass, DEBUGGEE_STOPATLINE); + objRef = findObjRef(thrRef, DEBUGGEE_NON_THROWABLE_VAR); + throwableRef = findObjRef(thrRef, DEBUGGEE_THROWABLE_VAR); - // this field is used to indicate that debuggee has to - // stop looping - doExit = rType.fieldByName(DEBUGGEE_FIELD); - - log.display("Resuming debuggee VM ..."); - vm.resume(); - log.display("Resumption of debuggee VM done"); - - log.display("\nTrying to stop debuggee thread \"" + thrRef - + "\"\n\tusing non-throwable object reference \"" - + objRef + "\" as a parameter ..."); - -// Check the tested assersion - try { - thrRef.stop(objRef); - log.complain("TEST FAILED: expected IllegalArgumentException was not thrown" - + "\n\twhen attempted to stop debuggee thread \"" + thrRef - + "\"\n\tusing non-throwable object reference \"" - + objRef + "\" as a parameter"); - tot_res = Consts.TEST_FAILED; - } catch(InvalidTypeException ee) { - log.display("CHECK PASSED: caught expected " + ee); - } catch(Exception ue) { - ue.printStackTrace(); - log.complain("TEST FAILED: ThreadReference.stop(): caught unexpected " - + ue + "\n\tinstead of InvalidTypeException" - + "\n\twhen attempted to stop debuggee thread \"" + thrRef - + "\"\n\tusing non-throwable object reference \"" - + objRef + "\" as a parameter"); - tot_res = Consts.TEST_FAILED; + // These fields are used to indicate that debuggee has to stop looping + stopLoop1 = mainClass.fieldByName(DEBUGGEE_STOP_LOOP1_FIELD); + stopLoop2 = mainClass.fieldByName(DEBUGGEE_STOP_LOOP2_FIELD); + if (stopLoop1 == null || stopLoop2 == null) { + throw new RuntimeException("Failed to find a \"stop loop\" field"); } + log.display("non-throwable object: \"" + objRef + "\""); + log.display("throwable object: \"" + throwableRef + "\""); + log.display("debuggee thread: \"" + thrRef + "\""); + + /* + * Test #1: verify using a non-throwable object with stop() fails appropriately. + */ + log.display("\nTEST #1: Trying to stop debuggee thread using non-throwable object."); + try { + thrRef.stop(objRef); // objRef is an instance of the debuggee class, not a Throwable + log.complain("TEST #1 FAILED: expected InvalidTypeException was not thrown"); + tot_res = Consts.TEST_FAILED; + } catch (InvalidTypeException ee) { + log.display("TEST #1 PASSED: caught expected " + ee); + } catch (Exception ue) { + ue.printStackTrace(); + log.complain("TEST #1 FAILED: caught unexpected " + ue + "instead of InvalidTypeException"); + tot_res = Consts.TEST_FAILED; + } + log.display("TEST #1: all done."); + + /* + * Test #2: verify that stop() works when suspended at a breakpoint. + */ + log.display("\nTEST #2: Trying to stop debuggee thread while suspended at a breakpoint."); + try { + thrRef.stop(throwableRef); + log.display("TEST #2 PASSED: stop() call succeeded."); + } catch (Exception ue) { + ue.printStackTrace(); + log.complain("TEST #2 FAILED: caught unexpected " + ue); + tot_res = Consts.TEST_FAILED; + } + log.display("TEST #2: Resuming debuggee VM to allow async exception to be handled"); + vm.resume(); + log.display("TEST #2: all done."); + + /* + * Test #3: verify that stop() works when not suspended in a loop. Expect + * IllegalThreadStateException for virtual threads. + */ + log.display("\nTEST #3: Trying to stop debuggee thread while not suspended in a loop."); + waitForTestReady(3); + try { + thrRef.stop(throwableRef); + if (vthreadMode) { + log.complain("TEST #3 FAILED: expected IllegalThreadStateException" + + " was not thrown for virtual thread"); + tot_res = Consts.TEST_FAILED; + } else { + log.display("TEST #3 PASSED: stop() call succeeded."); + } + } catch (Exception ue) { + if (vthreadMode && ue instanceof IllegalThreadStateException) { + log.display("TEST #3 PASSED: stop() call threw IllegalThreadStateException" + + " for virtual thread"); + } else { + ue.printStackTrace(); + log.complain("TEST #3 FAILED: caught unexpected " + ue); + tot_res = Consts.TEST_FAILED; + } + } finally { + // Force the debuggee out of the loop. Not really needed if the stop() call + // successfully threw the async exception, but it's easier to just always do this. + log.display("TEST #3: clearing loop flag."); + objRef.setValue(stopLoop1, vm.mirrorOf(true)); + } + log.display("TEST #3: all done."); + + /* + * Test #4: verify that stop() works when suspended in a loop + */ + log.display("\nTEST #4: Trying to stop debuggee thread while suspended in a loop."); + waitForTestReady(4); + try { + thrRef.suspend(); + log.display("TEST #4: thread is suspended."); + thrRef.stop(throwableRef); + log.display("TEST #4 PASSED: stop() call succeeded."); + } catch (Throwable ue) { + ue.printStackTrace(); + log.complain("TEST #4 FAILED: caught unexpected " + ue); + tot_res = Consts.TEST_FAILED; + } finally { + log.display("TEST #4: resuming thread."); + thrRef.resume(); + // Force the debuggee out of the loop. Not really needed if the stop() call + // successfully threw the async exception, but it's easier to just always do this. + log.display("TEST #4: clearing loop flag."); + objRef.setValue(stopLoop2, vm.mirrorOf(true)); + } + log.display("TEST #4: all done."); + + /* + * Test #5: verify that stop() works when suspended in Thread.sleep(). Expect + * OpaqueFrameException for virtual threads. + */ + log.display("\nTEST #5: Trying to stop debuggee thread while suspended in Thread.sleep()."); + waitForTestReady(5); + // Allow debuggee to reach Thread.sleep() first. + log.display("TEST #5: waiting for debuggee to sleep..."); + while (true) { + int status = thrRef.status(); + if (status == ThreadReference.THREAD_STATUS_SLEEPING || + status == ThreadReference.THREAD_STATUS_WAIT) + { + break; + } + Thread.sleep(50); + } + log.display("TEST #5: debuggee is sleeping."); + try { + thrRef.suspend(); + log.display("TEST #5: thread is suspended."); + thrRef.stop(throwableRef); + if (vthreadMode) { + log.complain("TEST #5 FAILED: expected OpaqueFrameException was not thrown"); + tot_res = Consts.TEST_FAILED; + } else { + log.display("TEST #5 PASSED: stop() call for suspended thread succeeded"); + } + } catch (Throwable ue) { + if (vthreadMode && ue instanceof OpaqueFrameException) { + log.display("TEST #5 PASSED: stop() call threw OpaqueFrameException for virtual thread"); + } else { + ue.printStackTrace(); + log.complain("TEST #5 FAILED: caught unexpected " + ue); + tot_res = Consts.TEST_FAILED; + } + } finally { + log.display("TEST #5: resuming thread."); + thrRef.resume(); + } + log.display("TEST #5: all done."); } catch (Exception e) { e.printStackTrace(); log.complain("TEST FAILURE: caught unexpected exception: " + e); tot_res = Consts.TEST_FAILED; } finally { -// Finish the test - // force an method to exit - if (objRef != null && doExit != null) { + // Force the debuggee out of both loops + if (objRef != null && stopLoop1 != null && stopLoop2 != null) { try { - objRef.setValue(doExit, vm.mirrorOf(true)); - } catch(Exception sve) { + objRef.setValue(stopLoop1, vm.mirrorOf(true)); + objRef.setValue(stopLoop2, vm.mirrorOf(true)); + } catch (Exception sve) { sve.printStackTrace(); + tot_res = Consts.TEST_FAILED; } } } @@ -170,6 +293,15 @@ public class stop002 { return quitDebuggee(); } + private void waitForTestReady(int testNum) { + log.display("TEST #" + testNum + ": waiting for test ready..."); + IntegerValue ival; + do { + ival = (IntegerValue)mainClass.getValue(mainClass.fieldByName("testNumReady")); + } while (ival.value() != testNum); + log.display("TEST #" + testNum + ": test ready."); + } + private ObjectReference findObjRef(ThreadReference thrRef, String varName) { try { List frames = thrRef.frames(); @@ -184,9 +316,9 @@ public class stop002 { return (ObjectReference) stackFr.getValue(locVar); } - } catch(AbsentInformationException e) { + } catch (AbsentInformationException e) { // this is not needed stack frame, ignoring - } catch(NativeMethodException ne) { + } catch (NativeMethodException ne) { // current method is native, also ignoring } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java index a3af4a17a9d..0b544905cb4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,10 @@ import nsk.share.jdi.*; public class stop002t { private Log log; private IOPipe pipe; - volatile boolean stopLooping = false; + volatile boolean stopLooping1 = false; + volatile boolean stopLooping2 = false; + volatile static int testNumReady = 0; + static final boolean vthreadMode = "Virtual".equals(System.getProperty("main.wrapper")); public static void main(String args[]) { System.exit(run(args) + Consts.JCK_STATUS_BASE); @@ -57,23 +60,138 @@ public class stop002t { // as wrong parameter of JDI method ThreadReference.stop() stop002t stop002tNonThrowable = this; + // throwable object which will be used by debugger + // as valid parameter of JDI method ThreadReference.stop() + Throwable stop002tThrowable = new MyThrowable("Async exception"); + // Now the debuggee is ready for testing pipe.println(stop002.COMMAND_READY); String cmd = pipe.readln(); if (cmd.equals(stop002.COMMAND_QUIT)) { - log.complain("Debuggee: exiting due to the command " + log.complain("Debuggee: premature debuggee exit due to the command " + cmd); - return Consts.TEST_PASSED; + return Consts.TEST_FAILED; } - int stopMeHere = 0; // stop002.DEBUGGEE_STOPATLINE + /* + * TEST #1: Tests that stop() properly throws InvalidTypeException if + * the specified throwable is not an instance of java.lang.Throwable + * in the debuggee. It does not involve the debuggee at all, so there + * is no code here for it. + */ - log.display("Debuggee: going to loop ..."); - while(!stopLooping) { // looping - stopMeHere++; stopMeHere--; + /* + * TEST #2: async exception while suspended at a breakpoint. + */ + int stopMeHere = 0; + try { + stopMeHere = 1; // stop002.DEBUGGEE_STOPATLINE + log.complain("TEST #2: Failed to throw expected exception"); + return Consts.TEST_FAILED; + } catch (Throwable t) { + // Call Thread.interrupted(). Workaround for JDK-8306324 + log.display("TEST #2: interrupted = " + Thread.interrupted()); + if (t instanceof MyThrowable) { + log.display("TEST #2: Caught expected exception while at breakpoint: " + t); + } else { + log.complain("TEST #2: Unexpected exception caught: " + t); + t.printStackTrace(); + return Consts.TEST_FAILED; + } } - log.display("Debuggee: looping done"); + log.display("TEST #2: all done"); + /* + * TEST #3: async exception while not suspended in a loop. + */ + log.display("TEST #3: going to loop ..."); + try { + while (!stopLooping1) { // looping + testNumReady = 3; // signal debugger side of test that we are ready + stopMeHere++; stopMeHere--; + } + if (vthreadMode) { + log.display("TEST #3: Correctly did not throw async exception for virtual thread"); + } else { + log.complain("TEST #3: Failed to throw expected exception"); + return Consts.TEST_FAILED; + } + } catch (Throwable t) { + // Call Thread.interrupted(). Workaround for JDK-8306324 + log.display("TEST #3: interrupted = " + Thread.interrupted()); + // We don't expect the exception to be thrown when in vthread mode. + if (!vthreadMode && t instanceof MyThrowable) { + log.display("TEST #3: Caught expected exception while in loop: " + t); + } else { + log.complain("TEST #3: Unexpected exception caught: " + t); + t.printStackTrace(); + return Consts.TEST_FAILED; + } + } + log.display("TEST #3: all done"); + + /* + * TEST #4: async exception while suspended in a loop. + */ + log.display("TEST #4: going to loop ..."); + try { + while (!stopLooping2) { // looping + testNumReady = 4; // signal debugger side of test that we are ready + stopMeHere++; stopMeHere--; + } + log.complain("TEST #4: Failed to throw expected exception"); + return Consts.TEST_FAILED; + } catch (Throwable t) { + // Call Thread.interrupted(). Workaround for JDK-8306324 + log.display("TEST #4: interrupted = " + Thread.interrupted()); + if (t instanceof MyThrowable) { + log.display("TEST #4: Caught expected exception while in loop: " + t); + } else { + log.complain("TEST #4: Unexpected exception caught: " + t); + t.printStackTrace(); + return Consts.TEST_FAILED; + } + } + log.display("TEST #4: all done"); + + /* + * TEST #5: async exception while suspended doing Thread.sleep(). + */ + try { + try { + // Signal debugger side of test that we are "almost" ready. The + // debugger will still need to check that we are in the sleep state. + testNumReady = 5; + log.display("TEST #5: going to sleep ..."); + Thread.sleep(10000); + } catch (InterruptedException e) { + log.complain("TEST #5: Unexpected InterruptedException"); + e.printStackTrace(); + return Consts.TEST_FAILED; + } + if (vthreadMode) { + log.display("TEST #5: Correctly did not throw exception while in sleep"); + } else { + log.complain("TEST #5: Failed to throw expected exception"); + return Consts.TEST_FAILED; + } + } catch (Throwable t) { + // Call Thread.interrupted(). Workaround for JDK-8306324 + log.display("TEST #5: interrupted = " + Thread.interrupted()); + // We don't expect the exception to be thrown when in vthread mode. + if (!vthreadMode && t instanceof MyThrowable) { + log.display("TEST #5: Caught expected exception while in loop: " + t); + } else { + log.complain("TEST #5: Unexpected exception caught: " + t); + t.printStackTrace(); + return Consts.TEST_FAILED; + } + } + log.display("TEST #5: all done"); + + /* + * Test shutdown. + */ cmd = pipe.readln(); if (!cmd.equals(stop002.COMMAND_QUIT)) { log.complain("TEST BUG: unknown debugger command: " @@ -83,3 +201,10 @@ public class stop002t { return Consts.TEST_PASSED; } } + +class MyThrowable extends Throwable +{ + MyThrowable(String message) { + super(message); + } +}