Signaling Ruby from C/C++

A

Asterix Gallier

Hello,

I would like to send a signal from c to ruby. So that ruby knows, that
there is some new data in c that can be fetched and extracted.

Currently I'm using ruby 1.8.5 on WindowsXP.

Are there any possibilities like Callbackfunction, Signals,
Interrupthandling ...

I've searched but found nothing for my needs.

Currently I have the following code. the Problem here is, that i have to
wait for the rubyscheduler until the ruby recvThread will be scheduled.
Although it could happen, that data will be lost, when there arrives new
data while ruby is currently "pop"-ing the array.

Have anybody got an idea?

Thanks for any tips.
Asterix

////////////////////////////////////////////////////////
// rubycode

require 'RubyDriver'

recvThread = Thread.new{
loop do
Thread.stop
while ( (msg = recvArray.pop) != nil) do
puts msg
end
end
}

RubyDriver::startReceiver(recvThread, recvArray)

////////////////////////////////////////////////////////
// c code

VALUE receiverThread, receiverArray;

void reciever(void* dummy) {

while(1) {
msg = recieveMessage(); // a C function wich
// returns if there is
// new data arrived
rb_ary_push(receiverArray, msg);
rb_thread_wakeup(receiverThread);

}
}


void startReceiver(VALUE thread, VALUE array) {
receiverThread = thread;
receiverArray = array;

hThread = (HANDLE)_beginthread( reciever, 0, NULL );
}
 
S

Suraj N. Kurapati

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Asterix said:
I would like to send a signal from c to ruby. So that ruby knows,
that there is some new data in c that can be fetched and
extracted.

How about raising an exception from your C code and catching it in
your Ruby code?
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (GNU/Linux)

iD8DBQFEyt14mV9O7RYnKMcRAhQAAJ4uNmwrApn+JQaXohcp1aHebcRxIwCfW2E0
z2Z+Ev5KbcEjUsrujWod3vI=
=Uore
-----END PGP SIGNATURE-----
 
A

Asterix Gallier

How about raising an exception from your C code and catching it in
your Ruby code?

Thank you vor this advice. But I didn't find a working solution with
this construct of ruby, so maybe I nee a little bit hand help.

The Code exits with an unknown software exception if rb_raise is called
inside the thread function. Inside the startReceiver function it works.

what is my mistake?

Thanks
Asterix


i extend the c- receiver- function with this line:
////////////////////////////////////////////////////////
// c code
rb_raise(rb_eRuntimeError, "MsgReceived");


////////////////////////////////////////////////////////
// and inside of ruby I call the c- startReceiver- function in a begin
rescue block

started = false
begin
RubyDriver::startReceiver(recvThread, recvArray) if !started
started = true
rescue
puts "Signaled from C"
retry
end
 
F

Francis Cianfrocca

Suraj said:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1



How about raising an exception from your C code and catching it in
your Ruby code?
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (GNU/Linux)

iD8DBQFEyt14mV9O7RYnKMcRAhQAAJ4uNmwrApn+JQaXohcp1aHebcRxIwCfW2E0
z2Z+Ev5KbcEjUsrujWod3vI=
=Uore
-----END PGP SIGNATURE-----

I haven't tried your solution, but I would be surprised if a Ruby
exception turned out to be thread-safe across a native thread.
 
A

Asterix Gallier

now I've tried another solution. I'm sending a signal to the Rubyprocess
and catch this signal with trap.

- Is this solution threadsafe? I didn't know in which way ruby handles
the signal exactly.
- Could it be, that some Signals are getting lost because of the
Handling Routine is already active?

////////////////////////////////////////////////////////
// rubycode

require 'RubyDriver'

Signal.trap("TERM") do
puts recvArray.pop.to_s
end

RubyDriver::startReceiver(recvThread, recvArray)

////////////////////////////////////////////////////////
// c code

void reciever(void* dummy) {

VALUE msgsignal[2];
msgsignal[0] = rb_str_new2("TERM");
msgsignal[1] = ProcessId;

while(1) {
msg = recieveMessage(); // a C function wich
// returns if there is
// new data arrived
rb_ary_push(receiverArray, msg);
rb_f_kill(2, msgsignal);
}
}


