diff --git a/reactor-core/src/main/java/reactor/core/Exceptions.java b/reactor-core/src/main/java/reactor/core/Exceptions.java index f82fe86b11..bfacf990ea 100644 --- a/reactor-core/src/main/java/reactor/core/Exceptions.java +++ b/reactor-core/src/main/java/reactor/core/Exceptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021 VMware Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2016-2022 VMware Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import reactor.core.publisher.Flux; +import reactor.util.Logger; +import reactor.util.Loggers; import reactor.util.annotation.Nullable; import reactor.util.retry.Retry; @@ -36,6 +38,8 @@ */ public abstract class Exceptions { + private static final Logger LOGGER = Loggers.getLogger(Exceptions.class); + /** * A common error message used when a reactive streams source doesn't seem to respect * backpressure signals, resulting in an operator's internal queue to be full. @@ -412,6 +416,68 @@ public static Throwable terminate(AtomicReferenceFieldUpdater return current; } + /** + * Check if a {@link Throwable} is considered by Reactor as Jvm Fatal and would be thrown + * by both {@link #throwIfFatal(Throwable)} and {@link #throwIfJvmFatal(Throwable)}. + * This is a subset of {@link #isFatal(Throwable)}, namely: + * + *

+ * Unless wrapped explicitly, such exceptions would always be thrown by operators instead of + * propagation through onError, potentially interrupting progress of Flux/Mono sequences. + * When they occur, the JVM itself is assumed to be in an unrecoverable state, and so is Reactor. + * + * @see #throwIfFatal(Throwable) + * @see #throwIfJvmFatal(Throwable) + * @see #isFatal(Throwable) + * @param t the {@link Throwable} to check + * @return true if the throwable is considered Jvm Fatal + */ + public static boolean isJvmFatal(@Nullable Throwable t) { + return t instanceof VirtualMachineError || + t instanceof ThreadDeath || + t instanceof LinkageError; + } + + /** + * Check if a {@link Throwable} is considered by Reactor as Fatal and would be thrown by + * {@link #throwIfFatal(Throwable)}. + *

+ *

+ * Unless wrapped explicitly, such exceptions would always be thrown by operators instead of + * propagation through onError, potentially interrupting progress of Flux/Mono sequences. + * When they occur, the assumption is that Reactor is in an unrecoverable state (notably + * because the JVM itself might be in an unrecoverable state). + * + * @see #throwIfFatal(Throwable) + * @see #isJvmFatal(Throwable) + * @param t the {@link Throwable} to check + * @return true if the throwable is considered fatal + */ + public static boolean isFatal(@Nullable Throwable t) { + return isFatalButNotJvmFatal(t) || isJvmFatal(t); + } + + /** + * Internal intermediate test that only detect Fatal but not Jvm Fatal exceptions. + */ + static boolean isFatalButNotJvmFatal(@Nullable Throwable t) { + return t instanceof BubblingException || t instanceof ErrorCallbackNotImplemented; + } + /** * Throws a particular {@code Throwable} only if it belongs to a set of "fatal" error * varieties. These varieties are as follows: