0%

C++执行Lua虚拟机调用时检查超时以及让出方法

以检测lua_pcall调用为例子,执行某个自定义的Lua脚本函数调用会进入一个死循环,现在要做的是让它在超过一定时间未执行完毕,就让出,跳出死循环。

基本原理

  1. 在调用lua_pcall之前记录调用前的时间戳

  2. 启动新线程,检测超时

  3. 调用lua_pcall

  4. 如果在新线程中检测到超时,那么启用钩子

1
2
// 这里最好用LUA_MASKLINE,即每执行一个新语句前断下来
lua_sethook(L, hook_func, LUA_MASKLINE, 0);
  1. 进入钩子函数后,先把钩子去掉
1
lua_sethook(L, nullptr, 0, 0);
  1. 然后调用luaL_error让出

封装C++钩子处理函数以及相关宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 钩子处理函数
void wxbox::plugin::internal::PluginVirtualMachineHookHandler(lua_State* L, lua_Debug* debugInfo)
{
WXBOX_UNREF(debugInfo);

lua_sethook(L, NULL, 0, 0);
luaL_error(L, "plugin virtual machine long task timeout");
}

// 获取当前以毫秒为单位的时间戳
std::time_t GetCurrentTimestamp()
{
auto t = std::chrono::system_clock::now().time_since_epoch();
return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
}

//
// 宏
//

#define BEGIN_PLUGIN_VIRTUAL_MACHINE_LONG_TASK_WITH_TIMEOUT(vm) \
{ \
auto __task_begin_timestamp = GetCurrentTimestamp(); \
std::promise<void> __task_complete_signal; \
\
auto __task_async_future = std::async( \
std::launch::async, [__task_begin_timestamp, vm](std::future<void> taskFuture) { \
for (;;) { \
if (taskFuture.wait_for(std::chrono::milliseconds(10)) != std::future_status::timeout) { \
/* task complete */ \
break; \
} \
\
if (GetCurrentTimestamp() - __task_begin_timestamp >= vm->longTaskTimeout) { \
/* timeout */ \
lua_sethook(vm->state, PluginVirtualMachineHookHandler, LUA_MASKLINE, 0); \
break; \
} \
} \
}, \
std::move(__task_complete_signal.get_future()));

#define END_PLUGIN_VIRTUAL_MACHINE_LONG_TASK_WITH_TIMEOUT(vm) \
CANCEL_PLUGIN_VIRTUAL_MACHINE_LONG_TASK(vm); \
}

#define CANCEL_PLUGIN_VIRTUAL_MACHINE_LONG_TASK(vm) \
{ \
__task_complete_signal.set_value(); \
__task_async_future.wait(); \
lua_sethook(vm->state, NULL, 0, 0); \
}

使用方法

1
2
3
4
5
6
7
8
// trigger event with timeout
BEGIN_PLUGIN_VIRTUAL_MACHINE_LONG_TASK_WITH_TIMEOUT(vm);
if (lua_pcall(vm->state, 0, 1, 0) != LUA_OK) {
CANCEL_PLUGIN_VIRTUAL_MACHINE_LONG_TASK(vm);
lua_pop(vm->state, 2);
return false;
}
END_PLUGIN_VIRTUAL_MACHINE_LONG_TASK_WITH_TIMEOUT(vm);
请我喝瓶肥仔快乐水?

欢迎关注我的其它发布渠道