スレッド初期化
メインスレッド
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
- thread_create_core @ thread.c
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_1 @ thread_(pthread|win32).ci
ここまででほぼ初期化は完了します。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の実行ループに突入します。