8289610: Degrade Thread.stop

Reviewed-by: rriggs, cjplummer, jpai, mchung, prr, mullan
This commit is contained in:
Alan Bateman 2022-09-23 07:55:29 +00:00
parent 05c8cabdad
commit acd5bcfc88
26 changed files with 179 additions and 226 deletions

View File

@ -190,17 +190,9 @@ public class FilterOutputStream extends OutputStream {
try {
out.close();
} 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) {
closeException.addSuppressed(flushException);
}
throw closeException;
}
}

View File

@ -2430,8 +2430,6 @@ public class ObjectInputStream
// Read fields of the current descriptor into a new FieldValues and discard
new FieldValues(slotDesc, true);
} else if (slotDesc.hasReadObjectMethod()) {
ThreadDeath t = null;
boolean reset = false;
SerialCallbackContext oldContext = curContext;
if (oldContext != null)
oldContext.check();
@ -2450,19 +2448,10 @@ public class ObjectInputStream
*/
handles.markException(passHandle, ex);
} finally {
do {
try {
curContext.setUsed();
if (oldContext!= null)
oldContext.check();
curContext = oldContext;
reset = true;
} catch (ThreadDeath x) {
t = x; // defer until reset is true
}
} while (!reset);
if (t != null)
throw t;
}
/*

View File

@ -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.
*
* 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}
* that indicates serious problems that a reasonable application
* 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>
* A method is not required to declare in its {@code throws}
* 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.
*
* @author Frank Yellin
* @see java.lang.ThreadDeath
* @jls 11.2 Compile-Time Checking of Exceptions
* @since 1.0
*/

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -179,16 +179,6 @@ import java.lang.module.ModuleFinder;
* </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>
* <td>modification of thread groups, e.g., via calls to ThreadGroup
* {@code destroy}, {@code getParent}, {@code resume},

View File

@ -129,9 +129,7 @@ class Shutdown {
}
if (hook != null) hook.run();
} catch (Throwable t) {
if (t instanceof ThreadDeath td) {
throw td;
}
// ignore
}
}

View File

