8035173: [TESTBUG] runtime/threads/CancellableThreadTest fails with OOM on windows-i586
Summary: Test ported to jtreg, thread pairs decreased from 1024 to 128 to avoid OOM on 32 bit Windows
Reviewed-by: sla, dsimms
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/Thread/CancellableThreadTest.java Tue Apr 15 19:03:51 2014 +0200
@@ -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;
+ }
+ }
+ }
+}