In need of Win32 fcntl (non-blocking)

Hi there,

I am making a multi-user, telnet based, bbs. At work I use Linux Ruby,
at home a Win32 port. (http://rubyinstaller.rubyforge.org/wiki/wiki.pl)

Now a multi-user server can be done in threads, but since I want to keep
it simple and avoid dead-locks and starvation problems, I decided not to
use threads. (I also read that threads can block the whole program if
they get stuck in system calls. That's not something I want.)

Instead I want to use a select, which is thankfully support in Ruby.
However a read to a socket which has data open always seems to block,
unless I specifically read what is available. Since I cannot know how
much data is waiting for me, I really need to do a non-blocking read. In
Linux it works, in Windows the fcntl call is not supported. :frowning: As far as
I searched and tried, there is no other way to read data without blocking.

This guy

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/3155

seems to have made a fix some time ago. Can I ask why it is not in the
standard distribution? On that note, can somebody point me to a binary
win32 distribution that _does_ have this patch incorporated?

One last thing. I ran the example below on both Linux and Win32.
Normally stdout buffering is done up until a '\n' after which output is
flushed (ANSI C, both on Linux and Win32). This behaviour is seen on the
Linux Ruby port, but not under Win32 where I had to put in
'STDOUT.sync=true'. Can this be considered a bug?

I'm using:
ruby 1.8.2 (2004-12-25) [i386-mswin32]
ruby 1.8.2 (2004-12-25) [i686-linux]

Server example, works on Linux, not on Win32:

# socket example - server side
# usage: ruby svr.rb

require "socket"
require "fcntl"

gs = TCPserver.open(0)
addr = gs.addr
addr.shift
printf("server is on %s\n", addr.join(":"))
socks = [gs]

loop do
   nsock = select(socks);
   next if nsock == nil
   for s in nsock[0]
     if s == gs
       ns = s.accept
       ns.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
       socks.push(ns)
       print(s, " is accepted\n")
     else
       if s.eof?
         print(s, " is gone\n")
         s.close
         socks.delete(s)
       else
         print "[" + s.read + "]\n"
       end
     end
   end
end

In article <41F65F1B.80706@palli.nl>,
  Ivo Palli <ivo@palli.nl> writes:

Instead I want to use a select, which is thankfully support in Ruby.
However a read to a socket which has data open always seems to block,
unless I specifically read what is available. Since I cannot know how
much data is waiting for me, I really need to do a non-blocking read. In
Linux it works, in Windows the fcntl call is not supported. :frowning: As far as
I searched and tried, there is no other way to read data without blocking.

sysread might be usable because sysread doesn't block when some data
available. If sysread is usable, you don't need non-blocking read.

require "socket"

gs = TCPserver.open(0)
addr = gs.addr
addr.shift
printf("server is on %s\n", addr.join(":"))
socks = [gs]

loop do
  nsock = select(socks);
  next if nsock == nil
  for s in nsock[0]
    if s == gs
      ns = s.accept
      socks.push(ns)
      print(s, " is accepted\n")
    else
      begin
        print "[" + s.sysread(4096) + "]\n"
      rescue EOFError
        print(s, " is gone\n")
        s.close
        socks.delete(s)
      end
    end
  end
end

···

--
Tanaka Akira

Hi,

At Wed, 26 Jan 2005 00:00:58 +0900,
Ivo Palli wrote in [ruby-talk:128097]:

This guy

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/3155

seems to have made a fix some time ago. Can I ask why it is not in the
standard distribution? On that note, can somebody point me to a binary
win32 distribution that _does_ have this patch incorporated?

[ruby-core:3157] has been committed to CVS HEAD instead, but
not backported to 1.8 yet.

···

--
Nobu Nakada

You are correct, sysread() returns without blocking. However this is a workaround at best. In the interest of portability over platforms, I still recommend for fcntl() to be implemented in the Win32 platform. Afterall Ruby aims to provide a unified platform regardless of OS. Right?

Are there any ways to detect the OS btw? If I'm going to run my script on multiple platforms, and I need to workaround, how can I do this automatically?

Regards,

   Ivo Palli

Tanaka Akira wrote:

···

In article <41F65F1B.80706@palli.nl>,
  Ivo Palli <ivo@palli.nl> writes:

Instead I want to use a select, which is thankfully support in Ruby.
However a read to a socket which has data open always seems to block,
unless I specifically read what is available. Since I cannot know how
much data is waiting for me, I really need to do a non-blocking read. In
Linux it works, in Windows the fcntl call is not supported. :frowning: As far as
I searched and tried, there is no other way to read data without blocking.

sysread might be usable because sysread doesn't block when some data
available. If sysread is usable, you don't need non-blocking read.

require "socket"

gs = TCPserver.open(0)
addr = gs.addr
addr.shift
printf("server is on %s\n", addr.join(":"))
socks = [gs]

loop do
  nsock = select(socks);
  next if nsock == nil
  for s in nsock[0]
    if s == gs
      ns = s.accept
      socks.push(ns)
      print(s, " is accepted\n")
    else
      begin
        print "[" + s.sysread(4096) + "]\n"
      rescue EOFError
        print(s, " is gone\n")
        s.close
        socks.delete(s)
      end
    end
  end
end

Hi,

···

On Sun, 30 Jan 2005 13:44:06 +0900, nobu.nokada@softhome.net <nobu.nokada@softhome.net> wrote:

Hi,

At Wed, 26 Jan 2005 00:00:58 +0900,
Ivo Palli wrote in [ruby-talk:128097]:
> This guy
>
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/3155
>
> seems to have made a fix some time ago. Can I ask why it is not in the
> standard distribution? On that note, can somebody point me to a binary
> win32 distribution that _does_ have this patch incorporated?

[ruby-core:3157] has been committed to CVS HEAD instead, but
not backported to 1.8 yet.

This is good news. I'm going to test this later. Thanks!

Regards,
Joao

Ivo Palli wrote:

Are there any ways to detect the OS btw? If I'm going to run my

script

on multiple platforms, and I need to workaround, how can I do this
automatically?

Regards,

   Ivo Palli

If you just need to detect Windows vs Unix, you can use
File::ALT_SEPARATOR. It's only true on Win32.

If you want a little more detail, you can use the RUBY_VERSION
constant.

If you want a lot more detail, see sys-uname, available on the RAA.
Regards,

Dan

require 'rbconfig'
Config::CONFIG['target']

Csaba

···

On 2005-01-25, Ivo Palli <ivo@palli.nl> wrote:

Are there any ways to detect the OS btw?