4

注意:下面有一些问题可以说明我的想法,但我正在寻找的唯一答案是标题中实际问题的答案。不是在这里要求“一本书”,也不是对所有这些的逐条回应。

我正在尝试从 C API 启动一个协程,让它产生,然后再继续(可能在从其他协程执行恢复之后)。这是一个相当简单的用例,但文档lua_resume()非常混乱:

int lua_resume (lua_State *L, lua_State *from, int nargs);

在给定的线程 L 中启动和恢复协程。

要启动协程,您将 main 函数和任何参数压入线程堆栈;然后你调用 lua_resume,其中 nargs 是参数的数量。当协程挂起或完成执行时,此调用返回。当它返回时,堆栈包含所有传递给 lua_yield 的值,或所有由 body 函数返回的值。如果协程产生,lua_resume 返回 LUA_YIELD,如果协程完成执行而没有错误,则返回 LUA_OK,或者在错误的情况下返回错误代码(参见 lua_pcall)。

如果出现错误,堆栈不会展开,因此您可以在其上使用调试 API。错误消息位于堆栈顶部。

要恢复协程,您需要从最后一个 lua_yield 中删除所有结果,仅将要作为 yield 的结果传递的值放入其堆栈,然后调用 lua_resume。

参数from代表正在恢复L的协程。如果没有这样的协程,该参数可以为NULL。

“表示正在恢复 L 的协程”的含义在这里非常不清楚。究竟什么时候“来自”nil,哪个L和哪个from是“线程堆栈”,以及恢复协程的确切要求是什么?可以在初始状态和实际恢复的第二个状态L之间修改状态吗?lua_resume()如果是这样,该州如何知道在哪里/恢复什么功能?如果不是(即每个线程一个线程lua_State),创建新线程的正确方法是什么,以便它与父线程共享执行上下文(全局、环境等),以及调用lua_resume()和展开的正确方法是什么?在这种情况下要求每个开始/恢复?'from' 论点是否与这些事情有关?

最后,在任何一种情况下 - 我如何获得完整的堆栈跟踪以在错误处理程序中进行调试(例如在错误时调用lua_pcall),它尊重/知道跨简历的调用?是否lua_getinfo()通过简历正确报告?这就是“来自”论点的目的吗?

我真的很想要一个来自 C API 的协程启动/恢复的完整工作示例,该示例说明了第二个参数的使用。gist 上有这个例子,但是它已经有多年历史了,并且 API 从那时起发生了变化——lua_resume()现在需要 3 个参数,而不是 2 个......

4

1 回答 1

3

当究竟是“从”零,L 和从是“线程堆栈”。

如果您要询问参数的含义则似乎没有太多 one。只有在执行恢复的 C 函数本身是从协程中调用时,才可以使用它。from

L是您将要恢复的参数推送到的堆栈(以及起始函数,如果它是初始恢复调用)。所以这最适合术语“线程堆栈”。

恢复协程的确切要求是什么?

文档中所述lua_statusLUA_OK,如果线程的状态为或,您可以恢复线程LUA_YIELD

请注意,恢复线程和恢复协程是有区别的。lua_resume恢复一个线程。如果状态为LUA_OK,则 没有活动的协程,因此您正在创建一个的协程,因此必须提供一个函数。如果线程是LUA_YIELD,那么恢复线程将恢复产生的协程。

可以在初始 lua_resume() 和第二个实际恢复的状态之间修改状态 L 吗?

当然可以;它必须是。毕竟,您将返回/收益值作为推送到L. 当你去恢复它时,你必须删除这些值并将新值作为将从 Luayield函数返回的值推送到堆栈上。

所以是的,你可以修改它。但是有一些关于你能做什么的规则。他们两个人:

规则#1:你不能戳不属于你的东西。

假设 Lua 堆栈看起来像这样,就在您的初始简历之前(从左到右增长):

[A][B][C][F][1][2][3]

F是您要启动的功能。1、2 和 3 是参数。所以你把 3 作为nargs. A, B, 和C只是你放在那个堆栈上的任意东西。

如果协程产生,您的堆栈将如下所示:

[A][B][C][LOTS OF STUFF][a][b][c]

LOTS OF STUFF表示由协程处理创建的任意数量的堆栈数据。没有办法知道那里会有多少或多少。

a, b, 和c是传递给yieldLua 的值。您可以随心所欲地玩它们。您甚至可以通过它们的绝对堆栈索引 1、2 和 3返回并戳AB和。C

不能做的就是改变LOTS OF STUFF。该数据代表产生的协程的状态。您不得更改它。完全没有。您可能也无法删除它下面的任何元素。所以你甚至不能删除A,BC.

规则 #2:您必须准确地从堆栈停止的地方恢复。

在协程产生后,您可以从堆栈中删除一些东西并向其中添加东西(根据规则#1)。因此,假设堆栈如下所示:

[A][B][C][LOTS OF STUFF][a][Q][R][D]

a作为原始返回值之一,其他东西是您在此期间创建的临时值。所以现在是时候恢复协程了。你有一些新的参数要传递,1 和 2。你是怎么做到的?

你的第一步必须是清理你的烂摊子。您必须将堆栈返回到这一点:

[A][B][C][LOTS OF STUFF]

nargs完成后,您可以推送 1 和 2,然后使用as 2恢复协程。

如果是这样,该州如何知道在哪里/恢复什么功能?

上面的LOTS OF STUFF部分告诉它“在哪里/恢复什么功能”。

什么是创建新线程的正确方法,以便它与父线程共享执行上下文(全局、环境等)

“执行上下文”与线程无关。这些东西与函数相关,而不是线程。因此,如果您希望一个函数与其他函数共享相同的环境,那么它们应该与该函数共享相同的环境。

这些函数的执行方式(协程与否)与它们的环境无关。

最后,在任何一种情况下 - 我如何获得完整的堆栈跟踪以在错误处理程序中进行调试(例如,从 lua_pcall 错误调用)尊重/知道跨简历的调用?

你不能。

lua_resume由于错误而失败时,它会在堆栈上留下信息以跟踪在简历和发出错误的地方之间发生的事情。但它无法知道简历本身是如何发生的。这是你必须做的事情,这取决于你如何恢复那个协程。

from字段的目的不是将恢复器连接到恢复器。

于 2016-06-05T20:22:35.673 回答