David Unric
David Unric

Reputation: 7719

Non-blocking readline for STDIN?

I can't figure out why IO methods won't work on STDIN, when properly set to non-blocking mode:

  require 'fcntl'

  stdin_flags = STDIN.fcntl(Fcntl::F_GETFL)
  p stdin_flags   #32770
  p STDIN.fcntl(Fcntl::F_SETFL, stdin_flags | Fcntl::O_NONBLOCK) # 0
  p STDIN.fcntl(Fcntl::F_GETFL)    # 34818
  #at_exit { STDIN.fcntl(Fcntl::F_SETFL, stdin_flags & ~Fcntl::O_NONBLOCK) }
  STDIN.readline    # this call blocks, IO::EAGAINWaitReadable expected
  exit

IO.fcntl successfully sets non-blocking mode but all IO functions like read, readline, gets, readchar ignore the mode and hang at reading when no input has been received.

Setting sync mode to true has no effect.

If I replace STDIN.readline with the shell call system('read line') it does work correctly. It won't wait or would wait for input depending if non-blocking mode was set.

I'm aware of IO.read_nonblock but looking for an efficient way how to read newline terminated strings. Calling read_nonblock for each single character is painfully slow.

Can anybody explain this (mis)behavior?

Upvotes: 6

Views: 1868

Answers (1)

David Unric
David Unric

Reputation: 7719

It's a bit unfortunate but standard functions from IO module seem to not respect status flags associated with a file descriptor.

One of the working solutions is use of IO.select class method for input polling, then read the data with regular methods as they become available. Be aware when line processing methods are used, code may hang until terminating newline character gets consumed. It's advisable to enclose polling code in Timeout block when things are going out of control.

In cases when amount of characters/bytes is known beforehand, stock IO.read_nonblock would simply serve well.

Upvotes: 3

Related Questions