LoginSignup
6
8

More than 5 years have passed since last update.

LuaのC APIライブラリをマルチスレッド対応にする

Last updated at Posted at 2016-02-01

Luaをマルチスレッド環境で動作させる場合、ライブラリをマルチスレッド対応にする必要があります。
ここでは、Lua APIをマルチスレッド対応にする方法を記しておきます。

はじめに

スレッドごとにlua_newstate()を呼び出す場合、各スレッドごとにLuaのグローバル環境は独立するので、排他制御は不要になります。
今回はlua_newstate()で作成したステートを元に、各スレッドでlua_newthread()を呼び出す場合の排他制御の方法となります。

Lua APIの排他制御

Lua APIには、lua_locklua_unlockというマクロが定義されています。
このマクロは、llimits.hで次のように定義されています。

llimits.h
#if !defined(lua_lock)
#define lua_lock(L)     ((void) 0)
#define lua_unlock(L)   ((void) 0)
#endif

Lua APIは必要な個所でこれらのマクロを呼び出し、グローバル環境の排他制御を行っています。
しかし、既定の振る舞いとしては同期処理を行いません。

実装方法

lua_locklua_unlockを定義するためには、Luaライブラリのユーザー定義インクルードファイルを利用します。
ユーザー定義インクルードファイルは、lua.hで次のように定義されています。

lua.h
/*
** generic extra include file
*/
#if defined(LUA_USER_H)
#include LUA_USER_H
#endif

まずは、lua_locklua_unlockを定義するために、次のようなヘッダファイルを作成します。

luauser.h
#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環境での各同期関数の実装を示します。

luauser.c
#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_Hluauser.hになるようにします。
Visual Studioでビルドする場合は、次のように指定することができます。

/DLUA_USER_H="\"luauser.h\""

後はビルドが通れば完了です。

まとめ(と注意点)

  • グローバル環境の排他制御により、非同期にlua_newthread()を作成することができる。
  • lua_newthread()で作成したlua_Stateに対して、複数のスレッドからアクセス(スタック操作)する場合はそれ用に排他制御が必要。
  • lua_newthread()は、元のLuaステートのスタックにスレッドオブジェクトを積む。
    • 元のLuaステート作成時にlua_newstate()に指定したスタック数に注意。
6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8