Forum: Ruby Non-blocking 'gets' ?

99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-03 19:46
Hi guys, sorry if this is a stupid question, but I've been reading
around online and in 'Programming Ruby,' and I can't find anything.

Is there a way to perform a non-blocking gets or getc?  As in a method
that reads a character from stdin if there is one in the buffer or
returns nil otherwise?  I'm writing a multi-threaded application that
communicates over the internet using sockets, but I want the person who
runs the script on the host machine to be able to enter commands via
stdin.  The 'gets' command hangs all my threads until it returns.

Any suggestions?  Thanks!
-Stephen
C10a24b158745048bc560671e4ea13e7?d=identicon&s=25 unknown (Guest)
on 2007-10-03 21:55
(Received via mailing list)
One option is use Kernel.select, or, equivalently, IO.select.

Basically, select waits for input on some ios.  But it allows for a
timeout value which I believe can be 0.

Another option is to use fcntl with F_SETFL and O_NONBLOCK.

e.g.:

requite 'fcntl'
STDIN.fcntl(Fcntl::F_SETFL,Fcntl::O_NONBLOCK)
STDIN.read

Will throw an Errno:EAGAIN if there is no input.

Note that gets waits for a newline so will still block.

c.


On Oct 3, 2007, at 12:46 PM, Stephen Ware wrote:

>
> Any suggestions?  Thanks!
> -Stephen
> --
> Posted via http://www.ruby-forum.com/.
>

--

[PGP Key available at www.keyserver.net - http://homepage.mac.com/
calfeld]
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2007-10-03 22:55
Stephen Ware wrote:
> Hi guys, sorry if this is a stupid question, but I've been reading
> around online and in 'Programming Ruby,' and I can't find anything.
>
> Is there a way to perform a non-blocking gets or getc?  As in a method
> that reads a character from stdin if there is one in the buffer or
> returns nil otherwise?  I'm writing a multi-threaded application that
> communicates over the internet using sockets, but I want the person who
> runs the script on the host machine to be able to enter commands via
> stdin.  The 'gets' command hangs all my threads until it returns.
>

If you don't wait for the user to input something, how can there ever be
input in a buffer?
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2007-10-03 23:57
Stephen Ware wrote:
>
> Is there a way to perform a non-blocking gets or getc?  As in a method
> that reads a character from stdin if there is one in the buffer or
> returns nil otherwise?  I'm writing a multi-threaded application that
> communicates over the internet using sockets, but I want the person who
> runs the script on the host machine to be able to enter commands via
> stdin.  The 'gets' command hangs all my threads until it returns.
>

It doesn't sound like you are writing a multi-threaded application if
gets hangs all your threads.  The whole point of threads is to execute
other blocks of code while there is idle time in a given block of code.
99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-04 00:16
> One option is use Kernel.select, or, equivalently, IO.select.
>
> Basically, select waits for input on some ios.  But it allows for a
> timeout value which I believe can be 0.

I tried this, but no luck.  The command works, but it's the same
problem... every thread is suspended until it returns.

> Another option is to use fcntl with F_SETFL and O_NONBLOCK.
>
> e.g.:
>
> requite 'fcntl'
> STDIN.fcntl(Fcntl::F_SETFL,Fcntl::O_NONBLOCK)
> STDIN.read
>
> Will throw an Errno:EAGAIN if there is no input.

Hmm.  I might try this if nothing else works, but that's going to be a
lot of wasted clock cycles.  Ideally, the thread would just sleep until
STDIN has something in it.  Is there no way to accomplish that?
99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-04 00:17
> If you don't wait for the user to input something, how can there ever be
> input in a buffer?

The idea is that while I'm waiting for input from STDIN, the other
threads in the application are still running.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2007-10-04 00:21
Stephen Ware wrote:
> Hi guys, sorry if this is a stupid question, but I've been reading
> around online and in 'Programming Ruby,' and I can't find anything.
>
> Is there a way to perform a non-blocking gets or getc?  As in a method
> that reads a character from stdin if there is one in the buffer or
> returns nil otherwise?  I'm writing a multi-threaded application that
> communicates over the internet using sockets, but I want the person who
> runs the script on the host machine to be able to enter commands via
> stdin.  The 'gets' command hangs all my threads until it returns.
>

