Hongli Lai wrote:
> You also seem to be using locks in a wrong way. In main() you have:
> 
>>       pthread_mutex_init(&gCProgLock, NULL);
>>       pthread_mutex_lock(&gCProgLock);
>>
>>       pthread_create(&gRubyThread, NULL, gRubyThread_body, NULL);
>>       pthread_mutex_lock(&gCProgLock); // C program blocks here
> 
> And in the thread you have:
> 
>>       pthread_mutex_unlock(&gCProgLock);
> 
> You are unlocking a mutex, which was locked by another thread. This is a
> violation of the POSIX API. I quote the man page:
> 
> "!pthread_mutex_unlock! unlocks the given mutex. The mutex is assumed to
> be  locked  and  owned  by  the   calling   thread   on   entrance   to
> !pthread_mutex_unlock!."

You are correct.

> If you want to wake up the main thread (which was sleeping on some kind
> of condition) then you should use conditioned variables instead.

Thanks for the tip.  I revised the example accordingly:

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

  pthread_t gRubyThread;
  pthread_mutex_t gMainLock;
  pthread_cond_t gRubyDone;

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

      printf("Ruby thread is done, waking up C program...\n");
      pthread_mutex_lock(&gMainLock);
      pthread_cond_signal(&gRubyDone);
      pthread_mutex_unlock(&gMainLock);

      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] = "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);

      pthread_mutex_init(&gMainLock, NULL);
      pthread_cond_init(&gRubyDone, NULL);
      pthread_create(&gRubyThread, NULL,
                     the_ruby_thread, rubyProgram);

      printf("C program is putting Ruby thread in control...\n");
      pthread_mutex_lock(&gMainLock);
      pthread_cond_wait(&gRubyDone, &gMainLock); // blocking call
      pthread_mutex_unlock(&gMainLock);

      printf("C program is back in control, exiting...\n");
      pthread_mutex_destroy(&gMainLock);
      pthread_cond_destroy(&gRubyDone);
      pthread_exit(NULL);
  }

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

And with hello.rb as:

  $ cat hello.rb
  puts "Hello World!"
  require 'rubygems'
  puts "rubygems OK!"
  p :'$0' => $0


The result of running the example 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
  Hello World!
  rubygems OK!
  {:$0=>"hello.rb"}
  Ruby thread is done, waking up C program...
  C program is back in control, exiting...