Nobuyoshi Nakada wrote:
> At Tue, 4 Mar 2008 14:57:36 +0900,
> Suraj N. Kurapati wrote in [ruby-core:15767]:
>> [1..2]

Thanks for the explanations.

>> 3. Is it safe to "compile" the hello.rb file into a NODE in the main
>> thread and then execute it inside a child thread?  Will there be any
>> cross-thread violations by doing this?
> 
> I guess safe.

I certainly hope so! :-)

> Your example does create new thread outside of ruby, the
> interpreter doesn't know about the thread.  You should use
> rb_thread_create() instead.

I tried your suggestion, but it did not work very well for me (see
below).  Perhaps I wrote a poor implementation?

Here is the new C program, revised according to your suggestion:

  $ cat main.c
  #include <stdio.h>
  #include <pthread.h>
  #include <ruby.h>

  // aRubyProgram:: program node for the interpreter to run
  VALUE the_ruby_thread(void* aRubyProgram)
  {
    printf("Ruby thread is starting interpreter\n");
    ruby_run_node(aRubyProgram);

    printf("Ruby thread is done...\n");
    return Qtrue;
  }

  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] = "ruby";
      fake_args[1] = file;
      char** fake_argv = fake_args;

      printf("C program is calling ruby_sysinit()\n");
      ruby_sysinit(&fake_argc, &fake_argv);

      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 calling ruby_init_loadpath()\n");
      ruby_init_loadpath();

      printf("C program is loading file: %s\n", file);
      void* rubyProgram = ruby_options(fake_argc, fake_argv);

      printf("C program is putting Ruby thread in control...\n");
      rb_thread_create(the_ruby_thread, rubyProgram);
      rb_thread_schedule();

      printf("C program is back in control, exiting...\n");
  }

  int main(int argc, char** argv)
  {
      the_c_program();
      return 0;
  }

With hello.rb as:

  $ cat hello.rb
  puts "Hello World!"
  require 'rubygems'
  puts "rubygems OK!"

The result of the C program is:

  $ ./main.so
  [BUG] rb_thread_terminate_all: called by child thread (0x8170160,
0x81bea60)
  ruby 1.9.0 (2007-12-25 revision 14709) [i686-linux]

  -- control frame ----------
  c:0001 p:---- s:0002 b:0002 l:000001 d:000001 TOP
  ---------------------------
  -- backtrace of native function call (Use addr2line) --
  0x80f0e0a
  0x810ed0c
  0x810ed7b
  0x80f3a75
  0x8060ab2
  0x8060b62
  0x805a93b
  0x80f4a3e
  0x80f4ac1
  0xb7f5c46b
  0xb7e7f6de
  -------------------------------------------------------
  C program is calling ruby_sysinit()
  C program is calling RUBY_INIT_STACK()
  C program is calling ruby_init()
  C program is calling ruby_init_loadpath()
  C program is loading file: hello.rb
  C program is putting Ruby thread in control...
  Ruby thread is starting interpreter

However, with hello.rb as:

  $ cat hello.rb
  puts "Hello World!"
  require 'rubygems'
  puts "rubygems OK!"

  print 'Ruby is waiting... '
  sleep 3
  puts 'done!'

The result of the C program is:

  $ ./main.so
  C program is calling ruby_sysinit()
  C program is calling RUBY_INIT_STACK()
  C program is calling ruby_init()
  C program is calling ruby_init_loadpath()
  C program is loading file: hello.rb
  C program is putting Ruby thread in control...
  Ruby thread is starting interpreter
  C program is back in control, exiting...

Notice that the C program did not wait for the_ruby_thread() to
finish.  Is there a function in the ruby C API that allows me to
wait for a child thread to finish (not rb_thread_join)?

I cannot simply join the child thread because in the real-world
application of this example, I need to repeatedly transfer control
from the main C program to the child thread about 30_000 times.

Thanks for your consideration.