切换堆栈帧时出现问题

时间:2016-10-14 11:24:36

标签: gcc assembly c++14 longjmp setjmp

为什么以下代码在Windows 10下崩溃?它会在yield()来电中setjmp()停留。我违反了哪些规则?我想我永远不会从调用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_();
    }
  }
};

以下是测试程序:

#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;
}

0 个答案:

没有答案