Skip to content

Commit bbffb11

Browse files
committed
some clean up proposed by Claude
1 parent a0895e7 commit bbffb11

File tree

2 files changed

+23
-21
lines changed

2 files changed

+23
-21
lines changed

mug-concurrent-testing/src/main/java/com/google/mu/testing/concurrent/Happenstance.java

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.google.errorprone.annotations.ThreadSafe;
1717
import com.google.mu.util.graph.Walker;
1818
import com.google.mu.util.stream.BiStream;
19+
import com.google.mu.util.stream.Joiner;
1920

2021
/**
2122
* A utility to manipulate temporal ordering (via {@link #checkpoint}) or happens-before (via {@link
@@ -69,15 +70,15 @@ public final class Happenstance<K> {
6970
private static final int SPIN_THRESHOLD = 1000;
7071
private final Map<K, Integer> pointToIndex;
7172
private final int[][] predecessors;
72-
private final int[] completedStatus; // completion counts
73+
private final int[] checkInStatus; // 1 if checked in.
7374

7475
private Happenstance(Builder<K> builder) {
7576
this.pointToIndex = BiStream.from(builder.pointToIndex).toMap();
7677
this.predecessors =
7778
builder.predecessors.stream()
7879
.map(list -> list.stream().mapToInt(Integer::intValue).toArray())
7980
.toArray(int[][]::new);
80-
this.completedStatus = new int[builder.pointToIndex.size()];
81+
this.checkInStatus = new int[builder.pointToIndex.size()];
8182
}
8283

8384
/**
@@ -117,15 +118,12 @@ public static final class Builder<K> {
117118
Builder() {}
118119

119120
/**
120-
* Defines a happens-before relationship between consecutive {@code sequencePoints}. For
121-
* example, {@code inOrder("A", "B", "C")} specifies that sequence points "A", "B", and "C" must
121+
* Defines a ordering between consecutive {@code sequencePoints}. For example,
122+
* {@code inOrder("A", "B", "C")} specifies that sequence points "A", "B", and "C" must
122123
* be completed in that order ("A" before "B", and "B" before "C").
123124
*
124125
* <p>This method should be called to define all sequence point orders before {@link #build} is
125126
* called.
126-
*
127-
* @throws IllegalArgumentException if adding an edge introduces a cycle in the dependency
128-
* graph.
129127
*/
130128
@CanIgnoreReturnValue
131129
@SafeVarargs
@@ -138,18 +136,24 @@ public final Builder<K> happenInOrder(K... sequencePoints) {
138136
int u = pointToIndex.get(predecessor);
139137
int v = pointToIndex.get(successor);
140138
if (u != v && successors.get(u).add(v)) {
141-
checkArgument(
142-
!isReachable(v, u),
143-
"Adding edge %s -> %s creates a cycle",
144-
predecessor,
145-
successor);
146139
predecessors.get(v).add(u);
147140
}
148141
});
149142
return this;
150143
}
151144

145+
/**
146+
* Builds a {@link Happenstance} instance respecting the sequence points ordering.
147+
*
148+
* @throws IllegalArgumentException if the ordering contains a cycle
149+
*/
152150
public Happenstance<K> build() {
151+
Walker.inGraph((Integer index) -> successors.get(index).stream())
152+
.detectCycleFrom(pointToIndex.values())
153+
.ifPresent(cycle -> {
154+
throw new IllegalArgumentException(
155+
"cycle detected: " + cycle.map(indexToPoint::get).collect(Joiner.on(" -> ")));
156+
});
153157
return new Happenstance<>(this);
154158
}
155159

@@ -165,12 +169,6 @@ private int declareSequencePoint(K id) {
165169
return index;
166170
});
167171
}
168-
169-
private boolean isReachable(int from, int to) {
170-
return Walker.inGraph((Integer index) -> successors.get(index).stream())
171-
.breadthFirstFrom(from)
172-
.anyMatch(node -> node.intValue() == to);
173-
}
174172
}
175173

176174
/**
@@ -218,7 +216,7 @@ public void checkpoint(K sequencePoint) {
218216

219217
private void checkIn(K sequencePoint, Ordering ordering) {
220218
int index = uponSequencePoint(sequencePoint);
221-
int[] statuses = this.completedStatus;
219+
int[] statuses = this.checkInStatus;
222220
for (int predecessor : predecessors[index]) {
223221
for (int spins = 0; ordering.read(statuses, predecessor) == 0; spins++) {
224222
if (spins < SPIN_THRESHOLD) {

mug-concurrent-testing/src/test/java/com/google/mu/testing/concurrent/HappenstanceTest.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,18 @@ public void inOrder_diamond() {
8787
@Test
8888
public void inOrder_cycle_throwsIllegalArgumentException() {
8989
Happenstance.Builder<String> builder = Happenstance.<String>builder().happenInOrder("A", "B");
90-
assertThrows(IllegalArgumentException.class, () -> builder.happenInOrder("B", "A"));
90+
IllegalArgumentException thrown = assertThrows(
91+
IllegalArgumentException.class, () -> builder.happenInOrder("B", "A").build());
92+
assertThat(thrown).hasMessageThat().contains("A -> B -> A");
9193
}
9294

9395
@Test
9496
public void inOrder_longCycle_throwsIllegalArgumentException() {
9597
Happenstance.Builder<String> builder =
9698
Happenstance.<String>builder().happenInOrder("A", "B", "C");
97-
assertThrows(IllegalArgumentException.class, () -> builder.happenInOrder("C", "A"));
99+
IllegalArgumentException thrown = assertThrows(
100+
IllegalArgumentException.class, () -> builder.happenInOrder("C", "A").build());
101+
assertThat(thrown).hasMessageThat().contains("A -> B -> C -> A");
98102
}
99103

100104
@Test

0 commit comments

Comments
 (0)