Gcc 切换堆栈帧时出现问题
为什么以下代码在Windows 10下崩溃?它卡在Gcc 切换堆栈帧时出现问题,gcc,assembly,c++14,setjmp,Gcc,Assembly,C++14,Setjmp,为什么以下代码在Windows 10下崩溃?它卡在setjmp()调用中的yield()。我违反了什么规则?我想我永远不会从调用setjmp()的函数返回。该代码在linux/amd64下运行良好 class coroutine { jmp_buf env_in_; jmp_buf env_out_; ::std::function<void()> f_; bool running_; bool terminated_; ::std::unique_pt
setjmp()
调用中的yield()
。我违反了什么规则?我想我永远不会从调用setjmp()
的函数返回。该代码在linux/amd64下运行良好
class coroutine
{
jmp_buf env_in_;
jmp_buf env_out_;
::std::function<void()> f_;
bool running_;
bool terminated_;
::std::unique_ptr<char[]> stack_;
char* const stack_top_;
public:
explicit coroutine(::std::size_t const N = 128 * 1024) :
running_{false},
terminated_{true},
stack_(new char[N]),
stack_top_(stack_.get() + N)
{
}
template <typename F>
explicit coroutine(::std::size_t const N, F&& f) :
coroutine(N)
{
assign(::std::forward<F>(f));
}
auto terminated() const noexcept
{
return terminated_;
}
template <typename F>
void assign(F&& f)
{
running_ = terminated_ = false;
f_ = [this, f = ::std::forward<F>(f)]() mutable
{
// stack switch
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
asm volatile(
"movq %%rsp, %0"
:
: "rm" (stack_top_)
: "rsp"
);
#elif defined(i386) || defined(__i386) || defined(__i386__)
asm volatile(
"movl %%esp, %0"
:
: "rm" (stack_top_)
: "esp"
);
#else
#error "can't switch stack frame"
#endif
f(*this);
running_ = false;
terminated_ = true;
yield();
};
}
void yield() noexcept
{
if (setjmp(env_out_))
{
return;
}
else
{
longjmp(env_in_, 1);
}
}
void resume() noexcept
{
if (setjmp(env_in_))
{
return;
}
else if (running_)
{
longjmp(env_out_, 1);
}
else
{
running_ = true;
f_();
}
}
};
类协同程序
{
jmp_buf env_in_;
jmp_buf env_out ;;
::std::函数f;
布尔跑步;
布尔终止;
::std::唯一的\u ptr堆栈\uu;
char*const stack\u top\u;
公众:
显式协程(::std::size\u t const N=128*1024):
正在运行{false},
终止{true},
堆栈(新字符[N]),
堆栈顶部(堆栈获取()+N)
{
}
模板
显式协程(::std::size\t const N,F&&F):
协同程序(N)
{
赋值(::std::forward(f));
}
自动终止()常量noexcept
{
返回终止日期;
}
模板
无效分配(F&F)
{
正在运行\终止\错误;
f=[this,f=::std::forward(f)]()可变
{
//堆栈开关
#如果已定义(uuuAMD64_uuuuuu124;)已定义(uuAMD64)|已定义(uux86_64_uuu124;)已定义(uux86_64)
挥发性物质(
“movq%%rsp,%0”
:
:“rm”(堆栈顶部)
:“rsp”
);
#elif已定义(i386)|已定义(uuI386)|已定义(uuI386)
挥发性物质(
“移动%%esp,%0”
:
:“rm”(堆栈顶部)
:“esp”
);
#否则
#错误“无法切换堆栈帧”
#恩迪夫
f(*本条);
运行=错误;
终止=真;
收益率();
};
}
无效收益率()无例外
{
如果(设置JMP(环境输出))
{
返回;
}
其他的
{
longjmp(env_in_,1);
}
}
void resume()无例外
{
if(setjmp(环境中))
{
返回;
}
else if(正在运行)
{
longjmp(env_out_,1);
}
其他的
{
运行=真;
f_u2;();
}
}
};
以下是测试程序:
#include <iostream>
#include "coroutine.hpp"
struct A
{
~A()
{
::std::cout << "destroyed" << ::std::endl;
}
};
int main()
{
coroutine c(1024 * 1024);
c.assign([](coroutine& c)
{
A a;
for (int i{}; i != 3; ++i)
{
::std::cout << i << ::std::endl;
c.yield();
}
}
);
while (!c.terminated())
{
c.resume();
}
return 0;
}
#包括
#包括“coroutine.hpp”
结构A
{
~A()
{
::std::cout At&t语法将操作数反转,因此您实际上不是在切换堆栈,而是在保存堆栈指针。不过,这可能不会导致故障。我认为将堆栈指针添加到clobber也没有意义,因为这可能会指示编译器保存和还原堆栈指针,从而使切换无效(即使您的操作方向正确)。还请注意,new char[]
可能无法正确对齐堆栈。@Jester您的点是正确的,但问题仍然存在。我可以更改问题吗?new是否返回一个对齐的指针以适应每个对齐方式?@Jester敲击堆栈指针无效().OTOH,切换上下文确实会引起关于所有其他寄存器的问题。也许它们都应该被删除(连同内存和标志)。确切地说,这就是问题所在。其他寄存器没有被还原。自定义setjmp/longjmp解决了这个问题。