ひとり勉強会

ひとり楽しく勉強会

スレッド初期化

メインスレッド

VMのメインスレッドに関する情報は、Init_VM 関数(2006-10-27 - ひとり勉強会)で記録されます。

Thread.new

Thread.new による新規スレッド起動の流れを見ていきます。まず、Threadクラスの定義は Init_Thread @ thread.c です。

void
Init_Thread(void)
{
  ...
  rb_define_singleton_method(rb_cThread, "new", thread_s_new, -2);
  ...
}

newメソッドはthread_s_new関数で実装されていることがわかります。thread_s_new では3段関数を転送してnative_thread_create 関数に到着。

  • thread_s_new @ thread.c
    • thread_create_core @ thread.c
      • native_thread_create @ thread_(pthread|win32).ci

native_thread_creat では、thread_start_func_1 @ thread_(pthread|win32).ci 関数から新規スレッドをスタートします。例えばWin32版では

  th->thread_id = w32_create_thread(stack_size, thread_start_func_1, th);

こういう実装です(w32_create_thread は _beginthreadex のラッパー関数)。thread_start_func_1 はWin32/pthread依存の処理をすませてから、thread_start_func_2 @ thread.c を呼び出します。

  • native_thread_create @ thread_(pthread|win32).ci
    • thread_start_func_1 @ thread_(pthread|win32).ci
      • thread_start_func_2 @ thread.c

ここまででほぼ初期化は完了します。thread_start_func_2では、Thread.new に渡されたブロック(をProcオブジェクト化したもの?)の実行を開始して、メインループに入ります。

static int
thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
{
  ...
  native_mutex_lock(&th->vm->global_interpreter_lock);
  {
    rb_thread_set_current(th);
    ...
    th->value = th_invoke_proc(th, proc, proc->block.self,
                               RARRAY_LEN(args), RARRAY_PTR(args));
    ...
  }
  thread_cleanup_func(th);
  native_mutex_unlock(&th->vm->global_interpreter_lock);
  return 0;
}

YARVの現在の実装は、VM全体で1個のMutexを共有していて、そのMutexを取ったスレッドしか稼働しないというものでした。vm->global_interpreter_lock がまさにそのMutexで、スレッドの本体実行開始前にロックを取得しています。
th_invoke_procの中ではth_eval_bodyに到達して、あとは前に読んだVMの実行ループに突入します。