Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot read integers from stdin using scala.io.StdIn.readInt() #543

Closed
aostiles opened this issue Mar 14, 2017 · 7 comments
Closed

cannot read integers from stdin using scala.io.StdIn.readInt() #543

aostiles opened this issue Mar 14, 2017 · 7 comments

Comments

@aostiles
Copy link

screen shot 2017-03-14 at 12 03 03 pm

@MasseGuillaume reproduced this bug.

@densh
Copy link
Member

densh commented Mar 15, 2017

Thanks for the bug report!

@densh densh added this to the 0.2 milestone Mar 15, 2017
@densh
Copy link
Member

densh commented Mar 15, 2017

The issue is that we don't wrap stdin in https://github.com/scala-native/scala-native/blob/master/javalib/src/main/scala/java/lang/System.scala#L35, should be trivial to fix.

@Korf74
Copy link
Contributor

Korf74 commented Mar 17, 2017

Is an implementation of a CFileInputStream class sufficient to fix this ?

@densh
Copy link
Member

densh commented Mar 17, 2017

@Korf74 Yes, that's exactly what needs to be done here.

@Korf74
Copy link
Contributor

Korf74 commented Mar 18, 2017

  var in: InputStream  = new CFileInputStream(stdio.stdin)
  var out: PrintStream = new PrintStream(new CFileOutputStream(stdio.stdout))
  var err: PrintStream = new PrintStream(new CFileOutputStream(stdio.stderr))

  private class CFileOutputStream(stream: Ptr[stdio.FILE])
      extends java.io.OutputStream {
    private val buf = stdlib.malloc(1)
    def write(b: Int): Unit = {
      !buf = b.toUByte.toByte
      stdio.fwrite(buf, 1, 1, stream)
    }
  }

  private class CFileInputStream(stream: Ptr[stdio.FILE])
      extends java.io.InputStream {
    private val buf = stdlib.malloc(1)
    override def read(): CInt = {
      stdio.fread(buf, 1, 1, stream)
      (!buf).toInt & 0xFF
    }
  }

Calls to scala.io.StdIn.readInt() using this implementation doesn't seem to work, it seems stdin reads 32 (whitespace) in loop

@fduraffourg
Copy link
Contributor

I tried with fread but it gets on our way because of the buffering. stdin is line buffered so we encounter the case where data is available but fread still waits for more data before returning. I tried to change the buffering method with setvbuf without any success.

The solution I found is to use read from unistd:

  private class CFileInputStream(stream: Ptr[stdio.FILE])
      extends java.io.InputStream {

    def read(): CInt = {
      stdio.fflush(stdio.stdout)
      val buffer = stackalloc[scala.Byte](1)
      while (unistd.read(0, buffer, 1) != 1) {
        println("Nothing was read on previous iteration")
      }
      buffer(0).toInt
    }

    override def read(b: Array[scala.Byte], off: Int, len: Int): Int = {
      val bufsize = len min 256
      val buffer = stackalloc[scala.Byte](bufsize)
      val nread = unistd.read(0, buffer, bufsize)
      var pos = 0
      while (pos < nread) {
        b(pos) = buffer(pos)
        pos += 1
      }
      nread.toInt
    }
  }
}

This way we get data directly from the kernel as soon as it is available and we let BufferedReader handle the buffering stuff.

There are still stuff to take care of:

  • When run from sbt run, stdin seams to be non blocking (or maybe not even opened) so we get into an infinite loop where each read call return immediatly without reading any data. I think using select or a blocking method that wait for data to be available should do the job. I'll try that.
  • Things can get out of order with stdout. For example calling println and then reading some stuff from stdin will wait for stdin before printing
  • Right now I create a buffer and then copy each byte from the buffer to the Array. Is it possible to get a Ptr from an Array to avoid this useless copy?
  • Maybe look at how sbt connect stdin to the running process and makes sure everything is OK

@MasseGuillaume
Copy link
Contributor

MasseGuillaume commented Apr 4, 2017

sbt run needs to connect it's input

val exitCode = Process(binary +: args).run(logger, connectInput = true).exitValue

https://github.com/scala-native/scala-native/pull/612/files#diff-9f8176d7a89fed51f9b5c82605327b36R436

densh pushed a commit that referenced this issue Apr 22, 2017
I use read from unistd instead of fread from stdio to prevent buffer
issues. Stdio is line buffered, and we want the line to be available as
soon as the user enter it, so we don't want fread to wait for the buffer
to be full before returning.

Resolves: #543
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants