--- /dev/null 2014-04-14 22:37:48.000000000 -0400 +++ new/test/runtime/Thread/CancellableThreadTest.java 2014-04-14 22:37:48.253121100 -0400 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2014, 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. + */ + +/** + * This test is useful for finding out whether a Thread can have a + * private variable indicate whether or not it is finished, and to illustrate + * the ease with which Threads terminate each other. + * + * @test + */ + +public class CancellableThreadTest { + public static final int THREADPAIRS = Integer.parseInt(System.getProperty("test.threadpairs", "128")); + + public static void main(String args[]) { + Thread[] threads = new Thread[THREADPAIRS]; + Canceller[] cancellers = new Canceller[THREADPAIRS]; + + System.out.println("Running with " + THREADPAIRS + " thread pairs"); + + for (int i = 0; i < THREADPAIRS; i++) { + cancellers[i] = new Canceller(i); + threads[i] = new Thread(cancellers[i]); + threads[i].start(); + } + + for (int i = 0; i < THREADPAIRS; i++) { + try { + threads[i].join(); + } catch (InterruptedException e) { + } + + if (cancellers[i].failed) { + throw new RuntimeException(" Test failed in " + i + " th pair. See error messages above."); + } + } + } +} + +class Canceller implements Runnable { + + private final CancellableTimer timer; + + public final String name; + public volatile boolean failed = false; + private volatile boolean hasBeenNotified = false; + + public Canceller(int index) { + this.name = "Canceller #" + index; + timer = new CancellableTimer(index, this); + } + + public void setHasBeenNotified() { + hasBeenNotified = true; + } + + /** + * This method contains the "action" of this Canceller Thread. + * It starts a CancellableTimer, waits, and then interrupts the + * CancellableTimer after the CancellableTimer notifies it. It then + * tries to join the CancellableTimer to itself and reports on whether + * it was successful in doing so. + */ + public void run() { + Thread timerThread = new Thread(timer); + + try { + synchronized(this) { + timerThread.start(); + wait(); + } + } catch (InterruptedException e) { + System.err.println(name + " was interrupted during wait()"); + failed = true; + } + + if (!hasBeenNotified) { + System.err.println(name + ".hasBeenNotified is not true as expected"); + failed = true; + } + + synchronized (timer) { + timerThread.interrupt(); + } + + try { + timerThread.join(); + } catch (InterruptedException ie) { + System.err.println(name + " was interrupted while joining " + + timer.name); + failed = true; + } + + if (timerThread.isAlive()) { + System.err.println(timer.name + " is still alive after " + name + + " attempted to join it."); + failed = true; + } + } +} + +/** + * This non-public class is the Thread which the Canceller Thread deliberately + * interrupts and then joins to itself after this Thread has slept for a few milliseconds. + */ + +class CancellableTimer implements Runnable { + + public final String name; + private final Canceller myCanceller; + + public CancellableTimer(int index, Canceller aCanceller) { + this.name = "CancellableTimer #" + index; + this.myCanceller = aCanceller; + } + + /** + * This is where this CancellableTimer does its work. It notifies its + * Canceller, waits for the Canceller to interrupt it, then catches the + * InterruptedException, and sleeps for a few milliseconds before exiting. + */ + public void run() { + try { + synchronized (this) { + synchronized (myCanceller) { + myCanceller.setHasBeenNotified(); + myCanceller.notify(); + } + wait(); + } + } catch (InterruptedException first) { + // isInterrupted should've been cleared here and we should not register a + // second interrupt + if (Thread.currentThread().isInterrupted()) { + System.err.println(name + " should not register an interrupt here"); + myCanceller.failed = true; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + System.err.println(name + " was interrupted when sleeping"); + myCanceller.failed = true; + } + } + } +}