8249627: Degrade Thread.suspend and Thread.resume

Reviewed-by: cjplummer, sspitsyn, dholmes, jpai
This commit is contained in:
Alan Bateman 2022-09-27 11:43:59 +00:00
parent bc12e9553d
commit 1abf971b93
15 changed files with 113 additions and 232 deletions

View file

@ -194,7 +194,6 @@ JVM_RegisterLambdaProxyClassForArchiving
JVM_RegisterSignal JVM_RegisterSignal
JVM_ReleaseUTF JVM_ReleaseUTF
JVM_ReportFinalizationComplete JVM_ReportFinalizationComplete
JVM_ResumeThread
JVM_ExtentLocalCache JVM_ExtentLocalCache
JVM_SetExtentLocalCache JVM_SetExtentLocalCache
JVM_SetArrayElement JVM_SetArrayElement
@ -207,7 +206,6 @@ JVM_Sleep
JVM_StartThread JVM_StartThread
JVM_StopThread JVM_StopThread
JVM_SupportsCX8 JVM_SupportsCX8
JVM_SuspendThread
JVM_TotalMemory JVM_TotalMemory
JVM_UnloadLibrary JVM_UnloadLibrary
JVM_WaitForReferencePendingList JVM_WaitForReferencePendingList

View file

@ -272,12 +272,6 @@ JVM_StopThread(JNIEnv *env, jobject thread, jobject exception);
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
JVM_IsThreadAlive(JNIEnv *env, jobject thread); JVM_IsThreadAlive(JNIEnv *env, jobject thread);
JNIEXPORT void JNICALL
JVM_SuspendThread(JNIEnv *env, jobject thread);
JNIEXPORT void JNICALL
JVM_ResumeThread(JNIEnv *env, jobject thread);
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
JVM_SetThreadPriority(JNIEnv *env, jobject thread, jint prio); JVM_SetThreadPriority(JNIEnv *env, jobject thread, jint prio);

View file

@ -3026,29 +3026,6 @@ JVM_ENTRY(jboolean, JVM_IsThreadAlive(JNIEnv* env, jobject jthread))
JVM_END JVM_END
JVM_ENTRY(void, JVM_SuspendThread(JNIEnv* env, jobject jthread))
ThreadsListHandle tlh(thread);
JavaThread* receiver = NULL;
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
if (is_alive) {
// jthread refers to a live JavaThread, but java_suspend() will
// detect a thread that has started to exit and will ignore it.
receiver->java_suspend();
}
JVM_END
JVM_ENTRY(void, JVM_ResumeThread(JNIEnv* env, jobject jthread))
ThreadsListHandle tlh(thread);
JavaThread* receiver = NULL;
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
if (is_alive) {
// jthread refers to a live JavaThread.
receiver->java_resume();
}
JVM_END
JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio)) JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio))
ThreadsListHandle tlh(thread); ThreadsListHandle tlh(thread);
oop java_thread = NULL; oop java_thread = NULL;

View file

