ステート
次はメモリ管理に進もうかと思ったが、読みすすめていくうちに、どうも lua_State の使い方を見ないと正しく理解できないのではないかと思った。そこで先に lstate を見て lua_State に簡単に触れる。次に ldo を見て lua のコールスタックの使い方を見る。それから lgc に進むことにする。
lstate.h にはいろんなデータ形式が保存されている。 lua_State は VM ではなく、実際には実行スレッドになっている。したがってコルーチンも lua_State である。グローバルな状態は global_State で抽象化しているので、ここを使って共有しているのだろう。
LUA_API lua_State *lua_open (void) { lua_State *L = mallocstate(NULL); if (L) { /* allocation OK? */ L->tt = LUA_TTHREAD; L->marked = 0; L->next = L->gclist = NULL; preinit_state(L); L->l_G = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { /* memory allocation error: free partial state */ close_state(L); L = NULL; } } lua_userstateopen(L); return L; }
luaD_rawrunprotected がちょっと奇妙に見えるかもしれない。これは setjmp を使ってエラーが起きたときの対処をしているものなので、ようするに f_luaopen を呼んでいるのだと考えればよい。実際のメンバの初期化は preinit_state と f_luaopen で行われていて、基本的には 0 や NULL で初期化したり、グローバル変数用のテーブルを作ったり、といったことが行われている。
一方、 lua_State はコルーチンでもあった。そちらには専用の関数がある。
lua_State *luaE_newthread (lua_State *L) { lua_State *L1 = mallocstate(L); luaC_link(L, valtogco(L1), LUA_TTHREAD); preinit_state(L1); L1->l_G = L->l_G; stack_init(L1, L); /* init stack */ setobj2n(gt(L1), gt(L)); /* share table of globals */ return L1; }
こちらは f_luaopen を使っていない。 stack_init で L を親としてスタックの初期化を行っている(f_luaopen 内でも自分自身を親にして stack_init は呼ばれている)。それからグローバルな状態を L1 にコピーして完了。 f_luaopen では実際にはグローバルな状態を初期化しているので、こちらはその処理が不要なぶん、短い。
lua_State を終了するときの関数は lua_close だ。
LUA_API void lua_close (lua_State *L) { lua_lock(L); L = G(L)->mainthread; /* only the main thread can be closed */ luaF_close(L, L->stack); /* close all upvalues for this thread */ luaC_separateudata(L); /* separate udata that have GC metamethods */ L->errfunc = 0; /* no error function during GC metamethods */ do { /* repeat until no more errors */ L->ci = L->base_ci; L->base = L->top = L->ci->base; L->nCcalls = 0; } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0); lua_assert(G(L)->tmudata == NULL); close_state(L); }
基本的には、保有する様々なデータのメモリ領域を開放していくだけなのだが、ユーザデータには問題がある。というのは、ユーザデータは GC にタグつきメソッドを用意することができるからだ。つまりファイナライザである。 luaF_separateudata では所定のタグのあるユーザデータを検出し、 callallgcTM で実行している。終わったら開放しておしまいだ。
luaF_separateudata は lgc で定義されている。また callallgcTM も実体は lgc にあるから、そちらで読むことにする。