Forum Stats

  • 3,840,393 Users
  • 2,262,599 Discussions
  • 7,901,262 Comments

Discussions

AttachCurrentThread hangs after a few calls in a Apache2 PHP5 Extension

850072
850072 Member Posts: 3
edited Mar 28, 2011 1:52AM in Java Native Interface (JNI)
Hello everybody,

I have created a small C Library/API that calls with JNI a simple Java class with a few methods to work with a underlying Java Framework (Carrot2). That JVM/JNI code functions some kind like a bridge between C and Java. In a single-threaded environment I experienced no problems, the complete runtime flow works well, I get no crashes or other strange behaviour.

Now I have built an PHP5 extension that makes use of that JNI/C code, the extension is built as a class with constructor/destructor and has a few methods that call functions of my C library. The JNI/C side I have 2 data structs, one is for the JVM and its handle (JavaVM) as well as the Java class name and its path, the jclass object of that class, jvm options, classpath, etc. A second data struct is for the Java environment containing the JNIEnv pointer, the jobject of the Java class and a few more application related variables. The C code consists of JNI functions to work with the Java class to call its methods, a few JNI helper functions and some internal application code and data handling.

At module initialization (Apache startup) I use the PHP_MINIT and PHP_MSHUTDOWN functions to load and unload (on apache stop) the JVM. The JVM gets created and destroyed at these points.

I use the PHP constructor (inside the extension) to attach to the JVM, that is AttachCurrentThread with the class constructor, I create and object of the Java class and preload a few Java primitives that I need for my methods later on and after all this I can work with the Java class through JNI. On PHP5 class destruction (also in the extension), I detach from the JVM using 'DetachCurrentThread', free some resources and "delete" the JNIEnv pointer (set it to NULL).

