Hello,
I am working at a ruby application that needs to be notified of some hw
event provided by third party driver/DLL. It should run on Win32.
The hardware events are generated in the context of a native win32 thread,
and I need to handle them in some ruby code. The main ruby process is
meanwhile used to handle a TCP/IP dialog with a GUI.
So basically I need an event queue which a ruby thread can suspend on,
waiting for events pushed in the queue by a win32 native thread.
I did some experiment with ruby API in a ruby extension but didn’t succeed
to make it work.
Can you guys tell me whether I’m trying something plainly impossible (in
that case are there alternatives ?) or am I just making some implementation
error ?
Thanks in advance for your help.
Here follows some details:
msgqueue.rb - (a working full ruby test, using SizeQueue to explain what I
want to achieve)
msgqueuesignals.c - (C code of the extension)
msgqueuesignals_test.rb - (ruby code using the msgqueuesignals.so extension)
The last approach works if I undefine the DOLOOP macro, if it is defined I
get the error:
"ERROR #<SystemStackError: c:/ruby/lib/ruby/1.8/thread.rb:259:in `push’:
stack level too deep>"
after 42 iterations.
···
msgqueue.rb.
require “thread”
queue = SizedQueue.new(10)
pusher_thread = Thread.new {
100.times {|i|
queue.push(i)
}
queue.push(“Quit”)
}
popper_thread = Thread.new {
while true
o = queue.pop()
puts o
break if o == "Quit"
end
}
[pusher_thread, popper_thread].each {|t| t.join }
// msgqueuesignals.c
#include <windows.h>
#include <stdio.h>
#include “ruby.h”
#define DOLOOP 1
VALUE cMsgqSignals;
int counter = 0;
DWORD WINAPI native_thread(
LPVOID lpParameter // thread data
)
{
int i;
char command[1000];
VALUE errinfo;
int error = 0;
#ifdef DOLOOP
for(i=0; i<100; i++)
{
counter++;
sprintf(command, “$queue.push(’%d’)”, i);
rb_eval_string_protect(command, &error);
if(error)
{
errinfo = rb_inspect(ruby_errinfo);
rb_backtrace();
printf(“ERROR %s\n”, STR2CSTR(errinfo));
}
}
#endif
rb_eval_string("$queue.push('Quit')");
return 0;
}
static VALUE msgqs_get_count()
{
return INT2FIX(counter);
}
static VALUE msgqs_initialize(VALUE self)
{
HANDLE thread;
DWORD threadid;
rb_eval_string("$queue.push('Start')");
thread = CreateThread(
NULL, // pointer to security attributes
0, // initial thread stack size
native_thread, // pointer to thread function
(LPVOID)0, // argument for new thread
0, // creation flags
&threadid // pointer to receive thread ID
);
Sleep(1); // give the thread a chance to execute
return self;
}
__declspec(dllexport) void Init_msgqueuesignals() {
cMsgqSignals = rb_define_class(“MsgQueueSignals”, rb_cObject);
rb_define_method(cMsgqSignals, “initialize”, msgqs_initialize, 0);
rb_define_method(cMsgqSignals, “count”, msgqs_get_count, 0);
}
msgqueuesignals_test.rb
require "thread"
require “msgqueuesignals”
$stdout.sync = true
$queue = Queue.new()
native_pusher = MsgQueueSignals.new
I need this idle thread not to get a deadlock error when joining the
popper thread
otherwise ruby thinks no one will ever possibly wake up the popper
idle_thread = Thread.new {
while true
sleep(5000)
$queue.push(“Idle”)
end
}
popper_thread = Thread.new {
while true
o = $queue.pop()
puts o
break if o == "Quit"
end
}