Skip to content

Commit db10ab0

Browse files
committed
Make Event a sealed interface instead
Java has proper sum types now. We should use them. This requires language level 21.
1 parent 5d68819 commit db10ab0

13 files changed

Lines changed: 92 additions & 120 deletions

File tree

framework/tst-self/dslabs/framework/testing/utils/CloningTest.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -416,17 +416,13 @@ public void serializableTraces()
416416
throws InvocationTargetException, InstantiationException, IllegalAccessException {
417417
List<Event> es = new ArrayList<>();
418418
es.add(
419-
new Event(
420-
new MessageEnvelope(
421-
new LocalAddress("foo"),
422-
new AddressExample("bar"),
423-
new MessageExample("asdf", false))));
419+
new MessageEnvelope(
420+
new LocalAddress("foo"), new AddressExample("bar"), new MessageExample("asdf", false)));
424421
es.add(
425-
new Event(
426-
new MessageEnvelope(
427-
new LocalAddress("asdf"),
428-
new AddressExample("1234"),
429-
new MessageExample("baz", false))));
422+
new MessageEnvelope(
423+
new LocalAddress("asdf"),
424+
new AddressExample("1234"),
425+
new MessageExample("baz", false)));
430426

431427
var con = SerializableTrace.class.getDeclaredConstructors()[0];
432428
con.setAccessible(true);

framework/tst/dslabs/framework/testing/Event.java

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,54 +24,17 @@
2424

2525
import dslabs.framework.Address;
2626
import java.io.Serializable;
27-
import lombok.EqualsAndHashCode;
28-
import lombok.Getter;
2927

