- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
在不久前的blog post中,Scott Vokes使用C函数setjmp
和longjmp
描述了与lua实现协程相关的技术问题:
The main limitation of Lua coroutines is that, since they are implemented with setjmp(3) and longjmp(3), you cannot use them to call from Lua into C code that calls back into Lua that calls back into C, because the nested longjmp will clobber the C function’s stack frames. (This is detected at runtime, rather than failing silently.)
I haven’t found this to be a problem in practice, and I’m not aware of any way to fix it without damaging Lua’s portability, one of my favorite things about Lua — it will run on literally anything with an ANSI C compiler and a modest amount of space. Using Lua means I can travel light. :)
setjmp
和
longjmp
的工作,但是我在某个时候读了一下,并意识到我并不真正了解它。为了解决这个问题,我尝试根据描述创建一个我认为应该引起问题的程序,它似乎可以正常工作。
extern "C" {
#include <lauxlib.h>
#include <lua.h>
}
#include <cassert>
#include <iostream>
#define CODE(C) \
case C: { \
std::cout << "When returning to " << where << " got code '" #C "'" << std::endl; \
break; \
}
void handle_resume_code(int code, const char * where) {
switch (code) {
CODE(LUA_OK)
CODE(LUA_YIELD)
CODE(LUA_ERRRUN)
CODE(LUA_ERRMEM)
CODE(LUA_ERRERR)
default:
std::cout << "An unknown error code in " << where << std::endl;
}
}
int trivial(lua_State *, int, lua_KContext) {
std::cout << "Called continuation function" << std::endl;
return 0;
}
int f(lua_State * L) {
std::cout << "Called function 'f'" << std::endl;
return 0;
}
int g(lua_State * L) {
std::cout << "Called function 'g'" << std::endl;
lua_State * T = lua_newthread(L);
lua_getglobal(T, "f");
handle_resume_code(lua_resume(T, L, 0), __func__);
return lua_yieldk(L, 0, 0, trivial);
}
int h(lua_State * L) {
std::cout << "Called function 'h'" << std::endl;
lua_State * T = lua_newthread(L);
lua_getglobal(T, "g");
handle_resume_code(lua_resume(T, L, 0), __func__);
return lua_yieldk(L, 0, 0, trivial);
}
int main () {
std::cout << "Starting:" << std::endl;
lua_State * L = luaL_newstate();
// init
{
lua_pushcfunction(L, f);
lua_setglobal(L, "f");
lua_pushcfunction(L, g);
lua_setglobal(L, "g");
lua_pushcfunction(L, h);
lua_setglobal(L, "h");
}
assert(lua_gettop(L) == 0);
// Some action
{
lua_State * T = lua_newthread(L);
lua_getglobal(T, "h");
handle_resume_code(lua_resume(T, nullptr, 0), __func__);
}
lua_close(L);
std::cout << "Bye! :-)" << std::endl;
}
Starting:
Called function 'h'
Called function 'g'
Called function 'f'
When returning to g got code 'LUA_OK'
When returning to h got code 'LUA_YIELD'
When returning to main got code 'LUA_YIELD'
Bye! :-)
longjmp
丢掉了一部分C堆栈,如果您希望以后再恢复堆栈,那就太糟糕了。
lua_yieldk
的
this to say:
Yields a coroutine.
This function should only be called as the return expression of a C function, as follows:
return lua_yieldk (L, n, i, k);
lua_yield
:
Yields a coroutine.
This function should only be called as the return expression of a C function, as follows:
return lua_yieldk (L, n, i, k);
return
无关紧要?如果lua_yieldk
将调用longjmp
,那么lua_yieldk
将永远不会返回,因此,如果我返回则没关系吗?所以那不可能是正在发生的事,对吧? lua_yieldk
只是在lua状态下做一个记号,说明当前C api调用已声明要屈服,然后在最终返回时,lua会弄清楚接下来会发生什么。然后,这解决了保存C堆栈帧的问题,不是吗?自从我们正常返回lua之后,那些堆栈帧无论如何都已经过期-因此@Nicol Bolas图片中描述的复杂性会绕开吗?其次,至少在5.2中,语义从来都不是我们应该还原C堆栈帧的地方,似乎-lua_yieldk
恢复为延续函数,而不是lua_yieldk
调用者,并且lua_yield
显然恢复为当前api调用的调用者,而不是lua_yield
调用者本身。 If I consistently use
lua_yieldk
in the formreturn lua_yieldk(...)
specified in the docs, returning from alua_CFunction
that was passed to lua, is it still possible to trigger theattempt to yield across a C-call boundary
error?
attempt to yield across a C-call boundary
错误的样子。我的想法是,可能存在与
setjmp
和
longjmp
扔掉以后需要的堆栈框架相关的问题,但是我想看到一些我可以指向的真正的lua/lua c api代码,并说“例如,不要那个”,这令人惊讶地难以捉摸。
lua_resume
。相反,用户在线程堆栈上使用了
dofile
,它在加载后在其中执行了该函数,而不是继续执行该函数。所以实际上是
yield outside of a coroutine
iiuc,当我对此进行修补时,他的代码可以很好地使用lua 5.3中的
lua_yield
和
lua_yieldk
。
#include <cassert>
#include <cstdio>
extern "C" {
#include "lua.h"
#include "lauxlib.h"
}
//#define USE_YIELDK
bool running = true;
int lua_print(lua_State * L) {
if (lua_gettop(L)) {
printf("lua: %s\n", lua_tostring(L, -1));
}
return 0;
}
int lua_finish(lua_State *L) {
running = false;
printf("%s called\n", __func__);
return 0;
}
int trivial(lua_State *, int, lua_KContext) {
printf("%s called\n", __func__);
return 0;
}
int lua_sleep(lua_State *L) {
printf("%s called\n", __func__);
#ifdef USE_YIELDK
printf("Calling lua_yieldk\n");
return lua_yieldk(L, 0, 0, trivial);
#else
printf("Calling lua_yield\n");
return lua_yield(L, 0);
#endif
}
const char * loop_lua =
"print(\"loop.lua\")\n"
"\n"
"local i = 0\n"
"while true do\n"
" print(\"lua_loop iteration\")\n"
" sleep()\n"
"\n"
" i = i + 1\n"
" if i == 4 then\n"
" break\n"
" end\n"
"end\n"
"\n"
"finish()\n";
int main() {
lua_State * L = luaL_newstate();
lua_pushcfunction(L, lua_print);
lua_setglobal(L, "print");
lua_pushcfunction(L, lua_sleep);
lua_setglobal(L, "sleep");
lua_pushcfunction(L, lua_finish);
lua_setglobal(L, "finish");
lua_State* cL = lua_newthread(L);
assert(LUA_OK == luaL_loadstring(cL, loop_lua));
/*{
int result = lua_pcall(cL, 0, 0, 0);
if (result != LUA_OK) {
printf("%s error: %s\n", result == LUA_ERRRUN ? "Runtime" : "Unknown", lua_tostring(cL, -1));
return 1;
}
}*/
// ^ This pcall (predictably) causes an error -- if we try to execute the
// script, it is going to call things that attempt to yield, but we did not
// start the script with lua_resume, we started it with pcall, so it's not
// okay to yield.
// The reported error is "attempt to yield across a C-call boundary", but what
// is really happening is just "yield from outside a coroutine" I suppose...
while (running) {
int status;
printf("Waking up coroutine\n");
status = lua_resume(cL, L, 0);
if (status == LUA_YIELD) {
printf("coroutine yielding\n");
} else {
running = false; // you can't try to resume if it didn't yield
if (status == LUA_ERRRUN) {
printf("Runtime error: %s\n", lua_isstring(cL, -1) ? lua_tostring(cL, -1) : "(unknown)" );
lua_pop(cL, -1);
break;
} else if (status == LUA_OK) {
printf("coroutine finished\n");
} else {
printf("Unknown error\n");
}
}
}
lua_close(L);
printf("Bye! :-)\n");
return 0;
}
USE_YIELDK
被注释掉后的输出:
Waking up coroutine
lua: loop.lua
lua: lua_loop iteration
lua_sleep called
Calling lua_yield
coroutine yielding
Waking up coroutine
lua: lua_loop iteration
lua_sleep called
Calling lua_yield
coroutine yielding
Waking up coroutine
lua: lua_loop iteration
lua_sleep called
Calling lua_yield
coroutine yielding
Waking up coroutine
lua: lua_loop iteration
lua_sleep called
Calling lua_yield
coroutine yielding
Waking up coroutine
lua_finish called
coroutine finished
Bye! :-)
USE_YIELDK
时的输出:
Waking up coroutine
lua: loop.lua
lua: lua_loop iteration
lua_sleep called
Calling lua_yieldk
coroutine yielding
Waking up coroutine
trivial called
lua: lua_loop iteration
lua_sleep called
Calling lua_yieldk
coroutine yielding
Waking up coroutine
trivial called
lua: lua_loop iteration
lua_sleep called
Calling lua_yieldk
coroutine yielding
Waking up coroutine
trivial called
lua: lua_loop iteration
lua_sleep called
Calling lua_yieldk
coroutine yielding
Waking up coroutine
trivial called
lua_finish called
coroutine finished
Bye! :-)
最佳答案
想一想,当协程执行yield
时会发生什么。它停止执行,并且处理返回到该协程中称为resume
的任何人,对吗?
好吧,假设您有以下代码:
function top()
coroutine.yield()
end
function middle()
top()
end
function bottom()
middle()
end
local co = coroutine.create(bottom);
coroutine.resume(co);
yield
的时刻,Lua堆栈如下所示:
-- top
-- middle
-- bottom
-- yield point
yield
时,将保留作为协程的一部分的Lua调用堆栈。当您执行
resume
时,将再次执行保留的调用堆栈,从之前中断的位置开始。
middle
实际上不是Lua函数。相反,它是一个C函数,并且该C函数调用Lua函数
top
。因此,从概念上讲,您的堆栈如下所示:
-- Lua - top
-- C - middle
-- Lua - bottom
-- Lua - yield point
lua_State
定义。还有C的堆栈。在即将调用
yield
时,Lua的内部堆栈看起来像这样:
-- top
-- Some C stuff
-- bottom
-- yield point
-- arbitrary Lua interpreter stuff
-- middle
-- arbitrary Lua interpreter stuff
-- setjmp
yield
时,它将调用
longjmp
。该函数基于C堆栈的行为。即,它将返回到
setjmp
所在的位置。
longjmp
和
setjmp
?之间的所有内容。没了凯普特。永远失去了。
yield
ed协程怎么办?
lua_callk
或
lua_pcallk
调用可能有用的Lua函数。这些调用函数带有一个附加参数:“继续”函数。
lua_*callk
函数将永远不会真正返回(因为您的C堆栈将被销毁)。相反,它将调用您在
lua_*callk
函数中提供的延续函数。顾名思义,延续功能的工作是从上一个功能中断的地方继续。
lua_*callk
)被删除,并返回该函数的值被压入堆栈。除此之外,堆栈都是一样的。
lua_yieldk
。这使您的C函数可以返回Lua,以便在协程恢复后调用提供的延续函数。
关于c++ - Lua协程-setjmp longjmp破坏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34303507/
有没有办法使用 setjmp 来实现多任务处理?和 longjmp职能 最佳答案 这是所谓的用户空间上下文切换的一种形式。 这是可能的,但很容易出错,特别是如果您使用 setjmp 和 longjmp
我想在 Linux 环境中实现 POSIX 兼容的微线程。基本思路如下: 使用描述的技术 here , 为每个纤程分配新的栈空间。 使用 setitimer,创建将以固定时间间隔发送信号的计时器。此计
ISO/IEC 9899:1999 7.13.1.1 The setjmp macro Environmental limits 4 An invocation of the setjmp macro
我一直在尝试追踪我的代码(使用 setjmp)中的间歇性崩溃错误,并将其缩小为:在使用/O2 编译时显示,使用/O2/Oy- 消失,即仅显示省略帧指针。 http://msdn.microsoft.c
通常 setjmp 和 longjmp 不关心调用栈——函数只是保存和恢复寄存器。 我想使用 setjmp 和 longjmp 以便保留调用堆栈,然后在不同的执行上下文中恢复 EnableFeatur
下面的代码无法正常工作。谁能指出原因 #define STACK_SIZE 1524 static void mt_allocate_stack(struct thread_struct *mythr
一段代码在这里 jmp_buf mark; int Sub_Func() { int be_modify, jmpret; be_modify = 0; jmp
我的问题针对的是 setjmp/longjmp 关于局部变量的行为。 示例代码: jmp_buf env; void abc() { int error; ... if(error)
我正在尝试为 C OSX Carbon 多线程 应用程序安装“崩溃处理程序”。在 Windows 上,我可以轻松使用简单高效的 __try{} __except{} SEH of Windows效果很
我必须使用 setjmp/longjmp 实现用户级线程库作为作业。这是我写的代码: #include #include #include #include #include #includ
这个问题来自 SetJmp/LongJmp: Why is this throwing a segfault? 当我使用 Debug模式运行代码时,它确实按预期崩溃了。但是如果我使用 release
我不明白为什么在函数 middleFunc() 中,当 entry_point(arg) 在 if ( setjmp(中间) ) 语句。 #include #include
为什么 setjmp 不保存堆栈? 考虑以下代码: #include jmp_buf Buf; jmp_buf Buf2; void MyFunction() { for(int i = 0
#include #include #include #include static jmp_buf env_alrm; static void sig_alarm(int signo) {
在不久前的blog post中,Scott Vokes使用C函数setjmp和longjmp描述了与lua实现协程相关的技术问题: The main limitation of Lua corouti
在jpeglib ,必须使用 setjmp/longjmp 来实现自定义错误处理。 有很多资源说 setjmp/longjmp 不能很好地与 c++ 配合使用(例如 this question 中的答
我的问题针对的是 setjmp/longjmp 关于局部变量的行为。 示例代码: jmp_buf env; void abc() { int error; ... if(error)
我试图理解以下代码中的 setjmp: http://androidxref.com/4.2.2_r1/xref/frameworks/base/core/jni/android/graphics/Y
我正在调查setjmp/longjmp,发现setjmp 保存指令指针、堆栈指针等寄存器... 然而,我在这里没有得到的是,在调用 setjmp 和 longjmp 之间,不能修改线程本身堆栈中的数据
在 Why volatile works for setjmp/longjmp , 用户 greggo评论: Actually modern C compilers do need to know t
我是一名优秀的程序员,十分优秀!