Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8362890

IsSameObject reports an invalid local ref for the thread in VirtualThreadEnd

XMLWordPrintable

    • x86_64
    • linux_redhat_6.0

      ADDITIONAL SYSTEM INFORMATION :
      Docker image used to reproduce the issue is 's390x/eclipse-temurin:24'.

      A DESCRIPTION OF THE PROBLEM :
      On Linux s390x, running a Java app that uses virtual threads with a JVMTI agent that uses the `VirtualThreadEnd` event callback with the `-Xcheck:jni` option, reports the following FATAL error when using `IsSameObject` with the thread object passed as virtual thread in `VirtualThreadEnd`:
      ```
      + ./run.sh
      starting virtual thread
      virtualThreadStart
      joining virtual thread
      IsVirtualThread(0x3fee8000f00): 1
      IsSameObject(0x3fee8000f00, nullptr): 0
      hello from the virtual thread
      virtualThreadEnd
      IsVirtualThread(0x3ff984b2b78): 1
      FATAL ERROR in native method: Bad global or local ref passed to JNI
      Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
      V [libjvm.so+0x77b8cc] ReportJNIFatalError+0x44
      V [libjvm.so+0x78d97e] checked_jni_IsSameObject+0x216
      ./run.sh: line 3: 10 Aborted (core dumped) java --enable-preview -Xcheck:jni -agentpath:agent/agent.so VirtualThreadsSample.java
      ```

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Compile the provided JVMTI agent code and run the provided Java code with
      ```
      java -Xcheck:jni -agentpath:agent/agent.so VirtualThreadsSample.java
      ```

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Program runs successfully without fatal error reported.
      ACTUAL -
      Program aborts with the following fatal error:
      ```
      FATAL ERROR in native method: Bad global or local ref passed to JNI
      Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
      V [libjvm.so+0x77b8cc] ReportJNIFatalError+0x44
      V [libjvm.so+0x78d97e] checked_jni_IsSameObject+0x216
      ```

      ---------- BEGIN SOURCE ----------
      JVMTI agent code (agent.cpp, C++17)
      ```
      #include <jvmti.h>

      #include <cstdio>
      #include <string>

      namespace {

      void printJvmtiError(jvmtiEnv* const jvmti, jvmtiError const error, std::string const& msg) {
      char* errorName = nullptr;
      jvmti->GetErrorName(error, &errorName);

      fprintf(stderr, "error: jvmti: %s: %s (%d)\n", msg.c_str(), (errorName != nullptr ? errorName : "<unknown>"), error);
      }

      void JNICALL threadStart(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
      fprintf(stderr, "threadStart\n");
      }

      void JNICALL threadEnd(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
      fprintf(stderr, "threadEnd\n");
      }

      void JNICALL virtualThreadStart(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
      fprintf(stderr, "virtualThreadStart\n");
      fprintf(stderr, "IsVirtualThread(%p): %d\n", thread, jni->IsVirtualThread(thread));
      fprintf(stderr, "IsSameObject(%p, nullptr): %d\n", thread, jni->IsSameObject(thread, nullptr));
      }

      void JNICALL virtualThreadEnd(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
      fprintf(stderr, "virtualThreadEnd\n");
      fprintf(stderr, "IsVirtualThread(%p): %d\n", thread, jni->IsVirtualThread(thread));
      fprintf(stderr, "IsSameObject(%p, nullptr): %d\n", thread, jni->IsSameObject(thread, nullptr));
      }

      } // anonymous namespace

      JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* /*options*/, void* /*reserved*/) {
      jvmtiEnv* jvmti = nullptr;

      if (auto const error = jvm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0); error != JNI_OK || jvmti == nullptr) {
      fprintf(stderr, "failed to get access to JVMTI version 1.0\n");
      return JNI_ERR;
      }

      jvmtiCapabilities capabilities{};
      capabilities.can_support_virtual_threads = 1;

      if (auto const error = jvmti->AddCapabilities(&capabilities); error != JVMTI_ERROR_NONE) {
      printJvmtiError(jvmti, error, "failed to add the required capabilities");
      return JNI_ERR;
      }

      if (auto const error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, nullptr); error != JVMTI_ERROR_NONE) {
      printJvmtiError(jvmti, error, "failed to enable thread start events");
      return JNI_ERR;
      }

      if (auto const error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_THREAD_END, nullptr); error != JVMTI_ERROR_NONE) {
      printJvmtiError(jvmti, error, "failed to enable thread end events");
      return JNI_ERR;
      }

      if (auto const error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VIRTUAL_THREAD_START, nullptr); error != JVMTI_ERROR_NONE) {
      printJvmtiError(jvmti, error, "failed to enable virtual thread start events");
      return JNI_ERR;
      }

      if (auto const error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VIRTUAL_THREAD_END, nullptr); error != JVMTI_ERROR_NONE) {
      printJvmtiError(jvmti, error, "failed to enable virtual thread end events");
      return JNI_ERR;
      }

      jvmtiEventCallbacks eventCallbacks{};
      // eventCallbacks.ThreadStart = threadStart;
      // eventCallbacks.ThreadEnd = threadEnd;
      eventCallbacks.VirtualThreadStart = virtualThreadStart;
      eventCallbacks.VirtualThreadEnd = virtualThreadEnd;

      if (auto const error = jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks)); error != JVMTI_ERROR_NONE) {
      printJvmtiError(jvmti, error, "failed to set event callbacks");
      return JNI_ERR;
      }

      return JNI_OK;
      }
      ```

      Java sample code (VirtualThreadsSample.java)
      ```
      import java.time.Duration;

      public class VirtualThreadsSample {
          public static void main(final String[] args) throws InterruptedException {
              System.out.println("starting virtual thread");
              final Thread virtualThread = Thread.ofVirtual().start(() -> {
                  System.out.println("hello from the virtual thread");
                  try {
                      Thread.sleep(Duration.ofSeconds(1));
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              });

              System.out.println("joining virtual thread");
              virtualThread.join(Duration.ofSeconds(10));

              System.out.println("finished sample");
          }
      }
      ```

      ---------- END SOURCE ----------

            sspitsyn Serguei Spitsyn
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: