Lua与C/Delphi函数之间的内部冲突?

我为我的项目编写了计时器实现(适用于lua 5.1,完整的源代码、dll和测试可在http://wintarif.narod.ru/products.htm获取,因此我将在问题中略去完整的源代码)。计时器创建对象并实现CreateTimerQueueTimer。我进行了3个不同行为的测试: 测试脚本的共享部分

require('timer')

-- 参数与CreateTimerQueueTimer相同:DueTime、Period、Flags
-- 标志WT_EXECUTEONLYONCE = 8,计时器将停止,启用设置为false
local mt = timer(1000, 1000, 0)

local i = 0;
function myOnTimer()
    print('wow!')
    if i < 5 then
        i = i + 1
    else
        print("stopping timer")
        mt:StopTimer()
    end
end

mt:SetEvent('OnTimer', myOnTimer)
mt:StartTimer()

当我使用

while mt:GetEnabled() do --更多的buggy way
end

时,会出现“未处理的异常”,但每秒都会不停地打印 wow!

使用

while true do --buggy way, stack conflict during callback?
    local enabled = mt:GetEnabled()
    if not(enabled) then
        break
    end
end

会出现错误,例如5:bad argument #-2 to 'GetEnabled' (attempt to concatenatetimerLOADLIB: a table valuetimerstring)5:bad argument #-2 to 'GetEnabled' (timer expected, got table),或者它可以在第一个来自dll的事件之前工作,然后停止而没有错误。

function WaitForTimer()
    while true do
        local is_enabled = mt:GetEnabled()
        if not(is_enabled) then
            print("not enabled")
            return coroutine.yield()
        end
    end
end

co = coroutine.create(WaitForTimer)
coroutine.resume(co)

可以在没有错误的情况下工作。

GetEnabled()的dll实现非常简单,静态cdecl函数

function StaticThunk(L: Plua_State): integer; cdecl;
var
  o: TLuaWrapper;
begin
  o := TLuaWrapper(lua_topointer(L, lua_upvalueindex(1)));
  result := o.Thunk(L);
end;

它提取对象、对象的Thunk

function TLuaWrapper.Thunk(L: Plua_State): integer;
var
  i: integer;
  pobj: PtrT;
begin
  { redirect method call to the real thing }
  i := lua_tointeger(L, lua_upvalueindex(2)); // function's index, index is 2 since 1 is self ptr now
  lua_pushnumber(L, 0);
  lua_gettable(L, 1); // get the class table (i.e, self)

  pobj := PtrT(luaL_checkudata(L, -1, PAnsiChar(REG_NAME)));
  lua_remove(L, -1); // remove the userdata from the stack
  lua_remove(L, 1); // remove object from the stack

  try
    result := ClassApiArray[i].func(L, pobj^); // execute the thunk
  except
      result := 0;
  end;
end;

确切地说是

function TLuaWrapper.GetEnabled(L: Plua_State; obj: TQueuedTimer): integer;
begin
  // lua_settop(L, 0);
  lua_pushboolean(L, obj.Enabled);
  result := 1;
end;

在Lua内部发生了什么?为什么会有冲突?

更多信息:脚本在LuaForWindowslua.exe下执行。

点赞
用户1847592
用户1847592

你传递给 CreateTimerQueueTimer() 的回调函数将由 Windows 在另一个线程中异步执行。

但你不允许同时在两个不同的线程中使用相同的 Lua 状态。

因此,在此 Lua 状态中运行主脚本时,你无法在 Lua 状态中执行回调函数。

在多个线程中使用 Lua 状态将导致不可预测的行为和奇怪的错误消息。

当你的回调函数变得更加复杂时,使用协程的代码,目前可以正常运行而不出错,也会出现错误。

Lua 状态不是线程安全的。

因此,CreateTimerQueueTimer 的功能无法绑定到 Lua。 :-(

2013-04-06 12:02:47