//
// Copyright (C) 2010 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.test.vm.threads;
import org.junit.Test;
import gov.nasa.jpf.util.test.TestJPF;
import gov.nasa.jpf.vm.Verify;
/**
* regression test for Thread.stop()
*/
public class ThreadStopTest extends TestJPF {
@Test
public void testStopNewThread(){
if (!isJPFRun()){
Verify.resetCounter(0);
}
if (verifyNoPropertyViolation()){
Thread t = new Thread(){
public void run(){
System.out.println("# t running, that's bad");
fail("t should never run");
}
};
t.stop();
System.out.println("# now starting the stopped thread");
t.start();
Thread.yield();
assertFalse( "t not terminated yet", t.isAlive());
System.out.println("# main got past start of stopped thread");
Verify.incrementCounter(0);
} else {
assertTrue( "main did not get past starting stopped thread", Verify.getCounter(0) > 0);
}
}
@Test
public void testStopNewSyncThread(){
if (!isJPFRun()){
Verify.resetCounter(0);
}
if (verifyNoPropertyViolation()){
Thread t = new Thread(){
public synchronized void run(){
System.out.println("# t running, that's bad");
fail("t should never run");
}
};
t.stop();
synchronized (t){
System.out.println("# now starting the stopped thread");
t.start(); // this should terminate the thread
System.out.println("# main got past start of stopped sync thread");
}
System.out.println("# main released lock for stopped sync thread");
Thread.yield();
assertFalse( "t not terminated yet", t.isAlive());
Verify.incrementCounter(0);
} else {
assertTrue( "main did not get past starting stopped thread", Verify.getCounter(0) > 0);
}
}
@Test
public void testStopSelf(){
if (!isJPFRun()){
Verify.resetCounter(0);
Verify.resetCounter(1);
}
if (verifyNoPropertyViolation()){
Thread t = new Thread(){
public synchronized void run(){
Verify.incrementCounter(1);
System.out.println("# t running");
foo();
fail("t should not have gotten past self stop");
}
void foo() {
stop();
}
};
System.out.println("# main now starting the thread");
t.start();
try {
System.out.println("# main now joining the thread..");
t.join();
System.out.println("# main joined thread");
assertFalse( "t not terminated yet", t.isAlive());
Verify.incrementCounter(0);
} catch (InterruptedException ix){
assert false : "main should not get an InterruptedException while joining";
}
} else {
assertTrue( "t did not run", Verify.getCounter(1) > 0);
assertTrue( "main did not get past joining stopped thread", Verify.getCounter(0) > 0);
}
}
// some sync helpers
static boolean isRunning;
@Test
public void testStopRunning(){
if (!isJPFRun()){
Verify.resetCounter(0);
Verify.resetCounter(1);
}
if (verifyNoPropertyViolation()){
isRunning = false;
Thread t = new Thread(){
public synchronized void run(){
isRunning = true;
Verify.incrementCounter(1);
System.out.println("# t running");
while (true){ // while(true) will cause no return insn !
// keep it alive
Thread.yield();
}
}
};
System.out.println("# main now starting t");
t.start();
while (!isRunning){
Thread.yield();
}
System.out.println("# main now stopping t");
t.stop();
try {
System.out.println("# main now joining the stopped thread..");
t.join();
System.out.println("# main joined thread");
assertFalse( "t not terminated yet", t.isAlive());
Verify.incrementCounter(0);
} catch (InterruptedException ix){
assert false : "main should not get an InterruptedException while joining";
}
} else {
assertTrue( "t did not run", Verify.getCounter(1) > 0);
assertTrue( "main did not get past join", Verify.getCounter(0) > 0);
}
}
static Object lock = new Object();
@Test
public void testStopBlocked(){
if (!isJPFRun()){
Verify.resetCounter(0);
Verify.resetCounter(1);
}
if (verifyNoPropertyViolation()){
isRunning = false;
Thread t = new Thread(){
public synchronized void run(){
isRunning = true;
Verify.incrementCounter(1);
System.out.println("# t running, now blocking on lock..");
synchronized(lock){
fail("t should never get here");
}
}
};
synchronized(lock){
System.out.println("# main now starting t");
t.start();
while (!isRunning){
Thread.yield();
}
assertTrue( "t not blocked", t.getState() == Thread.State.BLOCKED);
System.out.println("# main now stopping t");
t.stop();
assertTrue("t dead despite main not giving up lock", t.isAlive());
System.out.println("# main now releasing lock");
}
try {
System.out.println("# main now joining the stopped thread..");
t.join();
System.out.println("# main joined thread");
assertFalse( "t not terminated yet", t.isAlive());
Verify.incrementCounter(0);
} catch (InterruptedException ix){
assert false : "main should not get an InterruptedException while joining";
}
} else {
assertTrue( "t did not run", Verify.getCounter(1) > 0);
assertTrue( "main did not get past join", Verify.getCounter(0) > 0);
}
}
@Test
public void testStopWaiting(){
if (!isJPFRun()){
Verify.resetCounter(0);
Verify.resetCounter(1);
}
if (verifyNoPropertyViolation()){
isRunning = false;
Thread t = new Thread(){
public synchronized void run(){
isRunning = true;
Verify.incrementCounter(1);
System.out.println("# t running, now waiting on lock");
synchronized(lock){
try {
lock.wait();
} catch (InterruptedException ix){
fail("should not get interrupted");
}
}
}
};
System.out.println("# main now starting t");
t.start();
while (t.getState() != Thread.State.WAITING) {
Thread.yield();
}
System.out.println("# main now stopping t");
t.stop();
Thread.yield();
assertTrue("t dead despite main not notifying", t.isAlive());
System.out.println("# main now notifying");
synchronized (lock) {
lock.notifyAll();
}
try {
System.out.println("# main now joining the stopped thread..");
t.join();
System.out.println("# main joined thread");
assertFalse( "t not terminated yet", t.isAlive());
Verify.incrementCounter(0);
} catch (InterruptedException ix){
assert false : "main should not get an InterruptedException while joining";
}
} else {
assertTrue( "t did not run", Verify.getCounter(1) > 0);
assertTrue( "main did not get past join", Verify.getCounter(0) > 0);
}
}
static boolean wasHandled;
@Test
public void testStopHandler() {
if (!isJPFRun()){
Verify.resetCounter(0);
Verify.resetCounter(1);
}
if (verifyNoPropertyViolation()){
isRunning = false;
wasHandled = false;
Thread t = new Thread(){
public synchronized void run(){
isRunning = true;
Verify.incrementCounter(1);
System.out.println("# t running, now waiting on lock");
try {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException ix) {
fail("should not get interrupted");
}
}
} catch (ThreadDeath td){
// usually not a good style, but can happen for exit processing
System.out.println("# t caught ThreadDeath");
wasHandled = true;
throw td; // rethrow to continue with kill
}
}
};
System.out.println("# main now starting t");
t.start();
while (t.getState() != Thread.State.WAITING) {
Thread.yield();
}
System.out.println("# main now stopping t");
t.stop();
Thread.yield();
assertTrue("t dead despite main not notifying", t.isAlive());
System.out.println("# main now notifying");
synchronized (lock) {
lock.notifyAll();
}
try {
System.out.println("# main now joining the stopped thread..");
t.join();
System.out.println("# main joined thread");
assertFalse( "t not terminated yet", t.isAlive());
assertTrue("t did not handle ThreadDeath", wasHandled);
Verify.incrementCounter(0);
} catch (InterruptedException ix){
assert false : "main should not get an InterruptedException while joining";
}
} else {
assertTrue( "t did not run", Verify.getCounter(1) > 0);
assertTrue( "main did not get past join", Verify.getCounter(0) > 0);
}
}
@Test
public void testStopTerminated(){
if (!isJPFRun()){
Verify.resetCounter(0);
Verify.resetCounter(1);
}
if (verifyNoPropertyViolation()){
Thread t = new Thread(){
public void run(){
Verify.incrementCounter(1);
System.out.println("# t running");
}
};
System.out.println("# main now starting t");
t.start();
while (t.isAlive()){
Thread.yield();
}
assertFalse("t is a zombie", t.isAlive());
System.out.println("# main now stopping dead t");
t.stop();
System.out.println("# main survived stopping t");
assertFalse("t is a zombie", t.isAlive());
Verify.incrementCounter(0);
} else {
assertTrue( "t did not run", Verify.getCounter(1) > 0);
assertTrue( "main did not get past join", Verify.getCounter(0) > 0);
}
}
static Thread waitee;
@Test
public void testStopJoin() {
// join() is a lockfree wait, which makes it interesting
if (!isJPFRun()){
Verify.resetCounter(0);
Verify.resetCounter(1);
Verify.resetCounter(2);
}
if (verifyNoPropertyViolation()){
waitee = new Thread(){
public void run(){
Verify.incrementCounter(2);
System.out.println("# waitee running");
synchronized(lock){
try {
System.out.println("# waitee now waiting for main to signal..");
lock.wait();
System.out.println("# waitee terminating");
} catch (InterruptedException ix) {
fail("waitee should not get interrupted");
}
}
}
};
waitee.start();
while (waitee.getState() != Thread.State.WAITING){
Thread.yield();
}
Thread t = new Thread(){
public synchronized void run(){
Verify.incrementCounter(1);
try {
System.out.println("# t now joining waitee..");
waitee.join();
fail("t should never get here");
} catch (InterruptedException ix){
fail("t should not get interrupted");
}
}
};
System.out.println("# main now starting t");
t.start();
while (t.getState() != Thread.State.WAITING){
Thread.yield();
}
assertTrue("waitee is a zombie", waitee.isAlive());
assertTrue("t is a zombie", t.isAlive());
System.out.println("# main now stopping t");
t.stop();
assertTrue("t should not have terminated since waitee not notified yet", t.isAlive());
System.out.println("# main now notifying waitee");
synchronized(lock){
lock.notifyAll();
}
try {
System.out.println("# main now joining waitee");
waitee.join();
} catch (InterruptedException ix){
fail("main should not get interupted joining waitee");
}
assertFalse("waitee is a zombie", waitee.isAlive());
try {
System.out.println("# main now joining t");
t.join();
} catch (InterruptedException ix){
fail("main should not get interupted joining t");
}
assertFalse("t is a zombie", t.isAlive());
Verify.incrementCounter(0);
} else {
assertTrue( "waitee did not run", Verify.getCounter(2) > 0);
assertTrue( "t did not run", Verify.getCounter(1) > 0);
assertTrue( "main did not get past t join", Verify.getCounter(0) > 0);
}
}
// and a lot more.. (e.g. stopping interrupted or timedout threads)
}