Signaling a ruby thread from a win32 native thread

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
}

puts "native_pusher event count: #{native_pusher.count}"
popper_thread.join
puts “native_pusher event count: #{native_pusher.count}”

Hi,

At Fri, 7 May 2004 03:55:54 +0900,
Vincenzo Piombo wrote in [ruby-talk:99416]:

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 ?

Current ruby interpreter implementation doesn’t allow to be
called from other native threads.

A) You may be possible to use rb_w32_main_context(), which
signals the main thread (native thread on which ruby runs),
handshakes with it and invokes the given function in it.

B) Once I wrote a patch to improve IO.select issue, which it
also provides rb_w32_wait_for_handle() function.
http://nokada.jin.gr.jp/ruby/win32/wait_events.diff
Note that it absolutely is untested.

···


Nobu Nakada