@ -1263,12 +1263,10 @@ jvmtiEnv *jvmti;
have this state flag set instead of <code>JVMTI_THREAD_STATE_SLEEPING</code>. have this state flag set instead of <code>JVMTI_THREAD_STATE_SLEEPING</code>.
</constant> </constant>
<constant id="JVMTI_THREAD_STATE_SUSPENDED" num="0x100000"> <constant id="JVMTI_THREAD_STATE_SUSPENDED" num="0x100000">
Thread suspended. Thread is suspended by a suspend function
<code>java.lang.Thread.suspend()</code> (such as <functionlink id="SuspendThread"></functionlink>).
or a <jvmti/> suspend function If this bit is set, the other bits refer to the thread state before
(such as <functionlink id="SuspendThread"></functionlink>) suspension.
has been called on the thread. If this bit
is set, the other bits refer to the thread state before suspension.
</constant> </constant>
<constant id="JVMTI_THREAD_STATE_INTERRUPTED" num="0x200000"> <constant id="JVMTI_THREAD_STATE_INTERRUPTED" num="0x200000">
Thread has been interrupted. Thread has been interrupted.
@ -1780,7 +1778,6 @@ jvmtiEnv *jvmti;
Any threads currently suspended through Any threads currently suspended through
a <jvmti/> suspend function (eg. a <jvmti/> suspend function (eg.
<functionlink id="SuspendThread"></functionlink>) <functionlink id="SuspendThread"></functionlink>)
or <code>java.lang.Thread.suspend()</code>
will resume execution; will resume execution;
all other threads are unaffected. all other threads are unaffected.
</description> </description>
@ -1815,7 +1812,6 @@ jvmtiEnv *jvmti;
Any thread suspended through Any thread suspended through
a <jvmti/> suspend function (eg. a <jvmti/> suspend function (eg.
<functionlink id="SuspendThreadList"></functionlink>) <functionlink id="SuspendThreadList"></functionlink>)
or <code>java.lang.Thread.suspend()</code>
will resume execution. will resume execution.
</description> </description>
<origin>jvmdi</origin> <origin>jvmdi</origin>

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,12 +27,9 @@ package java.lang;
/** /**
* Thrown to indicate that a thread is not in an appropriate state * Thrown to indicate that a thread is not in an appropriate state
* for the requested operation. See, for example, the * for the requested operation.
* {@code suspend} and {@code resume} methods in class
* {@code Thread}.
* *
* @see java.lang.Thread#resume() * @see Thread#start()
* @see java.lang.Thread#suspend()
* @since 1.0 * @since 1.0
*/ */
public class IllegalThreadStateException extends IllegalArgumentException { public class IllegalThreadStateException extends IllegalArgumentException {

View file

@ -171,9 +171,8 @@ import java.lang.module.ModuleFinder;
* <tr> * <tr>
* <th scope="row">modifyThread</th> * <th scope="row">modifyThread</th>
* <td>Modification of threads, e.g., via calls to Thread * <td>Modification of threads, e.g., via calls to Thread
* {@code interrupt, stop, suspend, resume, setDaemon, setPriority, * {@code interrupt, setDaemon, setPriority, setName} and
* setName} and {@code setUncaughtExceptionHandler} * {@code setUncaughtExceptionHandler} methods</td>
* methods</td>
* <td>This allows an attacker to modify the behaviour of * <td>This allows an attacker to modify the behaviour of
* any thread in the system.</td> * any thread in the system.</td>
* </tr> * </tr>
@ -181,9 +180,7 @@ import java.lang.module.ModuleFinder;
* <tr> * <tr>
* <th scope="row">modifyThreadGroup</th> * <th scope="row">modifyThreadGroup</th>
* <td>modification of thread groups, e.g., via calls to ThreadGroup * <td>modification of thread groups, e.g., via calls to ThreadGroup
* {@code destroy}, {@code getParent}, {@code resume}, * {@code getParent}, {@code setDaemon}, and {@code setMaxPriority} methods</td>
* {@code setDaemon}, {@code setMaxPriority}, {@code stop},
* and {@code suspend} methods</td>
* <td>This allows an attacker to create thread groups and * <td>This allows an attacker to create thread groups and
* set their run priority.</td> * set their run priority.</td>
* </tr> * </tr>

View file

@ -494,7 +494,6 @@ public class SecurityManager {
* calling thread is not allowed to modify the thread argument. * calling thread is not allowed to modify the thread argument.
* <p> * <p>
* This method is invoked for the current security manager by the * This method is invoked for the current security manager by the
* {@code stop}, {@code suspend}, {@code resume},
* {@code setPriority}, {@code setName}, and * {@code setPriority}, {@code setName}, and
* {@code setDaemon} methods of class {@code Thread}. * {@code setDaemon} methods of class {@code Thread}.
* <p> * <p>
@ -523,12 +522,9 @@ public class SecurityManager {
* permission to modify the thread. * permission to modify the thread.
* @throws NullPointerException if the thread argument is * @throws NullPointerException if the thread argument is
* {@code null}. * {@code null}.
* @see java.lang.Thread#resume() resume
* @see java.lang.Thread#setDaemon(boolean) setDaemon * @see java.lang.Thread#setDaemon(boolean) setDaemon
* @see java.lang.Thread#setName(java.lang.String) setName * @see java.lang.Thread#setName(java.lang.String) setName
* @see java.lang.Thread#setPriority(int) setPriority * @see java.lang.Thread#setPriority(int) setPriority
* @see java.lang.Thread#stop() stop
* @see java.lang.Thread#suspend() suspend
* @see #checkPermission(java.security.Permission) checkPermission * @see #checkPermission(java.security.Permission) checkPermission
*/ */
public void checkAccess(Thread t) { public void checkAccess(Thread t) {
@ -547,9 +543,8 @@ public class SecurityManager {
* <p> * <p>
* This method is invoked for the current security manager when a * This method is invoked for the current security manager when a
* new child thread or child thread group is created, and by the * new child thread or child thread group is created, and by the
* {@code setDaemon}, {@code setMaxPriority}, * {@code setDaemon} and {@code setMaxPriority} methods of class
* {@code stop}, {@code suspend}, {@code resume}, and * {@code ThreadGroup}.
* {@code destroy} methods of class {@code ThreadGroup}.
* <p> * <p>
* If the thread group argument is the system thread group ( * If the thread group argument is the system thread group (
* has a {@code null} parent) then * has a {@code null} parent) then
@ -576,12 +571,8 @@ public class SecurityManager {
* permission to modify the thread group. * permission to modify the thread group.
* @throws NullPointerException if the thread group argument is * @throws NullPointerException if the thread group argument is
* {@code null}. * {@code null}.
* @see java.lang.ThreadGroup#destroy() destroy
* @see java.lang.ThreadGroup#resume() resume
* @see java.lang.ThreadGroup#setDaemon(boolean) setDaemon * @see java.lang.ThreadGroup#setDaemon(boolean) setDaemon
* @see java.lang.ThreadGroup#setMaxPriority(int) setMaxPriority * @see java.lang.ThreadGroup#setMaxPriority(int) setMaxPriority
* @see java.lang.ThreadGroup#stop() stop
* @see java.lang.ThreadGroup#suspend() suspend
* @see #checkPermission(java.security.Permission) checkPermission * @see #checkPermission(java.security.Permission) checkPermission
*/ */
public void checkAccess(ThreadGroup g) { public void checkAccess(ThreadGroup g) {

View file

@ -1798,65 +1798,41 @@ public class Thread implements Runnable {
private native boolean isAlive0(); private native boolean isAlive0();
/** /**
* Suspends this thread. * Throws {@code UnsupportedOperationException}.
* <p>
* First, the {@code checkAccess} method of this thread is called
* with no arguments. This may result in throwing a
* {@code SecurityException} (in the current thread).
* <p>
* If the thread is alive, it is suspended and makes no further
* progress unless and until it is resumed.
* *
* @throws SecurityException if the current thread cannot modify * @throws UnsupportedOperationException always
* this thread. *
* @throws UnsupportedOperationException if invoked on a virtual thread * @deprecated This method was originally specified to suspend a thread.
* @see #checkAccess * It was inherently deadlock-prone. If the target thread held a lock on
* @deprecated This method has been deprecated, as it is * a monitor protecting a critical system resource when it was suspended,
* inherently deadlock-prone. If the target thread holds a lock on the * no thread could access the resource until the target thread was resumed.
* monitor protecting a critical system resource when it is suspended, no * If the thread intending to resume the target thread attempted to lock
* thread can access this resource until the target thread is resumed. If * the monitor prior to calling {@code resume}, deadlock would result.
* the thread that would resume the target thread attempts to lock this * Such deadlocks typically manifested themselves as "frozen" processes.
* monitor prior to calling {@code resume}, deadlock results. Such * For more information, see
* deadlocks typically manifest themselves as "frozen" processes. * <a href="{@docRoot}/java.base/java/lang/doc-files/threadPrimitiveDeprecation.html">Why
* For more information, see * are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
* <a href="{@docRoot}/java.base/java/lang/doc-files/threadPrimitiveDeprecation.html">Why
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
*/ */
@Deprecated(since="1.2", forRemoval=true) @Deprecated(since="1.2", forRemoval=true)
public final void suspend() { public final void suspend() {
checkAccess(); throw new UnsupportedOperationException();
if (isVirtual())
throw new UnsupportedOperationException();
suspend0();
} }
/** /**
* Resumes a suspended thread. * Throws {@code UnsupportedOperationException}.
* <p>
* First, the {@code checkAccess} method of this thread is called
* with no arguments. This may result in throwing a
* {@code SecurityException} (in the current thread).
* <p>
* If the thread is alive but suspended, it is resumed and is
* permitted to make progress in its execution.
* *
* @throws SecurityException if the current thread cannot modify this * @throws UnsupportedOperationException always
* thread. *
* @throws UnsupportedOperationException if invoked on a virtual thread * @deprecated This method was originally specified to resume a thread
* @see #checkAccess * suspended with {@link #suspend()}. Suspending a thread was
* @see #suspend() * inherently deadlock-prone.
* @deprecated This method exists solely for use with {@link #suspend},
* which has been deprecated because it is deadlock-prone.
* For more information, see * For more information, see
* <a href="{@docRoot}/java.base/java/lang/doc-files/threadPrimitiveDeprecation.html">Why * <a href="{@docRoot}/java.base/java/lang/doc-files/threadPrimitiveDeprecation.html">Why
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>. * are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
*/ */
@Deprecated(since="1.2", forRemoval=true) @Deprecated(since="1.2", forRemoval=true)
public final void resume() { public final void resume() {
checkAccess(); throw new UnsupportedOperationException();
if (isVirtual())
throw new UnsupportedOperationException();
resume0();
} }
/** /**
@ -3035,8 +3011,6 @@ public class Thread implements Runnable {
/* Some private helper methods */ /* Some private helper methods */
private native void setPriority0(int newPriority); private native void setPriority0(int newPriority);
private native void suspend0();
private native void resume0();
private native void interrupt0(); private native void interrupt0();
private static native void clearInterruptEvent(); private static native void clearInterruptEvent();
private native void setNativeName(String name); private native void setNativeName(String name);

View file

@ -160,13 +160,14 @@ operations for which thread.stop and thread.interrupt do not work
properly.</p> properly.</p>
<hr> <hr>
<h2>Why are <code>Thread.suspend</code> and <h2>Why are <code>Thread.suspend</code> and
<code>Thread.resume</code> deprecated?</h2> <code>Thread.resume</code> deprecated and the ability to suspend or
<p><code>Thread.suspend</code> is inherently deadlock-prone. If the resume a thread removed?</h2>
target thread holds a lock on the monitor protecting a critical <p><code>Thread.suspend</code> was inherently deadlock-prone. If the
system resource when it is suspended, no thread can access this target thread held a lock on a monitor protecting a critical
resource until the target thread is resumed. If the thread that system resource when it is suspended, no thread could access the
would resume the target thread attempts to lock this monitor prior resource until the target thread was resumed. If the thread intending
to calling <code>resume</code>, deadlock results. Such deadlocks to resume the target thread attempted to lock the monitor prior
to calling <code>resume</code>, deadlock resulted. Such deadlocks
typically manifest themselves as "frozen" processes.</p> typically manifest themselves as "frozen" processes.</p>
<hr> <hr>
<h2>What should I use instead of <code>Thread.suspend</code> and <h2>What should I use instead of <code>Thread.suspend</code> and

View file

@ -38,8 +38,6 @@
static JNINativeMethod methods[] = { static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread}, {"start0", "()V", (void *)&JVM_StartThread},
{"isAlive0", "()Z", (void *)&JVM_IsThreadAlive}, {"isAlive0", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority}, {"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield0", "()V", (void *)&JVM_Yield}, {"yield0", "()V", (void *)&JVM_Yield},
{"sleep0", "(J)V", (void *)&JVM_Sleep}, {"sleep0", "(J)V", (void *)&JVM_Sleep},

View file

@ -182,8 +182,7 @@ JDWP "Java(tm) Debug Wire Protocol"
"Suspends the execution of the application running in the target " "Suspends the execution of the application running in the target "
"VM. All Java threads currently running will be suspended. " "VM. All Java threads currently running will be suspended. "
"<p>" "<p>"
"Unlike java.lang.Thread.suspend, " "Suspends of both the virtual machine and individual threads are "
"suspends of both the virtual machine and individual threads are "
"counted. Before a thread will run again, it must be resumed through " "counted. Before a thread will run again, it must be resumed through "
"the <a href=\"#JDWP_VirtualMachine_Resume\">VM-level resume</a> command " "the <a href=\"#JDWP_VirtualMachine_Resume\">VM-level resume</a> command "
"or the <a href=\"#JDWP_ThreadReference_Resume\">thread-level resume</a> command " "or the <a href=\"#JDWP_ThreadReference_Resume\">thread-level resume</a> command "
@ -1835,21 +1834,17 @@ JDWP "Java(tm) Debug Wire Protocol"
(Command Suspend=2 (Command Suspend=2
"Suspends the thread. " "Suspends the thread. "
"<p>" "<p>"
"Unlike java.lang.Thread.suspend(), suspends of both " "Suspends of both the virtual machine and individual threads are "
"the virtual machine and individual threads are counted. Before " "counted. Before a thread will run again, it must be resumed the "
"a thread will run again, it must be resumed the same number " "same number of times it has been suspended. "
"of times it has been suspended. "
"<p>" "<p>"
"Suspending single threads with command has the same " "Suspending single threads is inherently deadlock-prone. If the "
"dangers java.lang.Thread.suspend(). If the suspended " "suspended thread holds a monitor needed by another running thread, "
"thread holds a monitor needed by another running thread, "
"deadlock is possible in the target VM (at least until the " "deadlock is possible in the target VM (at least until the "
"suspended thread is resumed again). " "suspended thread is resumed again). "
"<p>" "<p>"
"The suspended thread is guaranteed to remain suspended until " "The suspended thread is guaranteed to remain suspended until "
"resumed through one of the JDI resume methods mentioned above; " "resumed through one of the JDI resume methods mentioned above. "
"the application in the target VM cannot resume the suspended thread "
"through {@link java.lang.Thread#resume}. "
"<p>" "<p>"
"Note that this doesn't change the status of the thread (see the " "Note that this doesn't change the status of the thread (see the "
"<a href=\"#JDWP_ThreadReference_Status\">ThreadStatus</a> command.) " "<a href=\"#JDWP_ThreadReference_Status\">ThreadStatus</a> command.) "

View file

@ -71,23 +71,21 @@ public interface ThreadReference extends ObjectReference {
* {@link #resume} or resumed with other threads through * {@link #resume} or resumed with other threads through
* {@link VirtualMachine#resume}. * {@link VirtualMachine#resume}.
* <p> * <p>
* Unlike {@link java.lang.Thread#suspend}, * Suspends of both the virtual machine and individual threads are
* suspends of both the virtual machine and individual threads are
* counted. Before a thread will run again, it must be resumed * counted. Before a thread will run again, it must be resumed
* (through {@link #resume} or {@link ThreadReference#resume}) * (through {@link #resume} or {@link VirtualMachine#resume})
* the same number of times it has been suspended. * the same number of times it has been suspended.
* <p> * <p>
* Suspending single threads with this method has the same dangers * Suspending single threads with this method is inherently deadlock-prone.
* as {@link java.lang.Thread#suspend()}. If the suspended thread * If the suspended thread holds a monitor needed by another running thread,
* holds a monitor needed by another running thread, deadlock is * deadlock is possible in the target VM (at least until the suspended thread
* possible in the target VM (at least until the suspended thread
* is resumed again). * is resumed again).
* <p> * <p>
* The suspended thread is guaranteed to remain suspended until * The suspended thread is guaranteed to remain suspended until
* resumed through one of the JDI resume methods mentioned above; * resumed through one of the JDI resume methods mentioned above.
* the application in the target VM cannot resume the suspended thread *
* through {@link java.lang.Thread#resume}. * @throws VMCannotBeModifiedException if the VirtualMachine is read-only
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}. * @see VirtualMachine#canBeModified()
*/ */
@SuppressWarnings("javadoc") @SuppressWarnings("javadoc")
void suspend(); void suspend();
@ -101,7 +99,9 @@ public interface ThreadReference extends ObjectReference {
* the thread will continue to execute. * the thread will continue to execute.
* Note: the normal way to resume from an event related suspension is * Note: the normal way to resume from an event related suspension is
* via {@link EventSet#resume}. * via {@link EventSet#resume}.
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}. *
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only
* @see VirtualMachine#canBeModified()
*/ */
void resume(); void resume();

View file

@ -277,13 +277,13 @@ public interface VirtualMachine extends Mirror {
* Suspends the execution of the application running in this * Suspends the execution of the application running in this
* virtual machine. All threads currently running will be suspended. * virtual machine. All threads currently running will be suspended.
* <p> * <p>
* Unlike {@link java.lang.Thread#suspend Thread.suspend()}, * Suspends of both the virtual machine and individual threads are
* suspends of both the virtual machine and individual threads are
* counted. Before a thread will run again, it must be resumed * counted. Before a thread will run again, it must be resumed
* (through {@link #resume} or {@link ThreadReference#resume}) * (through {@link #resume} or {@link ThreadReference#resume})
* the same number of times it has been suspended. * the same number of times it has been suspended.
* *
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}. * @throws VMCannotBeModifiedException if the VirtualMachine is read-only
* @see #canBeModified()
*/ */
void suspend(); void suspend();
@ -292,9 +292,9 @@ public interface VirtualMachine extends Mirror {
* virtual machine. All threads are resumed as documented in * virtual machine. All threads are resumed as documented in
* {@link ThreadReference#resume}. * {@link ThreadReference#resume}.
* *
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}. * @throws VMCannotBeModifiedException if the VirtualMachine is read-only
* * @see #suspend()
* @see #suspend * @see #canBeModified()
*/ */
void resume(); void resume();

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2019, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8205132
* @summary Test Thread.countStackFrames()
* @run testng CountStackFrames
*/
import org.testng.annotations.Test;
public class CountStackFrames {
// current thread
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testCurrentThread() {
Thread.currentThread().countStackFrames();
}
// unstarted thread
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testUnstartedThread() {
Thread thread = new Thread(() -> { });
thread.countStackFrames();
}
// terminated thread
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testTerminatedThread() throws Exception {
Thread thread = new Thread(() -> { });
thread.start();
thread.join();
thread.countStackFrames();
}
}

View file

@ -22,43 +22,61 @@
*/ */
/* @test /* @test
* @bug 8289610 * @bug 8289610 8249627 8205132
* @summary Test Thread.stop throws UnsupportedOperationException * @summary Test that Thread stop/suspend/resume/countStackFrames throw UOE
* @run testng StopTest * @run junit DegradedMethodsThrowUOE
*/ */
import java.time.Duration; import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import org.testng.annotations.Test; import java.util.function.Consumer;
import static org.testng.Assert.*; import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
public class StopTest { class DegradedMethodsThrowUOE {
/** /**
* Test stop on the current thread. * Returns a stream of operations on a Thread that should throw UOE.
*/ */
@Test static Stream<Consumer<Thread>> ops() {
public void testCurrentThread() { return Stream.<Consumer<Thread>>of(
var thread = Thread.currentThread(); Thread::stop,
assertThrows(UnsupportedOperationException.class, thread::stop); Thread::suspend,
Thread::resume,
Thread::countStackFrames
);
} }
/** /**
* Test stop on an unstarted thread. * Test degraded method on current thread.
*/ */
@Test @ParameterizedTest
public void testUnstartedThread() { @MethodSource("ops")
void testCurrentThread(Consumer<Thread> op) {
var thread = Thread.currentThread();
assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
}
/**
* Test degraded method on an unstarted thread.
*/
@ParameterizedTest
@MethodSource("ops")
void testUnstartedThread(Consumer<Thread> op) {
Thread thread = new Thread(() -> { }); Thread thread = new Thread(() -> { });
assertThrows(UnsupportedOperationException.class, thread::stop); assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
assertTrue(thread.getState() == Thread.State.NEW); assertTrue(thread.getState() == Thread.State.NEW);
} }
/** /**
* Test stop on a thread spinning in a loop. * Test degraded method on a thread spinning in a loop.
*/ */
@Test @ParameterizedTest
public void testRunnableThread() throws Exception { @MethodSource("ops")
void testRunnableThread(Consumer<Thread> op) throws Exception {
AtomicBoolean done = new AtomicBoolean(); AtomicBoolean done = new AtomicBoolean();
Thread thread = new Thread(() -> { Thread thread = new Thread(() -> {
while (!done.get()) { while (!done.get()) {
@ -67,7 +85,7 @@ public class StopTest {
}); });
thread.start(); thread.start();
try { try {
assertThrows(UnsupportedOperationException.class, thread::stop); assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
// thread should not terminate // thread should not terminate
boolean terminated = thread.join(Duration.ofMillis(500)); boolean terminated = thread.join(Duration.ofMillis(500));
@ -79,10 +97,11 @@ public class StopTest {
} }
/** /**
* Test stop on a thread that is parked. * Test degraded method on a thread that is parked.
*/ */
@Test @ParameterizedTest
public void testWaitingThread() throws Exception { @MethodSource("ops")
void testWaitingThread(Consumer<Thread> op) throws Exception {
Thread thread = new Thread(LockSupport::park); Thread thread = new Thread(LockSupport::park);
thread.start(); thread.start();
try { try {
@ -90,7 +109,7 @@ public class StopTest {
while ((thread.getState() != Thread.State.WAITING)) { while ((thread.getState() != Thread.State.WAITING)) {
Thread.sleep(10); Thread.sleep(10);
} }
assertThrows(UnsupportedOperationException.class, thread::stop); assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
assertTrue(thread.getState() == Thread.State.WAITING); assertTrue(thread.getState() == Thread.State.WAITING);
} finally { } finally {
LockSupport.unpark(thread); LockSupport.unpark(thread);
@ -99,15 +118,15 @@ public class StopTest {
} }
/** /**
* Test stop on a terminated thread. * Test degraded method on a terminated thread.
*/ */
@Test @ParameterizedTest
public void testTerminatedThread() throws Exception { @MethodSource("ops")
void testTerminatedThread(Consumer<Thread> op) throws Exception {
Thread thread = new Thread(() -> { }); Thread thread = new Thread(() -> { });
thread.start(); thread.start();
thread.join(); thread.join();
assertThrows(UnsupportedOperationException.class, thread::stop); assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
assertTrue(thread.getState() == Thread.State.TERMINATED); assertTrue(thread.getState() == Thread.State.TERMINATED);
} }
} }