Gcc 切换堆栈帧时出现问题

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

为什么以下代码在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_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解决了这个问题。