Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running haskell executable using Java ProcessBuilder: Why does hGetLine only return when I close outputStream?

Tags:

java

haskell

I'm wrapping the FM-SBLEX Saldo program in a Java library.

Saldo is written in Haskell and does lookups in a lexicon for lines given on stdin, e.g.

echo "ord"|./sblex/bin/saldo dicts/saldo.dict

prints something like the following to stdout

{"ord":{"s_1":{"word":"ord","head":"sanna mina ord","pos":"abm","param":"invar 1:3-3","inhs":[],"id":"sanna_mina_ord..abm.1","p":"abm_i_till_exempel","attr":"3"},...

If I run it with

./sblex/bin/saldo dicts/saldo.dict

it does a lookup for each line I enter at the console until I send EOF.

In my Java library, I start it with ProcessBuilder and set up a thread to dump stdout and stderr to my program's stdout, and another thread writes a word and a newline, then flushes outputStream

On the console, saldo returns results each time I press return, but in my wrapper, it returns results for all my input only once I close outputStream (see .close() is commented out in the next code block)

    ProcessBuilder pb = new ProcessBuilder(binPath, dictPath);

    pb.redirectErrorStream(true);
    saldoProcess = pb.start();

    new Thread(new Reader(saldoProcess.getInputStream())).start();
    new Thread(new Writer(saldoProcess.getOutputStream())).start();

    saldoProcess.waitFor();
    System.out.println("saldo exited.");
    Thread.sleep(2000);

Writer's run override:

    public void run() {
        try {
            outputStream.write("ord\n".getBytes());
            outputStream.flush();
            //outputStream.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

The Haskell code that reads the input

run' :: Language a => a -> (String -> [Tok]) -> (String -> [[String]]) -> AnaType -> Stats -> IO Stats
run' l tokenizer f a st = 
 do b <- hIsEOF stdin
    if b then return st 
     else do 
       s <- hGetLine stdin
       analyze l a f (tokenizer s) st >>= run' l tokenizer f a

If binPath="cat" and dictPath="-" my java program outputs the input after each flush. Any idea why this haskell program is only dealing with the input after I close the outputStream?

NB: as the answer shows, it was not hGetLine that didn't return (as I assumed) but the output that was buffered because the Haskell implementation I'm using defaults to block buffer if it's not run from the console.

like image 507
jbothma Avatar asked Jul 31 '26 16:07

jbothma


1 Answers

Your Haskell program is probably buffering its output. (Line-buffered when it's writing to a terminal, block-buffered when writing to anything else.)

Try adding

hSetBuffering stdout LineBuffering

near the start of the program.

More about buffering in Haskell.

(Edited in response to Daniel Wagner's comment.)

like image 93
dave4420 Avatar answered Aug 03 '26 04:08

dave4420