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;
}