The following is an example that uses one thread to execute some code in
the background while another thread waits for user input.  After being
prompted for input, if you wait 5 seconds before entering any input, you
will see that the background thread's output is written to the file
while the main thread waits for your input.

If instead, you enter your input right away, you will see that what you
entered will be interleaved with the thread's output.


require "monitor"

f = File.new("aaa.txt", "w")
lock = Monitor.new

t = Thread.new do
  lock.synchronize do
    f.puts("thread output1")
  end

  sleep(3)

  lock.synchronize do
    f.puts("thread output2")
  end
end

print "Enter command: "
STDOUT.flush
input = gets

lock.synchronize do
  f.puts(input)
end

t.join
f.close

File.open("aaa.txt") do |file|
  print file.read
end

--output:--
(waiting 5 seconds):
Enter command: hello
thread ouput1
thread output2
hello

(entering input immediately when prompted):
Enter command: hello
thread output1
hello
thread output2


One thing I found puzzling is having to call STOUT.flush.  When I don't
include that statement, then the print statement is skipped.  Yet, if I
use puts instead of print, then the flush call isn't necessary.  Anyone
know why?
99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-04 00:25
> It doesn't sound like you are writing a multi-threaded application if
> gets hangs all your threads.  The whole point of threads is to execute
> other blocks of code while there is idle time in a given block of code.

Well... I think my code is written properly.  For simplicity's sake,
let's consider it a chat room.  The main thread spawns a "listener"
thread and then sleeps forever.  The listener thread listens for socket
connections on a port and spawns a new thread for each socket, basically
allowing multiple users to connect at the same time and interact with
one another.  That much works, and I have logged in with multiple users
to test it.

The change I'm trying to make is in the main thread... instead of
sleeping forever, I want to accept commands from STDIN, such as
'shutdown,' for example.  But when I replaced 'sleep(0)' with an
infinite loop of 'command = gets', it freezes the whole application.  I
am no longer able to connect, even with one user.  Actually, it will
still connect but only if I type characters into STDIN very rapidly.
That's what leads me to conclude that 'gets' is hanging all my threads.
99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-04 00:32
I'm using a Windows XP 'cmd' shell if that has anything to do with it.
Eceb5a31c67be3331ce165e63de832ad?d=identicon&s=25 Rudi Cilibrasi (Guest)
on 2007-10-04 00:32
(Received via mailing list)
On 10/3/07, 7stud -- <dolgun@excite.com> wrote:
> Stephen Ware wrote:
> One thing I found puzzling is having to call STOUT.flush.  When I don't
> include that statement, then the print statement is skipped.  Yet, if I
> use puts instead of print, then the flush call isn't necessary.  Anyone
> know why?

I guess it's because in Unix most text I/O at the text console is
line-buffered by default.  See "cooked mode" e.g.
http://meshier.com/docs/oreilly/unix2/upt/ch41_02.htm

for more info.  Roughly, the kernel is expected to collect input into
lines,
and to allow stuff like backspace to work simply, it has to buffer a
whole line
up and wait for return before sending it on to the application.  So that
is why
your print doesn't print: it is not sending the return to cause the
flush.  But
sending one yourself fixes it.  The puts has the \n so the flush
happens automatically I guess.
99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-04 00:35
> But when I replaced 'sleep(0)' with an
> infinite loop of 'command = gets', it freezes the whole application.

