This is an error that I encountered in a large ruby script. I've managed to boil it down to what seems like the essence of the problem. In discussing this problem, the script was run by someone on OS/X without encountering the same problem, so rather then a ruby problem it would seem to be a linux problem.
#!/usr/bin/env ruby
require "io/console"
def read_char
state=`stty -g`
`stty raw -echo`
input = $stdin.getc.chr
input << $stdin.read_nonblock(3) rescue nil
return input
ensure
`stty #{state}`
# Have also tried `stty -raw echo`
end
system("strace -o strace.log ./trace the start")
puts "press a cursor key"
c=read_char
system("strace -o strace2.log ./trace regular key")
puts "press a regular key"
c=read_char
system("strace -o strace3.log ./trace cursor key")
puts "done press return to exit"
Things to mention about this code for non Rubyists, <<
appends to an array [1].
Backticks act pretty mush the same as in sh
. At the moment, I do not know how Ruby specifically implements read_nonblock
. I am digging into it. Will mention it if the question is still unanswered.
Later I will post the C++ source to the "trace" function but it is easy to understand without the source. Read the command-line and write to a predetermined file. Then ask for input. After reading some input from STDIN save it to the same file and exit. It is essentially a debugging function which temporarily replaces some more complicated functions.
The problem
The third system call behaves inappropriately, sort of like it was receiving it's input from /dev/zero
.
The log entries for the reads from STDIN look like:
strace.log:read(0, "1\n", 1024) = 2
strace2.log:read(0, "2\n", 1024) = 2
strace3.log:read(0, 0x7fb388ba0000, 1024) = -1 EAGAIN (Resource temporarily unavailable)
So why is the third trace reading memory mapped IO?
The entry for the command which allocated the memory is:
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb388ba0000
Why is the mmap
command taking an fd
of -1 as an argument? [2]
Additional Comment. Based on what I've described so far, I pretty much have to conclude that when the EAGAIN error happens, something is not properly clean up, or the stdin state is somehow messed up, and now stdin is clogged. THat this then gets inherited by the system command. Thus the title question: how do you reset stdin, so that it behaves as it did so initially.
[1] Yeah as a C++ programmer I understand how this can be annoying.
[2] I'm not that familar with Unix memory mapped IO never having written anything using it myself. Windows memory mapped IO I did use a long time ago.