8289610: Degrade Thread.stop
Reviewed-by: rriggs, cjplummer, jpai, mchung, prr, mullan
This commit is contained in:
parent
05c8cabdad
commit
acd5bcfc88
@ -190,17 +190,9 @@ public class FilterOutputStream extends OutputStream {
|
|||||||
try {
|
try {
|
||||||
out.close();
|
out.close();
|
||||||
} catch (Throwable closeException) {
|
} catch (Throwable closeException) {
|
||||||
// evaluate possible precedence of flushException over closeException
|
|
||||||
if ((flushException instanceof ThreadDeath) &&
|
|
||||||
!(closeException instanceof ThreadDeath)) {
|
|
||||||
flushException.addSuppressed(closeException);
|
|
||||||
throw (ThreadDeath) flushException;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flushException != closeException) {
|
if (flushException != closeException) {
|
||||||
closeException.addSuppressed(flushException);
|
closeException.addSuppressed(flushException);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw closeException;
|
throw closeException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2430,8 +2430,6 @@ public class ObjectInputStream
|
|||||||
// Read fields of the current descriptor into a new FieldValues and discard
|
// Read fields of the current descriptor into a new FieldValues and discard
|
||||||
new FieldValues(slotDesc, true);
|
new FieldValues(slotDesc, true);
|
||||||
} else if (slotDesc.hasReadObjectMethod()) {
|
} else if (slotDesc.hasReadObjectMethod()) {
|
||||||
ThreadDeath t = null;
|
|
||||||
boolean reset = false;
|
|
||||||
SerialCallbackContext oldContext = curContext;
|
SerialCallbackContext oldContext = curContext;
|
||||||
if (oldContext != null)
|
if (oldContext != null)
|
||||||
oldContext.check();
|
oldContext.check();
|
||||||
@ -2450,19 +2448,10 @@ public class ObjectInputStream
|
|||||||
*/
|
*/
|
||||||
handles.markException(passHandle, ex);
|
handles.markException(passHandle, ex);
|
||||||
} finally {
|
} finally {
|
||||||
do {
|
curContext.setUsed();
|
||||||
try {
|
if (oldContext!= null)
|
||||||
curContext.setUsed();
|
oldContext.check();
|
||||||
if (oldContext!= null)
|
curContext = oldContext;
|
||||||
oldContext.check();
|
|
||||||
curContext = oldContext;
|
|
||||||
reset = true;
|
|
||||||
} catch (ThreadDeath x) {
|
|
||||||
t = x; // defer until reset is true
|
|
||||||
}
|
|
||||||
} while (!reset);
|
|
||||||
if (t != null)
|
|
||||||
throw t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1995, 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
|
||||||
@ -29,9 +29,6 @@ package java.lang;
|
|||||||
* An {@code Error} is a subclass of {@code Throwable}
|
* An {@code Error} is a subclass of {@code Throwable}
|
||||||
* that indicates serious problems that a reasonable application
|
* that indicates serious problems that a reasonable application
|
||||||
* should not try to catch. Most such errors are abnormal conditions.
|
* should not try to catch. Most such errors are abnormal conditions.
|
||||||
* The {@code ThreadDeath} error, though a "normal" condition,
|
|
||||||
* is also a subclass of {@code Error} because most applications
|
|
||||||
* should not try to catch it.
|
|
||||||
* <p>
|
* <p>
|
||||||
* A method is not required to declare in its {@code throws}
|
* A method is not required to declare in its {@code throws}
|
||||||
* clause any subclasses of {@code Error} that might be thrown
|
* clause any subclasses of {@code Error} that might be thrown
|
||||||
@ -42,7 +39,6 @@ package java.lang;
|
|||||||
* exceptions for the purposes of compile-time checking of exceptions.
|
* exceptions for the purposes of compile-time checking of exceptions.
|
||||||
*
|
*
|
||||||
* @author Frank Yellin
|
* @author Frank Yellin
|
||||||
* @see java.lang.ThreadDeath
|
|
||||||
* @jls 11.2 Compile-Time Checking of Exceptions
|
* @jls 11.2 Compile-Time Checking of Exceptions
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 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
|
||||||
@ -179,16 +179,6 @@ import java.lang.module.ModuleFinder;
|
|||||||
* </tr>
|
* </tr>
|
||||||
*
|
*
|
||||||
* <tr>
|
* <tr>
|
||||||
* <th scope="row">stopThread</th>
|
|
||||||
* <td>Stopping of threads via calls to the Thread {@code stop}
|
|
||||||
* method</td>
|
|
||||||
* <td>This allows code to stop any thread in the system provided that it is
|
|
||||||
* already granted permission to access that thread.
|
|
||||||
* This poses as a threat, because that code may corrupt the system by
|
|
||||||
* killing existing threads.</td>
|
|
||||||
* </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 destroy}, {@code getParent}, {@code resume},
|
||||||
|
@ -129,9 +129,7 @@ class Shutdown {
|
|||||||
}
|
}
|
||||||
if (hook != null) hook.run();
|
if (hook != null) hook.run();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
if (t instanceof ThreadDeath td) {
|
// ignore
|
||||||
throw td;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1629,59 +1629,19 @@ public class Thread implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forces the thread to stop executing.
|
* Throws {@code UnsupportedOperationException}.
|
||||||
* <p>
|
|
||||||
* If there is a security manager installed, its {@code checkAccess}
|
|
||||||
* method is called with {@code this}
|
|
||||||
* as its argument. This may result in a
|
|
||||||
* {@code SecurityException} being raised (in the current thread).
|
|
||||||
* <p>
|
|
||||||
* If this thread is different from the current thread (that is, the current
|
|
||||||
* thread is trying to stop a thread other than itself), the
|
|
||||||
* security manager's {@code checkPermission} method (with a
|
|
||||||
* {@code RuntimePermission("stopThread")} argument) is called in
|
|
||||||
* addition.
|
|
||||||
* Again, this may result in throwing a
|
|
||||||
* {@code SecurityException} (in the current thread).
|
|
||||||
* <p>
|
|
||||||
* The thread represented by this thread is forced to stop whatever
|
|
||||||
* it is doing abnormally and to throw a newly created
|
|
||||||
* {@code ThreadDeath} object as an exception.
|
|
||||||
* <p>
|
|
||||||
* It is permitted to stop a thread that has not yet been started.
|
|
||||||
* If the thread is eventually started, it immediately terminates.
|
|
||||||
* <p>
|
|
||||||
* An application should not normally try to catch
|
|
||||||
* {@code ThreadDeath} unless it must do some extraordinary
|
|
||||||
* cleanup operation (note that the throwing of
|
|
||||||
* {@code ThreadDeath} causes {@code finally} clauses of
|
|
||||||
* {@code try} statements to be executed before the thread
|
|
||||||
* officially terminates). If a {@code catch} clause catches a
|
|
||||||
* {@code ThreadDeath} object, it is important to rethrow the
|
|
||||||
* object so that the thread actually terminates.
|
|
||||||
* <p>
|
|
||||||
* The top-level error handler that reacts to otherwise uncaught
|
|
||||||
* exceptions does not print out a message or otherwise notify the
|
|
||||||
* application if the uncaught exception is an instance of
|
|
||||||
* {@code ThreadDeath}.
|
|
||||||
*
|
*
|
||||||
* @throws SecurityException if the current thread cannot
|
* @throws UnsupportedOperationException always
|
||||||
* modify this thread.
|
*
|
||||||
* @throws UnsupportedOperationException if invoked on a virtual thread
|
* @deprecated This method was originally specified to "stop" a victim
|
||||||
* @see #interrupt()
|
* thread by causing the victim thread to throw a {@link ThreadDeath}.
|
||||||
* @see #checkAccess()
|
* It was inherently unsafe. Stopping a thread caused it to unlock
|
||||||
* @see ThreadDeath
|
* all of the monitors that it had locked (as a natural consequence
|
||||||
* @see ThreadGroup#uncaughtException(Thread,Throwable)
|
* of the {@code ThreadDeath} exception propagating up the stack). If
|
||||||
* @see SecurityManager#checkAccess(Thread)
|
|
||||||
* @see SecurityManager#checkPermission
|
|
||||||
* @deprecated This method is inherently unsafe. Stopping a thread with
|
|
||||||
* Thread.stop causes it to unlock all of the monitors that it
|
|
||||||
* has locked (as a natural consequence of the unchecked
|
|
||||||
* {@code ThreadDeath} exception propagating up the stack). If
|
|
||||||
* any of the objects previously protected by these monitors were in
|
* any of the objects previously protected by these monitors were in
|
||||||
* an inconsistent state, the damaged objects become visible to
|
* an inconsistent state, the damaged objects became visible to
|
||||||
* other threads, potentially resulting in arbitrary behavior. Many
|
* other threads, potentially resulting in arbitrary behavior.
|
||||||
* uses of {@code stop} should be replaced by code that simply
|
* Usages of {@code stop} should be replaced by code that simply
|
||||||
* modifies some variable to indicate that the target thread should
|
* modifies some variable to indicate that the target thread should
|
||||||
* stop running. The target thread should check this variable
|
* stop running. The target thread should check this variable
|
||||||
* regularly, and return from its run method in an orderly fashion
|
* regularly, and return from its run method in an orderly fashion
|
||||||
@ -1695,26 +1655,7 @@ public class Thread implements Runnable {
|
|||||||
*/
|
*/
|
||||||
@Deprecated(since="1.2", forRemoval=true)
|
@Deprecated(since="1.2", forRemoval=true)
|
||||||
public final void stop() {
|
public final void stop() {
|
||||||
@SuppressWarnings("removal")
|
throw new UnsupportedOperationException();
|
||||||
SecurityManager security = System.getSecurityManager();
|
|
||||||
if (security != null) {
|
|
||||||
checkAccess();
|
|
||||||
if (this != Thread.currentThread()) {
|
|
||||||
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVirtual())
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
|
||||||
// A zero status value corresponds to "NEW", it can't change to
|
|
||||||
// not-NEW because we hold the lock.
|
|
||||||
if (holder.threadStatus != 0) {
|
|
||||||
resume(); // Wake up thread if it was suspended; no-op otherwise
|
|
||||||
}
|
|
||||||
|
|
||||||
// The VM can handle all thread states
|
|
||||||
stop0(new ThreadDeath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3094,7 +3035,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 stop0(Object o);
|
|
||||||
private native void suspend0();
|
private native void suspend0();
|
||||||
private native void resume0();
|
private native void resume0();
|
||||||
private native void interrupt0();
|
private native void interrupt0();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1995, 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
|
||||||
@ -26,26 +26,19 @@
|
|||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of {@code ThreadDeath} is thrown in the victim thread
|
* An instance of {@code ThreadDeath} was originally specified to be thrown
|
||||||
* when the (deprecated) {@link Thread#stop()} method is invoked.
|
* by a victim thread when "stopped" with {@link Thread#stop()}.
|
||||||
*
|
*
|
||||||
* <p>An application should catch instances of this class only if it
|
* @deprecated {@link Thread#stop()} was originally specified to "stop" a victim
|
||||||
* must clean up after being terminated asynchronously. If
|
* thread by causing the victim thread to throw a {@code ThreadDeath}. It
|
||||||
* {@code ThreadDeath} is caught by a method, it is important that it
|
* was inherently unsafe and deprecated in an early JDK release. The ability
|
||||||
* be rethrown so that the thread actually dies.
|
* to "stop" a thread with {@code Thread.stop} has been removed and the
|
||||||
*
|
* {@code Thread.stop} method changed to throw an exception. Consequently,
|
||||||
* <p>The {@linkplain ThreadGroup#uncaughtException top-level error
|
* {@code ThreadDeath} is also deprecated, for removal.
|
||||||
* handler} does not print out a message if {@code ThreadDeath} is
|
|
||||||
* never caught.
|
|
||||||
*
|
|
||||||
* <p>The class {@code ThreadDeath} is specifically a subclass of
|
|
||||||
* {@code Error} rather than {@code Exception}, even though it is a
|
|
||||||
* "normal occurrence", because many applications catch all
|
|
||||||
* occurrences of {@code Exception} and then discard the exception.
|
|
||||||
*
|
*
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since="20", forRemoval=true)
|
||||||
public class ThreadDeath extends Error {
|
public class ThreadDeath extends Error {
|
||||||
@java.io.Serial
|
@java.io.Serial
|
||||||
private static final long serialVersionUID = -4417128565033088268L;
|
private static final long serialVersionUID = -4417128565033088268L;
|
||||||
|
@ -674,12 +674,9 @@ public class ThreadGroup implements Thread.UncaughtExceptionHandler {
|
|||||||
* uncaught exception handler} installed, and if so, its
|
* uncaught exception handler} installed, and if so, its
|
||||||
* {@code uncaughtException} method is called with the same
|
* {@code uncaughtException} method is called with the same
|
||||||
* two arguments.
|
* two arguments.
|
||||||
* <li>Otherwise, this method determines if the {@code Throwable}
|
* <li>Otherwise, a message containing the thread's name, as returned
|
||||||
* argument is an instance of {@link ThreadDeath}. If so, nothing
|
* from the thread's {@link Thread#getName getName} method, and a
|
||||||
* special is done. Otherwise, a message containing the
|
* stack backtrace, using the {@code Throwable}'s {@link
|
||||||
* thread's name, as returned from the thread's {@link
|
|
||||||
* Thread#getName getName} method, and a stack backtrace,
|
|
||||||
* using the {@code Throwable}'s {@link
|
|
||||||
* Throwable#printStackTrace() printStackTrace} method, is
|
* Throwable#printStackTrace() printStackTrace} method, is
|
||||||
* printed to the {@linkplain System#err standard error stream}.
|
* printed to the {@linkplain System#err standard error stream}.
|
||||||
* </ul>
|
* </ul>
|
||||||
@ -699,9 +696,8 @@ public class ThreadGroup implements Thread.UncaughtExceptionHandler {
|
|||||||
Thread.getDefaultUncaughtExceptionHandler();
|
Thread.getDefaultUncaughtExceptionHandler();
|
||||||
if (ueh != null) {
|
if (ueh != null) {
|
||||||
ueh.uncaughtException(t, e);
|
ueh.uncaughtException(t, e);
|
||||||
} else if (!(e instanceof ThreadDeath)) {
|
} else {
|
||||||
System.err.print("Exception in thread \""
|
System.err.print("Exception in thread \"" + t.getName() + "\" ");
|
||||||
+ t.getName() + "\" ");
|
|
||||||
e.printStackTrace(System.err);
|
e.printStackTrace(System.err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<!--
|
<!--
|
||||||
Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
|
Copyright (c) 2005, 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
|
||||||
@ -31,31 +31,32 @@
|
|||||||
<body>
|
<body>
|
||||||
<h1>Java Thread Primitive Deprecation</h1>
|
<h1>Java Thread Primitive Deprecation</h1>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>Why is <code>Thread.stop</code> deprecated?</h2>
|
<h2>Why is <code>Thread.stop</code> deprecated and the ability to
|
||||||
<p>Because it is inherently unsafe. Stopping a thread causes it to
|
stop a thread removed?</h2>
|
||||||
unlock all the monitors that it has locked. (The monitors are
|
<p>Because it was inherently unsafe. Stopping a thread caused it to
|
||||||
unlocked as the <code>ThreadDeath</code> exception propagates up
|
unlock all the monitors that it had locked. (The monitors were
|
||||||
|
unlocked as the <code>ThreadDeath</code> exception propagated up
|
||||||
the stack.) If any of the objects previously protected by these
|
the stack.) If any of the objects previously protected by these
|
||||||
monitors were in an inconsistent state, other threads may now view
|
monitors were in an inconsistent state, other threads may have viewed
|
||||||
these objects in an inconsistent state. Such objects are said to be
|
these objects in an inconsistent state. Such objects are said to be
|
||||||
<i>damaged</i>. When threads operate on damaged objects, arbitrary
|
<i>damaged</i>. When threads operate on damaged objects, arbitrary
|
||||||
behavior can result. This behavior may be subtle and difficult to
|
behavior can result. This behavior may be subtle and difficult to
|
||||||
detect, or it may be pronounced. Unlike other unchecked exceptions,
|
detect, or it may be pronounced. Unlike other unchecked exceptions,
|
||||||
<code>ThreadDeath</code> kills threads silently; thus, the user has
|
<code>ThreadDeath</code> killed threads silently; thus, the user had
|
||||||
no warning that his program may be corrupted. The corruption can
|
no warning that their program may be corrupted. The corruption could
|
||||||
manifest itself at any time after the actual damage occurs, even
|
manifest itself at any time after the actual damage occurs, even
|
||||||
hours or days in the future.</p>
|
hours or days in the future.</p>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>Couldn't I just catch the <code>ThreadDeath</code> exception
|
<h2>Couldn't I have just caught <code>ThreadDeath</code> and fixed
|
||||||
and fix the damaged object?</h2>
|
the damaged object?</h2>
|
||||||
<p>In theory, perhaps, but it would <em>vastly</em> complicate the
|
<p>In theory, perhaps, but it would <em>vastly</em> complicate the
|
||||||
task of writing correct multithreaded code. The task would be
|
task of writing correct multithreaded code. The task would be
|
||||||
nearly insurmountable for two reasons:</p>
|
nearly insurmountable for two reasons:</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li>A thread can throw a <code>ThreadDeath</code> exception
|
<li>A thread could throw a <code>ThreadDeath</code> exception
|
||||||
<i>almost anywhere</i>. All synchronized methods and blocks would
|
<i>almost anywhere</i>. All synchronized methods and blocks would
|
||||||
have to be studied in great detail, with this in mind.</li>
|
have to be studied in great detail, with this in mind.</li>
|
||||||
<li>A thread can throw a second <code>ThreadDeath</code> exception
|
<li>A thread could throw a second <code>ThreadDeath</code> exception
|
||||||
while cleaning up from the first (in the <code>catch</code> or
|
while cleaning up from the first (in the <code>catch</code> or
|
||||||
<code>finally</code> clause). Cleanup would have to be repeated till
|
<code>finally</code> clause). Cleanup would have to be repeated till
|
||||||
it succeeded. The code to ensure this would be quite complex.</li>
|
it succeeded. The code to ensure this would be quite complex.</li>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2017, 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
|
||||||
@ -178,7 +178,7 @@ final class BootstrapMethodInvoker {
|
|||||||
catch (Error e) {
|
catch (Error e) {
|
||||||
// Pass through an Error, including BootstrapMethodError, any other
|
// Pass through an Error, including BootstrapMethodError, any other
|
||||||
// form of linkage error, such as IllegalAccessError if the bootstrap
|
// form of linkage error, such as IllegalAccessError if the bootstrap
|
||||||
// method is inaccessible, or say ThreadDeath/OutOfMemoryError
|
// method is inaccessible, or say OutOfMemoryError
|
||||||
// See the "Linking Exceptions" section for the invokedynamic
|
// See the "Linking Exceptions" section for the invokedynamic
|
||||||
// instruction in JVMS 6.5.
|
// instruction in JVMS 6.5.
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -335,7 +335,7 @@ abstract sealed class CallSite permits ConstantCallSite, MutableCallSite, Volati
|
|||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
// Pass through an Error, including BootstrapMethodError, any other
|
// Pass through an Error, including BootstrapMethodError, any other
|
||||||
// form of linkage error, such as IllegalAccessError if the bootstrap
|
// form of linkage error, such as IllegalAccessError if the bootstrap
|
||||||
// method is inaccessible, or say ThreadDeath/OutOfMemoryError
|
// method is inaccessible, or say OutOfMemoryError
|
||||||
// See the "Linking Exceptions" section for the invokedynamic
|
// See the "Linking Exceptions" section for the invokedynamic
|
||||||
// instruction in JVMS 6.5.
|
// instruction in JVMS 6.5.
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -124,10 +124,6 @@ public final class SecurityConstants {
|
|||||||
public static final RuntimePermission GET_CLASSLOADER_PERMISSION =
|
public static final RuntimePermission GET_CLASSLOADER_PERMISSION =
|
||||||
new RuntimePermission("getClassLoader");
|
new RuntimePermission("getClassLoader");
|
||||||
|
|
||||||
// java.lang.Thread
|
|
||||||
public static final RuntimePermission STOP_THREAD_PERMISSION =
|
|
||||||
new RuntimePermission("stopThread");
|
|
||||||
|
|
||||||
// java.lang.Thread
|
// java.lang.Thread
|
||||||
public static final RuntimePermission GET_STACK_TRACE_PERMISSION =
|
public static final RuntimePermission GET_STACK_TRACE_PERMISSION =
|
||||||
new RuntimePermission("getStackTrace");
|
new RuntimePermission("getStackTrace");
|
||||||
|
@ -37,7 +37,6 @@
|
|||||||
|
|
||||||
static JNINativeMethod methods[] = {
|
static JNINativeMethod methods[] = {
|
||||||
{"start0", "()V", (void *)&JVM_StartThread},
|
{"start0", "()V", (void *)&JVM_StartThread},
|
||||||
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
|
|
||||||
{"isAlive0", "()Z", (void *)&JVM_IsThreadAlive},
|
{"isAlive0", "()Z", (void *)&JVM_IsThreadAlive},
|
||||||
{"suspend0", "()V", (void *)&JVM_SuspendThread},
|
{"suspend0", "()V", (void *)&JVM_SuspendThread},
|
||||||
{"resume0", "()V", (void *)&JVM_ResumeThread},
|
{"resume0", "()V", (void *)&JVM_ResumeThread},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2011, 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
|
||||||
@ -167,11 +167,6 @@ public abstract class LWToolkit extends SunToolkit implements Runnable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ThreadDeath td) {
|
|
||||||
//XXX: if there isn't native code on the stack, the VM just
|
|
||||||
//kills the thread right away. Do we expect to catch it
|
|
||||||
//nevertheless?
|
|
||||||
break;
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
// TODO: log
|
// TODO: log
|
||||||
System.err.println("Exception on the toolkit thread");
|
System.err.println("Exception on the toolkit thread");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 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
|
||||||
@ -202,10 +202,6 @@ class EventDispatchThread extends Thread {
|
|||||||
|
|
||||||
eq.dispatchEvent(event);
|
eq.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
catch (ThreadDeath death) {
|
|
||||||
doDispatch = false;
|
|
||||||
throw death;
|
|
||||||
}
|
|
||||||
catch (InterruptedException interruptedException) {
|
catch (InterruptedException interruptedException) {
|
||||||
doDispatch = false; // AppContext.dispose() interrupts all
|
doDispatch = false; // AppContext.dispose() interrupts all
|
||||||
// Threads in the AppContext
|
// Threads in the AppContext
|
||||||
|
@ -198,13 +198,6 @@ class TimerQueue implements Runnable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (ThreadDeath td) {
|
|
||||||
// Mark all the timers we contain as not being queued.
|
|
||||||
for (DelayedTimer delayedTimer : queue) {
|
|
||||||
delayedTimer.getTimer().cancelEvent();
|
|
||||||
}
|
|
||||||
throw td;
|
|
||||||
} finally {
|
} finally {
|
||||||
running = false;
|
running = false;
|
||||||
runningLock.unlock();
|
runningLock.unlock();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1998, 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
|
||||||
@ -2386,11 +2386,6 @@ class Parser implements DTDConstants {
|
|||||||
errorContext();
|
errorContext();
|
||||||
error("exception", e.getClass().getName(), e.getMessage());
|
error("exception", e.getClass().getName(), e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (ThreadDeath e) {
|
|
||||||
errorContext();
|
|
||||||
error("terminated");
|
|
||||||
e.printStackTrace();
|
|
||||||
throw e;
|
|
||||||
} finally {
|
} finally {
|
||||||
for (; stack != null ; stack = stack.next) {
|
for (; stack != null ; stack = stack.next) {
|
||||||
handleEndTag(stack.tag);
|
handleEndTag(stack.tag);
|
||||||
|
@ -727,9 +727,6 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatchEvent(ev);
|
dispatchEvent(ev);
|
||||||
} catch (ThreadDeath td) {
|
|
||||||
XBaseWindow.ungrabInput();
|
|
||||||
return;
|
|
||||||
} catch (Throwable thr) {
|
} catch (Throwable thr) {
|
||||||
XBaseWindow.ungrabInput();
|
XBaseWindow.ungrabInput();
|
||||||
processException(thr);
|
processException(thr);
|
||||||
@ -1994,8 +1991,6 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
task.run();
|
task.run();
|
||||||
} catch (ThreadDeath td) {
|
|
||||||
throw td;
|
|
||||||
} catch (Throwable thr) {
|
} catch (Throwable thr) {
|
||||||
processException(thr);
|
processException(thr);
|
||||||
}
|
}
|
||||||
|
@ -2666,8 +2666,6 @@ public class LogManager {
|
|||||||
for (Runnable c : listeners.values().toArray(new Runnable[0])) {
|
for (Runnable c : listeners.values().toArray(new Runnable[0])) {
|
||||||
try {
|
try {
|
||||||
c.run();
|
c.run();
|
||||||
} catch (ThreadDeath death) {
|
|
||||||
throw death;
|
|
||||||
} catch (Error | RuntimeException x) {
|
} catch (Error | RuntimeException x) {
|
||||||
if (t == null) t = x;
|
if (t == null) t = x;
|
||||||
else t.addSuppressed(x);
|
else t.addSuppressed(x);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
@ -1030,7 +1030,7 @@ public class DOMSerializerImpl implements LSSerializer, DOMConfiguration {
|
|||||||
try {
|
try {
|
||||||
return doc.getXmlVersion();
|
return doc.getXmlVersion();
|
||||||
} // The VM ran out of memory or there was some other serious problem. Re-throw.
|
} // The VM ran out of memory or there was some other serious problem. Re-throw.
|
||||||
catch (VirtualMachineError | ThreadDeath vme) {
|
catch (VirtualMachineError vme) {
|
||||||
throw vme;
|
throw vme;
|
||||||
} // Ignore all other exceptions and errors
|
} // Ignore all other exceptions and errors
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
@ -1046,7 +1046,7 @@ public class DOMSerializerImpl implements LSSerializer, DOMConfiguration {
|
|||||||
try {
|
try {
|
||||||
return doc.getInputEncoding();
|
return doc.getInputEncoding();
|
||||||
} // The VM ran out of memory or there was some other serious problem. Re-throw.
|
} // The VM ran out of memory or there was some other serious problem. Re-throw.
|
||||||
catch (VirtualMachineError | ThreadDeath vme) {
|
catch (VirtualMachineError vme) {
|
||||||
throw vme;
|
throw vme;
|
||||||
} // Ignore all other exceptions and errors
|
} // Ignore all other exceptions and errors
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
@ -1062,7 +1062,7 @@ public class DOMSerializerImpl implements LSSerializer, DOMConfiguration {
|
|||||||
try {
|
try {
|
||||||
return doc.getXmlEncoding();
|
return doc.getXmlEncoding();
|
||||||
} // The VM ran out of memory or there was some other serious problem. Re-throw.
|
} // The VM ran out of memory or there was some other serious problem. Re-throw.
|
||||||
catch (VirtualMachineError | ThreadDeath vme) {
|
catch (VirtualMachineError vme) {
|
||||||
throw vme;
|
throw vme;
|
||||||
} // Ignore all other exceptions and errors
|
} // Ignore all other exceptions and errors
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 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
|
||||||
@ -261,10 +261,6 @@ public abstract class AttachProvider {
|
|||||||
try {
|
try {
|
||||||
providers.add(i.next());
|
providers.add(i.next());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
if (t instanceof ThreadDeath) {
|
|
||||||
ThreadDeath td = (ThreadDeath)t;
|
|
||||||
throw td;
|
|
||||||
}
|
|
||||||
// Log errors and exceptions since we cannot return them
|
// Log errors and exceptions since we cannot return them
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 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
|
||||||
@ -75,9 +75,6 @@ public abstract class HotSpotAttachProvider extends AttachProvider {
|
|||||||
if (t instanceof ExceptionInInitializerError) {
|
if (t instanceof ExceptionInInitializerError) {
|
||||||
t = t.getCause();
|
t = t.getCause();
|
||||||
}
|
}
|
||||||
if (t instanceof ThreadDeath) {
|
|
||||||
throw (ThreadDeath)t;
|
|
||||||
}
|
|
||||||
if (t instanceof SecurityException) {
|
if (t instanceof SecurityException) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -101,9 +98,7 @@ public abstract class HotSpotAttachProvider extends AttachProvider {
|
|||||||
result.add(new HotSpotVirtualMachineDescriptor(this, pid, name));
|
result.add(new HotSpotVirtualMachineDescriptor(this, pid, name));
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
if (t instanceof ThreadDeath) {
|
// ignore
|
||||||
throw (ThreadDeath)t;
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
if (mvm != null) {
|
if (mvm != null) {
|
||||||
mvm.detach();
|
mvm.detach();
|
||||||
@ -138,10 +133,6 @@ public abstract class HotSpotAttachProvider extends AttachProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
if (t instanceof ThreadDeath) {
|
|
||||||
ThreadDeath td = (ThreadDeath)t;
|
|
||||||
throw td;
|
|
||||||
}
|
|
||||||
// we do not know what this id is
|
// we do not know what this id is
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1998, 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
|
||||||
@ -99,12 +99,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManagerService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
connector = connectors.next();
|
connector = connectors.next();
|
||||||
} catch (ThreadDeath x) {
|
} catch (Exception | Error x) {
|
||||||
throw x;
|
|
||||||
} catch (Exception x) {
|
|
||||||
System.err.println(x);
|
|
||||||
continue;
|
|
||||||
} catch (Error x) {
|
|
||||||
System.err.println(x);
|
System.err.println(x);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -128,12 +123,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManagerService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
transportService = transportServices.next();
|
transportService = transportServices.next();
|
||||||
} catch (ThreadDeath x) {
|
} catch (Exception | Error x) {
|
||||||
throw x;
|
|
||||||
} catch (Exception x) {
|
|
||||||
System.err.println(x);
|
|
||||||
continue;
|
|
||||||
} catch (Error x) {
|
|
||||||
System.err.println(x);
|
System.err.println(x);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,7 @@ public class LocalExecutionControl extends DirectExecutionControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("removal")
|
||||||
protected String invoke(Method doitMethod) throws Exception {
|
protected String invoke(Method doitMethod) throws Exception {
|
||||||
if (allStop == null) {
|
if (allStop == null) {
|
||||||
super.load(new ClassBytecodes[]{ genCancelClass() });
|
super.load(new ClassBytecodes[]{ genCancelClass() });
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 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
|
||||||
@ -160,7 +160,7 @@ public class RemoteExecutionControl extends DirectExecutionControl implements Ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("serial") // serialVersionUID intentionally omitted
|
@SuppressWarnings({"serial", "removal"}) // serialVersionUID intentionally omitted
|
||||||
private class StopExecutionException extends ThreadDeath {
|
private class StopExecutionException extends ThreadDeath {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
113
test/jdk/java/lang/Thread/StopTest.java
Normal file
113
test/jdk/java/lang/Thread/StopTest.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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
|
||||||
|
* 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 8289610
|
||||||
|
* @summary Test Thread.stop throws UnsupportedOperationException
|
||||||
|
* @run testng StopTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.*;
|
||||||
|
|
||||||
|
public class StopTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test stop on the current thread.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCurrentThread() {
|
||||||
|
var thread = Thread.currentThread();
|
||||||
|
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test stop on an unstarted thread.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testUnstartedThread() {
|
||||||
|
Thread thread = new Thread(() -> { });
|
||||||
|
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||||
|
assertTrue(thread.getState() == Thread.State.NEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test stop on a thread spinning in a loop.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRunnableThread() throws Exception {
|
||||||
|
AtomicBoolean done = new AtomicBoolean();
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
while (!done.get()) {
|
||||||
|
Thread.onSpinWait();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
try {
|
||||||
|
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||||
|
|
||||||
|
// thread should not terminate
|
||||||
|
boolean terminated = thread.join(Duration.ofMillis(500));
|
||||||
|
assertFalse(terminated);
|
||||||
|
} finally {
|
||||||
|
done.set(true);
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test stop on a thread that is parked.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testWaitingThread() throws Exception {
|
||||||
|
Thread thread = new Thread(LockSupport::park);
|
||||||
|
thread.start();
|
||||||
|
try {
|
||||||
|
// wait for thread to park
|
||||||
|
while ((thread.getState() != Thread.State.WAITING)) {
|
||||||
|
Thread.sleep(10);
|
||||||
|
}
|
||||||
|
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||||
|
assertTrue(thread.getState() == Thread.State.WAITING);
|
||||||
|
} finally {
|
||||||
|
LockSupport.unpark(thread);
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test stop on a terminated thread.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testTerminatedThread() throws Exception {
|
||||||
|
Thread thread = new Thread(() -> { });
|
||||||
|
thread.start();
|
||||||
|
thread.join();
|
||||||
|
assertThrows(UnsupportedOperationException.class, thread::stop);
|
||||||
|
assertTrue(thread.getState() == Thread.State.TERMINATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user