Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Native Interface sneaky forking behavior

After a very long hunt and for a related bug, I came to this strange behavior:

If on Linux I run a single JNI method to do a select:

JNIEXPORT void JNICALL Java_SelectJNI_select(JNIEnv *env, jobject thisObj) {
  // Print the curerent PID
  fprintf(stderr, "PID: %d\n", getpid());

  // Wait for 30 seconds
  struct timeval *timeout = (struct timeval *) calloc(1, sizeof(struct timeval));
  timeout->tv_sec = 30;
  timeout->tv_usec = 0;
  select(0, NULL, NULL, NULL, timeout);

  return;
}

and then I run the executable with strace, the select is not executed with the PID I have printed, but with the PID of a child, with the original object actually waiting on a mutex (this doesn't happen if I execute the same call in a plain small C program).

Say strace -f -o strace_output.txt java SelectJNI prints:

PID: 46811 

then grep select\( strace_output.txt will return:

46812 select(0, NULL, NULL, NULL, {tv_sec=30, tv_usec=0} <unfinished ...>

My guess is that JNI is forking and, in some way replacing the original select with its own wrapped version, probably to remain responsive.

I have a lot of questions, but the ones I care more about are:

  1. Is my hypothesis correct? JNI replacing functions under my feet?
  2. Is this behavior documented somewhere?
  3. The process where the actual select is invoked seems always to be that of the first child. Can I rely on that? If not, how do I find out where select is actually running?
like image 884
Rick77 Avatar asked Nov 28 '25 03:11

Rick77


2 Answers

The JVM may indeed fork, but it does so to create new JVM threads, rather than whole processes. While 46811 is the PID, the thread that's actually running your code in question has TID 46812 (which is what strace prints), while still running under PID 46811. Replacing getpid with gettid in the sample should lead to a consistent output.

like image 50
nanofarad Avatar answered Nov 30 '25 15:11

nanofarad


I want to elaborate on the accepted answer by @nanofarad and address the 3 points of my own question explicitly.

My guess is that JNI is forking and, in some way replacing the original select with its own wrapped version, probably to remain responsive. [...]

  1. Is my hypothesis correct? JNI replacing functions under my feet?

No, it is not.

The select executed by JNI has nothing special to it.

The hypothesis that JNI was replacing it with "something that forks the process" was wrong: I just misinterpreted the TID printed by strace for a PID.

JNI just executes the strace in the Java thread.

  1. Is this behavior documented somewhere?

No need to: since the JNI call is executed in the calling Java thread there is nothing to write on the matter.

  1. The process where the actual select is invoked seems always to be that of the first child (et cetera...)

It's the TID of the first spawned thread that appears to be always equal to PID + 1, but i's a likely behavior (the Java thread is created right after the runtime is started), it is not bound to be.

like image 22
Rick77 Avatar answered Nov 30 '25 17:11

Rick77



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!