The JVM data struct (containing the JVM object) is a static global in the PHP5 module since I think there is no need for thread safety. This data is only modified/used in module startup/shutdown. The JNI worker struct (data struct with the JNIEnv pointer and jobject, etc is safely handled by the Zend object store throughout the complete class, so this should be thread-safe. So I do not suspect any problem here.

Testing the PHP 5 extension with a small PHP5 script using that PHP class and a few methods in Apache debug mode (that is the -X switch for just one worker process) works like a charm, no problems, crashes or misbehaving situations, not up to now with my testing though. But running Apache in regular mode with multiple processes, I run in to problems. One major problem is, after a few reloads of the php script using the extension class (lets say 10-15 reloads; The request is handled by the same apache child process) the `AttachCurrentThread' call hangs and the child process remains in that state. Continuing reloading the script a new child process answers and also here, after 10-15 reloads of the script, I get the same problem. So I am not sure what is really happening here nor I do not know how to solve that issue. Is that a signal problem (I read, that there can be signal mask issues and the JVM hangs when a new thread tries to attach to it, for example, the Jakarta Tomcat JK JNI worker uses a signal hack because of this problem. But that does not help me.

Or is there some problem with the multi-threading itself (since the JVM is multithreaded too)? Another problem is, besides that hanging thread attach, that the processing sometimes stops in middle of a java call (I have one method that insert data (calling a add method in a for loop) with JNI into a Java list on the Java class side, there it can stop in middle of the insertion process.

So, for now, I have no idea how to continue. Like said, in a single threaded environment, as a process, the application runs fine without a flaw.

Some details of my development machines: Ubuntu Lucic 10.04 LTS (also another Ubuntu 8 LTS), Apache2 MPM Worker (multithreaded model) and also on another machine the prefork version, Java JRE 6.0.22, PHP 5.2.16 (compiled from source).

So any hints that can help me proceeding the development of this module for PHP would be great. Any help is appreciated. If there are questions or some unclear points, please let me know.

Andreas

Answers

  • jschellSomeoneStoleMyAlias
    jschellSomeoneStoleMyAlias Member Posts: 24,877 Gold Badge
    847069 wrote:
    I use the PHP constructor (inside the extension) to attach to the JVM, that is AttachCurrentThread with the class constructor, I create and object of the Java class and preload a few Java primitives that I need for my methods later on and after all this I can work with the Java class through JNI. On PHP5 class destruction (also in the extension), I detach from the JVM using 'DetachCurrentThread', free some resources and "delete" the JNIEnv pointer (set it to NULL).
    That is unclear.

    I presume that you have a PHP class which is making JNI calls.
    Presumably you have a single thread in the caller of the PHP class, specifically a single method, which creates the PHP class, interacts with it, and then destroys it.

    Most problems with JNI is caused by pointer problems or misusing the API (like not handling exceptions correctly.)

    Other than that the Sun VM has a command line option that reports useful information when using JNI.
  • 850072
    850072 Member Posts: 3
    edited Mar 25, 2011 1:36AM
    jschell wrote:
    847069 wrote:
    I use the PHP constructor (inside the extension) to attach to the JVM, that is AttachCurrentThread with the class constructor, I create and object of the Java class and preload a few Java primitives that I need for my methods later on and after all this I can work with the Java class through JNI. On PHP5 class destruction (also in the extension), I detach from the JVM using 'DetachCurrentThread', free some resources and "delete" the JNIEnv pointer (set it to NULL).
    That is unclear.

    I presume that you have a PHP class which is making JNI calls.
    Presumably you have a single thread in the caller of the PHP class, specifically a single method, which creates the PHP class, interacts with it, and then destroys it.
    Yes, programming PHP5 extensions in C or C++ let you implement a class or just flat functions that can be used in the PHP scripting language. I implemented a class. So my constructor attaches to the (already at Apache/Module startup created JVM). After that, I can use the methods I have implemented to use the carrot2 framework. To communicate with the Carrot2 library, I have written a simple Java Class containing static functions for adding documents, executing clustering algorithms and receiving the search results, so all pretty straightforward I think. When the PHP script exists (request finished) then the class destructor is called (also implemented in my PHP5 extension) to detach from the JVM and make some cleanup. This is generally how things work.
    Most problems with JNI is caused by pointer problems or misusing the API (like not handling exceptions correctly.)

    Other than that the Sun VM has a command line option that reports useful information when using JNI.
    I can compile and run my JNI code with some test data from command line and I pass to the JVM some debugging parameters (such as -Xcheck:jni, -verbose:jni and Xint) for testing and debugging. I get no exceptions, errors or warnings. Everything is executed cleanly, I see some JNI/JVM debugging output (such as Dynamic-linking native method messages) along my own debugging output and the program finishes normally. Most JNI calls are wrapped in special macros and I check for exceptions, describe and clear them and act with these situtions (if any), I also added a method to the Java Class where I am able to receive the Java backrace for further investigation and abort execution.

    So for now I am out of ideas. This is why I don't understand these problems within the Apache/PHP process. Like stated above, Apache with one worker "-X" the complete code runs fine, even with consecutive calls all is fine, but then in regular multiprocess mode, I get the above problems like the AttachCurrentThread hangs or the execution of called methods of my Java class (inserting documents to a ArrayList or executing one of the clustering algorithms) hang and I have no other choice but to stop Apache.

    One more thing, I valgrinded my C code as far as possible. It is not possible to valgrind C code that creates and executes a JVM. Valgrind will stop and report "Error occurred during initialization of VM". No wonder though, I do not expect valgrind to execute the JVM beast in its context. The PHP5 version i compiled from source is thread-safe (at least I compiled it with the proper flags for that).

    So any other debugging hints or ideas are welcome.

    Edited by: 847069 on Mar 24, 2011 10:31 PM

    Edited by: 847069 on Mar 24, 2011 10:35 PM
  • jschellSomeoneStoleMyAlias
    jschellSomeoneStoleMyAlias Member Posts: 24,877 Gold Badge
    847069 wrote:
    jschell wrote:
    847069 wrote:
    I use the PHP constructor (inside the extension) to attach to the JVM, that is AttachCurrentThread with the class constructor, I create and object of the Java class and preload a few Java primitives that I need for my methods later on and after all this I can work with the Java class through JNI. On PHP5 class destruction (also in the extension), I detach from the JVM using 'DetachCurrentThread', free some resources and "delete" the JNIEnv pointer (set it to NULL).
    That is unclear.

    I presume that you have a PHP class which is making JNI calls.
    Presumably you have a single thread in the caller of the PHP class, specifically a single method, which creates the PHP class, interacts with it, and then destroys it.
    Yes, programming PHP5 extensions in C or C++ let you implement a class or just flat functions that can be used in the PHP scripting language. I implemented a class. So my constructor attaches to the (already at Apache/Module startup created JVM). After that, I can use the methods I have implemented to use the carrot2 framework. To communicate with the Carrot2 library, I have written a simple Java Class containing static functions for adding documents, executing clustering algorithms and receiving the search results, so all pretty straightforward I think. When the PHP script exists (request finished) then the class destructor is called (also implemented in my PHP5 extension) to detach from the JVM and make some cleanup. This is generally how things work.
    That description does not follow what I said.

    The language you are using to call java is irrelevant in terms of my description.

    What matter is that you MUST do the following in a single thread of execution.
    1. Attach thread
    2. JNI calls
    3. Un-attach

    Your description above certainly seems to suggest you are not doing that.
  • 850072
    850072 Member Posts: 3
    jschell wrote:
    847069 wrote:
    jschell wrote:
    847069 wrote:
    I use the PHP constructor (inside the extension) to attach to the JVM, that is AttachCurrentThread with the class constructor, I create and object of the Java class and preload a few Java primitives that I need for my methods later on and after all this I can work with the Java class through JNI. On PHP5 class destruction (also in the extension), I detach from the JVM using 'DetachCurrentThread', free some resources and "delete" the JNIEnv pointer (set it to NULL).
    That is unclear.

    I presume that you have a PHP class which is making JNI calls.
    Presumably you have a single thread in the caller of the PHP class, specifically a single method, which creates the PHP class, interacts with it, and then destroys it.
    Yes, programming PHP5 extensions in C or C++ let you implement a class or just flat functions that can be used in the PHP scripting language. I implemented a class. So my constructor attaches to the (already at Apache/Module startup created JVM). After that, I can use the methods I have implemented to use the carrot2 framework. To communicate with the Carrot2 library, I have written a simple Java Class containing static functions for adding documents, executing clustering algorithms and receiving the search results, so all pretty straightforward I think. When the PHP script exists (request finished) then the class destructor is called (also implemented in my PHP5 extension) to detach from the JVM and make some cleanup. This is generally how things work.
    That description does not follow what I said.

    The language you are using to call java is irrelevant in terms of my description.

    What matter is that you MUST do the following in a single thread of execution.
    1. Attach thread
    2. JNI calls
    3. Un-attach

    Your description above certainly seems to suggest you are not doing that.
    Sorry, if that was unclear. Yes, I know that should all be executed in ONE single thread. And I also assume, that a Apache requests and therefore the execution of a PHP script in a request (in this case the script using my PHP 5 Class extension with the JNI calls) should be executing in one single thread.

    But maybe there is some issue with the thread-safety in the PHP core (even it is compiled with thread-safety flag) and in fact that is one point where I need to get some more clarification. Also the point when I say "I assume, a request is handled and executed in one single thread" I need to get some light on.

    Anyway, like said, running Apache in "-X" mode with just one process, things work fine, so I think there is a problem when Apache/PHP is operated with multiple processes. Possibly there problem is with the stack and its data consistency. If the PHP code with Apache would be reentrant, each thread would maintain its own stack and maybe I wouldn't have that problem. With this said, i need to do some more investigation to be really sure and to make a step in the right direction to solve that issue. Maybe also a test with the pre-forked version of Apache will help. I hope I get things solved and can continue with the actual code development.

    Andreas
This discussion has been closed.