2

I am trying to run a simple echo program from Java (using ProcessBuilder), there is an infinite loop in which I get input from user (using Scanner). Then I use OutputStreamWriter to write it to stdin of the process, and I expect to see the same string echoed back through the stdout, but it doesn't unless I close the stream (which I do not want to) or I write a long string. ven when calling flush, if the string is short (like, 1 character) it does not work.

How can we force flushing, even for single character (short) strings?

ProcessBuilder pb = new ProcessBuilder(cmd);
Process p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
OutputStream stdin = p.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(stdin);
Scanner scanner = new Scanner(System.in);
String line;
while (true) {
    String input = scanner.nextLine();
    if (input.equals("q"))
        break;
    writer.write(input+"\n");
    writer.flush();
    // writer.close(); (uncommenting this makes it work once, then throw error)
    line = br.readLine();
    System.out.println(line);
}

Edit 3/18: I have tried windows sysinternals tools to track the write syscalls and it indeed looks like my guess is correct, the flush does not work unless it is a long string (or unless you close the stream).
Edit 3/19: I found this: Does fgets() locks stdout preventing printf which makes things even more interesting.

Iman Hosseini
  • 33
  • 1
  • 6

2 Answers2

1

The problem is in the interaction with a.exe.

a.exe uses fgets. It stops when either (n-1) characters are read, the newline character is read, or the end-of-file is reached, whichever comes first.

You do not push a new line to a.exe, thus fgets can only complete with EOF, which happens when you close the stream.

Either you may push an end-of-line from Java, or you may use another read function on the C side.

To push an end-of-line, you may use:

writer.write(System.lineSeparator());

See: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm

Kartal Tabak
  • 760
  • 1
  • 7
  • 18
  • I added "writer.write(System.lineSeparator());" but it still does not work. I also tried changing "writer.write(input);" to "writer.write(input+"\n");" still, did not work. – Iman Hosseini Mar 17 '19 at 21:09
  • Also, this cannot be the problem as even without doing the line seperator, it works if you type in a very long string. So I'm guessing the issue is with the flushing, as even if you have a short string and a newline, it still does not work which probably means it is not flushed onto "a.exe" – Iman Hosseini Mar 17 '19 at 21:17
1

The question is [finally] solved thanks to this question:
Does fgets() locks stdout preventing printf
So the problem was not actually java's fault, it is one of the idiosynchrasies of windows, here as I have hooked up streams to both stdin and stdout and while I have not closed stdin, it does not write to stdout.
So the solution: Either 1) flush stdout on "a.exe" side. OR 2) just don't use both streams. For example, on "a.exe" side, instead of stdout, write to file, and on java side consume from that file.
(1) is the better option IF you have access to "a.exe" but this might be an external program you have no access to.

Iman Hosseini
  • 33
  • 1
  • 6