Running your tests with multiple threads does not work. Bugs depend on a specific thread interleaving, which is often impossible to reach by simply rerunning your test multiple times. And data races only occur on specific hardware architectures and JVMs.
Therefore vmlens uses the Java Memory Model to execute all possible thread interleavings and to check for data races in the program flow.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.Using vmlens is easy.
Surround your test with a while loop iterating over all thread interleavings using the class AllInterleaving.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()); } } } }
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(); } } } }
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(); } } } }
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
© 2020 vmlens Legal Notice Privacy Policy