Correction: it was an infinite loop of 'sleep(1)' commands.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2007-10-04 00:45
Rudi Cilibrasi wrote:
> On 10/3/07, 7stud -- <dolgun@excite.com> wrote:
>> Stephen Ware wrote:
>> One thing I found puzzling is having to call STOUT.flush.  When I don't
>> include that statement, then the print statement is skipped.  Yet, if I
>> use puts instead of print, then the flush call isn't necessary.  Anyone
>> know why?
>
> I guess it's because in Unix most text I/O at the text console is
> line-buffered by default.  See "cooked mode" e.g.
> http://meshier.com/docs/oreilly/unix2/upt/ch41_02.htm
>
> for more info.  Roughly, the kernel is expected to collect input into
> lines,
> and to allow stuff like backspace to work simply, it has to buffer a
> whole line
> up and wait for return before sending it on to the application.  So that
> is why
> your print doesn't print: it is not sending the return to cause the
> flush.  But
> sending one yourself fixes it.  The puts has the \n so the flush
> happens automatically I guess.

Then why does this print command produce output without a call to flush:

print "Enter data: "
line = gets
Eceb5a31c67be3331ce165e63de832ad?d=identicon&s=25 Rudi Cilibrasi (Guest)
on 2007-10-04 00:56
(Received via mailing list)
> line = gets
>

I would imagine because it is the interactive terminal (not
backgrounded, probably first thread right?) and so the gets itself
causes a flush of the associated file descriptor automatically.  That
is, stdin and stdout are linked in this way so that reading from stdin
triggers a flush for stdout first.  This happens in order to support
prompting at interactive terminals, but obviously only one thread is
supposed to read from stdin at the same time so only one is granted
the special power of having a linked stdin / stdout.  I think
non-interactive output (not a tty) is not linked this way so you don't
get extra convenient flushing for free.   I would presume the "main"
(first) thread would be the only one able to take advantage of this
feature.  Does that make sense?
I would imagine the behavior is similar under windows also but I
noticed there are sometimes differences.  Like stdout and stderr are
usually not different under windows.  Cheers, -r.
1d53b088a989e069b94597c282eebbbc?d=identicon&s=25 Simon Krahnke (Guest)
on 2007-10-04 01:06
(Received via mailing list)
* 7stud -- <dolgun@excite.com> (00:21) schrieb:

> One thing I found puzzling is having to call STOUT.flush.  When I don't
> include that statement, then the print statement is skipped.  Yet, if I
> use puts instead of print, then the flush call isn't necessary.  Anyone
> know why?

stdout is auto-flushed on newlines. puts includes a newline, print
doesn't.

mfg,                             simon .... l
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2007-10-04 01:06
Rudi Cilibrasi wrote:
>
> I would presume the "main"
> (first) thread would be the only one able to take advantage of this
> feature.  Does that make sense?
>

No, because if you look at the example I posted, the main thread has the
statements:

print "Enter command: "
STDOUT.flush
input = gets

According to your explanation, the flush call should be unnecessary.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2007-10-04 01:09
Simon Krahnke wrote:
>
>> One thing I found puzzling is having to call STOUT.flush.  When I don't
>> include that statement, then the print statement is skipped.  Yet, if I
>> use puts instead of print, then the flush call isn't necessary.  Anyone
>> know why?
>
> stdout is auto-flushed on newlines. puts includes a newline, print
> doesn't.
>

Then how come this program:

print "Enter command: "
input = gets

displays "Enter command: " to my terminal?
Eceb5a31c67be3331ce165e63de832ad?d=identicon&s=25 Rudi Cilibrasi (Guest)
on 2007-10-04 01:15
(Received via mailing list)
hmm, well the first-thread thing was really a guess.  i don't know
what else would
cause the different behvaior but perhaps there is another reason one
thread is privileged over another in terms of interactive terminal
ownership.  the only other thing i can think of is that somehow the
"interactive terminal" indication is switched around. maybe as a
result of the backtick or something?  i have also experienced strange
behavior with puts and gets but always chalked it up to one of the two
issues we've mentioned: buffering (with newline interactions) and
linked stdin-stdout file descriptor pairs.  i wish i had a simpler
explanation to offer but i suspect we are on the right track because
somebody else said something similar to what i did.  cheers, -r.
99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-04 01:46
Err... does anyone have an answer to my original question?
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-10-04 01:55
(Received via mailing list)
On Oct 3, 2007, at 5:32 PM, Stephen Ware wrote:

