8249627: Degrade Thread.suspend and Thread.resume
Reviewed-by: cjplummer, sspitsyn, dholmes, jpai
This commit is contained in:
parent
bc12e9553d
commit
1abf971b93
@ -194,7 +194,6 @@ JVM_RegisterLambdaProxyClassForArchiving
|
||||
JVM_RegisterSignal
|
||||
JVM_ReleaseUTF
|
||||
JVM_ReportFinalizationComplete
|
||||
JVM_ResumeThread
|
||||
JVM_ExtentLocalCache
|
||||
JVM_SetExtentLocalCache
|
||||
JVM_SetArrayElement
|
||||
@ -207,7 +206,6 @@ JVM_Sleep
|
||||
JVM_StartThread
|
||||
JVM_StopThread
|
||||
JVM_SupportsCX8
|
||||
JVM_SuspendThread
|
||||
JVM_TotalMemory
|
||||
JVM_UnloadLibrary
|
||||
JVM_WaitForReferencePendingList
|
||||
|
@ -272,12 +272,6 @@ JVM_StopThread(JNIEnv *env, jobject thread, jobject exception);
|
||||
JNIEXPORT jboolean JNICALL
|
||||
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
|
||||
JVM_SetThreadPriority(JNIEnv *env, jobject thread, jint prio);
|
||||
|
||||
|
@ -3026,29 +3026,6 @@ JVM_ENTRY(jboolean, JVM_IsThreadAlive(JNIEnv* env, jobject jthread))
|
||||
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))
|
||||
ThreadsListHandle tlh(thread);
|
||||
oop java_thread = NULL;
|
||||
|
@ -1263,12 +1263,10 @@ jvmtiEnv *jvmti;
|
||||
have this state flag set instead of <code>JVMTI_THREAD_STATE_SLEEPING</code>.
|
||||
</constant>
|
||||
<constant id="JVMTI_THREAD_STATE_SUSPENDED" num="0x100000">
|
||||
Thread suspended.
|
||||
<code>java.lang.Thread.suspend()</code>
|
||||
or a <jvmti/> suspend function
|
||||
(such as <functionlink id="SuspendThread"></functionlink>)
|
||||
has been called on the thread. If this bit
|
||||
is set, the other bits refer to the thread state before suspension.
|
||||
Thread is suspended by a suspend function
|
||||
(such as <functionlink id="SuspendThread"></functionlink>).
|
||||
If this bit is set, the other bits refer to the thread state before
|
||||
suspension.
|
||||
</constant>
|
||||
<constant id="JVMTI_THREAD_STATE_INTERRUPTED" num="0x200000">
|
||||
Thread has been interrupted.
|
||||
@ -1780,7 +1778,6 @@ jvmtiEnv *jvmti;
|
||||
Any threads currently suspended through
|
||||
a <jvmti/> suspend function (eg.
|
||||
<functionlink id="SuspendThread"></functionlink>)
|
||||
or <code>java.lang.Thread.suspend()</code>
|
||||
will resume execution;
|
||||
all other threads are unaffected.
|
||||
</description>
|
||||
@ -1815,7 +1812,6 @@ jvmtiEnv *jvmti;
|
||||
Any thread suspended through
|
||||
a <jvmti/> suspend function (eg.
|
||||
<functionlink id="SuspendThreadList"></functionlink>)
|
||||
or <code>java.lang.Thread.suspend()</code>
|
||||
will resume execution.
|
||||
</description>
|
||||
<origin>jvmdi</origin>
|
||||
|
@ -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.
|
||||
*
|
||||
* 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
|
||||
* for the requested operation. See, for example, the
|
||||
* {@code suspend} and {@code resume} methods in class
|
||||
* {@code Thread}.
|
||||
* for the requested operation.
|
||||
*
|
||||
* @see java.lang.Thread#resume()
|
||||
* @see java.lang.Thread#suspend()
|
||||
* @see Thread#start()
|
||||
* @since 1.0
|
||||
*/
|
||||
public class IllegalThreadStateException extends IllegalArgumentException {
|
||||
|
@ -171,9 +171,8 @@ import java.lang.module.ModuleFinder;
|
||||
* <tr>
|
||||
* <th scope="row">modifyThread</th>
|
||||
* <td>Modification of threads, e.g., via calls to Thread
|
||||
* {@code interrupt, stop, suspend, resume, setDaemon, setPriority,
|
||||
* setName} and {@code setUncaughtExceptionHandler}
|
||||
* methods</td>
|
||||
* {@code interrupt, setDaemon, setPriority, setName} and
|
||||
* {@code setUncaughtExceptionHandler} methods</td>
|
||||
* <td>This allows an attacker to modify the behaviour of
|
||||
* any thread in the system.</td>
|
||||
* </tr>
|
||||
@ -181,9 +180,7 @@ import java.lang.module.ModuleFinder;
|
||||
* <tr>
|
||||
* <th scope="row">modifyThreadGroup</th>
|
||||
* <td>modification of thread groups, e.g., via calls to ThreadGroup
|
||||
* {@code destroy}, {@code getParent}, {@code resume},
|
||||
* {@code setDaemon}, {@code setMaxPriority}, {@code stop},
|
||||
* and {@code suspend} methods</td>
|
||||
* {@code getParent}, {@code setDaemon}, and {@code setMaxPriority} methods</td>
|
||||
* <td>This allows an attacker to create thread groups and
|
||||
* set their run priority.</td>
|
||||
* </tr>
|
||||
|
@ -494,7 +494,6 @@ public class SecurityManager {
|
||||
* calling thread is not allowed to modify the thread argument.
|
||||
* <p>
|
||||
* This method is invoked for the current security manager by the
|
||||
* {@code stop}, {@code suspend}, {@code resume},
|
||||
* {@code setPriority}, {@code setName}, and
|
||||
* {@code setDaemon} methods of class {@code Thread}.
|
||||
* <p>
|
||||
@ -523,12 +522,9 @@ public class SecurityManager {
|
||||
* permission to modify the thread.
|
||||
* @throws NullPointerException if the thread argument is
|
||||
* {@code null}.
|
||||
* @see java.lang.Thread#resume() resume
|
||||
* @see java.lang.Thread#setDaemon(boolean) setDaemon
|
||||
* @see java.lang.Thread#setName(java.lang.String) setName
|
||||
* @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
|
||||
*/
|
||||
public void checkAccess(Thread t) {
|
||||
@ -547,9 +543,8 @@ public class SecurityManager {
|
||||
* <p>
|
||||
* This method is invoked for the current security manager when a
|
||||
* new child thread or child thread group is created, and by the
|
||||
* {@code setDaemon}, {@code setMaxPriority},
|
||||
* {@code stop}, {@code suspend}, {@code resume}, and
|
||||
* {@code destroy} methods of class {@code ThreadGroup}.
|
||||
* {@code setDaemon} and {@code setMaxPriority} methods of class
|
||||
* {@code ThreadGroup}.
|
||||
* <p>
|
||||
* If the thread group argument is the system thread group (
|
||||
* has a {@code null} parent) then
|
||||
@ -576,12 +571,8 @@ public class SecurityManager {
|
||||
* permission to modify the thread group.
|
||||
* @throws NullPointerException if the thread group argument is
|
||||
* {@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#setMaxPriority(int) setMaxPriority
|
||||
* @see java.lang.ThreadGroup#stop() stop
|
||||
* @see java.lang.ThreadGroup#suspend() suspend
|
||||
* @see #checkPermission(java.security.Permission) checkPermission
|
||||
*/
|
||||
public void checkAccess(ThreadGroup g) {
|
||||
|
@ -1798,65 +1798,41 @@ public class Thread implements Runnable {
|
||||
private native boolean isAlive0();
|
||||
|
||||
/**
|
||||
* Suspends this thread.
|
||||
* <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 {@code UnsupportedOperationException}.
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* this thread.
|
||||
* @throws UnsupportedOperationException if invoked on a virtual thread
|
||||
* @see #checkAccess
|
||||
* @deprecated This method has been deprecated, as it is
|
||||
* inherently deadlock-prone. If the target thread holds a lock on the
|
||||
* monitor protecting a critical system resource when it is suspended, no
|
||||
* thread can access this resource until the target thread is resumed. If
|
||||
* the thread that would resume the target thread attempts to lock this
|
||||
* monitor prior to calling {@code resume}, deadlock results. Such
|
||||
* deadlocks typically manifest themselves as "frozen" processes.
|
||||
* For more information, see
|
||||
* <a href="{@docRoot}/java.base/java/lang/doc-files/threadPrimitiveDeprecation.html">Why
|
||||
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
|
||||
* @throws UnsupportedOperationException always
|
||||
*
|
||||
* @deprecated This method was originally specified to suspend a thread.
|
||||
* It was inherently deadlock-prone. If the target thread held a lock on
|
||||
* a monitor protecting a critical system resource when it was suspended,
|
||||
* no thread could access the resource until the target thread was resumed.
|
||||
* If the thread intending to resume the target thread attempted to lock
|
||||
* the monitor prior to calling {@code resume}, deadlock would result.
|
||||
* Such deadlocks typically manifested themselves as "frozen" processes.
|
||||
* For more information, see
|
||||
* <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)
|
||||
public final void suspend() {
|
||||
checkAccess();
|
||||
if (isVirtual())
|
||||
throw new UnsupportedOperationException();
|
||||
suspend0();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes a suspended thread.
|
||||
* <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 {@code UnsupportedOperationException}.
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify this
|
||||
* thread.
|
||||
* @throws UnsupportedOperationException if invoked on a virtual thread
|
||||
* @see #checkAccess
|
||||
* @see #suspend()
|
||||
* @deprecated This method exists solely for use with {@link #suspend},
|
||||
* which has been deprecated because it is deadlock-prone.
|
||||
* @throws UnsupportedOperationException always
|
||||
*
|
||||
* @deprecated This method was originally specified to resume a thread
|
||||
* suspended with {@link #suspend()}. Suspending a thread was
|
||||
* inherently deadlock-prone.
|
||||
* For more information, see
|
||||
* <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)
|
||||
public final void resume() {
|
||||
checkAccess();
|
||||
if (isVirtual())
|
||||
throw new UnsupportedOperationException();
|
||||
resume0();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3035,8 +3011,6 @@ public class Thread implements Runnable {
|
||||
|
||||
/* Some private helper methods */
|
||||
private native void setPriority0(int newPriority);
|
||||
private native void suspend0();
|
||||
private native void resume0();
|
||||
private native void interrupt0();
|
||||
private static native void clearInterruptEvent();
|
||||
private native void setNativeName(String name);
|
||||
|
@ -160,13 +160,14 @@ operations for which thread.stop and thread.interrupt do not work
|
||||
properly.</p>
|
||||
<hr>
|
||||
<h2>Why are <code>Thread.suspend</code> and
|
||||
<code>Thread.resume</code> deprecated?</h2>
|
||||
<p><code>Thread.suspend</code> is inherently deadlock-prone. If the
|
||||
target thread holds a lock on the monitor protecting a critical
|
||||
system resource when it is suspended, no thread can access this
|
||||
resource until the target thread is resumed. If the thread that
|
||||
would resume the target thread attempts to lock this monitor prior
|
||||
to calling <code>resume</code>, deadlock results. Such deadlocks
|
||||
<code>Thread.resume</code> deprecated and the ability to suspend or
|
||||
resume a thread removed?</h2>
|
||||
<p><code>Thread.suspend</code> was inherently deadlock-prone. If the
|
||||
target thread held a lock on a monitor protecting a critical
|
||||
system resource when it is suspended, no thread could access the
|
||||
resource until the target thread was resumed. If the thread intending
|
||||
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>
|
||||
<hr>
|
||||
<h2>What should I use instead of <code>Thread.suspend</code> and
|
||||
|
@ -38,8 +38,6 @@
|
||||
static JNINativeMethod methods[] = {
|
||||
{"start0", "()V", (void *)&JVM_StartThread},
|
||||
{"isAlive0", "()Z", (void *)&JVM_IsThreadAlive},
|
||||
{"suspend0", "()V", (void *)&JVM_SuspendThread},
|
||||
{"resume0", "()V", (void *)&JVM_ResumeThread},
|
||||
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
|
||||
{"yield0", "()V", (void *)&JVM_Yield},
|
||||
{"sleep0", "(J)V", (void *)&JVM_Sleep},
|
||||
|
@ -182,8 +182,7 @@ JDWP "Java(tm) Debug Wire Protocol"
|
||||
"Suspends the execution of the application running in the target "
|
||||
"VM. All Java threads currently running will be suspended. "
|
||||
"<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 "
|
||||
"the <a href=\"#JDWP_VirtualMachine_Resume\">VM-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
|
||||
"Suspends the thread. "
|
||||
"<p>"
|
||||
"Unlike java.lang.Thread.suspend(), suspends of both "
|
||||
"the virtual machine and individual threads are counted. Before "
|
||||
"a thread will run again, it must be resumed the same number "
|
||||
"of times it has been suspended. "
|
||||
"Suspends of both the virtual machine and individual threads are "
|
||||
"counted. Before a thread will run again, it must be resumed the "
|
||||
"same number of times it has been suspended. "
|
||||
"<p>"
|
||||
"Suspending single threads with command has the same "
|
||||
"dangers java.lang.Thread.suspend(). If the suspended "
|
||||
"thread holds a monitor needed by another running thread, "
|
||||
"Suspending single threads is inherently deadlock-prone. If the "
|
||||
"suspended thread holds a monitor needed by another running thread, "
|
||||
"deadlock is possible in the target VM (at least until the "
|
||||
"suspended thread is resumed again). "
|
||||
"<p>"
|
||||
"The suspended thread is guaranteed to remain suspended until "
|
||||
"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}. "
|
||||
"resumed through one of the JDI resume methods mentioned above. "
|
||||
"<p>"
|
||||
"Note that this doesn't change the status of the thread (see the "
|
||||
"<a href=\"#JDWP_ThreadReference_Status\">ThreadStatus</a> command.) "
|
||||
|
@ -71,23 +71,21 @@ public interface ThreadReference extends ObjectReference {
|
||||
* {@link #resume} or resumed with other threads through
|
||||
* {@link VirtualMachine#resume}.
|
||||
* <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
|
||||
* (through {@link #resume} or {@link ThreadReference#resume})
|
||||
* (through {@link #resume} or {@link VirtualMachine#resume})
|
||||
* the same number of times it has been suspended.
|
||||
* <p>
|
||||
* Suspending single threads with this method has the same dangers
|
||||
* as {@link java.lang.Thread#suspend()}. If the suspended thread
|
||||
* holds a monitor needed by another running thread, deadlock is
|
||||
* possible in the target VM (at least until the suspended thread
|
||||
* Suspending single threads with this method is inherently deadlock-prone.
|
||||
* If the suspended thread holds a monitor needed by another running thread,
|
||||
* deadlock is possible in the target VM (at least until the suspended thread
|
||||
* is resumed again).
|
||||
* <p>
|
||||
* The suspended thread is guaranteed to remain suspended until
|
||||
* 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 - see {@link VirtualMachine#canBeModified()}.
|
||||
* resumed through one of the JDI resume methods mentioned above.
|
||||
*
|
||||
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only
|
||||
* @see VirtualMachine#canBeModified()
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
void suspend();
|
||||
@ -101,7 +99,9 @@ public interface ThreadReference extends ObjectReference {
|
||||
* the thread will continue to execute.
|
||||
* Note: the normal way to resume from an event related suspension is
|
||||
* 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();
|
||||
|
||||
|
@ -277,13 +277,13 @@ public interface VirtualMachine extends Mirror {
|
||||
* Suspends the execution of the application running in this
|
||||
* virtual machine. All threads currently running will be suspended.
|
||||
* <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
|
||||
* (through {@link #resume} or {@link ThreadReference#resume})
|
||||
* 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();
|
||||
|
||||
@ -292,9 +292,9 @@ public interface VirtualMachine extends Mirror {
|
||||
* virtual machine. All threads are resumed as documented in
|
||||
* {@link ThreadReference#resume}.
|
||||
*
|
||||
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}.
|
||||
*
|
||||
* @see #suspend
|
||||
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only
|
||||
* @see #suspend()
|
||||
* @see #canBeModified()
|
||||
*/
|
||||
void resume();
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -22,43 +22,61 @@
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8289610
|
||||
* @summary Test Thread.stop throws UnsupportedOperationException
|
||||
* @run testng StopTest
|
||||
* @bug 8289610 8249627 8205132
|
||||
* @summary Test that Thread stop/suspend/resume/countStackFrames throw UOE
|
||||
* @run junit DegradedMethodsThrowUOE
|
||||
*/
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
import java.util.function.Consumer;
|
||||
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
|
||||
public void testCurrentThread() {
|
||||
var thread = Thread.currentThread();
|
||||
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||
static Stream<Consumer<Thread>> ops() {
|
||||
return Stream.<Consumer<Thread>>of(
|
||||
Thread::stop,
|
||||
Thread::suspend,
|
||||
Thread::resume,
|
||||
Thread::countStackFrames
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test stop on an unstarted thread.
|
||||
* Test degraded method on current thread.
|
||||
*/
|
||||
@Test
|
||||
public void testUnstartedThread() {
|
||||
@ParameterizedTest
|
||||
@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(() -> { });
|
||||
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||
assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
|
||||
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
|
||||
public void testRunnableThread() throws Exception {
|
||||
@ParameterizedTest
|
||||
@MethodSource("ops")
|
||||
void testRunnableThread(Consumer<Thread> op) throws Exception {
|
||||
AtomicBoolean done = new AtomicBoolean();
|
||||
Thread thread = new Thread(() -> {
|
||||
while (!done.get()) {
|
||||
@ -67,7 +85,7 @@ public class StopTest {
|
||||
});
|
||||
thread.start();
|
||||
try {
|
||||
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||
assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
|
||||
|
||||
// thread should not terminate
|
||||
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
|
||||
public void testWaitingThread() throws Exception {
|
||||
@ParameterizedTest
|
||||
@MethodSource("ops")
|
||||
void testWaitingThread(Consumer<Thread> op) throws Exception {
|
||||
Thread thread = new Thread(LockSupport::park);
|
||||
thread.start();
|
||||
try {
|
||||
@ -90,7 +109,7 @@ public class StopTest {
|
||||
while ((thread.getState() != Thread.State.WAITING)) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||
assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
|
||||
assertTrue(thread.getState() == Thread.State.WAITING);
|
||||
} finally {
|
||||
LockSupport.unpark(thread);
|
||||
@ -99,15 +118,15 @@ public class StopTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test stop on a terminated thread.
|
||||
* Test degraded method on a terminated thread.
|
||||
*/
|
||||
@Test
|
||||
public void testTerminatedThread() throws Exception {
|
||||
@ParameterizedTest
|
||||
@MethodSource("ops")
|
||||
void testTerminatedThread(Consumer<Thread> op) throws Exception {
|
||||
Thread thread = new Thread(() -> { });
|
||||
thread.start();
|
||||
thread.join();
|
||||
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||
assertThrows(UnsupportedOperationException.class, () -> op.accept(thread));
|
||||
assertTrue(thread.getState() == Thread.State.TERMINATED);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user