Mercury Currency Engine
coroutine.hpp
Go to the documentation of this file.
1 //SPDX-License-Identifier: Apache-2.0
2 //Author: Blayne Dennis
7 #ifndef __MERCURY_COROUTINE_ENGINE_COROUTINE__
8 #define __MERCURY_COROUTINE_ENGINE_COROUTINE__
9 
10 // c++
11 #include <thread>
12 #include <memory>
13 
14 // boost
15 #include <boost/coroutine2/all.hpp>
16 
17 // local
18 #include "function_utility.hpp"
19 
20 // test, only uncomment for development of this library
21 //#include "dev_print.hpp"
22 
23 namespace mce {
24 
25 struct coroutine;
26 
27 namespace detail {
28 
29 // always points to the running coroutine
30 coroutine*& tl_this_coroutine();
31 
32 }
33 
34 //-----------------------------------------------------------------------------
35 // coroutine
36 //-----------------------------------------------------------------------------
42 struct coroutine
43 {
50  template <typename Callable, typename... As>
51  static std::unique_ptr<coroutine> make(Callable&& cb, As&&... as)
52  {
53  return std::unique_ptr<coroutine>(
54  new coroutine(
55  make_thunk(
56  std::forward<Callable>(cb),
57  std::forward<As>(as)...)));
58  }
59 
61  template <typename THUNK>
62  coroutine(THUNK&& th) :
63  yield_(nullptr),
64  co_(co_t::pull_type(wrapper_functor{ yield_, std::forward<THUNK>(th) }))
65  { }
66 
68  template <typename StackAllocator, typename THUNK>
69  coroutine(StackAllocator&& sa, THUNK&& th) :
70  yield_(nullptr),
71  co_(std::forward<StackAllocator>(sa),
72  co_t::pull_type(wrapper_functor{ yield_, std::forward<THUNK>(th) }))
73  { }
74 
75  // Copy Constructor (Not allowed)
76  coroutine(const coroutine& source) = delete;
77 
79  coroutine(coroutine&& rhs) noexcept :
80  yield_(std::move(rhs.yield_)),
81  co_(std::move(rhs.co_))
82  { }
83 
84  // Copy Assignment (Not allowed)
85  coroutine& operator=(const coroutine& rhs) = delete;
86 
88  coroutine& operator=(coroutine&& rhs) noexcept
89  {
90  yield_ = std::move(rhs.yield_);
91  co_ = std::move(rhs.co_);
92  return *this;
93  }
94 
95  virtual ~coroutine(){}
96 
105  virtual inline void run()
106  {
107  if(!complete())
108  {
109  auto& tl_co = detail::tl_this_coroutine();
110 
111  // store parent coroutine
112  auto parent_co = tl_co;
113 
114  // set current coroutine ptr
115  tl_co = this;
116 
117  // continue coroutine execution
118  try { co_(); }
119  catch(...)
120  {
121  // restore parent coroutine ptr
122  tl_co = parent_co;
123  std::rethrow_exception(std::current_exception());
124  }
125 
126  // restore parent coroutine ptr
127  tl_co = parent_co;
128  };
129  }
130 
137  inline void yield()
138  {
139  (*yield_)();
140  }
141 
143  inline bool complete()
144  {
145  return !(co_.operator bool());
146  }
147 
148 private:
149  typedef boost::coroutines2::coroutine<void> co_t;
150 
151  struct wrapper_functor
152  {
153  inline void operator()(coroutine::co_t::push_type& yield)
154  {
155  yield_ = &yield;
156  yield(); // yield after coroutine construction but before execution
157  th(); // execute thunk when coroutine resumes
158  }
159 
160  co_t::push_type*& yield_;
161  mce::thunk th;
162  };
163 
164  co_t::push_type* yield_;
165  co_t::pull_type co_;
166 };
167 
169 inline bool in_coroutine()
170 {
171  return detail::tl_this_coroutine() ? true : false;
172 }
173 
179 {
180  return detail::tl_this_coroutine();
181 }
182 
186 inline void yield()
187 {
188  coroutine* c = detail::tl_this_coroutine();
189  if(c) { c->yield(); }
190 }
191 
192 }
193 
194 #endif
void yield()
Definition: coroutine.hpp:186
coroutine * this_coroutine()
Definition: coroutine.hpp:178
bool in_coroutine()
Returns true if executing in a coroutine, else false.
Definition: coroutine.hpp:169
std::function< void()> thunk
thunk type definition. Also known as a nullary function
Definition: function_utility.hpp:72
Definition: coroutine.hpp:43
void yield()
Pause execution and return to run() caller.
Definition: coroutine.hpp:137
virtual void run()
Execute until thunk completes or yield() is called.
Definition: coroutine.hpp:105
static std::unique_ptr< coroutine > make(Callable &&cb, As &&... as)
construct an allocated coroutine from a Callable and arguments
Definition: coroutine.hpp:51
coroutine(coroutine &&rhs) noexcept
Move Constructor.
Definition: coroutine.hpp:79
coroutine(THUNK &&th)
construct a coroutine from a thunk
Definition: coroutine.hpp:62
bool complete()
Returns true if thunk is complete, else false since the coroutine yielded early.
Definition: coroutine.hpp:143
coroutine(StackAllocator &&sa, THUNK &&th)
construct a coroutine from a stack allocator and thunk
Definition: coroutine.hpp:69
coroutine & operator=(coroutine &&rhs) noexcept
Move Assignment.
Definition: coroutine.hpp:88