Skip to content

Commit 053fb87

Browse files
authored
chore: configurable global UncaughtExceptionHandler for wiring framework (#19164)
Signed-off-by: Michael Heinrichs <[email protected]>
1 parent 48d3a62 commit 053fb87

File tree

5 files changed

+63
-5
lines changed

5 files changed

+63
-5
lines changed

platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/turtle/TurtleNode.java

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.apache.logging.log4j.LogManager;
4848
import org.apache.logging.log4j.Logger;
4949
import org.apache.logging.log4j.ThreadContext;
50+
import org.assertj.core.api.Assertions;
5051
import org.hiero.consensus.model.node.KeysAndCerts;
5152
import org.hiero.consensus.model.node.NodeId;
5253
import org.hiero.consensus.model.status.PlatformStatus;
@@ -318,6 +319,9 @@ private void doStartNode() {
318319

319320
model = WiringModelBuilder.create(platformContext.getMetrics(), time)
320321
.withDeterministicModeEnabled(true)
322+
.withUncaughtExceptionHandler((t, e) -> {
323+
Assertions.fail("Unexpected exception in wiring framework", e);
324+
})
321325
.build();
322326
final SemanticVersion version =
323327
currentConfiguration.getValue(TurtleNodeConfiguration.SOFTWARE_VERSION, SemanticVersion.class);

platform-sdk/swirlds-common/src/main/java/com/swirlds/common/utility/RuntimeObjectRegistry.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public final class RuntimeObjectRegistry {
3030
/**
3131
* The time provider. This is used to get the current time for the records.
3232
*
33-
* <p>We also use this field to check if the registry has already been initialized by checking if it is {code null}.
33+
* <p>We also use this field to check if the registry has already been initialized by checking if it is {@code null}.
3434
* An {@link AtomicReference} is used to make the check thread-safe.
3535
*
3636
*/

platform-sdk/swirlds-component-framework/src/main/java/com/swirlds/component/framework/model/DeterministicWiringModel.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import com.swirlds.component.framework.wires.output.OutputWire;
1010
import com.swirlds.metrics.api.Metrics;
1111
import edu.umd.cs.findbugs.annotations.NonNull;
12+
import edu.umd.cs.findbugs.annotations.Nullable;
13+
import java.lang.Thread.UncaughtExceptionHandler;
1214
import java.time.Duration;
1315
import java.time.Instant;
1416
import java.util.ArrayList;
@@ -36,16 +38,23 @@ public class DeterministicWiringModel extends TraceableWiringModel {
3638

3739
private final DeterministicHeartbeatScheduler heartbeatScheduler;
3840

41+
private final UncaughtExceptionHandler taskSchedulerExceptionHandler;
42+
3943
/**
4044
* Constructor.
4145
*
4246
* @param metrics the metrics
4347
* @param time the time
48+
* @param taskSchedulerExceptionHandler the global {@link UncaughtExceptionHandler}
4449
*/
45-
DeterministicWiringModel(@NonNull final Metrics metrics, @NonNull final Time time) {
50+
DeterministicWiringModel(
51+
@NonNull final Metrics metrics,
52+
@NonNull final Time time,
53+
@Nullable final UncaughtExceptionHandler taskSchedulerExceptionHandler) {
4654
super(false);
4755
this.metrics = Objects.requireNonNull(metrics);
4856
this.heartbeatScheduler = new DeterministicHeartbeatScheduler(this, time, "heartbeat");
57+
this.taskSchedulerExceptionHandler = taskSchedulerExceptionHandler;
4958
}
5059

5160
/**
@@ -80,7 +89,12 @@ private void submitWork(@NonNull final Runnable work) {
8089
@NonNull
8190
@Override
8291
public <O> TaskSchedulerBuilder<O> schedulerBuilder(@NonNull final String name) {
83-
return new DeterministicTaskSchedulerBuilder<>(metrics, this, name, this::submitWork);
92+
final DeterministicTaskSchedulerBuilder<O> builder =
93+
new DeterministicTaskSchedulerBuilder<>(metrics, this, name, this::submitWork);
94+
if (taskSchedulerExceptionHandler != null) {
95+
builder.withUncaughtExceptionHandler(taskSchedulerExceptionHandler);
96+
}
97+
return builder;
8498
}
8599

86100
/**

platform-sdk/swirlds-component-framework/src/main/java/com/swirlds/component/framework/model/StandardWiringModel.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.swirlds.metrics.api.Metrics;
2020
import edu.umd.cs.findbugs.annotations.NonNull;
2121
import edu.umd.cs.findbugs.annotations.Nullable;
22+
import java.lang.Thread.UncaughtExceptionHandler;
2223
import java.time.Duration;
2324
import java.time.Instant;
2425
import java.util.ArrayList;
@@ -84,6 +85,11 @@ public class StandardWiringModel extends TraceableWiringModel {
8485
*/
8586
private final Duration healthyReportThreshold;
8687

88+
/**
89+
* The (optional) global {@link UncaughtExceptionHandler} for the wiring framework
90+
*/
91+
private final UncaughtExceptionHandler taskSchedulerExceptionHandler;
92+
8793
/**
8894
* Constructor.
8995
*
@@ -119,6 +125,8 @@ public class StandardWiringModel extends TraceableWiringModel {
119125
} else {
120126
anchor = null;
121127
}
128+
129+
taskSchedulerExceptionHandler = builder.getTaskSchedulerExceptionHandler();
122130
}
123131

124132
/**
@@ -128,7 +136,12 @@ public class StandardWiringModel extends TraceableWiringModel {
128136
@Override
129137
public final <O> TaskSchedulerBuilder<O> schedulerBuilder(@NonNull final String name) {
130138
throwIfStarted();
131-
return new StandardTaskSchedulerBuilder<>(this.time, this.metrics, this, name, defaultPool);
139+
final StandardTaskSchedulerBuilder<O> builder =
140+
new StandardTaskSchedulerBuilder<>(this.time, this.metrics, this, name, defaultPool);
141+
if (taskSchedulerExceptionHandler != null) {
142+
builder.withUncaughtExceptionHandler(taskSchedulerExceptionHandler);
143+
}
144+
return builder;
132145
}
133146

134147
/**

platform-sdk/swirlds-component-framework/src/main/java/com/swirlds/component/framework/model/WiringModelBuilder.java

+28-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.swirlds.base.time.Time;
55
import com.swirlds.metrics.api.Metrics;
66
import edu.umd.cs.findbugs.annotations.NonNull;
7+
import java.lang.Thread.UncaughtExceptionHandler;
78
import java.time.Duration;
89
import java.util.Objects;
910
import java.util.concurrent.ForkJoinPool;
@@ -25,6 +26,7 @@ public class WiringModelBuilder {
2526
private final Metrics metrics;
2627
private final Time time;
2728
private Duration healthyReportThreshold = Duration.ofSeconds(1);
29+
private UncaughtExceptionHandler taskSchedulerExceptionHandler = null;
2830

2931
/**
3032
* Create a new builder.
@@ -174,6 +176,21 @@ public WiringModelBuilder withHealthyReportThreshold(@NonNull final Duration hea
174176
return this;
175177
}
176178

179+
/**
180+
* Set the global {@link UncaughtExceptionHandler}. Default is {@code null}. Allows to set a global uncaught
181+
* exception handler for all threads created by the wiring model. This is useful for tests and during development
182+
* while in production the default handler should be used.
183+
*
184+
* @param taskSchedulerExceptionHandler the global uncaught exception handler
185+
* @return this
186+
*/
187+
@NonNull
188+
public WiringModelBuilder withUncaughtExceptionHandler(
189+
@NonNull final UncaughtExceptionHandler taskSchedulerExceptionHandler) {
190+
this.taskSchedulerExceptionHandler = Objects.requireNonNull(taskSchedulerExceptionHandler);
191+
return this;
192+
}
193+
177194
/**
178195
* Build the wiring model.
179196
*
@@ -184,7 +201,7 @@ public WiringModelBuilder withHealthyReportThreshold(@NonNull final Duration hea
184201
@NonNull
185202
public <T extends WiringModel> T build() {
186203
if (deterministicModeEnabled) {
187-
return (T) new DeterministicWiringModel(metrics, time);
204+
return (T) new DeterministicWiringModel(metrics, time, taskSchedulerExceptionHandler);
188205
} else {
189206
return (T) new StandardWiringModel(this);
190207
}
@@ -296,4 +313,14 @@ Time getTime() {
296313
Duration getHealthyReportThreshold() {
297314
return healthyReportThreshold;
298315
}
316+
317+
/**
318+
* Get the global {@link UncaughtExceptionHandler}.
319+
*
320+
* @return the global {@code UncaughtExceptionHandler}
321+
*/
322+
@NonNull
323+
UncaughtExceptionHandler getTaskSchedulerExceptionHandler() {
324+
return taskSchedulerExceptionHandler;
325+
}
299326
}

0 commit comments

Comments
 (0)