安全的跨平台协同程序

时间:2011-11-01 23:31:01

标签: c++ cross-platform c++11 coroutine

我遇到的所有协程实现都使用汇编或检查jmp_buf的内容。问题在于它本身并不是跨平台。

我认为以下实现不会进入未定义的行为或依赖于实现细节。但是我从来没有遇到过像这样写的协程。

是否有一些固有的缺陷是使用线程跳远? 这段代码中有隐藏的问题吗?

#include <setjmp.h>
#include <thread>

class Coroutine
{
public:
   Coroutine( void ) :
      m_done( false ),
      m_thread( [&](){ this->start(); } )
   { }

   ~Coroutine( void )
   {
      std::lock_guard<std::mutex> lock( m_mutex );

      m_done = true;
      m_condition.notify_one();

      m_thread.join();
   }

   void start( void )
   {
      if( setjmp( m_resume ) == 0 )
      {
         std::unique_lock<std::mutex> lock( m_mutex );
         m_condition.wait( lock, [&](){ return m_done; } );
      }
      else
      {
         routine();
         longjmp( m_yield, 1 );
      }
   }

   void resume( void )
   {
      if( setjmp( m_yield ) == 0 )
      {
         longjmp( m_resume, 1 );
      }
   }

   void yield( void )
   {
      if( setjmp( m_resume ) == 0 )
      {
         longjmp( m_yield, 1 );
      }
   }

private:
   virtual void routine( void ) = 0;

   jmp_buf m_resume;
   jmp_buf m_yield;

   bool m_done;
   std::mutex m_mutex;
   std::condition_variable m_condition;
   std::thread m_thread;
};

4 个答案:

答案 0 :(得分:9)

  

更新 2013-05-13这些天有Boost Coroutine(基于Boost Context构建,尚未在所有目标平台上实施,但可能会受到支持所有主要平台都要早点而不是晚些时候。)


我不知道无堆叠协程是否符合您的预期用途,但我建议您在这里查看它们:

Boost Asio:The Proactor Design Pattern: Concurrency Without Threads

Asio还有一个基于单个(IIRC)简单预处理器宏的协同过程“仿真”模型,结合了一些狡猾设计的模板工具,这些工具非常接近编译器支持_stack-less co程序。 / p>

示例 HTTP Server 4 是该技术的一个示例。

Boost Asio(Kohlhoff)的作者解释了Blog here: A potted guide to stackless coroutines

上的机制和样本

请务必查找该系列中的其他帖子!

答案 1 :(得分:6)

有一个C++ standard proposal for coroutine support - N3708 Oliver Kowalke Boost.Coroutine 的作者)和 Goodspeed 撰写

我想这最终会成为最终的清洁解决方案(如果它发生......) 因为我们没有来自C ++编译器的堆栈交换支持,所以协同程序当前需要低级别(通常是汇编级别,或setjmp / longjmp)hack,并且这超出了C ++的抽象范围。然后实现是脆弱的,需要编译器的帮助才能健壮。

例如,设置协程上下文的堆栈大小真的很难,如果溢出堆栈,程序将被静默损坏。或者如果你很幸运会崩溃。分段堆栈似乎可以帮助解决这个问题,但同样需要编译器级别的支持。

如果它成为标准,编译器编写者会小心。但在那之前,Boost.Coroutine对我来说是C ++中唯一实用的解决方案。

在C中,由 Russ Cox (他是Go团队成员)编写的libtasklibtask效果很好,但似乎不再维护了。

P.S。如果有人知道如何支持标准提案,请告诉我。我真的支持这个提议。

答案 2 :(得分:4)

没有通用的跨平台方式来实现协同例程。虽然某些实现可以使用setjmp / longjmp来捏合协同例程,但这种做法并不符合标准。如果routine1使用setjmp()创建jmp_buf1,然后调用使用setjmp()创建jmp_buf2的routine2(),那么任何longjmp()到jmp_buf1都会使jmp_buf2无效(如果它还没有被无效)。

我已经在各种各样的CPU上完成了我的共同例程实现。我总是使用至少一些汇编代码。它通常不需要太多(例如8x51上的任务切换的四条指令),但使用汇编代码可以帮助确保编译器不会应用会破坏所有内容的创意优化。

答案 3 :(得分:2)

我不相信你可以通过跳远来完全实现合作程序。 WinAPI本身支持协同例程,它们被称为光纤。例如,请参阅CreateFiber()。我不认为其他操作系统有本机协同支持。如果您查看SystemC库,其中协同例程是其中心部分,它们将在程序集中针对除Windows之外的每个受支持平台实现。 GBL库还使用协同例程进行基于Windows光纤的事件驱动模拟。尝试实现协同例程和事件驱动设计很难调试错误,因此我建议使用现有的库,这些库已经过全面测试,并且具有更高级别的抽象来处理这个概念。