Class Happenstance<K>
- Type Parameters:
K- the type of the sequence points
checkpoint(K)) or happens-before (via join(K)) relationships between events in concurrent operations. This is useful for testing, where
you want to ensure that certain actions are executed in a specific order.
Example:
class MyConcurrentTest {
@Test
public void testConcurrent() {
var happens =
Happenstance.<String>builder()
.happenInOrder("writtenB", "readingA", "writtenA")
.happenInOrder("readingB", "writtenB")
.build();
Stream.of("A", "B")
.parallel()
.forEach(
input -> {
happens.join("reading" + input);
sut.read(input);
sut.write(input);
happens.join("written" + input);
dut.finish(input);
});
}
}
Implementation note: this class uses VarHandle instead of high-level synchronization
primitives to avoid introducing unintended memory barrier that may result in false negative tests
(the test would have failed without the sequence points). When waiting for predecessors, a
two-stage back-off strategy is employed: Thread.onSpinWait() is called up to 1000 times to
catch tight visibility races in CPU-bound tests without triggering a context switch; if the
predecessor is still not ready, Thread.yield() is called to prevent deadlocks or extreme
performance degradation in I/O-bound or heavily over-provisioned environments.
The Happenstance.Builder.happenInOrder(K...) method is intended to be called from the main thread to set
up the DAG of relationships between sequence points before the checkpoint() or
join() method is called from any threads.
- Since:
- 9.9.3
-
Nested Class Summary
Nested Classes -
Method Summary
Modifier and TypeMethodDescriptionstatic <K> Happenstance.Builder<K> Returns a newHappenstance.Builderinitialized withsequencePoints.static <K> Happenstance.Builder<K> builder(K... sequencePoints) Returns a newHappenstance.Builderinitialized withsequencePoints.voidcheckpoint(K sequencePoint) Waits for all predecessors ofsequencePointto have checked in, then markssequencePointas checked-in and returns.voidJoins until all predecessors ofsequencePointhave checked in, then markssequencePointas checked-in and returns.
-
Method Details
-
builder
Returns a newHappenstance.Builderinitialized withsequencePoints. No order is defined among these sequence points, until you explicitly callHappenstance.Builder.happenInOrder(K...). -
builder
Returns a newHappenstance.Builderinitialized withsequencePoints. No order is defined among these sequence points, until you explicitly callHappenstance.Builder.happenInOrder(K...). -
join
Joins until all predecessors ofsequencePointhave checked in, then markssequencePointas checked-in and returns.This method differs from
checkpoint(K)in that it establishes happens-before relationship between sequence points, which means writes happening beforejoin(A)are visible to code afterjoin(B)as long ashappenInOrder(A, B)is specified.Warning:Using
join()inappropriately may result in false negative tests if the SUT has a bug that writes to non-volatile state, because thejoin()call will accidentally "fix" the bug by making the write visible to other threads.- Parameters:
sequencePoint- the sequence point to wait for and mark as completed.- Throws:
IllegalArgumentException- ifsequencePointwasn't defined viaHappenstance.Builder.happenInOrder(K...).IllegalStateException- ifsequencePointhas already been marked as completed.
-
checkpoint
Waits for all predecessors ofsequencePointto have checked in, then markssequencePointas checked-in and returns.To avoid introducing unintended memory barriers, this method only establishes temporal ordering; no additional happens-before relationship between sequence points is established, which means writes before the checkpoint A may still be invisible to reads after checkpoint B even with
happenInOrder(A, B). The SUT itself should establish happens-before relationship if necessary.If extra memory barrier doesn't defeat your concurrency tests, and you need to establish happens-before relationships, use
join(K)instead.- Parameters:
sequencePoint- the sequence point to wait for and mark as completed.- Throws:
IllegalArgumentException- ifsequencePointwasn't defined viaHappenstance.Builder.happenInOrder(K...).IllegalStateException- ifsequencePointhas already been marked as completed.
-