> I'm using a Windows XP 'cmd' shell if that has anything to do with it.

I'm pretty sure you can never put STDIN into a nonblocking mode on
Windows, just FYI.  It's certainly possible for sockets though.

James Edward Gray II
99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-04 03:18
> I'm pretty sure you can never put STDIN into a nonblocking mode on
> Windows, just FYI.

Maybe that's the problem :(

>It's certainly possible for sockets though.

Yeah... that's how I'm doing it now.  Basically you have to connect as
an admin user and send commands over a socket.
Ddbfebb47432f6599da361df6a135c7c?d=identicon&s=25 Adam Shelly (Guest)
on 2007-10-04 08:34
(Received via mailing list)
On 10/3/07, Stephen Ware <sgware@gmail.com> wrote:
> Is there a way to perform a non-blocking gets or getc?  As in a method
> that reads a character from stdin if there is one in the buffer or
> returns nil otherwise?

If I was programming a Windows console app in C, I would use kbhit()
to check if there was a character in the buffer.   So I decided this
was a good time to try writing my first ruby c extension.

on Windows, save and complie conio.c into conio.dll, and then the
following example should work.
-Adam

--- test_conio.rb ---
require 'conio'
#background work:
  Thread.new { print('.') or sleep(1) while true }
#check for input in this thread
  Thread.new do
    while 1
      while !CONIO::kbhit?; end  #--wait for key, nonblocking
     #s = STDIN::getc            #--this blocks until return pressed
      s = CONIO::getch           #--this doesn't
      p "#{s} (#{s.chr})"
      exit if s==?q
    end
  end
#do some foreground work
  puts("waiting") or sleep(10) while 1

---
---conio.c---
/* conio.c -- Windows conio functions
   3 Oct 2007, Adam Shelly
   Ruby license applies
 */
#include "ruby.h"
#include <conio.h>

static VALUE c_kbhit(VALUE self)
{
  return _kbhit()? Qtrue : Qfalse;
}

static VALUE c_getch(VALUE self)
{
  int n = _getch();
  return INT2FIX(n);
}
static VALUE c_ungetch(VALUE self, VALUE cv)
{
  int c = FIX2INT(cv);
  int n = _ungetch(c);
  return INT2FIX(n);
}

VALUE mConio;

__declspec( dllexport ) void Init_conio()
{
 mConio = rb_define_module("CONIO");
 rb_define_module_function(mConio, "kbhit?", c_kbhit, 0);
 rb_define_module_function(mConio, "getch", c_getch, 0);
 rb_define_module_function(mConio, "ungetch", c_ungetch, 1);
}
99a6cfc3e326f20d4a096f4a5b1cef26?d=identicon&s=25 Stephen Ware (sgware)
on 2007-10-04 15:36
Wow, that is quite complex.  Thank you!

I also managed to discover another usable method: run a 'system' command
in a new thread.  That seems to release the blocking on gets by creating
a second process, and as far as I can tell will work on both Windows and
*nix.  The downside is that the two processes can't really communicate
with each other much, but in my case I don't need them to.

Thanks everyone for you help :)
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-10-04 15:45
(Received via mailing list)
On Oct 3, 2007, at 12:46 PM, Stephen Ware wrote:

> Is there a way to perform a non-blocking gets or getc?

I vote that you let HighLine do the work for you:

http://blog.grayproductions.net/articles/i_just_wa...

James Edward Gray II
1131188c5716180f78fd2a8b537dbf6f?d=identicon&s=25 Wurzel Cidermaker (wurzel)
on 2007-10-31 16:28
Stephen Ware wrote:
> Wow, that is quite complex.  Thank you!
>
> I also managed to discover another usable method: run a 'system' command
> in a new thread.  That seems to release the blocking on gets by creating
> a second process, and as far as I can tell will work on both Windows and
> *nix.  The downside is that the two processes can't really communicate
> with each other much, but in my case I don't need them to.
>
> Thanks everyone for you help :)