void startReceiver(VALUE pId, VALUE array) {
processId = pId;
receiverArray = array;

hThread = (HANDLE)_beginthread( reciever, 0, NULL );
}
 
F

Francis Cianfrocca

Does this actually work??? Windows doesn't have signals, so maybe
you've gotten lucky with whatever mechanism Ruby uses to simulate
signals on Windows. I would doubt this solution works reliably on Unix
or Linux, because a signal is delivered to the whole process, and in
general you can't predict which thread will handle it. (Unless there's
something magic about rb_f_kill that I don't know about.)

Have you tried sending data to a file descriptor in the native thread,
and selecting the descriptor for readable in the Ruby thread? I know
this works, and there have been several threads about this approach on
this board in the last few months.
 
A

Asterix Gallier

Francis said:
Does this actually work??? Windows doesn't have signals, so maybe
you've gotten lucky with whatever mechanism Ruby uses to simulate
signals on Windows. I would doubt this solution works reliably on Unix
or Linux, because a signal is delivered to the whole process, and in
general you can't predict which thread will handle it. (Unless there's
something magic about rb_f_kill that I don't know about.)

Have you tried sending data to a file descriptor in the native thread,
and selecting the descriptor for readable in the Ruby thread? I know
this works, and there have been several threads about this approach on
this board in the last few months.

Hello Francis,

the solution above works somehow, but i experienced that this is not
very reliable.

Your solution by using a file descriptor sounds very interesting. I've
searched but didn't find any adequate example or documentation beside
the ruby source itself.

Can you please give me any support for this approach.

As far as i can see, i need to simulate a file filedescriptor to ruby.
Am I right? I've got no idea how to do this. I take a look at the
ruby-serialport source but i think that this is not applicable to my
situation or?

With Regards
Asterix
 
F

Francis Cianfrocca

Asterix said:
Your solution by using a file descriptor sounds very interesting. I've
searched but didn't find any adequate example or documentation beside
the ruby source itself.

Can you please give me any support for this approach.

As far as i can see, i need to simulate a file filedescriptor to ruby.
Am I right? I've got no idea how to do this. I take a look at the
ruby-serialport source but i think that this is not applicable to my
situation or?

Asterix, I don't have time right this moment to write and test a working
code sample, but you can try using Ruby's IO.pipe to get a pair of
descriptors connected to each other.

rd,wr = IO.pipe
writeable_descriptor = wr.fileno

Now your Windows thread can write the writeable_descriptor and your Ruby
thread can select([rd]).

Hope that helps. There are probably pitfalls with this approach on
Windows. I know I've done this before or something like it, but will
need a bit of time to find the code.
 
A

Asterix Gallier

Francis said:
Asterix said:
Your solution by using a file descriptor sounds very interesting. I've
searched but didn't find any adequate example or documentation beside
the ruby source itself.

Can you please give me any support for this approach.

As far as i can see, i need to simulate a file filedescriptor to ruby.
Am I right? I've got no idea how to do this. I take a look at the
ruby-serialport source but i think that this is not applicable to my
situation or?

Asterix, I don't have time right this moment to write and test a working
code sample, but you can try using Ruby's IO.pipe to get a pair of
descriptors connected to each other.

rd,wr = IO.pipe
writeable_descriptor = wr.fileno

Now your Windows thread can write the writeable_descriptor and your Ruby
thread can select([rd]).

Hope that helps. There are probably pitfalls with this approach on
Windows. I know I've done this before or something like it, but will
need a bit of time to find the code.

Ok I guess I know how it should work, but I'm not able to implement.

For testing I tried the sample code below.

I experienced following problems:
* the select statement returns immediately weather there is data or not
* if count is higher than one, the skript blocks because of the wrong
behavior
of select
* how to write data to the IO Object (wr) with C functions.

Thanks for any help
Asterix

////////////////////////////////////////////////////////
// rubycode

rd, wr = IO.pipe
count = 3

