1

A quick overview of the code of my C++ Qt application (simplified for the purpose of readability).

  1. Make an out-of-process call with QProcess
  2. Invoke a java class via JNI
  3. Make another out-of-process call with QProcess

The problem is that any QProcess calls after (2) does not end (stuck at waitForFinished). I believe the application does not receive SIGCHLD and is trapped by JVM.

However, if I create JVM first before any QProcess call, then all QProcess calls after JVM works.

In summary,

QProcess() -> invokeJNI() -> QProcess == gets stuck at 2nd QProcess' waitForFinished()

invokeJNI() -> QProcess ->QProcess .. == WORKS!

void invokeQProcess(int id) {
    QProcess process;
    QString program = "/sbin/md5";
    QStringList args;
    args << "/tmp/tempFile.txt";
    process.start(program, args);
    if (!process.waitForFinished(-1)) {
        QString output(process.readAllStandardOutput());
        qDebug() << id << " QProcess failed with exit code -1 " << output;
        return;
    }
    QString output(process.readAllStandardOutput());
    qDebug() << id << " QProcess succeeded with output - " << output;
}

void invokeJNI() {
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=."
    JavaVMInitArgs vm_args;
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;

    JavaVM *jvm;
    JNIEnv *env;

    JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
    jclass sampleClass = env->FindClass("SampleClass");
    jmethodID method = env->GetStaticMethodID(sampleClass, "main", "([Ljava/lang/String;)V");
    env->CallStaticVoidMethod(sampleClass, method, objArray);
    jvm->DestroyJavaVM();
}

int main() {
    invokeQProcess(1);
    invokeJNI();
    invokeQProcess(2);
    return 0;
}
public class SampleClass {
    public static void main(String[] args) {
        List<String> command = new ArrayList<String>();
        command.add("echo");
        command.add("HelloJava");
        ProcessBuilder pb = new ProcessBuilder(command);
        process = pb.start();
        try {
            process.waitFor();
        } catch (InterruptedException e) {
            process.destroy();
        }
    }
}

Setup: Qt 5.12 | OpenJDK 11.0.5 | issue in both Win & macOS

EDIT: The problem arises in the first sequence only when the java method in JNI call has an out-of-process call inside (I'm using ProcessBuilder). Updated JNI call and added Java class.

XOR
  • 314
  • 5
  • 11
  • 1
    Check `SIGCHLD` handling configuration before and after. Although I wouldn't expect to have that problem on Windows, where waiting for a process exit doesn't involve signals. – Ben Voigt Feb 05 '21 at 19:40
  • I'm unable to find a way to check the configuration. Can you point me to some resource? Also, I've updated the question with some new developments. – XOR Feb 06 '21 at 06:20
  • Trace the system calls for your example code. On MacOS: https://stackoverflow.com/questions/31045575/how-to-trace-system-calls-of-a-program-in-mac-os-x You'll see where your processes are getting stuck. I suspect your suspicion about the JVM interfering with Qt child process handling is correct, and why it works if you do the JVM first is that Qt has a static initialization that gets wiped out by the JVM if the JVM does its init after Qt initializes itself. – Andrew Henle Feb 06 '21 at 13:00
  • Shouldn't the JVM chain signals as mentioned here - https://docs.oracle.com/javase/10/vm/signal-chaining.htm – XOR Feb 08 '21 at 15:02
  • 1
    @XOR: I was thinking in terms of this "signal() returns the previous value of the signal handler, or `SIG_ERR` on error." and use of the "old action" parameter to `sigaction`. In particular you can pass null as the new action to `sigaction()` to read the existing configuration without making any changes. However `libjsig.so` would cause the return value of `signal()` and results of `sigaction()` to lie to you anyway (as noted in that documentation you linked on chaining). Are you linking with `libjsig.so` ? – Ben Voigt Feb 08 '21 at 17:14
  • For reading configuration of signal handling, see [here](https://pubs.opengroup.org/onlinepubs/7908799/xsh/sigaction.html) "If the argument oact is not a null pointer, the action previously associated with the signal is stored in the location pointed to by the argument oact. If the argument act is a null pointer, signal handling is unchanged; thus, the call can be used to enquire about the current handling of a given signal." – Ben Voigt Feb 08 '21 at 17:21
  • @XOR *Shouldn't the JVM chain signals* There might be more going on. Are you linking in the same C++ runtime libraries as the JVM, for example. If your code links in different C++ runtime libraries, you're lucky anything works at all. – Andrew Henle Feb 10 '21 at 11:59
  • @BenVoigt Tried out as you mentioned. `sigaction(SIGCHLD,NULL,&info)`. `info.sa_handler` was set to **SIG_DFL initially**; after 1st QProcess it **changed** (it was neither SIG_[DFL | IGN | ERR]; after JNI call it changed **back to SIG_DFL**; after which 2nd Qprocess is stuck (with sa_handler still pointing to SIG_DFL). And NO, I'm not linking with libjsig.so – XOR Mar 01 '21 at 07:46
  • @AndrewHenle: Tried `dtruss` but I'm unable to proceed much with the verbose output. From my previous comments, it seems that JVM wiped out the existing handler. Also, I'm not linking same libraries. I'm able to repro this in my bare minimum code with nothing but a QProcess call and a JNI call to java class having one simple out-of-process call. – XOR Mar 01 '21 at 07:57
  • Also, I stumbled upon this Qt bug report that almost overlaps with this case. https://bugreports.qt.io/browse/QTBUG-56338. If I understand it correctly, I should either upgrade to Qt 5.15 or not use JVM after any QProcess call (since it is stomping the previous handler). Is that right? – XOR Mar 01 '21 at 08:01
  • @XOR: If you want the signal chaining to happen, you should link with `libjsig`. Have you tried that; does it change anything? – Ben Voigt Mar 01 '21 at 16:22
  • @BenVoigt: Tried setting the 2 env variables as mentioned in the docs. Didn't make a difference. – XOR Mar 01 '21 at 19:07

0 Answers0