Stephen,

Can you please provide a very simple 'system' command example as I'm
having the same problem.
84dc575c33a123789521d53cad0f62ae?d=identicon&s=25 Lloyd Linklater (lloyd)
on 2007-10-31 18:21
Why don't you just bind a keypress event?  That would capture any given
keystroke in a non blocking way.
1131188c5716180f78fd2a8b537dbf6f?d=identicon&s=25 Wurzel Cidermaker (wurzel)
on 2007-10-31 18:38
Lloyd Linklater wrote:
> Why don't you just bind a keypress event?  That would capture any given
> keystroke in a non blocking way.

Can you provide an example?
84dc575c33a123789521d53cad0f62ae?d=identicon&s=25 Lloyd Linklater (lloyd)
on 2007-11-01 02:04
Wurzel Cidermaker wrote:
> Lloyd Linklater wrote:
>> Why don't you just bind a keypress event?  That would capture any given
>> keystroke in a non blocking way.
>
> Can you provide an example?

I got the idea from the pickaxe book starting on page 246.
38e4ddd3dc7770cc1e47164dc50debd7?d=identicon&s=25 Muriel Salvan (murielsalvan)
on 2014-03-26 12:51
Stephen Ware wrote in post #565434:
> Hi guys, sorry if this is a stupid question, but I've been reading
> around online and in 'Programming Ruby,' and I can't find anything.
>
> Is there a way to perform a non-blocking gets or getc?  As in a method
> that reads a character from stdin if there is one in the buffer or
> returns nil otherwise?  I'm writing a multi-threaded application that
> communicates over the internet using sockets, but I want the person who
> runs the script on the host machine to be able to enter commands via
> stdin.  The 'gets' command hangs all my threads until it returns.
>
> Any suggestions?  Thanks!
> -Stephen

I know this is an old post, but still appears among Google's top
results.

By combinig the various solutions I just read, I came up with a
cross-platform way to solve that problem.
Details here:
http://blog.x-aeon.com/2014/03/26/how-to-read-one-...

The relevant piece of code: a `GetKey.getkey` method
returning the ASCII code or `nil` if none was pressed.

Should work both on Windows and Unix.

module GetKey

  # Check if Win32API is accessible or not
  @use_stty = begin
    require 'Win32API'
    false
  rescue LoadError
    # Use Unix way
    true
  end

  # Return the ASCII code last key pressed, or nil if none
  #
  # Return::
  # * _Integer_: ASCII code of the last key pressed, or nil if none
  def self.getkey
    if @use_stty
      system('stty raw -echo') # => Raw mode, no echo
      char = (STDIN.read_nonblock(1).ord rescue nil)
      system('stty -raw echo') # => Reset terminal mode
      return char
    else
      return Win32API.new('crtdll', '_kbhit', [ ], 'I').Call.zero? ? nil
: Win32API.new('crtdll', '_getch', [ ], 'L').Call
    end
  end

end

And here is a simple program to test it:

loop do
  k = GetKey.getkey
  puts "Key pressed: #{k.inspect}"
  sleep 1
end

In the link provided above, I also show how to use the `curses` library,
but the result gets a bit whacky on Windows.

Hope this will help others.
0ddd7f465250bc41c656682af1aa6d20?d=identicon&s=25 Premium Watch (watchdivergent)
on 2014-03-30 19:17
Hello folks!
Due to friends request, I'd like to share the way I watched Divergent
Full HD Movie 1080p Online. Here we go:

1. Register here:


   ►►►► http://goo.gl/hbxLi6 ◄◄◄◄

   ►►►► http://goo.gl/hbxLi6 ◄◄◄◄

   ►►►► http://goo.gl/hbxLi6 ◄◄◄◄

   ►►►► http://goo.gl/hbxLi6 ◄◄◄◄

2. Make your self enjoy watching Divergent Full HD Movie 1080p Online
not to mention, you will gain so much member benefits.

That's it and have a nice day!

http://www.jimdo.com/app/forum/ask-a-question--f25...
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.