a = Thread.new {
count.times do
puts "#{__LINE__} Waiting for data: ..."
ra, wa, ea = select([rd], nil, nil, nil)
puts __LINE__
p ra[0].read
puts __LINE__
end
rd.close
}

b = Thread.new {
count.times do
puts "#{__LINE__} Sending message..."
wr.write("Hello")
puts __LINE__
end
wr.close
}
puts __LINE__

[a, b].each {|t| t.join if t != nil}

# Output when Count = 1
ruby test.rb
6 Waiting for data: ...
17 Sending message...
8
23
19
"Hello"
10
Exit code: 0

# Output when Count = 2 --> blocks
ruby test.rb
6 Waiting for data: ...
17 Sending message...
8
23
19
17 Sending message...
Exit code: -1073741510

# Output when Count = 2 and "read" replaced with "inspect"
ruby test.rb
6 Waiting for data: ...
17 Sending message...
8
"#<IO:0x282385c>"
23
10
6 Waiting for data: ...
19
17 Sending message...
8
"#<IO:0x282385c>"
10
test_2.rb:18:in `write': Invalid argument (Errno::EINVAL)
from test_2.rb:25:in `join'
from test_2.rb:25
from test_2.rb:25
 
F

Francis Cianfrocca

Asterix, try this:

#---------------------------------

rd,wr = IO.pipe

Thread.new {
loop {
puts rd.readpartial(1)
}
}

loop {
sleep 1
wr.write "+"
}

#---------------------------------

This works on Linux but I haven't tested it on Windows. And you're
right, if you try to use Kernel#select in the reader loop, it doesn't
work reliably! (I'm using a Ruby 1.8.4 snapshot from early June, which
has changes in nonblocking I/O.) This may be a Ruby bug.
 
A

Asterix Gallier

Francis said:
Asterix, try this:

#---------------------------------

rd,wr = IO.pipe

Thread.new {
loop {
puts rd.readpartial(1)
}
}

loop {
sleep 1
wr.write "+"
}

#---------------------------------

This works on Linux but I haven't tested it on Windows. And you're
right, if you try to use Kernel#select in the reader loop, it doesn't
work reliably! (I'm using a Ruby 1.8.4 snapshot from early June, which
has changes in nonblocking I/O.) This may be a Ruby bug.

What a shame! It doesn't work on Windows. When comming to the
readpartial statement the program blocks.

