Advertisement
zhangsongcui

Yield V4.0 ( Add boost fcontext support )

Jun 9th, 2018
201
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.84 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>
  56. namespace std {
  57.     using ::std::experimental::optional;
  58.     using ::std::experimental::nullopt;
  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 YieldValueType>
  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 YieldValueType 子纤程返回类型
  92.      */
  93.     template <typename YieldValueType>
  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<YieldValueType> yieldedValue;
  102.         /// \brief 存储子纤程抛出的异常
  103.         std::exception_ptr eptr = nullptr;
  104.         /// \brief 子纤程是否结束
  105.         FiberStatus status = FiberStatus::unstarted;
  106.         /// \brief 真子纤程入口,第一个参数传入纤程对象的引用
  107.         FuncType func;
  108.  
  109.         /// \brief 纤程信息
  110. #if USE_FCONTEXT
  111.         fctx::detail::fcontext_t ctx_main, ctx_fnew;
  112. #   ifdef NDEBUG
  113.         fctx::fixedsize_stack stack_allocator;
  114. #   else
  115.         fctx::protected_fixedsize_stack stack_allocator;
  116. #endif
  117.         fctx::stack_context fnew_stack;
  118. #elif USE_WINFIB
  119.         PVOID pMainFiber, pNewFiber;
  120.         bool isFormerAThread;
  121. #elif USE_UCONTEXT
  122.         ::ucontext_t ctx_main, ctx_fnew;
  123.         const std::unique_ptr<std::array<uint8_t, SIGSTKSZ>> fnew_stack = std::make_unique<std::array<uint8_t, SIGSTKSZ>>();
  124. #elif USE_SJLJ
  125.         ::sigjmp_buf buf_main, buf_new;
  126.         const std::unique_ptr<std::array<uint8_t, SIGSTKSZ>> fnew_stack = std::make_unique<std::array<uint8_t, SIGSTKSZ>>();
  127.         struct sigaction old_sa;
  128.         static Fiber *that;
  129. #endif
  130.  
  131.     public:
  132.         /** \brief 构造函数
  133.          *
  134.          * 把主线程转化为纤程,并创建一个子纤程
  135.          *
  136.          * \param f 子纤程入口
  137.          */
  138.         explicit Fiber(FuncType f) : func(std::move(f)) {
  139. #if USE_FCONTEXT
  140.             this->fnew_stack = this->stack_allocator.allocate();
  141.             this->ctx_fnew = fctx::detail::make_fcontext(this->fnew_stack.sp, this->fnew_stack.size, fEntry);
  142. #elif USE_WINFIB
  143.             this->isFormerAThread = !IsThreadAFiber();
  144.             if (this->isFormerAThread) {
  145.                 this->pMainFiber = ::ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
  146.             } else {
  147.                 this->pMainFiber = ::GetCurrentFiber();
  148.             }
  149.             // default stack size
  150.             this->pNewFiber = ::CreateFiberEx(0, 0, FIBER_FLAG_FLOAT_SWITCH, (void(*)(void *))&fEntry, this);
  151. #elif USE_UCONTEXT
  152.             ::getcontext(&this->ctx_fnew);
  153.             this->ctx_fnew.uc_stack.ss_sp = this->fnew_stack.get();
  154.             this->ctx_fnew.uc_stack.ss_size = this->fnew_stack->size();
  155.             this->ctx_fnew.uc_link = &this->ctx_main;
  156.             ::makecontext(&this->ctx_fnew, (void(*)())&fEntry, 1, this);
  157. #elif USE_SJLJ
  158.             ::stack_t sigstk, oldstk;
  159.             sigstk.ss_sp = this->fnew_stack.get();
  160.             sigstk.ss_size = this->fnew_stack->size();
  161.             sigstk.ss_flags = 0;
  162.             assert(::sigaltstack(&sigstk, &oldstk) == 0 && "Error while set sigstk");
  163.  
  164.             struct sigaction sa;
  165.             sa.sa_flags = SA_ONSTACK;
  166.             sa.sa_handler = fEntry;
  167.             sigemptyset(&sa.sa_mask);
  168.             assert(::sigaction(SIGUSR2, &sa, &this->old_sa) == 0 && "Error while installing a signal handler");
  169.  
  170.             if (!sigsetjmp(this->buf_main, 0)) {
  171.                 Fiber::that = this; // Android doesn't support sigqueue,
  172.                 assert(::raise(SIGUSR2) == 0 && "Failed to queue the signal");
  173.             }
  174.             assert(::sigaltstack(&oldstk, nullptr) == 0 && "Error while reset sigstk");
  175.  
  176.             ::sigset_t sa_mask;
  177.             sigemptyset(&sa_mask);
  178.             sigaddset(&sa_mask, SIGUSR2);
  179.             ::sigprocmask(SIG_UNBLOCK, &sa_mask, nullptr);
  180. #endif
  181.         }
  182.  
  183.         template <class Fp, class ...Args,
  184.             class = typename std::enable_if
  185.                 <
  186.                     (sizeof...(Args) > 0)
  187.                 >::type
  188.             >
  189.             explicit Fiber(Fp&& f, Args&&... args) : Fiber(std::bind(std::forward<Fp>(f), std::placeholders::_1, std::forward<Args>(args)...)) {}
  190.  
  191.         /** \brief 析构函数
  192.          *
  193.          * 删除子纤程,并将主纤程转回线程
  194.          *
  195.          * \warning 主类析构时如子纤程尚未结束(return),则会在子纤程中抛出 FiberReturn 来确保子纤程函数内所有对象都被正确析构
  196.          */
  197.         ~Fiber() noexcept {
  198.             if (!isFinished()) {
  199.                 return_();
  200.             }
  201.  
  202. #if USE_FCONTEXT
  203.             this->stack_allocator.deallocate(this->fnew_stack);
  204. #elif USE_WINFIB
  205.             ::DeleteFiber(this->pNewFiber);
  206.             if (this->isFormerAThread) {
  207.                 ::ConvertFiberToThread();
  208.             }
  209. #endif
  210.         }
  211.  
  212.         /** \brief 调用子纤程
  213.          *
  214.          * 程序流程转入子纤程
  215.          *
  216.          * \warning 子纤程必须尚未结束
  217.          * \return 返回子纤程是否尚未结束
  218.          */
  219.         bool next() {
  220.             assert(!isFinished());
  221. #if USE_FCONTEXT
  222.             this->ctx_fnew = fctx::detail::jump_fcontext(this->ctx_fnew, this).fctx;
  223. #elif USE_WINFIB
  224.             assert(GetCurrentFiber() != this->pNewFiber && "如果你想递归自己,请创建一个新纤程");
  225.             ::SwitchToFiber(this->pNewFiber);
  226. #elif USE_UCONTEXT
  227.             ::swapcontext(&this->ctx_main, &this->ctx_fnew);
  228. #elif USE_SJLJ
  229.             if (!::sigsetjmp(this->buf_main, 0)) {
  230.                 ::siglongjmp(this->buf_new, 1);
  231.             }
  232. #endif
  233.             if (this->eptr) {
  234.                 std::rethrow_exception(std::exchange(this->eptr, nullptr));
  235.             }
  236.  
  237.             return !isFinished();
  238.         }
  239.  
  240.         /** \brief 向子纤程内部抛出异常
  241.          *
  242.          * 程序流程转入子纤程,并在子纤程内部抛出异常
  243.          *
  244.          * \param eptr 需抛出异常的指针(可以通过 std::make_exception_ptr 获取)
  245.          * \warning 子纤程必须尚未结束
  246.          * \return 返回子纤程是否尚未结束
  247.          */
  248.         bool throw_(std::exception_ptr&& eptr) {
  249.             assert(!isFinished());
  250.             this->eptr = std::exchange(eptr, nullptr);
  251.             return next();
  252.         }
  253.  
  254.         /** \brief 强制退出子纤程
  255.          *
  256.          * 向子纤程内部抛出 FiberReturn 异常,以强制退出子纤程,并确保子纤程函数中所有对象都正确析构
  257.          *
  258.          * \warning 子纤程必须尚未结束
  259.          */
  260.         void return_() {
  261.             assert(!isFinished());
  262.             throw_(std::make_exception_ptr(FiberReturn()));
  263.             assert(isFinished() && "请勿吃掉 FiberReturn 异常!!!");
  264.         }
  265.  
  266.         /** \brief 获得子纤程返回的值
  267.          * \return 子纤程返回的值。如果子纤程没有启动,则返回默认构造值
  268.          */
  269.         const YieldValueType& current() const {
  270.             return *this->yieldedValue;
  271.         }
  272.  
  273.         /** \brief 判断子纤程是否结束
  274.          * \return 子纤程已经结束(return)返回true,否则false
  275.          */
  276.         bool isFinished() const noexcept {
  277.             return this->status == FiberStatus::closed;
  278.         }
  279.  
  280.         /** \brief 转回主纤程并输出值
  281.          *
  282.          * \warning 必须由子纤程调用
  283.          *          参数类型必须与子纤程返回值相同,无类型安全
  284.          * \param value 输出到主纤程的值
  285.          */
  286.         void yield(YieldValueType value) {
  287.             assert(!isFinished());
  288.             this->status = FiberStatus::suspended;
  289.             this->yieldedValue = std::move(value);
  290. #if USE_FCONTEXT
  291.             this->ctx_main = fctx::detail::jump_fcontext(this->ctx_main, this).fctx;
  292. #elif USE_WINFIB
  293.             assert(GetCurrentFiber() != this->pMainFiber && "这虽然是游戏,但绝不是可以随便玩的");
  294.             ::SwitchToFiber(this->pMainFiber);
  295. #elif USE_UCONTEXT
  296.             ::swapcontext(&this->ctx_fnew, &this->ctx_main);
  297. #else
  298.             if (!::sigsetjmp(this->buf_new, 0)) {
  299.                 ::siglongjmp(this->buf_main, 1);
  300.             }
  301. #endif
  302.             this->status = FiberStatus::running;
  303.  
  304.             if (this->eptr) {
  305.                 std::rethrow_exception(std::exchange(this->eptr, nullptr));
  306.             }
  307.         }
  308.  
  309.         /** \brief 输出子纤程的所有值
  310.          * \param fiber 另一子纤程
  311.          */
  312.         void yieldAll(Fiber& fiber) {
  313.             assert(&fiber != this);
  314.             while (fiber.next()) {
  315.                 this->yield(fiber.current());
  316.             }
  317.         }
  318.  
  319.         void yieldAll(Fiber&& fiber) {
  320.             this->yieldAll(fiber);
  321.         }
  322.  
  323.     private:
  324.         /// \brief 子纤程入口的warpper
  325. #if USE_FCONTEXT
  326.         static void fEntry(fctx::detail::transfer_t transfer) {
  327.             Fiber *fiber = (Fiber *)transfer.data;
  328.             fiber->ctx_main = transfer.fctx;
  329. #elif USE_WINFIB
  330.         static void WINAPI fEntry(Fiber *fiber) {
  331. #elif USE_UCONTEXT
  332.         static void fEntry(Fiber *fiber) {
  333. #elif USE_SJLJ
  334.         static void fEntry(int signo) {
  335.             Fiber *fiber = std::exchange(Fiber::that, nullptr);
  336.             assert(sigaction(signo, &fiber->old_sa, nullptr) == 0 && "Failed to reset signal handler");
  337.             if (!::sigsetjmp(fiber->buf_new, 0)) {
  338.                 ::siglongjmp(fiber->buf_main, 1);
  339.             }
  340. #endif
  341.  
  342.             if (!fiber->eptr) {
  343.                 fiber->status = FiberStatus::running;
  344.                 try {
  345.                     fiber->func(*fiber);
  346.                 }
  347.                 catch (FiberReturn &) {
  348.                     // 主 Fiber 对象正在析构
  349.                 }
  350.                 catch (...) {
  351.                     fiber->eptr = std::current_exception();
  352.                 }
  353.             }
  354.             fiber->status = FiberStatus::closed;
  355.             fiber->yieldedValue = std::nullopt;
  356. #if USE_FCONTEXT
  357.             fiber->ctx_main = fctx::detail::jump_fcontext(fiber->ctx_main, fiber).fctx;
  358. #elif USE_WINFIB
  359.             ::SwitchToFiber(fiber->pMainFiber);
  360. #elif USE_SJLJ
  361.             ::siglongjmp(fiber->buf_main, 1);
  362. #endif
  363.         }
  364.     };
  365.    
  366. #if USE_SJLJ
  367.     template <typename YieldValueType>
  368.     Fiber<YieldValueType> *Fiber<YieldValueType>::that;
  369. #endif
  370.  
  371.     /** \brief 纤程迭代器类
  372.      *
  373.      * 它通过使用 yield 函数对数组或集合类执行自定义迭代。
  374.      * 用于 C++11 for (... : ...)
  375.      */
  376.     template <typename YieldValueType>
  377.     struct FiberIterator : std::iterator<std::output_iterator_tag, YieldValueType> {
  378.         /// \brief 迭代器尾
  379.         FiberIterator() noexcept : fiber(nullptr) {}
  380.         /** \brief 迭代器首
  381.          * \param _f 主线程类的引用
  382.          */
  383.         FiberIterator(Fiber<YieldValueType>& _f) : fiber(&_f) {
  384.             next();
  385.         }
  386.  
  387.         /// \brief 转入子纤程
  388.         FiberIterator& operator ++() {
  389.             next();
  390.             return *this;
  391.         }
  392.  
  393.         /// \brief 取得返回值
  394.         const YieldValueType &operator *() const {
  395.             assert(fiber != nullptr);
  396.             return fiber->current();
  397.         }
  398.  
  399.         /** \brief 比较迭代器相等
  400.          *
  401.          * 通常用于判断迭代是否结束
  402.          * 最好别干别的 ;P
  403.          */
  404.         bool operator ==(const FiberIterator& rhs) const noexcept {
  405.             return fiber == rhs.fiber;
  406.         }
  407.         bool operator !=(const FiberIterator& rhs) const noexcept {
  408.             return !(*this == rhs);
  409.         }
  410.  
  411.     private:
  412.         void next() {
  413.             assert(fiber);
  414.             if (!fiber->next()) fiber = nullptr;
  415.         }
  416.  
  417.         Fiber<YieldValueType>* fiber;
  418.     };
  419.  
  420.     /// \brief 返回迭代器首
  421.     template <typename YieldValueType>
  422.     FiberIterator<YieldValueType> begin(Fiber<YieldValueType>& fiber) {
  423.         return FiberIterator<YieldValueType>(fiber);
  424.     }
  425.  
  426.     /// \brief 返回迭代器尾
  427.     template <typename YieldValueType>
  428.     FiberIterator<YieldValueType> end(Fiber<YieldValueType>&) noexcept {
  429.         return FiberIterator<YieldValueType>();
  430.     }
  431. }
  432.  
  433. using namespace std;
  434. using FiberSpace::Fiber;
  435.  
  436. bool destructedFlag = false;
  437.  
  438. struct TestDestruct {
  439.     ~TestDestruct() {
  440.         destructedFlag = true;
  441.     }
  442. };
  443.  
  444. void foo(Fiber<bool>& fiber, int arg) {
  445.     TestDestruct test;
  446.     for (int i = 1; i < 5; i++) {
  447.         printf("goroutine :%d\n", arg + i);
  448.         fiber.yield(false);
  449.     }
  450. }
  451.  
  452. void do_permutation(Fiber<array<int, 4>>& fiber, array<int, 4> arr, int length) {
  453.     if (length) {
  454.         for (auto i = 0; i < length; ++i) {
  455.             array<int, 4> newArr(arr);
  456.             std::copy_n(arr.begin(), i, newArr.begin());
  457.             std::copy_n(arr.begin() + i + 1, arr.size() - i - 1, newArr.begin() + i);
  458.             newArr.back() = arr[i];
  459.             fiber.yieldAll(Fiber<array<int, 4>>(do_permutation, newArr, length - 1));
  460.         }
  461.     }
  462.     else {
  463.         fiber.yield(arr);
  464.     }
  465. }
  466.  
  467. void permutation(Fiber<array<int, 4>>& fiber, array<int, 4> arr) {
  468.     do_permutation(fiber, arr, (int)arr.size());
  469. }
  470.  
  471. int main() {
  472.     {
  473.         Fiber<bool> arg1Fiber(foo, 0);
  474.         arg1Fiber.next();
  475.         arg1Fiber.next();
  476.         arg1Fiber.next();
  477.         arg1Fiber.next();
  478.     }
  479.     assert(destructedFlag);
  480.     {
  481.         Fiber<bool> arg1Fiber(foo, 0);
  482.         arg1Fiber.next();
  483.         arg1Fiber.next();
  484.         arg1Fiber.next();
  485.     }
  486.     assert(destructedFlag);
  487.     {
  488.         Fiber<bool> arg1Fiber(foo, 0);
  489.         for (auto&& result : arg1Fiber) {}
  490.     }
  491.     assert(destructedFlag);
  492.  
  493.     //for (auto&& result : Fiber<array<int, 4>>(permutation, array<int, 4> { 1, 2, 3, 4 })) {
  494.     //    copy(result.begin(), result.end(), std::ostream_iterator<int>(cout, ","));
  495.     //    cout << endl;
  496.     //}
  497. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement