//
// Copyright (C) 2006 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.
//
/**
* this is a raw test class for detection of thread-shared fields, i.e.
* it executes the garbage collection based reachability analysis
*/
package gov.nasa.jpf.test.mc.threads;
import org.junit.Test;
import gov.nasa.jpf.util.TypeRef;
import gov.nasa.jpf.util.test.TestJPF;
class SharedObject {
int instanceField;
int whatEver;
}
public class RaceTest extends TestJPF {
static final TypeRef PROPERTY = new TypeRef("gov.nasa.jpf.listener.PreciseRaceDetector");
static final String LISTENER = "+listener=gov.nasa.jpf.listener.PreciseRaceDetector";
static int staticField;
@Test
public void testStaticRace () {
if (verifyUnhandledException("java.lang.RuntimeException")) {
Runnable r1 = new Runnable() {
public void run() {
staticField = 1;
if (staticField != 1) {
throw new RuntimeException("r1 detected race!");
}
}
};
Runnable r2 = new Runnable() {
public void run() {
staticField = 0;
if (staticField != 0) {
throw new RuntimeException("r2 detected race!");
}
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
@Test
public void testStaticRaceNoThrow () {
if (verifyPropertyViolation(PROPERTY, LISTENER)) {
Runnable r1 = new Runnable() {
public void run() {
staticField = 1;
}
};
Runnable r2 = new Runnable() {
public void run() {
staticField = 0;
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
// this represents the case where the class loading thread is non-deterministic
static class Container {
static int data; // that's what we race for
}
static class StaticRacer extends Thread {
public void run(){
Container.data++;
}
}
@Test
public void testSymmetricStaticRace(){
if (verifyUnhandledExceptionDetails("java.lang.RuntimeException", "got race",
"+vm.por.shared.class=.vm.GlobalTrackingPolicy")) {
StaticRacer t1 = new StaticRacer();
StaticRacer t2 = new StaticRacer();
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException ix){
fail("got interrupted");
}
if (Container.data != 2){
System.out.print("Container.data = ");
System.out.print( Container.data);
System.out.println(" => throwing RuntimeException");
throw new RuntimeException("got race");
}
}
}
@Test
public void testInstanceRace () {
if (verifyUnhandledException("java.lang.RuntimeException")) {
final SharedObject o = new SharedObject();
Runnable r1 = new Runnable() {
SharedObject d = o;
public void run() {
d.instanceField = 1;
if (d.instanceField != 1) {
throw new RuntimeException("r1 detected race!");
}
}
};
Runnable r2 = new Runnable() {
SharedObject d = o;
public void run() {
d.instanceField = 0;
if (d.instanceField != 0) {
throw new RuntimeException("r2 detected race!");
}
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
@Test
public void testInstanceRaceNoThrow () {
if (verifyPropertyViolation(PROPERTY, LISTENER)) {
final SharedObject o = new SharedObject();
Runnable r1 = new Runnable() {
SharedObject d = o;
public void run() {
d.instanceField = 1;
}
};
Runnable r2 = new Runnable() {
SharedObject d = o;
public void run() {
d.instanceField = 0;
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
@Test
public void testInstanceRaceListenerExclude () {
if (verifyNoPropertyViolation(LISTENER, "+race.exclude="+ RaceTest.class.getName() + "*")){
testInstanceRaceNoThrow();
}
}
@Test
public void testInstanceRaceListenerInclude () {
if (verifyPropertyViolation(PROPERTY, LISTENER,
"+race.include=" + RaceTest.class.getName() + "*")){
testInstanceRaceNoThrow();
}
}
@Test
public void testStaticRaceListenerIncludeOther () {
if (verifyNoPropertyViolation(LISTENER, "+race.include=sho.bi.Doo*")){
testStaticRaceNoThrow();
}
}
@Test
public void testArrayRaceNoThrow () {
if (verifyPropertyViolation(PROPERTY, LISTENER, "+cg.threads.break_arrays")){
final int[] shared = new int[1];
Runnable r1 = new Runnable(){
int[] a = shared;
public void run() {
a[0] = 0;
}
};
Runnable r2 = new Runnable(){
int[] a = shared;
public void run() {
a[0] = 1;
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
/*
* mostly the same as above except of that the race candidates are the same insn instance, i.e. use the same
* cached insn fields values
*/
static class AT extends Thread {
int[] a;
int idx;
AT (int[] a, int idx) {
this.a = a;
this.idx = idx;
}
public void run (){
//assertTrue( a[idx] == 0);
a[idx] = 1;
}
}
@Test
public void testNoArrayRaceSameInsn (){
if (verifyNoPropertyViolation(LISTENER, "+cg.threads.break_arrays")){
int[] a = new int[2];
AT t1 = new AT(a, 0);
t1.start();
AT t2 = new AT(a, 1);
t2.start();
}
}
// the dual
@Test
public void testArrayRaceSameInsn (){
if (verifyPropertyViolation(PROPERTY, LISTENER, "+cg.threads.break_arrays")){
int[] a = new int[2];
AT t1 = new AT(a, 1);
t1.start();
AT t2 = new AT(a, 1);
t2.start();
}
}
@Test
public void testNoArrayRaceElements () {
if (verifyNoPropertyViolation(LISTENER, "+cg.threads.break_arrays")){
final int[] shared = new int[2];
Runnable r1 = new Runnable(){
int[] a = shared;
public void run() {
a[0] = 0;
}
};
Runnable r2 = new Runnable(){
int[] a = shared;
public void run() {
a[1] = 1;
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
//--- these are tests to check false positives
static class SameInsnRunnable implements Runnable {
SharedObject o = new SharedObject();
public void run () {
o.instanceField = 42; // same insn, different 'o', no race
}
}
@Test
public void testSameInsnOtherObject () {
if (verifyNoPropertyViolation(LISTENER)) {
SameInsnRunnable r1 = new SameInsnRunnable();
SameInsnRunnable r2 = new SameInsnRunnable();
Thread t = new Thread(r1);
t.start();
r2.run();
}
}
@SuppressWarnings("unused")
@Test
public void testSameObjectOtherField() {
if (verifyNoPropertyViolation(LISTENER)) {
final SharedObject o = new SharedObject();
Runnable r = new Runnable() {
public void run() {
o.instanceField = 42;
}
};
Thread t = new Thread(r);
o.whatEver = -42; // different field, no race
}
}
//--- try variations of locks
class AnotherSharedObject {
Object lock1 = new Object();
Object lock2 = new Object();
int x = 0;
}
@Test
public void testNoSync() {
if (verifyUnhandledException("java.lang.RuntimeException")) {
final AnotherSharedObject o = new AnotherSharedObject();
Runnable r = new Runnable() {
public void run() {
o.x++;
if (o.x == 0) {
throw new RuntimeException("testNoSync race");
}
}
};
Thread t = new Thread(r);
t.start();
o.x--;
}
}
@Test
public void testTSync() {
if (verifyUnhandledException("java.lang.RuntimeException")) {
final AnotherSharedObject o = new AnotherSharedObject();
Runnable r = new Runnable() {
public void run() {
synchronized (o.lock1) {
o.x++;
if (o.x == 0) {
throw new RuntimeException("testT1Sync race");
}
}
}
};
Thread t = new Thread(r);
t.start();
// no sync
o.x--;
}
}
@Test
public void testMainSync () {
if (verifyUnhandledException("java.lang.RuntimeException")) {
final AnotherSharedObject o = new AnotherSharedObject();
Runnable r = new Runnable() {
public void run() {
// not synchronized
o.x++;
if (o.x == 0) {
throw new RuntimeException("testMainSync race");
}
}
};
Thread t = new Thread(r);
t.start();
synchronized (o.lock1) {
o.x--;
}
}
}
@Test
public void testBothSync () {
if (verifyNoPropertyViolation()) {
final AnotherSharedObject o = new AnotherSharedObject();
Runnable r = new Runnable() {
public void run() {
synchronized (o.lock1) {
o.x++;
if (o.x == 0) {
throw new RuntimeException("testBothSync race??");
}
}
}
};
Thread t = new Thread(r);
t.start();
synchronized (o.lock1) {
o.x = 0;
}
}
}
@Test
public void testWrongSync () {
if (verifyUnhandledException("java.lang.RuntimeException")) {
final AnotherSharedObject o = new AnotherSharedObject();
Runnable r = new Runnable() {
public void run() {
synchronized (o.lock1) {
o.x++;
if (o.x == 0) {
throw new RuntimeException("testWrongSync race");
}
}
}
};
Thread t = new Thread(r);
t.start();
synchronized (o.lock2) {
o.x--;
}
}
}
}