require 'rubygems'
require 'mechanize'
require 'io/console'
flag = 0
t2 =Thread.new do
puts flag
loop do
temp = STDIN.getch
if temp=="\n"
flag = (flag+1)%2
puts flag
end
end
end
# => Some foreground code
t2.join
When i run the code i get the value of flag
printed 0
as it should. But the thread does not change the value of flag
on the first Enter I hit. Hitting Enter the second time changes flag to 1
although. The thread works normally toggling the value of flag
on further Enter hits. Why is this happening? What have I done wrong?
Problem seems to be only with getch
as when I use gets
in place of getch
the problem disappears. But I cant use gets
because I want the user to hit a single key without needing to press Enter after the key to give input.
For example flag
should not change when the user inputs a instead of Enter and so I have used getch
to make sure the input is given after a single keyboard hit.
A similar problem was described here but it isn't a duplicate.
Edit 1:
The problem seems to be with getch
and not the check what do ever.
flag = 0
t2 =Thread.new do
puts flag
loop do
temp = STDIN.getch
flag = (flag+1)%2
puts flag
end
end
t2.join
Even after removing the if statement, the first Enter is ignored no matter what but other characters seem to respond to the first time. The problem is coming only when I hit Enter. It doesn't count the first Enter I hit.
ruby 2.3.3p222 (2016-11-21 revision 56859) [x64-mingw32]
I tried your code on a Windows machine and was able to re-create the problem. As you correctly guessed it has nothing to do with threading and everything with how getch
works (on Windows).
If you add a p temp.inspect
to your loop you will see that it is not so much that the first '\n' is swallowed, rather it is that it is somehow "held back". The best way to see this if you press Enter and another key alternatively. You will see that the inspect is "off-by-one".
A good explanation about the problem is discussed here: https://www.rubytapas.com/2016/12/14/ruby-code-on-windows/
With that information, a simple solution (that has the added benefit to run also on Linux, not sure about Mac) is:
require 'rubygems'
require 'mechanize'
require 'io/console'
STDIN.binmode #this line added to prevent line-end translation
flag = 0
t2 =Thread.new do
puts flag
loop do
temp = STDIN.getch
if temp=="\r" # note: changed from LF to CR
flag = (flag+1)%2
puts flag
end
end
end
# => Some foreground code
t2.join
Notes: Admittedly I don't fully get the way it works. I was expecting that Enter would cause a "\r\n" sequence in binmode, but I only see "\r". Not sure what happens to the "\n", but it seems to work reliably this way. Also note that in the current version the program can not be terminated with Ctrl+C. You'll have to add a check for that.