@ -1629,59 +1629,19 @@ public class Thread implements Runnable {
}
/**
* Forces the thread to stop executing.
* <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 {@code UnsupportedOperationException}.
*
* @throws SecurityException if the current thread cannot
* modify this thread.
* @throws UnsupportedOperationException if invoked on a virtual thread
* @see #interrupt()
* @see #checkAccess()
* @see ThreadDeath
* @see ThreadGroup#uncaughtException(Thread,Throwable)
* @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
* @throws UnsupportedOperationException always
*
* @deprecated This method was originally specified to "stop" a victim
* thread by causing the victim thread to throw a {@link ThreadDeath}.
* It was inherently unsafe. Stopping a thread caused it to unlock
* all of the monitors that it had locked (as a natural consequence
* of the {@code ThreadDeath} exception propagating up the stack). If
* any of the objects previously protected by these monitors were in
* an inconsistent state, the damaged objects become visible to
* other threads, potentially resulting in arbitrary behavior. Many
* uses of {@code stop} should be replaced by code that simply
* an inconsistent state, the damaged objects became visible to
* other threads, potentially resulting in arbitrary behavior.
* Usages of {@code stop} should be replaced by code that simply
* modifies some variable to indicate that the target thread should
* stop running. The target thread should check this variable
* 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)
public final void stop() {
@SuppressWarnings("removal")
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 */
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,26 +26,19 @@
package java.lang;
/**
* An instance of {@code ThreadDeath} is thrown in the victim thread
* when the (deprecated) {@link Thread#stop()} method is invoked.
* An instance of {@code ThreadDeath} was originally specified to be thrown
* by a victim thread when "stopped" with {@link Thread#stop()}.
*
* <p>An application should catch instances of this class only if it
* must clean up after being terminated asynchronously. If
* {@code ThreadDeath} is caught by a method, it is important that it
* be rethrown so that the thread actually dies.
*
* <p>The {@linkplain ThreadGroup#uncaughtException top-level error
* 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.
* @deprecated {@link Thread#stop()} was originally specified to "stop" a victim
* thread by causing the victim thread to throw a {@code ThreadDeath}. It
* was inherently unsafe and deprecated in an early JDK release. The ability
* to "stop" a thread with {@code Thread.stop} has been removed and the
* {@code Thread.stop} method changed to throw an exception. Consequently,
* {@code ThreadDeath} is also deprecated, for removal.
*
* @since 1.0
*/
@Deprecated(since="20", forRemoval=true)
public class ThreadDeath extends Error {
@java.io.Serial
private static final long serialVersionUID = -4417128565033088268L;

View File

@ -674,12 +674,9 @@ public class ThreadGroup implements Thread.UncaughtExceptionHandler {
* uncaught exception handler} installed, and if so, its
* {@code uncaughtException} method is called with the same
* two arguments.
* <li>Otherwise, this method determines if the {@code Throwable}
* argument is an instance of {@link ThreadDeath}. If so, nothing
* special is done. Otherwise, a message containing the
* thread's name, as returned from the thread's {@link
* Thread#getName getName} method, and a stack backtrace,
* using the {@code Throwable}'s {@link
* <li>Otherwise, a message containing the 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
* printed to the {@linkplain System#err standard error stream}.
* </ul>
@ -699,9 +696,8 @@ public class ThreadGroup implements Thread.UncaughtExceptionHandler {
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
} else {
System.err.print("Exception in thread \"" + t.getName() + "\" ");
e.printStackTrace(System.err);
}
}

View File

@ -1,6 +1,6 @@
<!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.
This code is free software; you can redistribute it and/or modify it
@ -31,31 +31,32 @@
<body>
<h1>Java Thread Primitive Deprecation</h1>
<hr>
<h2>Why is <code>Thread.stop</code> deprecated?</h2>
<p>Because it is inherently unsafe. Stopping a thread causes it to
unlock all the monitors that it has locked. (The monitors are
unlocked as the <code>ThreadDeath</code> exception propagates up
<h2>Why is <code>Thread.stop</code> deprecated and the ability to
stop a thread removed?</h2>
<p>Because it was inherently unsafe. Stopping a thread caused it to
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
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
<i>damaged</i>. When threads operate on damaged objects, arbitrary
behavior can result. This behavior may be subtle and difficult to
detect, or it may be pronounced. Unlike other unchecked exceptions,
<code>ThreadDeath</code> kills threads silently; thus, the user has
no warning that his program may be corrupted. The corruption can
<code>ThreadDeath</code> killed threads silently; thus, the user had
no warning that their program may be corrupted. The corruption could
manifest itself at any time after the actual damage occurs, even
hours or days in the future.</p>
<hr>
<h2>Couldn't I just catch the <code>ThreadDeath</code> exception
and fix the damaged object?</h2>
<h2>Couldn't I have just caught <code>ThreadDeath</code> and fixed
the damaged object?</h2>
<p>In theory, perhaps, but it would <em>vastly</em> complicate the
task of writing correct multithreaded code. The task would be
nearly insurmountable for two reasons:</p>
<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
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
<code>finally</code> clause). Cleanup would have to be repeated till
it succeeded. The code to ensure this would be quite complex.</li>

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -178,7 +178,7 @@ final class BootstrapMethodInvoker {
catch (Error e) {
// Pass through an Error, including BootstrapMethodError, any other
// 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
// instruction in JVMS 6.5.
throw e;

View File

@ -335,7 +335,7 @@ abstract sealed class CallSite permits ConstantCallSite, MutableCallSite, Volati
} catch (Error e) {
// Pass through an Error, including BootstrapMethodError, any other
// 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
// instruction in JVMS 6.5.
throw e;

View File

@ -124,10 +124,6 @@ public final class SecurityConstants {
public static final RuntimePermission GET_CLASSLOADER_PERMISSION =
new RuntimePermission("getClassLoader");
// java.lang.Thread
public static final RuntimePermission STOP_THREAD_PERMISSION =
new RuntimePermission("stopThread");
// java.lang.Thread
public static final RuntimePermission GET_STACK_TRACE_PERMISSION =
new RuntimePermission("getStackTrace");

View File

@ -37,7 +37,6 @@
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive0", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},

View File

@ -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.
*
* 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;
}
}
} 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) {
// TODO: log
System.err.println("Exception on the toolkit thread");

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -202,10 +202,6 @@ class EventDispatchThread extends Thread {
eq.dispatchEvent(event);
}
catch (ThreadDeath death) {
doDispatch = false;
throw death;
}
catch (InterruptedException interruptedException) {
doDispatch = false; // AppContext.dispose() interrupts all
// Threads in the AppContext

View File

@ -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 {
running = false;
runningLock.unlock();

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -2386,11 +2386,6 @@ class Parser implements DTDConstants {
errorContext();
error("exception", e.getClass().getName(), e.getMessage());
e.printStackTrace();
} catch (ThreadDeath e) {
errorContext();
error("terminated");
e.printStackTrace();
throw e;
} finally {
for (; stack != null ; stack = stack.next) {
handleEndTag(stack.tag);

View File

@ -727,9 +727,6 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
}
dispatchEvent(ev);
} catch (ThreadDeath td) {
XBaseWindow.ungrabInput();
return;
} catch (Throwable thr) {
XBaseWindow.ungrabInput();
processException(thr);
@ -1994,8 +1991,6 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
try {
task.run();
} catch (ThreadDeath td) {
throw td;
} catch (Throwable thr) {
processException(thr);
}

View File

@ -2666,8 +2666,6 @@ public class LogManager {
for (Runnable c : listeners.values().toArray(new Runnable[0])) {
try {
c.run();
} catch (ThreadDeath death) {
throw death;
} catch (Error | RuntimeException x) {
if (t == null) t = x;
else t.addSuppressed(x);

View File

@ -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
@ -1030,7 +1030,7 @@ public class DOMSerializerImpl implements LSSerializer, DOMConfiguration {
try {
return doc.getXmlVersion();
} // The VM ran out of memory or there was some other serious problem. Re-throw.
catch (VirtualMachineError | ThreadDeath vme) {
catch (VirtualMachineError vme) {
throw vme;
} // Ignore all other exceptions and errors
catch (Throwable t) {
@ -1046,7 +1046,7 @@ public class DOMSerializerImpl implements LSSerializer, DOMConfiguration {
try {
return doc.getInputEncoding();
} // The VM ran out of memory or there was some other serious problem. Re-throw.
catch (VirtualMachineError | ThreadDeath vme) {
catch (VirtualMachineError vme) {
throw vme;
} // Ignore all other exceptions and errors
catch (Throwable t) {
@ -1062,7 +1062,7 @@ public class DOMSerializerImpl implements LSSerializer, DOMConfiguration {
try {
return doc.getXmlEncoding();
} // The VM ran out of memory or there was some other serious problem. Re-throw.
catch (VirtualMachineError | ThreadDeath vme) {
catch (VirtualMachineError vme) {
throw vme;
} // Ignore all other exceptions and errors
catch (Throwable t) {

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -261,10 +261,6 @@ public abstract class AttachProvider {
try {
providers.add(i.next());
} catch (Throwable t) {
if (t instanceof ThreadDeath) {
ThreadDeath td = (ThreadDeath)t;
throw td;
}
// Log errors and exceptions since we cannot return them
t.printStackTrace();
}

View File

@ -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.
*
* 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) {
t = t.getCause();
}
if (t instanceof ThreadDeath) {
throw (ThreadDeath)t;
}
if (t instanceof SecurityException) {
return result;
}
@ -101,9 +98,7 @@ public abstract class HotSpotAttachProvider extends AttachProvider {
result.add(new HotSpotVirtualMachineDescriptor(this, pid, name));
}
} catch (Throwable t) {
if (t instanceof ThreadDeath) {
throw (ThreadDeath)t;
}
// ignore
} finally {
if (mvm != null) {
mvm.detach();
@ -138,10 +133,6 @@ public abstract class HotSpotAttachProvider extends AttachProvider {
return;
}
} catch (Throwable t) {
if (t instanceof ThreadDeath) {
ThreadDeath td = (ThreadDeath)t;
throw td;
}
// we do not know what this id is
return;
} finally {

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -99,12 +99,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManagerService {
try {
connector = connectors.next();
} catch (ThreadDeath x) {
throw x;
} catch (Exception x) {
System.err.println(x);
continue;
} catch (Error x) {
} catch (Exception | Error x) {
System.err.println(x);
continue;
}
@ -128,12 +123,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManagerService {
try {
transportService = transportServices.next();
} catch (ThreadDeath x) {
throw x;
} catch (Exception x) {
System.err.println(x);
continue;
} catch (Error x) {
} catch (Exception | Error x) {
System.err.println(x);
continue;
}

View File

@ -114,6 +114,7 @@ public class LocalExecutionControl extends DirectExecutionControl {
}
@Override
@SuppressWarnings("removal")
protected String invoke(Method doitMethod) throws Exception {
if (allStop == null) {
super.load(new ClassBytecodes[]{ genCancelClass() });

View File

@ -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.
*
* 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 {
@Override

View 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);
}
}