signaling a ruby thread from a win32 native thread

  • Thread starter Vincenzo Piombo
  • Start date
V

Vincenzo Piombo

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}"
=================================
 
N

nobu.nokada

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.
 

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,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top