Hello, and a question about finding a defined Class from C


#1

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.


#2

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


#3

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?


#4

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


#5

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.)


#6

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/search?group=comp.lang.ruby&q=gets+windows+block+sockets&qt_g=1&searchnow=Search+this+group


#7

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