Advertisement
zhangsongcui

Yield V4.1 ( code refactor, bugfix )

Jun 9th, 2018
223
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.49 KB | None | 0 0
  1. #ifndef USE_FCONTEXT
  2. #   if __has_include(<boost/context/detail/fcontext.hpp>)
  3. #       define USE_FCONTEXT 1
  4. #   endif
  5. #endif
  6. #if USE_FCONTEXT
  7. #   include <boost/assert.hpp>
  8. #   include <boost/context/detail/fcontext.hpp>
  9. #   ifdef NDEBUG
  10. #       include <boost/context/fixedsize_stack.hpp>
  11. #   else
  12. #       include <boost/context/protected_fixedsize_stack.hpp>
  13. #   endif
  14. #else
  15. #   ifdef _WIN32
  16. #       define USE_WINFIB 1
  17. #       ifdef _WIN32_WINNT
  18. #           if _WIN32_WINNT < 0x0601
  19. #               error 需要 Windows 7 以上系统支持
  20. #           endif
  21. #       else
  22. #           define _WIN32_WINNT 0x0601
  23. #       endif
  24. #       ifdef WIN32_LEAD_AND_MEAN
  25. #           include <Windows.h>
  26. #       else
  27. #           define WIN32_LEAD_AND_MEAN 1
  28. #           include <Windows.h>
  29. #           undef WIN32_LEAD_AND_MEAN
  30. #       endif
  31. #   elif !defined(__ANDROID__) && __has_include(<ucontext.h>)
  32. #       if defined(__APPLE__)
  33. #           define _XOPEN_SOURCE
  34. #       endif
  35. #       include <ucontext.h>
  36. #       define USE_UCONTEXT 1
  37. #   else
  38. #       include <setjmp.h>
  39. #       include <signal.h>
  40. #       include <unistd.h>
  41. #       define USE_SJLJ 1
  42. #   endif
  43. #endif
  44.  
  45. #include <functional>
  46. #include <cassert>
  47. #include <iterator>
  48. #include <iostream>
  49. #include <array>
  50. #include <memory>
  51. #include <algorithm>
  52. #if __has_include(<optional>)
  53. #   include <optional>
  54. #else
  55. #   include <experimental/optional> // Apple Clang doesn't support std::optional (yet)
  56. namespace std {
  57.     using ::std::experimental::optional;
  58.     using ::std::experimental::nullopt; // std::experimental::optional doesn't support reset
  59. }
  60. #endif
  61.  
  62. namespace FiberSpace {
  63.     enum class FiberStatus {
  64.         unstarted = -1,
  65.         running = 1,
  66.         suspended = 2,
  67.         closed = 0,
  68.     };
  69.  
  70.  
  71.     /** \brief 主纤程类析构异常类
  72.      *
  73.      * \warning 用户代码吃掉此异常可导致未定义行为。如果捕获到此异常,请转抛出去。
  74.      */
  75.     struct FiberReturn {
  76.         template <typename ValueType>
  77.         friend class Fiber;
  78.  
  79.     private:
  80.         FiberReturn() = default;
  81.     };
  82.  
  83.  
  84. #if USE_FCONTEXT
  85.     namespace fctx = boost::context;
  86. #endif
  87.  
  88.     /** \brief 主纤程类
  89.      *
  90.      * \warning 线程安全(?)
  91.      * \tparam ValueType 子纤程返回类型
  92.      */
  93.     template <typename ValueType>
  94.     class Fiber {
  95.         Fiber(const Fiber &) = delete;
  96.         Fiber& operator =(const Fiber &) = delete;
  97.  
  98.         typedef std::function<void(Fiber& fiber)> FuncType;
  99.  
  100.         /// \brief 子纤程返回值
  101.         std::optional<ValueType> currentValue;
  102.         /// \brief 存储子纤程抛出的异常
  103.         std::exception_ptr eptr = nullptr;
  104.         /// \brief 子纤程是否结束
  105.         FiberStatus status = FiberStatus::unstarted;
  106.         /// \brief 真子纤程入口,第一个参数传入纤程对象的引用
  107.         FuncType func;
  108.        
  109. #if USE_UCONTEXT || USE_SJLJ
  110.         struct alignas(16) StackBuf {
  111.             uint8_t* get() { return buf; }
  112.             size_t size() { return SIGSTKSZ; }
  113.            
  114.         private:
  115.             uint8_t buf[SIGSTKSZ];
  116.         };
  117. #endif
  118.  
  119.         /// \brief 纤程信息
  120. #if USE_FCONTEXT
  121.         fctx::detail::fcontext_t ctx_main, ctx_fnew;
  122. #   ifdef NDEBUG
  123.         fctx::fixedsize_stack stack_allocator;
  124. #   else
  125.         fctx::protected_fixedsize_stack stack_allocator;
  126. #endif
  127.         fctx::stack_context fnew_stack;
  128. #elif USE_WINFIB
  129.         PVOID pMainFiber, pNewFiber;
  130. #elif USE_UCONTEXT
  131.         ::ucontext_t ctx_main, ctx_fnew;
  132.         const std::unique_ptr<StackBuf> fnew_stack = std::make_unique<StackBuf>();
  133. #elif USE_SJLJ
  134.         ::sigjmp_buf buf_main, buf_new;
  135.         const std::unique_ptr<StackBuf> fnew_stack = std::make_unique<StackBuf>();
  136.         struct sigaction old_sa;
  137.         static Fiber *that;
  138. #endif
  139.  
  140.     public:
  141.         /** \brief 构造函数
  142.          *
  143.          * 把主线程转化为纤程,并创建一个子纤程
  144.          *
  145.          * \param f 子纤程入口
  146.          */
  147.         explicit Fiber(FuncType f) : func(std::move(f)) {
  148. #if USE_FCONTEXT
  149.             this->fnew_stack = this->stack_allocator.allocate();
  150.             this->ctx_fnew = fctx::detail::make_fcontext(this->fnew_stack.sp, this->fnew_stack.size, fEntry);
  151. #elif USE_WINFIB
  152.             if (!IsThreadAFiber()) {
  153.                 // WARNING: We won't convert main fiber back to thread for performance reasons
  154.                 this->pMainFiber = ::ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
  155.             } else {
  156.                 this->pMainFiber = ::GetCurrentFiber();
  157.             }
  158.             // default stack size
  159.             this->pNewFiber = ::CreateFiberEx(0, 0, FIBER_FLAG_FLOAT_SWITCH, (void(*)(void *))&fEntry, this);
  160. #elif USE_UCONTEXT
  161.             ::getcontext(&this->ctx_fnew);
  162.             this->ctx_fnew.uc_stack.ss_sp = this->fnew_stack.get();
  163.             this->ctx_fnew.uc_stack.ss_size = this->fnew_stack->size();
  164.             this->ctx_fnew.uc_link = &this->ctx_main;
  165.             ::makecontext(&this->ctx_fnew, (void(*)())&fEntry, 1, this);
  166. #elif USE_SJLJ
  167.             ::stack_t sigstk, oldstk;
  168.             sigstk.ss_sp = this->fnew_stack.get();
  169.             sigstk.ss_size = this->fnew_stack->size();
  170.             sigstk.ss_flags = 0;
  171.             assert(::sigaltstack(&sigstk, &oldstk) == 0 && "Error while set sigstk");
  172.  
  173.             struct sigaction sa;
  174.             sa.sa_flags = SA_ONSTACK;
  175.             sa.sa_handler = fEntry;
  176.             sigemptyset(&sa.sa_mask);
  177.             assert(::sigaction(SIGUSR2, &sa, &this->old_sa) == 0 && "Error while installing a signal handler");
  178.  
  179.             if (!sigsetjmp(this->buf_main, 0)) {
  180.                 Fiber::that = this; // Android doesn't support sigqueue,
  181.                 assert(::raise(SIGUSR2) == 0 && "Failed to queue the signal");
  182.             }
  183.             assert(::sigaltstack(&oldstk, nullptr) == 0 && "Error while reset sigstk");
  184.  
  185.             ::sigset_t sa_mask;
  186.             sigemptyset(&sa_mask);
  187.             sigaddset(&sa_mask, SIGUSR2);
  188.             ::sigprocmask(SIG_UNBLOCK, &sa_mask, nullptr);
  189. #endif
  190.         }
  191.  
  192.         template <class Fp, class ...Args,
  193.             class = typename std::enable_if
  194.                 <
  195.                     (sizeof...(Args) > 0)
  196.                 >::type
  197.             >
  198.             explicit Fiber(Fp&& f, Args&&... args) : Fiber(std::bind(std::forward<Fp>(f), std::placeholders::_1, std::forward<Args>(args)...)) {}
  199.  
  200.         /** \brief 析构函数
  201.          *
  202.          * 删除子纤程,并将主纤程转回线程
  203.          *
  204.          * \warning 主类析构时如子纤程尚未结束(return),则会在子纤程中抛出 FiberReturn 来确保子纤程函数内所有对象都被正确析构
  205.          */
  206.         ~Fiber() noexcept {
  207.             if (!isFinished()) {
  208.                 return_();
  209.             }
  210.  
  211. #if USE_FCONTEXT
  212.             this->stack_allocator.deallocate(this->fnew_stack);
  213. #elif USE_WINFIB
  214.             ::DeleteFiber(this->pNewFiber);
  215. #endif
  216.         }
  217.  
  218.         /** \brief 调用子纤程
  219.          *
  220.          * 程序流程转入子纤程
  221.          *
  222.          * \warning 子纤程必须尚未结束
  223.          * \return 返回子纤程是否尚未结束
  224.          */
  225.         bool next() {
  226.             this->currentValue = std::nullopt;
  227.             this->jumpNew();
  228.             return !isFinished();
  229.         }
  230.  
  231.         /** \brief 调用子纤程
  232.          *
  233.          * 程序流程转入子纤程,并传入值(可通过 current() 获取)
  234.          *
  235.          * \param value 传入子纤程的值
  236.          * \warning 子纤程必须尚未结束
  237.          * \return 返回子纤程是否尚未结束
  238.          */
  239.         bool next(ValueType value) {
  240.             this->currentValue = std::move(value);
  241.             this->jumpNew();
  242.             return !isFinished();
  243.         }
  244.  
  245.         /** \brief 向子纤程内部抛出异常
  246.          *
  247.          * 程序流程转入子纤程,并在子纤程内部抛出异常
  248.          *
  249.          * \param eptr 需抛出异常的指针(可以通过 std::make_exception_ptr 获取)
  250.          * \warning 子纤程必须尚未结束
  251.          * \return 返回子纤程是否尚未结束
  252.          */
  253.         bool throw_(std::exception_ptr&& eptr) {
  254.             assert(!isFinished());
  255.             this->eptr = std::move(eptr);
  256.             return next();
  257.         }
  258.  
  259.         /** \brief 强制退出子纤程
  260.          *
  261.          * 向子纤程内部抛出 FiberReturn 异常,以强制退出子纤程,并确保子纤程函数中所有对象都正确析构
  262.          *
  263.          * \warning 如果子纤程尚未开始或已经结束,本函数不做任何事情
  264.          */
  265.         void return_() {
  266.             if (this->status == FiberStatus::unstarted || isFinished()) return;
  267.             throw_(std::make_exception_ptr(FiberReturn()));
  268.             assert(isFinished() && "请勿吃掉 FiberReturn 异常!!!");
  269.         }
  270.  
  271.         /** \brief 获得子纤程返回的值
  272.          * \warning 可能为空(nullopt)
  273.          * \return 子纤程返回的值
  274.          */
  275.         const auto& current() const noexcept {
  276.             return this->currentValue;
  277.         }
  278.  
  279.         /** \brief 获得子纤程返回的值
  280.          * \warning 可能为空(nullopt)
  281.          * \return 子纤程返回的值
  282.          */
  283.         auto&& current() noexcept {
  284.             return this->currentValue;
  285.         }
  286.  
  287.         /** \brief 判断子纤程是否结束
  288.          * \return 子纤程已经结束(return)返回true,否则false
  289.          */
  290.         bool isFinished() const noexcept {
  291.             return this->status == FiberStatus::closed;
  292.         }
  293.  
  294.         /** \brief 转回主纤程并输出值
  295.          *
  296.          * \warning 必须由子纤程调用
  297.          *          参数类型必须与子纤程返回值相同,无类型安全
  298.          * \param value 输出到主纤程的值
  299.          */
  300.         void yield(ValueType value) {
  301.             this->currentValue = std::move(value);
  302.             this->jumpMain();
  303.         }
  304.  
  305.         /** \brief 转回主纤程并但不输出值
  306.          *
  307.          * \warning 必须由子纤程调用
  308.          */
  309.         void yield() {
  310.             this->currentValue = std::nullopt;
  311.             this->jumpMain();
  312.         }
  313.  
  314.         /** \brief 输出子纤程的所有值
  315.          * \param fiber 另一子纤程
  316.          */
  317.         void yieldAll(Fiber& fiber) {
  318.             assert(&fiber != this);
  319.             while (fiber.next()) {
  320.                 this->yield(*fiber.current());
  321.             }
  322.         }
  323.  
  324.         void yieldAll(Fiber&& fiber) {
  325.             this->yieldAll(fiber);
  326.         }
  327.  
  328.     private:
  329.         /// \brief 控制流跳转主纤程
  330.         void jumpMain() {
  331.             assert(!isFinished());
  332.             this->status = FiberStatus::suspended;
  333.            
  334. #if USE_FCONTEXT
  335.             this->ctx_main = fctx::detail::jump_fcontext(this->ctx_main, this).fctx;
  336. #elif USE_WINFIB
  337.             assert(GetCurrentFiber() != this->pMainFiber && "这虽然是游戏,但绝不是可以随便玩的");
  338.             ::SwitchToFiber(this->pMainFiber);
  339. #elif USE_UCONTEXT
  340.             ::swapcontext(&this->ctx_fnew, &this->ctx_main);
  341. #elif USE_SJLJ
  342.             if (!::sigsetjmp(this->buf_new, 0)) {
  343.                 ::siglongjmp(this->buf_main, 1);
  344.             }
  345. #endif
  346.             // We are back to new coroutine now
  347.             this->status = FiberStatus::running;
  348.  
  349.             if (this->eptr) {
  350.                 std::rethrow_exception(std::exchange(this->eptr, nullptr));
  351.             }
  352.         }
  353.        
  354.         /// \brief 控制流跳转子纤程
  355.         void jumpNew() {
  356.             assert(!isFinished());
  357. #if USE_FCONTEXT
  358.             this->ctx_fnew = fctx::detail::jump_fcontext(this->ctx_fnew, this).fctx;
  359. #elif USE_WINFIB
  360.             assert(GetCurrentFiber() != this->pNewFiber && "如果你想递归自己,请创建一个新纤程");
  361.             ::SwitchToFiber(this->pNewFiber);
  362. #elif USE_UCONTEXT
  363.             ::swapcontext(&this->ctx_main, &this->ctx_fnew);
  364. #elif USE_SJLJ
  365.             if (!::sigsetjmp(this->buf_main, 0)) {
  366.                 ::siglongjmp(this->buf_new, 1);
  367.             }
  368. #endif
  369.             // We are back to main coroutine now
  370.            
  371.             if (this->eptr) {
  372.                 std::rethrow_exception(std::exchange(this->eptr, nullptr));
  373.             }
  374.         }
  375.        
  376.         /// \brief 子纤程入口的warpper
  377. #if USE_FCONTEXT
  378.         static void fEntry(fctx::detail::transfer_t transfer) {
  379.             Fiber *fiber = (Fiber *)transfer.data;
  380.             fiber->ctx_main = transfer.fctx;
  381. #elif USE_WINFIB
  382.         static void WINAPI fEntry(Fiber *fiber) {
  383. #elif USE_UCONTEXT
  384.         static void fEntry(Fiber *fiber) {
  385. #elif USE_SJLJ
  386.         static void fEntry(int signo) {
  387.             Fiber *fiber = std::exchange(Fiber::that, nullptr);
  388.             assert(sigaction(signo, &fiber->old_sa, nullptr) == 0 && "Failed to reset signal handler");
  389.             if (!::sigsetjmp(fiber->buf_new, 0)) {
  390.                 ::siglongjmp(fiber->buf_main, 1);
  391.             }
  392. #endif
  393.  
  394.             if (!fiber->eptr) {
  395.                 fiber->status = FiberStatus::running;
  396.                 try {
  397.                     fiber->func(*fiber);
  398.                 }
  399.                 catch (FiberReturn &) {
  400.                     // 主 Fiber 对象正在析构
  401.                 }
  402.                 catch (...) {
  403.                     fiber->eptr = std::current_exception();
  404.                 }
  405.             }
  406.             fiber->status = FiberStatus::closed;
  407.             fiber->currentValue = std::nullopt;
  408. #if USE_FCONTEXT
  409.             fiber->ctx_main = fctx::detail::jump_fcontext(fiber->ctx_main, fiber).fctx;
  410. #elif USE_WINFIB
  411.             ::SwitchToFiber(fiber->pMainFiber);
  412. #elif USE_SJLJ
  413.             ::siglongjmp(fiber->buf_main, 1);
  414. #endif
  415.         }
  416.     };
  417.    
  418. #if USE_SJLJ
  419.     template <typename ValueType>
  420.     Fiber<ValueType> *Fiber<ValueType>::that;
  421. #endif
  422.  
  423.     /** \brief 纤程迭代器类
  424.      *
  425.      * 它通过使用 yield 函数对数组或集合类执行自定义迭代。
  426.      * 用于 C++11 for (... : ...)
  427.      */
  428.     template <typename ValueType>
  429.     struct FiberIterator : std::iterator<std::output_iterator_tag, ValueType> {
  430.         /// \brief 迭代器尾
  431.         FiberIterator() noexcept : fiber(nullptr) {}
  432.         /** \brief 迭代器首
  433.          * \param _f 主线程类的引用
  434.          */
  435.         FiberIterator(Fiber<ValueType>& _f) : fiber(&_f) {
  436.             next();
  437.         }
  438.  
  439.         /// \brief 转入子纤程
  440.         FiberIterator& operator ++() {
  441.             next();
  442.             return *this;
  443.         }
  444.  
  445.         /// \brief 取得返回值
  446.         const ValueType &operator *() const {
  447.             assert(fiber != nullptr);
  448.             return *fiber->current();
  449.         }
  450.  
  451.         /** \brief 比较迭代器相等
  452.          *
  453.          * 通常用于判断迭代是否结束
  454.          * 最好别干别的 ;P
  455.          */
  456.         bool operator ==(const FiberIterator& rhs) const noexcept {
  457.             return fiber == rhs.fiber;
  458.         }
  459.         bool operator !=(const FiberIterator& rhs) const noexcept {
  460.             return !(*this == rhs);
  461.         }
  462.  
  463.     private:
  464.         void next() {
  465.             assert(fiber);
  466.             if (!fiber->next()) fiber = nullptr;
  467.         }
  468.  
  469.         Fiber<ValueType>* fiber;
  470.     };
  471.  
  472.     /// \brief 返回迭代器首
  473.     template <typename ValueType>
  474.     FiberIterator<ValueType> begin(Fiber<ValueType>& fiber) {
  475.         return FiberIterator<ValueType>(fiber);
  476.     }
  477.  
  478.     /// \brief 返回迭代器尾
  479.     template <typename ValueType>
  480.     FiberIterator<ValueType> end(Fiber<ValueType>&) noexcept {
  481.         return FiberIterator<ValueType>();
  482.     }
  483. }
  484.  
  485. using namespace std;
  486. using FiberSpace::Fiber;
  487.  
  488. bool destructedFlag = true;
  489.  
  490. struct TestDestruct {
  491.     TestDestruct() {
  492.         destructedFlag = false;
  493.     }
  494.     ~TestDestruct() {
  495.         destructedFlag = true;
  496.     }
  497. };
  498.  
  499. void foo(Fiber<bool>& fiber, int arg) {
  500.     TestDestruct test;
  501.     for (int i = 1; i < 5; i++) {
  502.         printf("goroutine :%d\n", arg + i);
  503.         fiber.yield();
  504.     }
  505. }
  506.  
  507. void do_permutation(Fiber<array<int, 4>>& fiber, array<int, 4> arr, int length) {
  508.     if (length) {
  509.         for (auto i = 0; i < length; ++i) {
  510.             array<int, 4> newArr(arr);
  511.             std::copy_n(arr.begin(), i, newArr.begin());
  512.             std::copy_n(arr.begin() + i + 1, arr.size() - i - 1, newArr.begin() + i);
  513.             newArr.back() = arr[i];
  514.             fiber.yieldAll(Fiber<array<int, 4>>(do_permutation, newArr, length - 1));
  515.         }
  516.     }
  517.     else {
  518.         fiber.yield(arr);
  519.     }
  520. }
  521.  
  522. void permutation(Fiber<array<int, 4>>& fiber, array<int, 4> arr) {
  523.     do_permutation(fiber, arr, (int)arr.size());
  524. }
  525.  
  526. int main() {
  527.     {
  528.         Fiber<bool> arg1Fiber(foo, 0);
  529.     }
  530.     assert(destructedFlag);
  531.     {
  532.         Fiber<bool> arg1Fiber(foo, 0);
  533.         arg1Fiber.next();
  534.         arg1Fiber.next();
  535.         arg1Fiber.next();
  536.         arg1Fiber.next();
  537.         arg1Fiber.next();
  538.     }
  539.     assert(destructedFlag);
  540.     {
  541.         Fiber<bool> arg1Fiber(foo, 0);
  542.         arg1Fiber.next();
  543.         arg1Fiber.next();
  544.         arg1Fiber.next();
  545.     }
  546.     assert(destructedFlag);
  547.     {
  548.         Fiber<bool> arg1Fiber(foo, 0);
  549.         for (auto&& result : arg1Fiber) {}
  550.     }
  551.     assert(destructedFlag);
  552.  
  553.     for (auto&& result : Fiber<array<int, 4>>(permutation, array<int, 4> { 1, 2, 3, 4 })) {
  554.         copy(result.begin(), result.end(), std::ostream_iterator<int>(cout, ","));
  555.         cout << endl;
  556.     }
  557. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement