Luaをマルチスレッド環境で動作させる場合、ライブラリをマルチスレッド対応にする必要があります。
ここでは、Lua APIをマルチスレッド対応にする方法を記しておきます。
はじめに
スレッドごとにlua_newstate()
を呼び出す場合、各スレッドごとにLuaのグローバル環境は独立するので、排他制御は不要になります。
今回はlua_newstate()
で作成したステートを元に、各スレッドでlua_newthread()
を呼び出す場合の排他制御の方法となります。
Lua APIの排他制御
Lua APIには、lua_lock
とlua_unlock
というマクロが定義されています。
このマクロは、llimits.hで次のように定義されています。
#if !defined(lua_lock)
#define lua_lock(L) ((void) 0)
#define lua_unlock(L) ((void) 0)
#endif
Lua APIは必要な個所でこれらのマクロを呼び出し、グローバル環境の排他制御を行っています。
しかし、既定の振る舞いとしては同期処理を行いません。
実装方法
lua_lock
とlua_unlock
を定義するためには、Luaライブラリのユーザー定義インクルードファイルを利用します。
ユーザー定義インクルードファイルは、lua.hで次のように定義されています。
/*
** generic extra include file
*/
#if defined(LUA_USER_H)
#include LUA_USER_H
#endif
まずは、lua_lock
とlua_unlock
を定義するために、次のようなヘッダファイルを作成します。
#ifndef luauser_h_
#define luauser_h_
#define lua_lock(L) LuaLock(L)
#define lua_unlock(L) LuaUnlock(L)
#define luai_userstateopen(L) LuaInitial(L) // lua_newstate内で呼び出される
#define luai_userstateclose(L) LuaFinal(L) // lau_close内で呼び出される
extern void LuaLock(lua_State *L);
extern void LuaUnlock(lua_State *L);
extern void LuaInitial(lua_State *L);
extern void LuaFinal(lua_State *L);
#endif
Windows環境での各同期関数の実装を示します。
#include "lua.h"
#include "luauser.h"
#include <stdbool.h>
#if defined(WIN32)
#include <Windows.h>
#endif
struct {
#if defined(WIN32)
CRITICAL_SECTION cs;
#endif
bool init;
} static GL;
void LuaInitial(lua_State *L)
{
if (false == GL.init) {
#if defined(WIN32)
InitializeCriticalSectionAndSpinCount(&GL.cs, 2000);
#endif
GL.init = true;
}
}
void LuaFinal(lua_State *L)
{
if (true == GL.init) {
#if defined(WIN32)
DeleteCriticalSection(&GL.cs);
#endif
GL.init = false;
}
}
void LuaLock(lua_State *L)
{
#if defined(WIN32)
EnterCriticalSection(&GL.cs);
#endif
}
void LuaUnlock(lua_State *L)
{
#if defined(WIN32)
LeaveCriticalSection(&GL.cs);
#endif
}
そして、Luaライブラリをビルドする時に、LUA_USER_H
がluauser.h
になるようにします。
Visual Studioでビルドする場合は、次のように指定することができます。
/DLUA_USER_H="\"luauser.h\""
後はビルドが通れば完了です。
まとめ(と注意点)
- グローバル環境の排他制御により、非同期に
lua_newthread()
を作成することができる。 -
lua_newthread()
で作成したlua_State
に対して、複数のスレッドからアクセス(スタック操作)する場合はそれ用に排他制御が必要。 -
lua_newthread()
は、元のLuaステートのスタックにスレッドオブジェクトを積む。- 元のLuaステート作成時に
lua_newstate()
に指定したスタック数に注意。
- 元のLuaステート作成時に