Forum: Ruby Hello, and a question about finding a defined Class from C

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
E96756693ee0fc927a805ba0d509df72?d=identicon&s=25 Jacob Repp (Guest)
on 2006-02-18 20:00
(Received via mailing list)
First of all I would like to say Hello! I am new to Ruby programming
having just started a few days ago. I have been watching the ruby
language with interest for a number of years now but finally got the
interest to dive in. I must say it has been an absolute joy.

If this is the wrong list for a question of this type I would be happy
to post it elsewhere.

My first project was a UDP client/server which went well up until I
ran the problem of blocking kernel calls. I have used the 'io/wait'
extension which works nicely for simple purposes but for fun I've
written an extension for Win32 IO Completion Ports.

I have the binding code written but currently I cannot get a VALUE
representing the TCPSocket Class object. I'm trying to define the
methods async_send and async_read in the TCPSocket class currently as
a test.


    id = rb_intern("TCPSocket");
    if (rb_const_defined(rb_cObject, id))
    {
         cTCPSocket = rb_const_get(rb_cObject, id);
         if (TYPE(cTCPSocket) != T_CLASS) {
             rb_raise(rb_eTypeError, "%s is not a class", "TCPSocket");
         }

         rb_define_method(cTCPSocket, "async_send", rb_async_send, 2);
         rb_define_method(cTCPSocket, "async_recv", rb_async_recv, 2);
    }
    else
    {
        rb_raise(rb_eTypeError, "class 'TCPSocket' was not defined you
must require 'socket'");
    }



When I execute the code:

require 'socket'
puts Object.constants

I see the constant TCPSocket is defined.
E96756693ee0fc927a805ba0d509df72?d=identicon&s=25 Jacob Repp (Guest)
on 2006-02-18 20:24
(Received via mailing list)
Not long after sending this I ran a little experiment to validate the
symbol IDs were the same between script and C code. My code below was
correct, the problem was that I was passing -r AIO on the command line
(in my debugger settings). Hence my extension init was running before
I had run the require 'socket' statement.

If anyone is interested in a IOCP wrapper for ruby on win32 I would
like to get some feedback/testing on the implementation.

Thanks, -j
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-02-18 21:37
(Received via mailing list)
Jacob Repp wrote:
...
> My first project was a UDP client/server which went well up until I
> ran the problem of blocking kernel calls. I have used the 'io/wait'
> extension which works nicely for simple purposes but for fun I've
> written an extension for Win32 IO Completion Ports.

Just wondering, why did you decide not to use ruby threads to wait on
the UDP sockets?
E96756693ee0fc927a805ba0d509df72?d=identicon&s=25 Jacob Repp (Guest)
on 2006-02-18 22:01
(Received via mailing list)
It's not that simple. For example:

# server
server = Thread.new {
  s = UDPSocket.open
  s.bind(nil, 5000)
  while(true)
    data, cli = s.recvfrom(256)
    puts "Server got #{data} from #{cli}"
    Thread.pass
  end
}
client = Thread.new {
  s = UDPSocket.open
  s.connect('localhost', 5000)
  while(true)
     s.send("hello", 0)
     Thread.pass
  end
}

The problem occurs on the first call that the sever thread makes to
recvfrom(). This is a blocking call and since ruby is actually a
single threaded process (with virtual soft threads) this hangs all of
the other virtual threads. So for my test app which is trying to run a
simulation thread as well as handle socket and console IO this becomes
a problem quickly.

You can get around with 'io/wait' with the added 'ready?' method:
  while(len = s.ready?)
    data, cli = s.recvfrom(len)

Since I was looking for something to cut my teeth on anyways I decided
to write an AIO module that mixes async methods into the existing
socket implementation.

When you require 'AIO' it mixes in the methods async_send(to),
async_recv(to), async_accept, async_connect.

I'm currently experimenting with how best to handle the completion
handling. My current thinking is that the IO completion performs a
specified ruby method call on a receiver. I originally thought I would
pass a block to the async* methods to execute on completion but the
problem is that you usually want to re-call the async method at which
point you would need another block.. kinda messy.

I'm still playing with this part though
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-02-18 22:50
(Received via mailing list)
Jacob Repp wrote:
>   end
> The problem occurs on the first call that the sever thread makes to
> recvfrom(). This is a blocking call and since ruby is actually a
> single threaded process (with virtual soft threads) this hangs all of
> the other virtual threads. So for my test app which is trying to run a
> simulation thread as well as handle socket and console IO this becomes
> a problem quickly.

That code works fine for me, using ruby-1.8.4, both linux and windows
(the one-click msvc-built version).

Also, the Thread.pass is not needed.

I did add a line at the end:

sleep

(You could also use Thread.join(client) or similar.)
E96756693ee0fc927a805ba0d509df72?d=identicon&s=25 Jacob Repp (Guest)
on 2006-02-18 23:14
(Received via mailing list)
I followed up and it seems that you're right, I had assumed the recv
was blocking the whole process but it was not. Based on what I've seen
after some further testing it seems that 'Kernel#gets' and
'Socket#connect' can both block the process. Is this correct?

Here's another test using TCPSocket that works just fine as you
suggested. If you comment out the lines at the bottom where I would
try to read from the console it freezes the application.

Opening multiple telnet sessions to localhost:5000 shows multiple
connection threads operating in parallel.

require 'socket'

def handle_session(sess)
    puts "accepted session from #{sess}"
    sclient = Thread.new {
        while(1)
            data, cli = sess.recv(256)
            puts "Server got #{data} from #{sess}"
        end
    }
end
server = Thread.new {
    s = TCPServer.new(nil, 5000)
    while(true)
        puts "ready to accept next session..."
        sess = s.accept
        handle_session(sess)
    end
}
client = Thread.new {
    s = TCPSocket.open('localhost', 5000)
    puts "client connected..."
    while(true)
        puts "sending hello"
        s.send("hello", 0)
        sleep 1
    end
}
sleep
#while(true)
#line = gets
#puts line
#end
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-02-19 00:36
(Received via mailing list)
Jacob Repp wrote:
> I followed up and it seems that you're right, I had assumed the recv
> was blocking the whole process but it was not. Based on what I've seen
> after some further testing it seems that 'Kernel#gets' and
> 'Socket#connect' can both block the process. Is this correct?

Yes, unfortunately, AFAIK. These are known problems on windows.

Search for "gets windows block sockets" on comp.lang.ruby:

http://groups.google.com/group/comp.lang.ruby/sear...
This topic is locked and can not be replied to.