C 中的简单 lua_yield 无法从 Lua 正确恢复
Simple lua_yield in C not resuming correctly from Lua
我刚刚开始研究 lua 与 C 的协程,我对我认为应该是我能想到的最简单的例子有疑问。
C:
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>
static int lua_test_yield(lua_State *L) {
printf("1\n");
lua_yield(L, 0);
printf("2\n");
lua_yield(L, 0);
printf("3\n");
lua_yield(L, 0);
printf("4\n");
lua_yield(L, 0);
printf("5\n");
lua_yield(L, 0);
return 0;
}
static const struct luaL_Reg mylib[] = {
{"test_yield", lua_test_yield},
{NULL, NULL}
};
// Used when the .so library is loaded from lua
int luaopen_mytest(lua_State *L) {
luaL_newlib(L, mylib);
return 1;
}
Lua
mytest = require 'mytest'
print("Loaded")
mytest_yeild = coroutine.create(function ()
mytest.test_yield()
end)
for i=1,5 do
print(coroutine.resume(mytest_yeild))
end
结果:
$ lua test.lua
Loaded
1
true
true
false cannot resume dead coroutine
false cannot resume dead coroutine
false cannot resume dead coroutine
我觉得这很奇怪。为什么报两次成功的resume却什么都不打印,然后又报失败的resume?我在这里错过了什么?谢谢。
lua_yield
是一个 C 函数,而 C 没有 神奇地跳回已停止的函数的机制。 lua_yield
使用 C 标准库 longjmp
函数任意跳转 出 调用它的函数。却回不来了
那么发生的事情是您的 C 函数产生,退出函数并将控制权返回给调用 coroutine.resume
的 Lua 代码。简历成功,所以打印true
。然后您再次恢复协程,它开始在 中 Lua 代码 的站点执行,该代码调用了产生的 C 函数。然后该代码正常退出协程。由于简历也成功了,所以再次打印true
但是协程现在已经耗尽,因此无法恢复。
C 函数没有一种干净的方式来“恢复”。事实上,Lua 5.1 documentation explicitly states:
This function should only be called as the return expression of a C function, as follows:
return lua_yield (L, nresults);
Lua 5.2 及以上版本有一些恢复 C 函数的能力,但只有 through a continuation API。您不能编写一个像 Lua 屈服函数那样工作的 C 函数。
为了return屈服后的C代码,你需要使用lua_yieldk
并让它在一个单独的C函数中获取,像这样:
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>
static int lua_test_yield_6(lua_State *L, int status, lua_KContext ctx) {
return 0;
}
static int lua_test_yield_5(lua_State *L, int status, lua_KContext ctx) {
printf("5\n");
return lua_yieldk(L, 0, 0, lua_test_yield_6);
}
static int lua_test_yield_4(lua_State *L, int status, lua_KContext ctx) {
printf("4\n");
return lua_yieldk(L, 0, 0, lua_test_yield_5);
}
static int lua_test_yield_3(lua_State *L, int status, lua_KContext ctx) {
printf("3\n");
return lua_yieldk(L, 0, 0, lua_test_yield_4);
}
static int lua_test_yield_2(lua_State *L, int status, lua_KContext ctx) {
printf("2\n");
return lua_yieldk(L, 0, 0, lua_test_yield_3);
}
static int lua_test_yield_1(lua_State *L) {
printf("1\n");
return lua_yieldk(L, 0, 0, lua_test_yield_2);
}
static const struct luaL_Reg mylib[] = {
{"test_yield", lua_test_yield_1},
{NULL, NULL}
};
// Used when the .so library is loaded from lua
int luaopen_mytest(lua_State *L) {
luaL_newlib(L, mylib);
return 1;
}
我刚刚开始研究 lua 与 C 的协程,我对我认为应该是我能想到的最简单的例子有疑问。
C:
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>
static int lua_test_yield(lua_State *L) {
printf("1\n");
lua_yield(L, 0);
printf("2\n");
lua_yield(L, 0);
printf("3\n");
lua_yield(L, 0);
printf("4\n");
lua_yield(L, 0);
printf("5\n");
lua_yield(L, 0);
return 0;
}
static const struct luaL_Reg mylib[] = {
{"test_yield", lua_test_yield},
{NULL, NULL}
};
// Used when the .so library is loaded from lua
int luaopen_mytest(lua_State *L) {
luaL_newlib(L, mylib);
return 1;
}
Lua
mytest = require 'mytest'
print("Loaded")
mytest_yeild = coroutine.create(function ()
mytest.test_yield()
end)
for i=1,5 do
print(coroutine.resume(mytest_yeild))
end
结果:
$ lua test.lua
Loaded
1
true
true
false cannot resume dead coroutine
false cannot resume dead coroutine
false cannot resume dead coroutine
我觉得这很奇怪。为什么报两次成功的resume却什么都不打印,然后又报失败的resume?我在这里错过了什么?谢谢。
lua_yield
是一个 C 函数,而 C 没有 神奇地跳回已停止的函数的机制。 lua_yield
使用 C 标准库 longjmp
函数任意跳转 出 调用它的函数。却回不来了
那么发生的事情是您的 C 函数产生,退出函数并将控制权返回给调用 coroutine.resume
的 Lua 代码。简历成功,所以打印true
。然后您再次恢复协程,它开始在 中 Lua 代码 的站点执行,该代码调用了产生的 C 函数。然后该代码正常退出协程。由于简历也成功了,所以再次打印true
但是协程现在已经耗尽,因此无法恢复。
C 函数没有一种干净的方式来“恢复”。事实上,Lua 5.1 documentation explicitly states:
This function should only be called as the return expression of a C function, as follows:
return lua_yield (L, nresults);
Lua 5.2 及以上版本有一些恢复 C 函数的能力,但只有 through a continuation API。您不能编写一个像 Lua 屈服函数那样工作的 C 函数。
为了return屈服后的C代码,你需要使用lua_yieldk
并让它在一个单独的C函数中获取,像这样:
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>
static int lua_test_yield_6(lua_State *L, int status, lua_KContext ctx) {
return 0;
}
static int lua_test_yield_5(lua_State *L, int status, lua_KContext ctx) {
printf("5\n");
return lua_yieldk(L, 0, 0, lua_test_yield_6);
}
static int lua_test_yield_4(lua_State *L, int status, lua_KContext ctx) {
printf("4\n");
return lua_yieldk(L, 0, 0, lua_test_yield_5);
}
static int lua_test_yield_3(lua_State *L, int status, lua_KContext ctx) {
printf("3\n");
return lua_yieldk(L, 0, 0, lua_test_yield_4);
}
static int lua_test_yield_2(lua_State *L, int status, lua_KContext ctx) {
printf("2\n");
return lua_yieldk(L, 0, 0, lua_test_yield_3);
}
static int lua_test_yield_1(lua_State *L) {
printf("1\n");
return lua_yieldk(L, 0, 0, lua_test_yield_2);
}
static const struct luaL_Reg mylib[] = {
{"test_yield", lua_test_yield_1},
{NULL, NULL}
};
// Used when the .so library is loaded from lua
int luaopen_mytest(lua_State *L) {
luaL_newlib(L, mylib);
return 1;
}