Nim osproc.startProcess input/output out of sync

90 views Asked by At

I'm using startProcess to communicate with a standalone module, but the input to and output from the module is out of sync with the calling program that catches the output.

I expect this:

Process started...
------------------------------------200
RCV:Test:  200
------------------------------------220
RCV:Test:  220

But instead I get this:

Process started...
------------------------------------200
------------------------------------220
RCV:Test:  200
RCV:Test:  220

I tried flushFile() on stdout thinking it might be buffered, but that didn't help. The behavior is different when using just readLine() directly or in a while hasData() loop, but it's still out of sync. This is compiled as nim c -mm:orc <file>. What am I doing wrong?

Main program

# startProcess test to show output of background process

import std/osproc
import std/os
import std/streams
import std/strutils
import std/strformat
import times

var
  projectModule = "listenreply"  
  pModule: Process
  pOut: Stream
  pIn: Stream
  pErr: Stream

proc pipeModule(message: string): string =
  var catchOutput: string = ""
  pIn.writeLine(message)
  pIn.flush()
  while hasData(pModule):
    catchOutput = catchOutput & "\n" & pOut.readLine()
  return catchOutput

proc loadModule(module: string) =
  debugEcho("Starting process...")
  pModule = startProcess(module, args = [""], options = {poUsePath}) #, poInteractive})
  pOut = pModule.outputStream()
  pIn = pModule.inputStream()
  pErr = pModule.errorStream()
  debugEcho("Process started...")

proc unloadModule(module: string) =
  discard pModule.waitForExit(2000)
  var exitCode = pModule.peekExitCode()
  echo "exit code: ", exitCode
  pModule.close()


when isMainModule:
  try:
    loadModule(projectModule)
    var
      text: string
      sendTxt: string
      catchOutput: string
      sleeper: int = 500
      counter: int = 0

    if pModule.running():
      for c in 0..20:
        echo "------------------------------------", sleeper
        catchOutput = pipeModule(fmt("Test:{sleeper:5d}"))  
        echo catchOutput

        text = text & catchOutput & " "

        sleep(sleeper)
        sleeper += 20
      echo "All catched output:", text
  
      discard pipeModule("QUIT")
      sleep(100)

      while hasData(pModule):
        catchOutput = catchOutput & "\n" & pOut.readLine()
      echo catchOutput
      echo "That were ", catchOutput.len, " bytes"
  
      # try getting stderr output
      sleep(1000)
      echo "Going to test standard error read..."
      echo "Data ?", hasData(pModule)
      for i in 0..8:
        var e: string = ""
        try:
          e = pErr.readLine()
        except:
          e = "no data"
        echo(fmt("Error {i:02d}: {e}"))

      unloadModule(projectModule)  
    else:  #not running
      echo "Process could not be started"
  except Exception as e:
    var error = getCurrentException() 
    echo error.msg

called program

# modifies input and writes to stdout/stderr to test nim startProcess

import os
import times
import std/strutils
import strformat

var
  stopApp: bool = false

proc errorEcho*(x: varargs[string, `$`]) = {.noSideEffect.}: 
  # echo to standard error (lie to the compiler it has no side effects) 
  stderr.writeLine x


when isMainModule:
  var line: string
  while not stopApp:
    line = stdin.readLine()
   
    if line.startsWith("QUIT"):
      stopApp = true
      echo "QUIT RECEIVED\n"
      errorEcho("TEST to standard error")
    else:
      stdout.writeLine("RECEIVED:" & line & "\n")
      stdout.flushFile()

  quit(0)
0

There are 0 answers