Class Happenstance<K>
- Type Parameters:
K- the type of the sequence points
join(K)) 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()
.sequence("writtenB", "readingA", "writtenA")
.sequence("readingB", "writtenB")
.build();
Stream.of("A", "B")
.parallel()
.forEach(
input -> {
happens.join("reading" + input);
sut.read(input);
sut.write(input);
happens.join("written" + input);
sut.finish(input);
});
}
}
Implementation note: when waiting for predecessors, a three-stage back-off strategy is
employed: Thread.onSpinWait() is called up to 1000 times to catch tight races in CPU-bound
tests without triggering a context switch; if the predecessor is still not ready, Thread.yield() is called up to 100 times to prevent deadlocks or extreme performance degradation
in heavily over-provisioned environments; beyond that the thread parks for 50µs at a time, so
that an I/O-bound SUT operation taking hundreds of milliseconds doesn't burn a core per waiter.
The Happenstance.Builder.sequence(K...) method is intended to be called from the main thread to set up
the DAG of relationships between sequence points before the 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) Deprecated.The JIT and the CPU can move the surrounding plain reads and writes across a checkpoint, so the ordering applies to the check-in calls themselves, not to the SUT code around them.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.sequence(K...). -
builder
Returns a newHappenstance.Builderinitialized withsequencePoints. No order is defined among these sequence points, until you explicitly callHappenstance.Builder.sequence(K...). -
join
Joins until all predecessors ofsequencePointhave checked in, then markssequencePointas checked-in and returns.This method establishes happens-before relationship between sequence points, which means writes happening before
join(A)are visible to code afterjoin(B)as long assequence(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.If the calling thread is interrupted while waiting, this method throws
AssertionErrorwithout checking in, and leaves the thread's interrupt status set. AnErroris used so that the bail-out isn't swallowed by acatch (Exception)in the code under test.- Parameters:
sequencePoint- the sequence point to wait for and mark as completed.- Throws:
IllegalArgumentException- ifsequencePointwasn't defined viaHappenstance.Builder.sequence(K...).IllegalStateException- ifsequencePointhas already been marked as completed.AssertionError- if the calling thread is interrupted while waiting for predecessors.
-
checkpoint
Deprecated.The JIT and the CPU can move the surrounding plain reads and writes across a checkpoint, so the ordering applies to the check-in calls themselves, not to the SUT code around them. Usejoin(K)instead.Waits for all predecessors ofsequencePointto have checked in, then markssequencePointas checked-in and returns.If the calling thread is interrupted while waiting, this method throws
AssertionErrorwithout checking in, and leaves the thread's interrupt status set. AnErroris used so that the bail-out isn't swallowed by acatch (Exception)in the code under test. The interrupt status is only consulted while actually waiting, so a sequence point with no predecessors, or whose predecessors have already checked in, checks in normally even on an interrupted thread.- Parameters:
sequencePoint- the sequence point to wait for and mark as completed.- Throws:
IllegalArgumentException- ifsequencePointwasn't defined viaHappenstance.Builder.sequence(K...).IllegalStateException- ifsequencePointhas already been marked as completed.AssertionError- if the calling thread is interrupted while waiting for predecessors.
-