Thank all very much for the time and effort to help me solving the
problem. Slowly I believe that windows is not able to do blocking
reading =(

Have you got any further ideas?

Greetings
Asterix
 
K

Kroeger, Simon (ext)

=20
What a shame! It doesn't work on Windows. When comming to the=20
readpartial statement the program blocks.
Thank all very much for the time and effort to help me solving the=20
problem. Slowly I believe that windows is not able to do blocking=20
reading =3D(
=20
Have you got any further ideas?

This does work on windows (it is a bit more code, see yourself):

----------------------------------------------------------
require 'socket'

include Socket::Constants
server =3D Socket.new(AF_INET, SOCK_STREAM, 0)
rd, wr =3D nil, Socket.new(AF_INET, SOCK_STREAM, 0)

server.bind(Socket.pack_sockaddr_in( 2200, 'localhost'))
thread =3D Thread.new do
server.listen(1)
rd, addr =3D server.accept
server.close
end
Thread.pass
wr.connect(Socket.pack_sockaddr_in( 2200, 'localhost'))
thread.join
=20
Thread.new {
loop do
puts rd.read(1)
end
}

loop do
sleep 1
wr.write "+"
end
----------------------------------------------------------

(instead of Thread.pass you should do some real synchronisation)

cheers

Simon
 
F

Francis Cianfrocca

Simon, that was a nyyyyz idea to use a TCP socket, as ugly as it is,
just to work around Windows' even-uglier-ness. Here's a simpler version
of your code (notice I commented out the server.close, which may cause
problems on some platforms). This code works on Linux. Haven't tested on
Windows. You don't need a thread because after the server socket calls
listen(2), the client can call connect(2) any time, and the
partially-completed connection will be held in the kernel as long as
necessary until the server calls accept(2). These events do not need to
be synchronous.

(I substituted Ruby's more-cooked TCP socket objects just to make it
simpler, even though under the covers these objects are not really
related to the Socket objects you used. In this case it doesn't seem to
matter- maybe it does on Windows.)

#---------------------------
require 'socket'

server = TCPServer.new( 'localhost', 2200 )
wr = TCPsocket.new( 'localhost', 2200 )
rd = server.accept
#server.close

Thread.new {
loop do
puts rd.read(1)
end
}

loop do
sleep 1
wr.write '+'
end
#----------------------------

What's ugly about this of course is that you can only run this process
once at a time because of the hardcoded port. I *know* for sure that I
have solved this problem in the past with Windows pipes, because an
early version of EventMachine used exactly that technique. I'll have to
dig through the source-code archives and find it.
 
L

Logan Capaldo

What's ugly about this of course is that you can only run this process
once at a time because of the hardcoded port. I *know* for sure that I
have solved this problem in the past with Windows pipes, because an
early version of EventMachine used exactly that technique. I'll
have to
dig through the source-code archives and find it.

It ain't perfect, but:

require 'timeout'
require 'socket'
port = 2200
server = nil
Timeout::timeout(2.0) do
begin
begin
server = TCPServer.new( port )
rescue Errno::EADDRINUSE
port += 1
retry
end
rescue Timeout::Error
puts "Gave up trying to get a port"
exit 1
end
end

client = TCPClient.new( 'localhost', port )
 
F

Francis Cianfrocca

Logan said:
It ain't perfect, but:

require 'timeout'
require 'socket'
port = 2200
server = nil
Timeout::timeout(2.0) do
begin
begin
server = TCPServer.new( port )
rescue Errno::EADDRINUSE
port += 1
retry
end
rescue Timeout::Error
puts "Gave up trying to get a port"
exit 1
end
end

client = TCPClient.new( 'localhost', port )



Of course. Still, I don't like this because what about well-secured
server machines with local firewall configurations that don't permit
this kind of swiss cheese? (Well, except for Windows servers, I guess-
anything that has to run Active Directory is hopeless in regard to
firewalls anyway.)
 
F

Francis Cianfrocca

Ok, Asterix, this was driving me crazy because I know I've done it
before.

Here's some code from an early version of EventMachine. Note that this
code is Windows-only. For Unix, you need something a little different
(socketpair).

/*******************************************/

static int Pipe[2];

// C code called from a Ruby instance method.
// We create an instance variable of whatever object
// self is.

int sp = _pipe (Pipe, 4096, _O_BINARY);
if (sp) {
// no pipe, handle the error somehow.
}

char buf [100];
snprintf (buf, sizeof(buf), "@rd = IO.new( %d, \"r\")", Pipe[0]);
rb_eval_string (buf);

/********************************************/

After executing this code, the object that called it has an instance
variable named @rd which is an IO object you can select for readability
in Ruby (or use a Ruby global if you want). The Windows program can
write bytes to Pipe[1]. Obviously make sure Pipe isn't allocated on the
stack, and obviously make sure you close both sides of the pipe when
you're done with it.

Also, be very wary that _pipe produces a half-duplex pipe in Windows!
Unlike some Unix flavors, you may not write Pipe[0] and you may not read
Pipe[1]. If you do, you'll get bizarre errors.
 
A

Asterix Gallier

Francis said:
Ok, Asterix, this was driving me crazy because I know I've done it
before.

Here's some code from an early version of EventMachine. Note that this
code is Windows-only. For Unix, you need something a little different
(socketpair).

/*******************************************/

static int Pipe[2];

// C code called from a Ruby instance method.
// We create an instance variable of whatever object
// self is.

int sp = _pipe (Pipe, 4096, _O_BINARY);
if (sp) {
// no pipe, handle the error somehow.
}

char buf [100];
snprintf (buf, sizeof(buf), "@rd = IO.new( %d, \"r\")", Pipe[0]);
rb_eval_string (buf);

/********************************************/

After executing this code, the object that called it has an instance
variable named @rd which is an IO object you can select for readability
in Ruby (or use a Ruby global if you want). The Windows program can
write bytes to Pipe[1]. Obviously make sure Pipe isn't allocated on the
stack, and obviously make sure you close both sides of the pipe when
you're done with it.

Also, be very wary that _pipe produces a half-duplex pipe in Windows!
Unlike some Unix flavors, you may not write Pipe[0] and you may not read
Pipe[1]. If you do, you'll get bizarre errors.

Thank you all very much for your help and so much code examples. You
helped me a lot. Especially thanks to Francis for searching this code
example creating and using a pipe. If it will work it would be the
perfect solution.

There is just one problem with it:
The call of IO.new() complains that the given FileNo is not valid and
then exits with EBADF. (It's exact the code example you gave me). Is
there some extra propagation of this filehandle to ruby necessary or
something else?

With regards
Asterix
 
F

Francis Cianfrocca

Asterix said:
There is just one problem with it:
The call of IO.new() complains that the given FileNo is not valid and
then exits with EBADF. (It's exact the code example you gave me). Is
there some extra propagation of this filehandle to ruby necessary or
something else?

With regards
Asterix

I just tested it on Windows 2KServer with Ruby 1.8.4(2005-12-24) and it
works fine. If you want to send me your code I can take perhaps a look.
 
A

Asterix Gallier

Francis said:
I just tested it on Windows 2KServer with Ruby 1.8.4(2005-12-24) and it
works fine. If you want to send me your code I can take perhaps a look.

I tried your code again by building a standard Ruby extension too, and
it worked; no idea what SWIG is doing different.

But there is although the problem, that if I'm reading from the
descriptor, and no data is there, the complete ruby application blocks.
=(

Concerning the solution with sockets.
For single messages it works perfectly, but if there are a higher load,
I experienced, that some Messages did not arrive in Ruby. Is there no
receiving buffer in ruby? Have got anybody an idea.


Greetings
Asterix


/* Ruby-Code ******************************************/

require 'socket'

sThread = Thread.new do # run server in a thread
server = TCPServer.new('localhost', '4321')
while(session = server.accept)
puts "Created new Session"
while(msg = session.read(1))
puts "Recieved Message: #{msg}"
end
end
end

/* C-Code ******************************************/

#define PRINTERROR(s) fprintf(stdout,"\n%: %d\n", s, WSAGetLastError())

SOCKET theSocket;
SOCKADDR_IN saServer;
#define SERVERPORT 4321


void createTCPSocket() {

char host[] = "localhost";

LPHOSTENT lpHostEntry;

WORD wVersionRequested = MAKEWORD(1,1);
WSADATA wsaData;

WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
fprintf(stderr,"\n Wrong version\n");
return;
}

// Find the server
lpHostEntry = gethostbyname(host);
if (lpHostEntry == NULL)
{
PRINTERROR("gethostbyname()");
return;
}

// Create a TCP/IP datagram socket
if ( (theSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) ==
INVALID_SOCKET) {
PRINTERROR("socket()");
return;
}

// Fill in the address structure for the server
saServer.sin_family = AF_INET;
saServer.sin_addr = *((LPIN_ADDR)*lpHostEntry->h_addr_list);
saServer.sin_port = htons(SERVERPORT);

// connect to the server
int nRet;

if( (nRet = connect(theSocket,
(LPSOCKADDR)&saServer,
sizeof(struct sockaddr))) == SOCKET_ERROR) {
PRINTERROR("socket()");
closesocket(theSocket);
return;
}

}

void sendTCPData(char *data) {
int nRet;

nRet = send(theSocket, // Connected socket
data, // Data buffer
strlen(data), // Length of data
0); // Flags

if (nRet == SOCKET_ERROR) {
PRINTERROR("send()");
closesocket(theSocket);
return;
}
}
 
F

Francis Cianfrocca

Asterix said:
But there is although the problem, that if I'm reading from the
descriptor, and no data is there, the complete ruby application blocks.
=(

You're going to face this problem in any case, since you want your Ruby
code to be responsive to interruptions from the native code. You can set
the reader socket nonblocking and poll it in your main Ruby loop, or you
can read the reader socket on a separate Ruby thread and have it signal
your main loop by scheduling objects onto a Queue.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top