3028
/**
3129
* Represents either a timer to fire or a message to deliver.
3230
*
3331
* @see dslabs.framework.testing.search.SearchState
3432
* @see dslabs.framework.testing.runner.RunState
3533
*/
36-
@Getter
37-
@EqualsAndHashCode
38-
public class Event implements Serializable {
39-
// invariant: exactly one of the {message, timer} fields is null
40-
private final MessageEnvelope message;
41-
private final TimerEnvelope timer;
34+
public sealed interface Event extends Serializable permits MessageEnvelope, TimerEnvelope {
35+
Address locationRootAddress();
4236

43-
public Event(MessageEnvelope messageEnvelope) {
44-
this.message = messageEnvelope;
45-
this.timer = null;
46-
}
37+
boolean isMessage();
4738

48-
public Event(TimerEnvelope timer) {
49-
this.message = null;
50-
this.timer = timer;
51-
}
52-
53-
public boolean isMessage() {
54-
return message != null;
55-
}
56-
57-
public boolean isTimer() {
58-
return timer != null;
59-
}
60-
61-
public Address locationRootAddress() {
62-
if (isMessage()) {
63-
return message.to().rootAddress();
64-
} else {
65-
return timer.to().rootAddress();
66-
}
67-
}
68-
69-
@Override
70-
public String toString() {
71-
if (isMessage()) {
72-
return message.toString();
73-
} else {
74-
return timer.toString();
75-
}
76-
}
39+
boolean isTimer();
7740
}

framework/tst/dslabs/framework/testing/MessageEnvelope.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,26 @@
2424

2525
import dslabs.framework.Address;
2626
import dslabs.framework.Message;
27-
import java.io.Serializable;
28-
import lombok.Data;
27+
import org.checkerframework.checker.nullness.qual.NonNull;
2928

30-
@Data
31-
public final class MessageEnvelope implements Serializable {
32-
private final Address from, to;
33-
private final Message message;
29+
public record MessageEnvelope(Address from, Address to, Message message) implements Event {
30+
@Override
31+
public Address locationRootAddress() {
32+
return to.rootAddress();
33+
}
34+
35+
@Override
36+
public boolean isMessage() {
37+
return true;
38+
}
3439

35-
public MessageEnvelope(Address from, Address to, Message message) {
36-
this.from = from;
37-
this.to = to;
38-
this.message = message;
40+
@Override
41+
public boolean isTimer() {
42+
return false;
3943
}
4044

4145
@Override
42-
public String toString() {
46+
public @NonNull String toString() {
4347
return String.format("Message(%s -> %s, %s)", from, to, message);
4448
}
4549
}

framework/tst/dslabs/framework/testing/TimerEnvelope.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
import dslabs.framework.Address;
2626
import dslabs.framework.Timer;
2727
import dslabs.framework.VizIgnore;
28-
import java.io.Serializable;
2928
import java.util.Random;
3029
import lombok.Data;
3130
import lombok.EqualsAndHashCode;
31+
import org.checkerframework.checker.nullness.qual.NonNull;
3232

3333
/**
3434
* Stores a timer, its delivery address, its duration, and its creation time. Equality is based on
@@ -38,7 +38,7 @@
3838
*/
3939
@Data
4040
@EqualsAndHashCode(of = {"to", "timer", "minTimerLengthMillis", "maxTimerLengthMillis"})
41-
public final class TimerEnvelope implements Serializable, Comparable<TimerEnvelope> {
41+
public final class TimerEnvelope implements Event, Comparable<TimerEnvelope> {
4242
private static final Random rand = new Random();
4343

4444
private final Address to;
@@ -69,6 +69,21 @@ public TimerEnvelope(
6969
this.startTimeNanos = System.nanoTime();
7070
}
7171

72+
@Override
73+
public Address locationRootAddress() {
74+
return to.rootAddress();
75+
}
76+
77+
@Override
78+
public boolean isMessage() {
79+
return false;
80+
}
81+
82+
@Override
83+
public boolean isTimer() {
84+
return true;
85+
}
86+
7287
public long endTimeNanos() {
7388
return startTimeNanos + (((long) timerLengthMillis()) * 1000000);
7489
}
@@ -82,7 +97,7 @@ public boolean isDue() {
8297
}
8398

8499
@Override
85-
public int compareTo(TimerEnvelope o) {
100+
public int compareTo(@NonNull TimerEnvelope o) {
86101
if (o == null) {
87102
return 1;
88103
}

framework/tst/dslabs/framework/testing/runner/Network.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ Event take() throws InterruptedException {
102102
newTimerEndTime.set(Long.MAX_VALUE);
103103
TimerEnvelope te = timers.peek();
104104
if (te != null && te.isDue()) {
105-
return new Event(timers.poll());
105+
return timers.poll();
106106
}
107107

108108
newMessageAvailable = false;
109109
MessageEnvelope me = messages.poll();
110110
if (me != null) {
111-
return new Event(me);
111+
return me;
112112
}
113113

114114
// Wait for new message or timer
@@ -129,7 +129,7 @@ Event take() throws InterruptedException {
129129
long endTime = te.endTimeNanos();
130130
long waitTime = endTime - System.nanoTime();
131131
if (waitTime <= MIN_WAIT_TIME_NANOS) {
132-
return new Event(timers.poll());
132+
return timers.poll();
133133
}
134134

135135
synchronized (this) {

framework/tst/dslabs/framework/testing/runner/RunState.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,16 @@ private void runNode(Address address, Node node, Inbox inbox) {
139139
break;
140140
}
141141

142-
if (item.isMessage()) {
143-
MessageEnvelope me = item.message();
144-
if (settings.shouldDeliver(me)) {
145-
node.handleMessage(me.message(), me.from(), me.to());
142+
switch (item) {
143+
case MessageEnvelope me -> {
144+
if (settings.shouldDeliver(me)) {
145+
node.handleMessage(me.message(), me.from(), me.to());
146+
}
146147
}
147-
}
148-
149-
if (item.isTimer()) {
150-
TimerEnvelope te = item.timer();
151-
if (settings.deliverTimers()) {
152-
node.onTimer(te.timer(), te.to());
148+
case TimerEnvelope te -> {
149+
if (settings.deliverTimers()) {
150+
node.onTimer(te.timer(), te.to());
151+
}
153152
}
154153
}
155154

framework/tst/dslabs/framework/testing/search/SearchState.java

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,15 @@ Collection<Event> events(SearchSettings settings) {
235235
// Deliver all possible messages
236236
for (MessageEnvelope message : network) {
237237
if (hasNode(message.to().rootAddress()) && settings.shouldDeliver(message)) {
238-
events.add(new Event(message));
238+
events.add(message);
239239
}
240240
}
241241

242242
// Deliver all possible timers
243243
for (Address address : addresses()) {
244244
if (settings.deliverTimers(address)) {
245245
for (TimerEnvelope timer : timers.get(address).deliverable()) {
246-
events.add(new Event(timer));
246+
events.add(timer);
247247
}
248248
}
249249
}
@@ -273,16 +273,10 @@ Collection<SearchState> step(SearchSettings settings) {
273273
}
274274

275275
public SearchState stepEvent(Event event, SearchSettings settings, boolean skipChecks) {
276-
// TODO: use enum for event type
277-
278-
if (event.isMessage()) {
279-
return stepMessage(event.message(), settings, skipChecks);
280-
}
281-
if (event.isTimer()) {
282-
return stepTimer(event.timer(), settings, skipChecks);
283-
}
284-
285-
return null;
276+
return switch (event) {
277+
case MessageEnvelope messageEnvelope -> stepMessage(messageEnvelope, settings, skipChecks);
278+
case TimerEnvelope timerEnvelope -> stepTimer(timerEnvelope, settings, skipChecks);
279+
};
286280
}
287281

288282
public SearchState stepMessage(
@@ -299,7 +293,7 @@ public SearchState stepMessage(
299293
return null;
300294
}
301295

302-
SearchState ns = new SearchState(this, toAddress, new Event(message));
296+
SearchState ns = new SearchState(this, toAddress, message);
303297
Message nm = Cloning.clone(message.message());
304298
Node n = ns.node(toAddress);
305299

@@ -355,7 +349,7 @@ public SearchState stepTimer(TimerEnvelope timer, SearchSettings settings, boole
355349
return null;
356350
}
357351

358-
SearchState ns = new SearchState(this, toAddress, new Event(timer));
352+
SearchState ns = new SearchState(this, toAddress, timer);
359353
Timer nt = Cloning.clone(timer.timer());
360354
Node n = ns.node(toAddress);
361355

@@ -397,8 +391,7 @@ class GraphNode {
397391
Event event = state.previousEvent;
398392
GraphNode node = new GraphNode(event);
399393

400-
if (event.isMessage()) {
401-
MessageEnvelope me = event.message();
394+
if (event instanceof MessageEnvelope me) {
402395
if (whenSent.containsKey(me)) {
403396
GraphNode p = whenSent.get(me);
404397
p.next.add(node);

framework/tst/dslabs/framework/testing/utils/CheckLogger.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import dslabs.framework.Node;
2626
import dslabs.framework.testing.ClientWorker;
2727
import dslabs.framework.testing.Event;
28+
import dslabs.framework.testing.MessageEnvelope;
29+
import dslabs.framework.testing.TimerEnvelope;
2830
import dslabs.framework.testing.Workload;
2931
import dslabs.framework.testing.search.SearchState;
3032
import java.util.Map;
@@ -53,15 +55,13 @@ public final class CheckLogger {
5355
}
5456

5557
private static String methodName(Event event, SearchState state) {
56-
String methodName;
57-
if (event.isMessage()) {
58-
methodName = "handle" + event.message().message().getClass().getSimpleName();
59-
} else if (event.isTimer()) {
60-
methodName = "on" + event.timer().timer().getClass().getSimpleName();
61-
} else {
62-
// Don't handle other methods for now
63-
return null;
64-
}
58+
String methodName =
59+
switch (event) {
60+
case MessageEnvelope messageEnvelope ->
61+
"handle" + messageEnvelope.message().getClass().getSimpleName();
62+
case TimerEnvelope timerEnvelope ->
63+
"on" + timerEnvelope.timer().getClass().getSimpleName();
64+
};
6565

6666
Node n = state.node(event.locationRootAddress().rootAddress());
6767
if (n instanceof ClientWorker) {

framework/tst/dslabs/framework/testing/visualization/EventTreeState.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ private EventTreeState(@NonNull SearchState state, EventTreeState parent) {
9191
assert e != null;
9292
Set<MessageEnvelope> temp = new HashSet<>(parent.undeliveredMessages);
9393
// Remove this message from the message list
94-
if (e.isMessage()) {
95-
temp.remove(e.message());
94+
if (e instanceof MessageEnvelope) {
95+
temp.remove(e);
9696
}
9797
temp.addAll(state.newMessages());
9898
undeliveredMessages = ImmutableSet.copyOf(temp);
@@ -200,7 +200,7 @@ boolean sendsDeliveredMessages() {
200200
continue;
201201
}
202202
Event e = state.previousEvent();
203-
if (e.isMessage() && !state.parent().undeliveredMessages().contains(e.message())) {
203+
if (e instanceof MessageEnvelope && !state.parent().undeliveredMessages().contains(e)) {
204204
return true;
205205
}
206206
}

framework/tst/dslabs/framework/testing/visualization/EventVisibilityPane.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import dslabs.framework.Message;
2828
import dslabs.framework.Timer;
2929
import dslabs.framework.testing.Event;
30+
import dslabs.framework.testing.MessageEnvelope;
31+
import dslabs.framework.testing.TimerEnvelope;
3032
import java.util.Comparator;
3133
import java.util.Map.Entry;
3234
import java.util.SortedMap;
@@ -150,7 +152,10 @@ boolean isHidden(Timer t) {
150152
}
151153

152154
boolean isHidden(Event e) {
153-
return e.isMessage() ? isHidden(e.message().message()) : isHidden(e.timer().timer());
155+
return switch (e) {
156+
case MessageEnvelope messageEnvelope -> isHidden(messageEnvelope.message());
157+
case TimerEnvelope timerEnvelope -> isHidden(timerEnvelope.timer());
158+
};
154159
}
155160
}
156161

0 commit comments

Comments
 (0)