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

[macos] FindClass in libjli.dylib never returns for a swing-related class

    XMLWordPrintable

    Details

    • CPU:
      x86_64
    • OS:
      os_x

      Description

      ADDITIONAL SYSTEM INFORMATION :
      Darwin mrmbp2019.local 20.3.0 Darwin Kernel Version 20.3.0: Thu Jan 21 00:07:06 PST 2021; root:xnu-7195.81.3~1/RELEASE_X86_64 x86_64


      A DESCRIPTION OF THE PROBLEM :
      After successfully creating a virtual machine with JNI_CreateJavaVM, the call to FindClass fails to return if the class parameter points to a class that extends, say, a JFrame. It works fine for simple use cases but not this one. It's actually never worked and that's probably why the appbundler for Java apps uses JLI_Launch, which is undocumented and has serious functional gaps.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Compile test.c
      2. Compile Test1.java and Test2.java
      3. Execute test.exe Test2
      4. Execute test.exe Test1

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      $ ./test1 Test2
      returned ok from JNI_CreateJavaVM
      returned ok from FindClass(Test2)
      returned ok from GetStaticMethodID
      Hello, world.
      returned ok from CallStaticVoidMethod

      $ ./test1 Test1
      returned ok from JNI_CreateJavaVM
      returned ok from FindClass(Test1)
      returned ok from GetStaticMethodID
      returned ok from CallStaticVoidMethod

      I realize test.c should have some code that waits for the swing threads to finish but before I could get to that part of the launcher, I discovered the problem that motivated me to open this bug.
      ACTUAL -
      $ ./test1 Test2
      returned ok from JNI_CreateJavaVM
      returned ok from FindClass(Test2)
      returned ok from GetStaticMethodID
      Hello, world.
      returned ok from CallStaticVoidMethod

      $ ./test.exe Test1
      returned ok from JNI_CreateJavaVM


      The Apple menubar changes to "test" but the menu appears to be unresponsive.

      If you print the call stack while Test1 is in limbo, you'll see many threads and the one that includes FindClass looks like this:

      $ ps -ef|grep test
        503 99615 54615 0 11:41AM ttys128 0:00.37 ./test1 Test1
        503 99625 8526 0 11:41AM ttys133 0:00.00 grep test

      $ echo "thread backtrace all" | lldb -p 99615
      (lldb) process attach --pid 99615
      Process 99615 stopped
      * thread #1, name = 'Java: AWT-AppKit', queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
      ...
      (lldb) thread backtrace all
      * thread #1, name = 'Java: AWT-AppKit', queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
        * frame #0: 0x00007fff20609e7e libsystem_kernel.dylib`mach_msg_trap + 10
          frame #1: 0x00007fff2060a1f0 libsystem_kernel.dylib`mach_msg + 60
          frame #2: 0x00007fff20736bf7 CoreFoundation`__CFRunLoopServiceMachPort + 316
          frame #3: 0x00007fff207352ca CoreFoundation`__CFRunLoopRun + 1315
          frame #4: 0x00007fff207346ce CoreFoundation`CFRunLoopRunSpecific + 563
          frame #5: 0x00007fff289bc630 HIToolbox`RunCurrentEventLoopInMode + 292
          frame #6: 0x00007fff289bc42c HIToolbox`ReceiveNextEventCommon + 709
          frame #7: 0x00007fff289bc14f HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 64
          frame #8: 0x00007fff22f549b1 AppKit`_DPSNextEvent + 883
          frame #9: 0x00007fff22f53177 AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1366
          frame #10: 0x00000001137f3ab8 libosxapp.dylib`-[NSApplicationAWT nextEventMatchingMask:untilDate:inMode:dequeue:] + 121
          frame #11: 0x00007fff22f4568a AppKit`-[NSApplication run] + 586
          frame #12: 0x00000001137f38fd libosxapp.dylib`+[NSApplicationAWT runAWTLoopWithApp:] + 222
          frame #13: 0x0000000113787ecd libawt_lwawt.dylib`-[AWTStarter starter:] + 869
          frame #14: 0x0000000113787a1c libawt_lwawt.dylib`+[AWTStarter start:] + 463
          frame #15: 0x0000000113788189 libawt_lwawt.dylib`JNI_OnLoad + 631
          frame #16: 0x000000010f0eb7f5 libjava.dylib`Java_java_lang_ClassLoader_00024NativeLibrary_load + 195
          frame #17: 0x0000000113b076c7
          frame #18: 0x0000000113af72bd
          frame #19: 0x0000000113af6a90
          frame #20: 0x0000000113af72bd
          frame #21: 0x0000000113af72bd
          frame #22: 0x0000000113aef7a7
          frame #23: 0x000000010e59f795 libjvm.dylib`JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*) + 1905
          frame #24: 0x000000010e5d082b libjvm.dylib`jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) + 633
          frame #25: 0x000000010e5d40a0 libjvm.dylib`jni_CallStaticVoidMethodV + 371
          frame #26: 0x000000010f0f83ef libjava.dylib`JNU_CallStaticMethodByName + 693
          frame #27: 0x0000000113637897 libawt.dylib`AWT_OnLoad + 569
          frame #28: 0x00000001136378e4 libawt.dylib`JNI_OnLoad + 9
          frame #29: 0x000000010f0eb7f5 libjava.dylib`Java_java_lang_ClassLoader_00024NativeLibrary_load + 195
          frame #30: 0x0000000113b076c7
          frame #31: 0x0000000113af72bd
          frame #32: 0x0000000113af6a90
          frame #33: 0x0000000113af72bd
          frame #34: 0x0000000113af72bd
          frame #35: 0x0000000113af72bd
          frame #36: 0x0000000113af7040
          frame #37: 0x0000000113aef7a7
          frame #38: 0x000000010e59f795 libjvm.dylib`JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*) + 1905
          frame #39: 0x000000010e603512 libjvm.dylib`JVM_DoPrivileged + 1692
          frame #40: 0x0000000113b076c7
          frame #41: 0x0000000113af7040
          frame #42: 0x0000000113af72bd
          frame #43: 0x0000000113aef7a7
          frame #44: 0x000000010e59f795 libjvm.dylib`JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*) + 1905
          frame #45: 0x000000010e56f1bc libjvm.dylib`InstanceKlass::call_class_initializer_impl(instanceKlassHandle, Thread*) + 242
          frame #46: 0x000000010e56deae libjvm.dylib`InstanceKlass::initialize_impl(instanceKlassHandle, Thread*) + 1136
          frame #47: 0x000000010e56da2a libjvm.dylib`InstanceKlass::initialize(Thread*) + 58
          frame #48: 0x000000010e69c970 libjvm.dylib`LinkResolver::resolve_static_call(CallInfo&, KlassHandle&, Symbol*, Symbol*, KlassHandle, bool, bool, Thread*) + 154
          frame #49: 0x000000010e69ffd0 libjvm.dylib`LinkResolver::resolve_invokestatic(CallInfo&, constantPoolHandle, int, Thread*) + 336
          frame #50: 0x000000010e69fb1d libjvm.dylib`LinkResolver::resolve_invoke(CallInfo&, Handle, constantPoolHandle, int, Bytecodes::Code, Thread*) + 643
          frame #51: 0x000000010e5986ea libjvm.dylib`InterpreterRuntime::resolve_invoke(JavaThread*, Bytecodes::Code) + 916
          frame #52: 0x0000000113b13d88
          frame #53: 0x0000000113aef7a7
          frame #54: 0x000000010e59f795 libjvm.dylib`JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*) + 1905
          frame #55: 0x000000010e56f1bc libjvm.dylib`InstanceKlass::call_class_initializer_impl(instanceKlassHandle, Thread*) + 242
          frame #56: 0x000000010e56deae libjvm.dylib`InstanceKlass::initialize_impl(instanceKlassHandle, Thread*) + 1136
          frame #57: 0x000000010e56da2a libjvm.dylib`InstanceKlass::initialize(Thread*) + 58
          frame #58: 0x000000010e56dc0e libjvm.dylib`InstanceKlass::initialize_impl(instanceKlassHandle, Thread*) + 464
          frame #59: 0x000000010e56da2a libjvm.dylib`InstanceKlass::initialize(Thread*) + 58
          frame #60: 0x000000010e56dc0e libjvm.dylib`InstanceKlass::initialize_impl(instanceKlassHandle, Thread*) + 464
          frame #61: 0x000000010e56da2a libjvm.dylib`InstanceKlass::initialize(Thread*) + 58
          frame #62: 0x000000010e56dc0e libjvm.dylib`InstanceKlass::initialize_impl(instanceKlassHandle, Thread*) + 464
          frame #63: 0x000000010e56da2a libjvm.dylib`InstanceKlass::initialize(Thread*) + 58
          frame #64: 0x000000010e56dc0e libjvm.dylib`InstanceKlass::initialize_impl(instanceKlassHandle, Thread*) + 464
          frame #65: 0x000000010e56da2a libjvm.dylib`InstanceKlass::initialize(Thread*) + 58
          frame #66: 0x000000010e56dc0e libjvm.dylib`InstanceKlass::initialize_impl(instanceKlassHandle, Thread*) + 464
          frame #67: 0x000000010e56da2a libjvm.dylib`InstanceKlass::initialize(Thread*) + 58
          frame #68: 0x000000010e60137f libjvm.dylib`find_class_from_class_loader(JNIEnv_*, Symbol*, unsigned char, Handle, Handle, unsigned char, Thread*) + 88
          frame #69: 0x000000010e5c57bd libjvm.dylib`jni_FindClass + 682
          frame #70: 0x000000010e278bab test1`main(argc=2, argv=0x00007ffee1989be0) at test1.c:33:18
          frame #71: 0x00007fff20659621 libdyld.dylib`start + 1


      If you dtruss that process for a few seconds, then you can see a count of function calls that it executes:
      sudo dtruss -c -f -p 99615
      ...
      kevent_id 117
      bsdthread_ctl 120
      sysctl 134
      ioctl 243
      select 474
      read 519
      ulock_wake 808
      ulock_wait 831
      workq_kernreturn 1007
      proc_info 1748
      write_nocancel 3747
      madvise 4525


      ---------- BEGIN SOURCE ----------
      $ cat Test1.java
      import java.awt.Dimension;
      import javax.swing.*;

      public class Test1 extends JFrame {
      public Test1 () {
              setSize(new Dimension(200,119));
      add(new JButton("HitMeWYRS"));
          }
      public static void main (final String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
      public void run () {
      Test1 t = new Test1();
      t.setVisible(true);
      }
      });
      }
      }

      $ cat Test2.java
      public class Test2 {
      public static void main (final String[] args) {
      System.out.println("Hello, world.");
      }
      }

      $ cat test1.c
      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <dlfcn.h>
      #include <string.h>
      #include "jni.h"

      int main ( int argc, char *argv[] ) {
          JavaVM *jvm;
          JNIEnv *jenv;
          JavaVMInitArgs jargs;
          JavaVMOption jopts[1];
          char lib[1024];
          char *className;

          className = argv[1];

          jopts[0].optionString = "-Djava.class.path=.";

          jargs.version = JNI_VERSION_1_8;
          jargs.nOptions = 1;
          jargs.options = jopts;
          jargs.ignoreUnrecognized = JNI_TRUE;

          long rc = JNI_CreateJavaVM(&jvm, (void**)&jenv, &jargs);
          if ( rc == JNI_ERR ) {
              printf("%ld error creating vm\n", rc);
              return 1;
          } else {
              printf("returned ok from JNI_CreateJavaVM\n");
          }

          jclass cls = (*jenv)->FindClass(jenv, className);
          if ( cls == NULL ) {
              printf("FindClass(%s) failed\n", className);
              (*jenv)->ExceptionDescribe(jenv);
              return 1;
          } else {
              printf("returned ok from FindClass(%s)\n", className);
          }

          jmethodID methid = (*jenv)->GetStaticMethodID(jenv, cls, "main", "([Ljava/lang/String;)V");
          if ( methid == NULL ) {
              printf("GetStaticMethodID failed\n");
              (*jenv)->ExceptionDescribe(jenv);
              return 1;
          } else {
              printf("returned ok from GetStaticMethodID\n");
          }

          jobjectArray arr = (*jenv)->NewObjectArray(
                  jenv,
                  1,
                  (*jenv)->FindClass(jenv, "java/lang/String"),
                  (*jenv)->NewStringUTF(jenv, "Hello, world.")
                  );
          (*jenv)->CallStaticVoidMethod(jenv, cls, methid, arr);
          if ( (*jenv)->ExceptionCheck(jenv) ) {
              printf("CallStaticVoidMethod failed\n");
              (*jenv)->ExceptionDescribe(jenv);
              return 1;
          } else {
              printf("returned ok from CallStaticVoidMethod\n");
          }
          return 0;
      }

      $ javac Test1.java Test2.java
      $ gcc -o test1 -g -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin test1.c $JAVA_HOME/lib/libjli.dylib
      $ ./test1 Test2 # this will work
      $ ./test1 Test1 # this will not work
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The only workaround is not a complete workaround. So far, the appbundler has to use the JLI_Launch function but that function does provide any feedback if, say, the class loader fails. The JNI_CreateJavaVM does indeed have sufficient affordance for getting failure information for every failure include those related to class loading.

      FREQUENCY : always

        Attachments

          Activity

            People

            Assignee:
            prr Philip Race
            Reporter:
            pnarayanaswa Praveen Narayanaswamy
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: