Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Runtime.exec() bug: hangs without providing a Process object

Whether I use this:

process = Runtime.getRuntime().exec("logcat -d time");

or that:

process = new ProcessBuilder()
              .command("logcat", "-d", "time")
              .redirectErrorStream(true)
              .start();

I get the same results: it often hangs within the exec() or start() call, no matter what I tried to do! The thread running this cannot even be interrupted with Thread.interrupt()! The child process is definitely started and if killed the above commands return.

These calls may fail on first attempt, so THERE IS NO WAY TO READ THEIR OUTPUT! I can also use a simple "su -c kill xxx" command line, same result!

EDIT: Started debugging the java_lang_ProcessManager.cpp file in an NDK project with some debugging logs! So here is what I found so far, after the fork() the parent does this:

int result;
int count = read(statusIn, &result, sizeof(int));            <- hangs there
close(statusIn);

Though the child process is not supposed to block on it: That's what the child does (if started at all!):

    // Make statusOut automatically close if execvp() succeeds.
    fcntl(statusOut, F_SETFD, FD_CLOEXEC);                      <- make the parent will not block

    // Close remaining unwanted open fds.
    closeNonStandardFds(statusOut, androidSystemPropertiesFd);  <- hangs here sometimes

    ...

    execvp(commands[0], commands);

    // If we got here, execvp() failed or the working dir was invalid.
    execFailed:
        int error = errno;
        write(statusOut, &error, sizeof(int));
        close(statusOut);
        exit(error);

The child can fail for 2 reproducible reasons: 1- child code is not running, but the parent believes it is! 2- child blocks on closeNonStandardFds(statusOut, androidSystemPropertiesFd);

In either case the read(statusIn...) in the parent ends in deadlock! and a child process is left dead (and cannot be accessed, pid unknown, no Process object)!

like image 206
3c71 Avatar asked Dec 31 '11 13:12

3c71


People also ask

What does runtime getRuntime () exec () do?

Runtime features a static method called getRuntime() , which retrieves the current Java Runtime Environment. That is the only way to obtain a reference to the Runtime object. With that reference, you can run external programs by invoking the Runtime class's exec() method.

What is runtime exec in Java?

exec(String command) method executes the specified string command in a separate process. This is a convenience method. An invocation of the form exec(command) behaves in exactly the same way as the invocation exec(command, null, null).


2 Answers

This problem is fixed in Jelly Bean (Android 4.1) but not in ICS (4.0.4) and I guess it will never be fixed in ICS.

like image 102
chrulri Avatar answered Sep 28 '22 01:09

chrulri


Above solution didn't prove to be reliable in any ways, causing more issues on some devices!

So I reverted back to the standard .exec() and kept digging...

Looking at the child code that hangs, I noticed the child process will hang while trying to close all file descriptors inherited from the parent (except the one created within the exec() call) !

So I search the whole app code for any BufferedReader/Writer and similar classes to make sure those would be closed when calling exec()!

The frequency of the issue was considerably reduced, and actually never occured again when I removed the last opened file descriptor before calling exec().

NB: Make sure SU binary is up-to-date, it can actually cause this issue too!

Enjoy your search ;)

like image 42
3c71 Avatar answered Sep 28 '22 00:09

3c71