From: Vincenzo Piombo Date: 2004-05-07T03:55:54+09:00 Subject: 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 #" 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 #include #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}" =================================