Lua処理系コード読み(12) lua_newstate

lua_newstateは一個のlua_Stateとglobal_Stateからなるメインスレッド(ネイティブスレッドではなくいわゆるグリーンスレッド)を初期化する関数。基本的には領域とってきて値を設定してるだけです。

LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
  int i;
  lua_State *L;
  global_State *g;
  void *l = (*f)(ud, NULL, 0, state_size(LG));
  if (l == NULL) return NULL;
  L = tostate(l);
  g = &((LG *)L)->g;
  L->next = NULL;
  L->tt = LUA_TTHREAD;
  g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
  L->marked = luaC_white(g);
  set2bits(L->marked, FIXEDBIT, SFIXEDBIT);
  preinit_state(L, g);
  g->frealloc = f;
  g->ud = ud;
  g->mainthread = L;
  g->uvhead.u.l.prev = &g->uvhead;
  g->uvhead.u.l.next = &g->uvhead;
  g->GCthreshold = 0;  /* mark it as unfinished state */
  g->strt.size = 0;
  g->strt.nuse = 0;
  g->strt.hash = NULL;
  setnilvalue(registry(L));
  luaZ_initbuffer(L, &g->buff);
  g->panic = NULL;
  g->gcstate = GCSpause;
  g->rootgc = obj2gco(L);
  g->sweepstrgc = 0;
  g->sweepgc = &g->rootgc;
  g->gray = NULL;
  g->grayagain = NULL;
  g->weak = NULL;
  g->tmudata = NULL;
  g->totalbytes = sizeof(LG);
  g->gcpause = LUAI_GCPAUSE;
  g->gcstepmul = LUAI_GCMUL;
  g->gcdept = 0;
  for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;
  if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {
    /* memory allocation error: free partial state */
    close_state(L);
    L = NULL;
  }
  else
    luai_userstateopen(L);
  return L;
}

メモリロケーション命令のエラーが発生しかねない初期化に関してはf_luaopen関数の中にまとめてluaD_rawrununprotected関数経由で呼ぶ。luaD_rawrununprotected内部にはtry/catch(のようなもの)があり、f_luaopen内部からthrowが飛んでくるとエラーを返す。f_luaopenが正常に実行できていれば0を返す。つまり

  else
    luai_userstateopen(L);

はf_luaopenがエラー(メモリが足りないとかアロケータが腐ってるとかそういう状況。つまり普通エラーにならない)じゃなければ呼ばれる。

LG, LUAI_EXTRASPACE, state_size(LG), tostate(l), fromstate(l)

typedef struct LG {
  lua_State l;
  global_State g;
} LG;

LGはまあ見たまんまの。

/*
@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State
@* (the data goes just *before* the lua_State pointer).
** CHANGE (define) this if you really need that. This value must be
** a multiple of the maximum alignment required for your machine.
*/
#define LUAI_EXTRASPACE		0

#define state_size(x)	(sizeof(x) + LUAI_EXTRASPACE)
#define fromstate(l)	(cast(lu_byte *, (l)) - LUAI_EXTRASPACE)
#define tostate(l)   (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))

state_size(LG)=state_size(L)+sizeof(global_State)=sizeof(L)+LUAI_EXTRASPACE+sizeof(global_State)。スレッド領域を確保するときにLUAI_EXTRASPACE分も確保してくれて、fromstate(L)とかすることでその領域のアドレスを得ることができる。スレッド毎に保持しておきたいデータがあればここをいじる。LuaさんはCアプリに組み込んで使うことが多いのでこういうことができるように。

/*
@@ luai_userstate* allow user-specific actions on threads.
** CHANGE them if you defined LUAI_EXTRASPACE and need to do something
** extra when a thread is created/deleted/resumed/yielded.
*/
#define luai_userstateopen(L)		((void)L)
#define luai_userstateclose(L)		((void)L)
#define luai_userstatethread(L,L1)	((void)L)
#define luai_userstatefree(L)		((void)L)
#define luai_userstateresume(L,n)	((void)L)
#define luai_userstateyield(L,n)	((void)L)

メインスレッド初期化のときにluai_userstateopen、他のスレッド初期化時にluastatethreadなど適宣呼んでくれるので初期化したい構造のときはこの名前でdefineしてくださいねということ。luaconf.hを参考に。

test