Skip to content

core: remove potential this-escapes by extracting constructions of StatusException and StatusRuntimeException to builders #10892

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

tharakadesilva
Copy link

@tharakadesilva tharakadesilva commented Feb 4, 2024

With JDK 21, this-escape issues are now being flagged by the compiler: https://bugs.openjdk.org/browse/JDK-8299995 by default and we started seeing these errors in our project:

external/io_grpc_grpc_java/api/src/main/java/io/grpc/StatusRuntimeException.java:50: warning: [this-escape] possible 'this' escape before subclass is fully initialized
    this(status, trailers, /*fillInStackTrace=*/ true);
        ^
external/io_grpc_grpc_java/api/src/main/java/io/grpc/StatusRuntimeException.java:58: warning: [this-escape] previous possible 'this' escape happens here via invocation
    fillInStackTrace();
                    ^
external/io_grpc_grpc_java/api/src/main/java/io/grpc/StatusException.java:48: warning: [this-escape] possible 'this' escape before subclass is fully initialized
    this(status, trailers, /*fillInStackTrace=*/ true);
        ^
external/io_grpc_grpc_java/api/src/main/java/io/grpc/StatusException.java:56: warning: [this-escape] previous possible 'this' escape happens here via invocation
    fillInStackTrace();

This solution is changing the bean creation to a builder pattern and moving the fillInStackTrace invocation happening in the constructor to the build function.

Copy link

linux-foundation-easycla bot commented Feb 4, 2024

CLA Signed

The committers listed above are authorized under a signed CLA.

…atusException and StatusRuntimeException to builders
@tharakadesilva tharakadesilva marked this pull request as ready for review February 4, 2024 23:24
@ejona86
Copy link
Member

ejona86 commented Feb 15, 2024

I think it'd probably be better to remove the fillInStackTrace stuff. It doesn't look used. If we end up using it again (it does have some advantages) then we'd extend the exception and make it unconditional in that new class.

What is the this leakage for this(status, trailers, /*fillInStackTrace=*/ true); ? I don't see how it happens.

*
* @since 1.0.0
*/
public StatusException(Status status) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no way we can delete this constructor. This is stable API.

@ejona86
Copy link
Member

ejona86 commented Mar 29, 2024

@tharakadesilva, are you still interested in this change?

@tharakadesilva
Copy link
Author

tharakadesilva commented Mar 29, 2024

Hi @ejona86 thanks for the feedback and sorry for the delay in response.

I think it'd probably be better to remove the fillInStackTrace stuff. It doesn't look used. If we end up using it again (it does have some advantages) then we'd extend the exception and make it unconditional in that new class.

Even if we move to an unconditional class we would have the same issue if we call the function from the constructor unless we make that class final.

What is the this leakage for this(status, trailers, /fillInStackTrace=/ true); ? I don't see how it happens.

It is only a possibility (or this might be a false positive). Here's my understanding (please bear with me in case this is wrong):

Let's take a sub-class like this:

public class MyException extends StatusException {
    private final AtomicInteger count;

    public MyException(@Nonnull final AtomicInteger count) {
        super();
        this.count = count;
    }

    @Override
    public void fillInStackTrace() {
        super.fillInStackTrace();
        count.increment();
    }
}
  1. We create a MyException object by passing an AtomicInteger.
  2. The constructor calls super constructor.
  3. Super constructor calls fillInStackTrace (from MyException coz it is overridden).
  4. It tries to do count.increment() but count is still null.

There's no way we can delete this constructor. This is stable API.

What would you recommend?

@tharakadesilva, are you still interested in this change?

I am. For now, I have a Bazel patch override with @SuppressWarning.

@panchenko
Copy link
Contributor

panchenko commented Mar 31, 2024

I think we can use the super constructor with 4 parameters which was introduced in java 1.7

diff --git a/api/src/main/java/io/grpc/StatusRuntimeException.java b/api/src/main/java/io/grpc/StatusRuntimeException.java
index 68b816fc7..70c4d10f0 100644
--- a/api/src/main/java/io/grpc/StatusRuntimeException.java
+++ b/api/src/main/java/io/grpc/StatusRuntimeException.java
@@ -29,8 +29,6 @@ public class StatusRuntimeException extends RuntimeException {
   private final Status status;
   private final Metadata trailers;
 
-  private final boolean fillInStackTrace;
-
   /**
    * Constructs the exception with both a status. See also {@link Status#asRuntimeException()}.
    *
@@ -51,21 +49,10 @@ public class StatusRuntimeException extends RuntimeException {
   }
 
   StatusRuntimeException(Status status, @Nullable Metadata trailers, boolean fillInStackTrace) {
-    super(Status.formatThrowableMessage(status), status.getCause());
+    super(Status.formatThrowableMessage(status), status.getCause(),
+            /* enableSuppression */ true, /* writableStackTrace */ fillInStackTrace);
     this.status = status;
     this.trailers = trailers;
-    this.fillInStackTrace = fillInStackTrace;
-    fillInStackTrace();
-  }
-
-  @Override
-  public synchronized Throwable fillInStackTrace() {
-    // Let's observe final variables in two states!  This works because Throwable will invoke this
-    // method before fillInStackTrace is set, thus doing nothing.  After the constructor has set
-    // fillInStackTrace, this method will properly fill it in.  Additionally, sub classes may call
-    // this normally, because fillInStackTrace will either be set, or this method will be
-    // overriden.
-    return fillInStackTrace ? super.fillInStackTrace() : this;
   }

@panchenko
Copy link
Contributor

created as #11064

@tharakadesilva
Copy link
Author

Closing in lieu of @panchenko's MR, thank you both!!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 1, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants