注意:下面有一些问题可以说明我的想法,但我唯一能找到的答案就是标题中实际问题的答案。不要求"一本书"在这里,或对所有这些的逐项回复。
我试图从C API启动一个协同程序,让它产生,并在以后继续它(可能在从其他协同程序执行恢复之后)。这是一个相当简单的用例,但lua_resume()
的文档极其混乱:
int lua_resume(lua_State * L,lua_State * from,int nargs);
在给定的线程L中开始并恢复协程。
要启动协程,您可以将主要功能加上线程堆栈 任何论点;然后你调用lua_resume,其中nargs是数字 参数。当协程暂停或完成它时,此调用返回 执行。返回时,堆栈包含传递给的所有值 lua_yield,或者body函数返回的所有值。 lua_resume返回 LUA_YIELD如果协程产生,LUA_OK如果协程完成它 没有错误的执行,或者出错的错误代码(参见 lua_pcall)。
如果出现错误,堆栈未展开,因此您可以使用调试API 超过它。错误消息位于堆栈顶部。
要恢复协程,请删除最后一个lua_yield,put的所有结果 在它的堆栈上只有作为yield的结果传递的值,然后 叫lua_resume。
参数from表示正在恢复L的协程。如果有 没有这样的协程,这个参数可以是NULL。
"的含义代表正在恢复L"在这里非常不清楚。恰好是"来自" nil,L
和from
中的哪一个是"线程堆栈",以及恢复协程的确切要求是什么?可以在初始L
和第二个实际恢复的状态之间修改状态lua_resume()
吗?如果是这样,国家如何知道恢复哪些/什么功能?如果不是(即每个lua_State
一个线程)什么是创建新线程的正确方法,以便它与父线程共享执行上下文(全局,环境等),以及正确的方法是什么在这种情况下调用lua_resume()
并解除每次启动/恢复的呼叫?来自'来自'参数是否与这些事情相关?
最后,在任何一种情况下 - 如何在错误处理程序中获取完整的堆栈跟踪(例如,在lua_pcall
错误时调用),这些错误处理程序尊重/知道跨越简历的调用? lua_getinfo()
是否通过简历正确报告?这是'来自'论证是为了?
我真的很喜欢C API协程启动/恢复的完整,有效的例子,它说明了第二个参数的使用。关于要点的this example,但它已经过去了几年,而且自那时起API发生了变化 - lua_resume()
现在有3个参数,而不是2 ...
答案 0 :(得分:3)
当确切地说是“from”nil时,L和from中的哪一个是“线程堆栈”。
如果您询问from
参数的{em>含义,it would appear that there isn't much of one。如果正在执行简历的C函数本身是从协程中调用的,那么只有使用它才有效。
L
是您已将参数推送到其上的堆栈(以及启动函数,如果它是初始恢复调用)。所以这最适合术语“线程堆栈”。
恢复协程的确切要求是什么?
如the docs for lua_status
中所述,如果帖子的状态为LUA_OK
或LUA_YIELD
,则可以恢复帖子。
请注意,恢复线程和恢复协程之间存在差异。 lua_resume
恢复一个帖子。如果状态为LUA_OK
,则没有活动协同程序,因此您正在创建新协同程序,因此必须提供一个函数。如果线程是LUA_YIELD
,则恢复线程将恢复生成的协程。
状态L是否可以在初始的lua_resume()和实际恢复的第二个之间进行修改?
当然可以; 必须是。毕竟,您将返回/收益率值作为推送到L
的值。当你继续恢复它时,你必须删除这些值并将新值作为将从Lua的yield
函数返回的值推送到堆栈上。
是的,你可以修改它。但是有关于你能做什么的规则。其中两个:
规则#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
是传递给Lua中yield
的值。你可以随心所欲地玩它们。你甚至可以回到A
,B
和C
,然后点击。
无法执行的操作是更改LOTS OF STUFF
。该数据代表了屈服的协程状态。您不得更改它。完全没有。您可能也无法删除它下面的任何元素。因此,您无法删除A
,B
或C
。
规则#2:您必须准确恢复堆栈停止的位置。
协程产生后,你可以从堆栈中删除一些东西并添加东西(根据规则#1)。所以我们假设堆栈看起来像这样:
[A][B][C][LOTS OF STUFF][a][Q][R][D]
a
是原始返回值之一,其他内容是您在此期间创建的临时值。所以现在是恢复协程的时候了。你有一些新的参数要传递,1和2.你怎么做?
你的第一步必须来清理你的烂摊子。您必须将堆栈返回到此点:
[A][B][C][LOTS OF STUFF]
完成后,您可以按1和2,然后以nargs
为2恢复协程。
如果是这样,状态如何知道恢复哪个/什么功能?
上面的LOTS OF STUFF
部分告诉它“恢复的位置/功能”。
创建新线程的正确方法是什么,以便它与父线程共享执行上下文(全局,环境等)
“执行上下文”与线程无关。这些东西与函数相关联,而不是线程。因此,如果您希望函数与其他函数共享相同的环境,那么它们应该与该函数共享相同的环境。
这些功能如何执行(协同与否)与其环境无关。
最后,在任何一种情况下 - 如何在错误处理程序中获取完整的堆栈跟踪(例如,在lua_pcall错误时调用),它尊重/意识到简历中的调用?
你不能。
当lua_resume
由于错误而失败时,它会在堆栈上留下信息,以跟踪简历和发出错误的位置之间发生的情况。但它不能知道简历本身是如何发生的。这是你必须要做的事情,取决于你开始时如何恢复该协程。
from
字段的目的是不将resumer连接到已恢复的。