From: "Suraj N. Kurapati" Date: 2008-03-14T14:52:27+09:00 Subject: Re: embedding Ruby 1.9.0 inside pthread Hongli Lai wrote: > This is better, but now your program has a racing condition. Suppose the > thread finished before your pthread_cond_wait() is called. Now you're > waiting for a condition that will never be signalled. > > The correct way to wait for a condition is to use the following pattern: Thanks for the correction. Below is the revised (and more complex) example that better shows the Ruby/C interaction of my final program. Notice how the hello.rb script is able to relay control back to the C program and vice versa. This is how my final program works: it allows a Ruby script (like hello.rb) to use a C extension (Ruby-VPI) and relay control back to the C program (a Verilog simulator). After some time, the C program relays control back to the Ruby script. One discrepancy I noticed is that this program works correctly with ruby-1.9.0-0 but crashes with the new ruby-1.9.0-1 release. For instance, here is the response when running with ruby-1.9.0-0: $ ./main.so C program is calling RUBY_INIT_STACK() C program is calling ruby_init() C program is registering Object#relay_to_c method C program is loading file: hello.rb Ruby thread has been started, but is paused. C program relaying to Ruby thread... 1st time Ruby thread is starting interpreter... 1st time rubygems OK! {:$0=>"hello.rb"} {:ARGV=>[]} ------------------------------------------------------------ Hello World! 0 C program relaying to Ruby thread... 2nd time Hello World! 1 C program relaying to Ruby thread... 3rd time Hello World! 2 C program is back in control, exiting... And here is the response when running with ruby-1.9.0-1: $ ./main.so C program is calling RUBY_INIT_STACK() C program is calling ruby_init() C program is registering Object#relay_to_c method C program is loading file: hello.rb Ruby thread has been started, but is paused. C program relaying to Ruby thread... 1st time Ruby thread is starting interpreter... 1st time :17: SystemStackError C program relaying to Ruby thread... 2nd time Ruby thread is starting interpreter... 2nd time : [BUG] Segmentation fault ruby 1.9.0 (2008-03-01 revision 15664) [i686-linux] -- control frame ---------- c:0001 p:0000 s:0002 b:0002 l:000001 d:000001 TOP --------------------------- -- backtrace of native function call (Use addr2line) -- 0x80faa1a 0x805f06c 0x805f0db 0x80c9680 0xffffe440 0x80fc2c6 0x8065dbe 0x8066279 0x8066462 0x805b39a 0xb7f0546b 0xb7e286de ------------------------------------------------------- Aborted (core dumped) There do not seem to be any difference between the main.c of these two Ruby 1.9.0 releases, so is there something extra I must to do make this example work in the new -1 release? Thanks for your consideration. --- The revised example code: $ cat hello.rb require 'rubygems' puts "rubygems OK!" p :'$0' => $0 p :ARGV => ARGV puts '-' * 60 3.times do |i| puts "Hello World! #{i}" relay_to_c end $ cat main.c #include #include #include #include pthread_t gRubyThread; pthread_mutex_t gRubyLock; pthread_cond_t gRubyCond; bool gRubyRunning; /// /// Transfers control from the Ruby script /// to the C program and pauses Ruby. /// VALUE relay_to_c(VALUE); /// /// Transfers control from the Ruby thread /// to the C program and pauses Ruby. /// void relay_from_ruby_to_c(); /// /// Transfers control from the C program to /// the Ruby thread and pauses the C program. /// void relay_from_c_to_ruby(); /// /// Makes the C program wait until the Ruby /// thread finishes running (for now). /// void wait_for_ruby(); /// /// Tells the C program that the Ruby /// thread has finished running (for now). /// void ruby_is_done(); /// /// Makes the Ruby thread wait until the /// C program finishes running (for now). /// void wait_for_c(); /// /// Tells the Ruby thread that the C /// program has finished running (for now). /// void c_is_done(); /// /// Body of the pthread which hosts the /// Ruby interpreter's flow of execution. /// /// aRubyProgram:: program node for the interpreter to run /// void* the_ruby_thread(void* aRubyProgram) { printf("Ruby thread has been started, but is paused.\n"); relay_from_ruby_to_c(); printf("Ruby thread is starting interpreter... 1st time\n"); ruby_run_node(aRubyProgram); relay_from_ruby_to_c(); printf("Ruby thread is starting interpreter... 2nd time\n"); ruby_run_node(aRubyProgram); relay_from_ruby_to_c(); printf("Ruby thread is starting interpreter... 3rd time\n"); ruby_run_node(aRubyProgram); relay_from_ruby_to_c(); printf("Ruby thread is done, goodbye C program...\n"); ruby_is_done(); pthread_exit(NULL); } RUBY_GLOBAL_SETUP void the_c_program() { char* file = "hello.rb"; // the file to run int fake_argc = 2; char* fake_args[fake_argc]; fake_args[0] = file; // $0 - name of the program fake_args[1] = file; char** fake_argv = fake_args; printf("C program is calling RUBY_INIT_STACK()\n"); RUBY_INIT_STACK; printf("C program is calling ruby_init()\n"); ruby_init(); printf("C program is registering Object#relay_to_c method\n"); rb_define_module_function(rb_cObject, "relay_to_c", relay_to_c, 0); printf("C program is loading file: %s\n", file); void* rubyProgram = ruby_options(fake_argc, fake_argv); pthread_mutex_init(&gRubyLock, NULL); pthread_cond_init(&gRubyCond, NULL); gRubyRunning = true; pthread_create(&gRubyThread, NULL, the_ruby_thread, rubyProgram); wait_for_ruby(); printf("C program relaying to Ruby thread... 1st time\n"); relay_from_c_to_ruby(); // C program blocks here printf("C program relaying to Ruby thread... 2nd time\n"); relay_from_c_to_ruby(); // C program blocks here printf("C program relaying to Ruby thread... 3rd time\n"); relay_from_c_to_ruby(); // C program blocks here printf("C program is back in control, exiting...\n"); pthread_mutex_destroy(&gRubyLock); pthread_cond_destroy(&gRubyCond); } int main(int argc, char** argv) { the_c_program(); return 0; } VALUE relay_to_c(VALUE self) { relay_from_ruby_to_c(); return self; } void relay_from_ruby_to_c() { ruby_is_done(); wait_for_c(); } void ruby_is_done() { pthread_mutex_lock(&gRubyLock); { gRubyRunning = false; pthread_cond_signal(&gRubyCond); } pthread_mutex_unlock(&gRubyLock); } void wait_for_c() { pthread_mutex_lock(&gRubyLock); { while (!gRubyRunning) { pthread_cond_wait(&gRubyCond, &gRubyLock); } } pthread_mutex_unlock(&gRubyLock); } void relay_from_c_to_ruby() { c_is_done(); wait_for_ruby(); } void c_is_done() { pthread_mutex_lock(&gRubyLock); { gRubyRunning = true; pthread_cond_signal(&gRubyCond); } pthread_mutex_unlock(&gRubyLock); } void wait_for_ruby() { pthread_mutex_lock(&gRubyLock); { while (gRubyRunning) { pthread_cond_wait(&gRubyCond, &gRubyLock); } } pthread_mutex_unlock(&gRubyLock); }