JDK 9 RFR of JDK-8132548: java/lang/ThreadGroup/Stop.java fails with "RuntimeException: Failure"
David Holmes
david.holmes at oracle.com
Tue Jul 12 06:44:01 UTC 2016
On 12/07/2016 4:17 PM, Martin Buchholz wrote:
> Amy, sorry you have so many picky reviewers!
Difference between patching a failing test and completely redesigning it
from scratch. The latter was not warranted in my opinion.
> Here's how I might write it:
>
> import java.util.concurrent.CountDownLatch;
> import java.util.concurrent.atomic.AtomicReference;
>
> public class Stop {
> private static final CountDownLatch ready = new CountDownLatch(1);
> private static final ThreadGroup group = new ThreadGroup("");
> private static final AtomicReference<Throwable> firstThrowable
> = new AtomicReference<>();
> private static final AtomicReference<Throwable> secondThrowable
> = new AtomicReference<>();
Simple volatile Throwable fields would suffice - AtomicReferences are
overkill.
> private static final Thread second = new Thread(group, () -> {
> ready.countDown();
> try {
You have to place the countDown() inside the try block else the
ThreadDeath may escape.
> do {} while (true);
Busy-loops are "bad".
> } catch (Throwable ex) {
> secondThrowable.set(ex);
> }
> });
>
> private static final Thread first = new Thread(group, () -> {
> // Wait until "second" is started
> try {
> ready.await();
> group.stop();
> } catch (Throwable ex) {
> firstThrowable.set(ex);
> }
> });
>
> public static void main(String[] args) throws Exception {
> // Launch two threads as part of the same thread group
> first.start();
> second.start();
>
> // Both threads should terminate when the first thread
> // terminates the thread group.
> second.join();
> first.join();
> if (! (firstThrowable.get() instanceof ThreadDeath) ||
> ! (secondThrowable.get() instanceof ThreadDeath))
> throw new AssertionError("should have thrown ThreadDeath");
> // Test passed - if never get here the test times out and fails.
> }
> }
I agree this meets the "spec" of checking "did group.stop() cause all
group members to encounter a ThreadDeath exception". But in relation to
fixing the original timing problem, well a simple untimed-join() fixed that.
David
-----
>
> On Mon, Jul 11, 2016 at 10:28 PM, Amy Lu <amy.lu at oracle.com
> <mailto:amy.lu at oracle.com>> wrote:
>
> Please help to review the updated webrev, getting rid of ThreadDeath
> and using join():
>
> http://cr.openjdk.java.net/~amlu/8132548/webrev.03/
>
> Thanks,
> Amy
>
>
> On 7/11/16 6:55 PM, David Holmes wrote:
>
>
>
> On 11/07/2016 6:14 PM, Amy Lu wrote:
>
> Thank you David, though the email crossed-by, I hope all the
> concerns
> have been resolved in the updated webrev:
> http://cr.openjdk.java.net/~amlu/8132548/webrev.02/
>
>
> Sorry but no, the changes are neither necessary nor sufficient.
> With the new code the ThreadDeath can hit anytime after
> countDown(), so it can hit before you enter the try block.
>
> I had rewrote the test with CountDownLatch and join(long
> millis). Also
> unlike the old version, test thread 'first' and 'second' do
> different
> things, and 'second' thread will keep alive for waiting to
> be killed by
> group.stop() from 'frist' thread in the same thread group. I
> think this
> could avoid the second call to second.stop() (group.stop())
> issue.
>
>
> Simply using join() with no timeout and relying on the test
> framework to kill the test allows you to do away with the second
> stop().
>
> David
> -----
>
> The time LONG_DELAY_MS used in join(long millis) is an
> adjusted timeout:
>
> private static final long LONG_DELAY_MS =
> Utils.adjustTimeout(1000L);
> ...
>
> second.join(LONG_DELAY_MS);
> boolean failed = second.isAlive();
>
> This gives second thread a more "reasonable" time to die,
> with taking
> account of the test harness timeout.
> This almost same as second.join()// test pass or timeout,
> but with
> additional chance of killing all threads in a bad case.
>
> Thanks,
> Amy
>
> On 7/11/16 3:25 PM, David Holmes wrote:
>
> Simplification ...
>
> On 11/07/2016 5:12 PM, David Holmes wrote:
>
> Hi Amy,
>
> Thanks for tackling this.
>
> On 8/07/2016 4:01 PM, Amy Lu wrote:
>
> Thank you Joe for your review.
>
> The intent is to give it more chance "for the
> thread group stop to be
> issued", not to extend the whole test execution
> timeout.
>
> I updated the webrev to make this in a retry,
> limit to 5 times of
> retry:
> http://cr.openjdk.java.net/~amlu/8132548/webrev.01/
>
>
> The retry serves no purpose here. groupStopped is
> being set to true and
> the waiting thread has been notified, so the loop
> will terminate after
> the first sleep. The failure happens when the main
> thread checks the
> isAlive() status of the second thread before the
> ThreadGroup.stop has
> actually had a chance to stop and terminate it -
> such that isAlive is
> now false. That is why I suggested waiting a bit
> longer by extending the
> sleep.
>
> I agree that making the test last at least 5 seconds
> is not ideal, but I
> didn't think it would be an issue in the big picture
> of testing.
>
> Ideally explicit "synchronization" is better than
> sleeps but that would
> again be missing the point with this test. We expect
> the thread to
> terminate, if it hasn't terminated in a "reasonable"
> time we consider
> the stop() to have failed and the test to fail. To
> that end we could
> remove the sleep altogether and change:
>
> boolean failed = second.isAlive();
>
> to
>
> try {
> second.join(1000);
> } catch (InterruptedException shouldNotHappen) {}
> boolean failed = second.isAlive();
>
> Now we use explicit event synchronization - great!
> But the test has the
> same failure issue now as it did with the sleep.
> Putting in a
> CountDownLatch would have exactly the same problem:
> we expect the second
> thread to signal the latch as it terminates, but if
> that doesn't happen
> within a "reasonable" amount of time, then we deem
> the stop() to have
> failed and the test to have failed.
>
> Also note that the more code we add the more likely
> the second call to
> second.stop() triggers an async exception in code
> that can't handle it
> and results in an even worse failure mode!
>
> The only thing I can suggest is to get rid of the
> explicit sleep (or
> join, or latch.await() etc) and simply recode as an
> infinite loop and
> rely on the test harness to tear things down when
> the overall test
> timeout kicks in. That way the test either passes or
> is timed-out, there
> is no explicit failure - but a busy loop is also bad
> so you would want a
> small sleep in there anyway:
>
> while (second.isAlive()) {
> Thread.sleep(10);
> }
> // Test passed - if we never get here the test times
> out and
> // we implicitly fail.
>
>
> Of course that was silly all you need is:
>
> second.join();
> // Test passed - if we never get here the test times out and
> // we implicitly fail.
>
> David
>
>
> Thanks,
> David
>
> Thanks,
> Amy
>
> On 7/8/16 12:15 PM, joe darcy wrote:
>
> Hi Amy,
>
> I'm a bit uncomfortable with the fix as-is.
>
> Rather than hard-coding sleep values, if
> sleep values are needed I
> think it is a better practice to use ones
> that are scaled with the
> jtreg timeout factors, etc. used to run the
> tests. Please instead use
> something like the adjustTimeout method of
>
> $JDK_FOREST_ROOT/test/lib/share/classes/jdk/test/lib/Utils
>
> As a general comment, I'd prefer we don't
> just up timeout values for
> tests. That can cause the whole test suite
> run to slow down, which is
> undesirable especially if the condition in
> question may actually be
> satisfied in many cases much faster than the
> timeout value.
>
> Thanks,
>
> -Joe
>
>
> On 7/7/2016 7:01 PM, Amy Lu wrote:
>
> Please review this trivial fix for
> test:java/lang/ThreadGroup/Stop.java
>
> Though this is a test for a deprecated
> API, failed with very very low
> frequency and hard to reproduce (I got
> no luck to reproduce it), I’d
> like to patch it as suggested: extend
> the sleep in the main thread
> from one second to five seconds. Also
> added 'volatile' to the boolean
> variable 'groupStopped'.
>
> bug:
> https://bugs.openjdk.java.net/browse/JDK-8132548
> webrev:
> http://cr.openjdk.java.net/~amlu/8132548/webrev.00/
>
> Thanks,
> Amy
>
>
> ---
> old/test/java/lang/ThreadGroup/Stop.java
> 2016-07-04
> 14:53:59.000000000 +0800
> +++
> new/test/java/lang/ThreadGroup/Stop.java
> 2016-07-04
> 14:53:58.000000000 +0800
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 1999, 2011, Oracle
> and/or its affiliates. All
> rights reserved.
> + * Copyright (c) 1999, 2016, 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,7 +29,7 @@
> */
>
> public class Stop implements Runnable {
> - private static boolean groupStopped
> = false ;
> + private static volatile boolean
> groupStopped = false ;
> private static final Object lock =
> new Object();
>
> private static final ThreadGroup
> group = new ThreadGroup("");
> @@ -70,7 +70,7 @@
> while (!groupStopped) {
> lock.wait();
> // Give the other
> thread a chance to stop
> - Thread.sleep(1000);
> + Thread.sleep(5000);
> }
> }
>
>
>
>
>
>
>
>
More information about the core-libs-dev
mailing list