vmlens, unit-testing multi-threaded applications on the JVM made easy

Why vmlens?

Even for complicated algorithms

To test the put method of the class ConcurrentHashMap using two threads takes 353 iterations and less than 3 seconds on my Intel i5 3,40 GHz 4 core CPU.

And by using the @atomic annotation we can build complicated algorithms out of smaller pieces.

Easy to use

Using vmlens is easy.

Surround your test with a while loop iterating over all thread interleavings using the class AllInterleaving.

Read-modify-write race condition

A read-modify-write race happens when reading, modifying, and writing consists, not of one but multiple atomic methods. In the example below get and put are atomic but the complete update method is not. vmlens executes all thread interleavings and reports the interleaving which led to the error.

public class TestUpdateWrong {
	public void update(ConcurrentHashMap<Integer, Integer> map) {
		Integer result = map.get(1);
		if (result == null) {
			map.put(1, 1);
		} else {
			map.put(1, result + 1);
		}
	}
	@Test
	public void testUpdate() throws InterruptedException {
		try (AllInterleavings allInterleavings = 
				new AllInterleavings("TestUpdateWrong");) {
			while (allInterleavings.hasNext()) {
				final ConcurrentHashMap<Integer, Integer> map = 
						new ConcurrentHashMap<Integer, Integer>();
				Thread first = new Thread(() -> {
					update(map);
				});
				Thread second = new Thread(() -> {
					update(map);
				});
				first.start();
				second.start();
				first.join();
				second.join();
				assertEquals(2, 
						map.get(1).intValue());
			}
		}
	}
}

Data race

A data race happens when reading and writing to a shared variable is not correctly synchronized. A data race means that thread might read stale or inconsistent values. vmlens traces the memory access and synchronization actions to detect data races during a test run. When vmlens has found a data race it reports the thread interleaving which contained the race. The example shows a data race in an inconsistent synchronized test.

public class TestInconsistentSynchronized {
	private static final Object LOCK_1 = new Object();
	private static final Object LOCK_2 = new Object();
	int i = 0;
	@Test
	public void test() throws InterruptedException {
		try (AllInterleavings allInterleavings = 
				new AllInterleavings
				("TestInconsistentSynchronized");) {
			while (allInterleavings.hasNext()) {
				Thread first = new Thread(() -> {
					synchronized (LOCK_1) {
						i++;
					}
				});
				Thread second = new Thread(() -> {
					synchronized (LOCK_2) {
						i++;
					}
				});
				first.start();
				second.start();

				first.join();
				second.join();
			}
		}
	}
}

Deadlock

To detect a deadlock during a test run both threads need to request the locks at the exact same time. To make deadlock detection timing independent, vmlens analysis the order in which the threads request the locks. If this order contains a cycle vmlens has found a deadlock.

public class TestUpdateRecursive {
	private final ConcurrentHashMap<Integer, Integer> 
		map = new ConcurrentHashMap<Integer, Integer>();
	public TestUpdateRecursive() {
		map.put(1, 1);
		map.put(2, 2);
	}
	public void update12() {
		map.compute(1, (key, value) -> {
			map.compute(2, (k, v) -> {
				return 2;
			});
			return 2;
		});
	}
	public void update21() {
		map.compute(2, (key, value) -> {
			map.compute(1, (k, v) -> {
				return 2;
			});
			return 2;
		});
	}
	@Test
	public void testUpdate() throws InterruptedException {
		try (AllInterleavings allInterleavings = 
				 new AllInterleavings("TestUpdateRecursive");) {
			while (allInterleavings.hasNext()) {
				Thread first = new Thread(() -> {
					update12();
				});
				Thread second = new Thread(() -> {
					update21();
				});
				first.start();
				first.join();

				second.start();
				second.join();
			}
		}
	}
}

A short note from Thomas:

(Developer of vmlens)

Hello!

The core count of the CPUs is growing exponentially. While it took four years to go from two cores to eight in the year 2009, it took only one year to go from 32 to 64 in the year 2018. To utilize all those cores we need scalable, multi-threaded software.

The JVM is a perfect choice. The JVM provides concurrent garbage collectors. And a standard library that contains concurrent data structures. And open-source projects which enable you to use different multi-threaded programming techniques.

And vmlens makes it possible to test those multi-threaded applications. vmlens enables you to use development techniques that use automatic unit tests like test-driven design, continuous delivery, refactoring also for multi-thread applications.

Cheers, Thomas

vmlens works with: Eclipse, Maven, Java, Kotlin, Scala, JUnit, TestNG

Want to start testing?

Download vmlens

© 2020 vmlens Legal Notice Privacy Policy