mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8249627: Degrade Thread.suspend and Thread.resume
Reviewed-by: cjplummer, sspitsyn, dholmes, jpai
This commit is contained in:
parent
bc12e9553d
commit
1abf971b93
15 changed files with 113 additions and 232 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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.) "
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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
|
/* @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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue