yield.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. // Copyright Oliver Kowalke, Nat Goodspeed 2015.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
  6. #define BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
  7. #include <boost/asio/async_result.hpp>
  8. #include <boost/asio/detail/config.hpp>
  9. #include <boost/asio/handler_type.hpp>
  10. #include <boost/assert.hpp>
  11. #include <boost/atomic.hpp>
  12. #include <boost/intrusive_ptr.hpp>
  13. #include <boost/system/error_code.hpp>
  14. #include <boost/system/system_error.hpp>
  15. #include <boost/throw_exception.hpp>
  16. #include <boost/fiber/all.hpp>
  17. #include <mutex> // std::unique_lock
  18. #ifdef BOOST_HAS_ABI_HEADERS
  19. # include BOOST_ABI_PREFIX
  20. #endif
  21. namespace boost {
  22. namespace fibers {
  23. namespace asio {
  24. namespace detail {
  25. //[fibers_asio_yield_completion
  26. // Bundle a completion bool flag with a spinlock to protect it.
  27. struct yield_completion {
  28. enum state_t {
  29. init,
  30. waiting,
  31. complete
  32. };
  33. typedef fibers::detail::spinlock mutex_t;
  34. typedef std::unique_lock< mutex_t > lock_t;
  35. typedef boost::intrusive_ptr< yield_completion > ptr_t;
  36. std::atomic< std::size_t > use_count_{ 0 };
  37. mutex_t mtx_{};
  38. state_t state_{ init };
  39. void wait() {
  40. // yield_handler_base::operator()() will set state_ `complete` and
  41. // attempt to wake a suspended fiber. It would be Bad if that call
  42. // happened between our detecting (complete != state_) and suspending.
  43. lock_t lk{ mtx_ };
  44. // If state_ is already set, we're done here: don't suspend.
  45. if ( complete != state_) {
  46. state_ = waiting;
  47. // suspend(unique_lock<spinlock>) unlocks the lock in the act of
  48. // resuming another fiber
  49. fibers::context::active()->suspend( lk);
  50. }
  51. }
  52. friend void intrusive_ptr_add_ref( yield_completion * yc) noexcept {
  53. BOOST_ASSERT( nullptr != yc);
  54. yc->use_count_.fetch_add( 1, std::memory_order_relaxed);
  55. }
  56. friend void intrusive_ptr_release( yield_completion * yc) noexcept {
  57. BOOST_ASSERT( nullptr != yc);
  58. if ( 1 == yc->use_count_.fetch_sub( 1, std::memory_order_release) ) {
  59. std::atomic_thread_fence( std::memory_order_acquire);
  60. delete yc;
  61. }
  62. }
  63. };
  64. //]
  65. //[fibers_asio_yield_handler_base
  66. // This class encapsulates common elements between yield_handler<T> (capturing
  67. // a value to return from asio async function) and yield_handler<void> (no
  68. // such value). See yield_handler<T> and its <void> specialization below. Both
  69. // yield_handler<T> and yield_handler<void> are passed by value through
  70. // various layers of asio functions. In other words, they're potentially
  71. // copied multiple times. So key data such as the yield_completion instance
  72. // must be stored in our async_result<yield_handler<>> specialization, which
  73. // should be instantiated only once.
  74. class yield_handler_base {
  75. public:
  76. yield_handler_base( yield_t const& y) :
  77. // capture the context* associated with the running fiber
  78. ctx_{ boost::fibers::context::active() },
  79. // capture the passed yield_t
  80. yt_( y ) {
  81. }
  82. // completion callback passing only (error_code)
  83. void operator()( boost::system::error_code const& ec) {
  84. BOOST_ASSERT_MSG( ycomp_,
  85. "Must inject yield_completion* "
  86. "before calling yield_handler_base::operator()()");
  87. BOOST_ASSERT_MSG( yt_.ec_,
  88. "Must inject boost::system::error_code* "
  89. "before calling yield_handler_base::operator()()");
  90. // If originating fiber is busy testing state_ flag, wait until it
  91. // has observed (completed != state_).
  92. yield_completion::lock_t lk{ ycomp_->mtx_ };
  93. yield_completion::state_t state = ycomp_->state_;
  94. // Notify a subsequent yield_completion::wait() call that it need not
  95. // suspend.
  96. ycomp_->state_ = yield_completion::complete;
  97. // set the error_code bound by yield_t
  98. * yt_.ec_ = ec;
  99. // unlock the lock that protects state_
  100. lk.unlock();
  101. // If ctx_ is still active, e.g. because the async operation
  102. // immediately called its callback (this method!) before the asio
  103. // async function called async_result_base::get(), we must not set it
  104. // ready.
  105. if ( yield_completion::waiting == state) {
  106. // wake the fiber
  107. fibers::context::active()->schedule( ctx_);
  108. }
  109. }
  110. //private:
  111. boost::fibers::context * ctx_;
  112. yield_t yt_;
  113. // We depend on this pointer to yield_completion, which will be injected
  114. // by async_result.
  115. yield_completion::ptr_t ycomp_{};
  116. };
  117. //]
  118. //[fibers_asio_yield_handler_T
  119. // asio uses handler_type<completion token type, signature>::type to decide
  120. // what to instantiate as the actual handler. Below, we specialize
  121. // handler_type< yield_t, ... > to indicate yield_handler<>. So when you pass
  122. // an instance of yield_t as an asio completion token, asio selects
  123. // yield_handler<> as the actual handler class.
  124. template< typename T >
  125. class yield_handler: public yield_handler_base {
  126. public:
  127. // asio passes the completion token to the handler constructor
  128. explicit yield_handler( yield_t const& y) :
  129. yield_handler_base{ y } {
  130. }
  131. // completion callback passing only value (T)
  132. void operator()( T t) {
  133. // just like callback passing success error_code
  134. (*this)( boost::system::error_code(), std::move(t) );
  135. }
  136. // completion callback passing (error_code, T)
  137. void operator()( boost::system::error_code const& ec, T t) {
  138. BOOST_ASSERT_MSG( value_,
  139. "Must inject value ptr "
  140. "before caling yield_handler<T>::operator()()");
  141. // move the value to async_result<> instance BEFORE waking up a
  142. // suspended fiber
  143. * value_ = std::move( t);
  144. // forward the call to base-class completion handler
  145. yield_handler_base::operator()( ec);
  146. }
  147. //private:
  148. // pointer to destination for eventual value
  149. // this must be injected by async_result before operator()() is called
  150. T * value_{ nullptr };
  151. };
  152. //]
  153. //[fibers_asio_yield_handler_void
  154. // yield_handler<void> is like yield_handler<T> without value_. In fact it's
  155. // just like yield_handler_base.
  156. template<>
  157. class yield_handler< void >: public yield_handler_base {
  158. public:
  159. explicit yield_handler( yield_t const& y) :
  160. yield_handler_base{ y } {
  161. }
  162. // nullary completion callback
  163. void operator()() {
  164. ( * this)( boost::system::error_code() );
  165. }
  166. // inherit operator()(error_code) overload from base class
  167. using yield_handler_base::operator();
  168. };
  169. //]
  170. // Specialize asio_handler_invoke hook to ensure that any exceptions thrown
  171. // from the handler are propagated back to the caller
  172. template< typename Fn, typename T >
  173. void asio_handler_invoke( Fn&& fn, yield_handler< T > *) {
  174. fn();
  175. }
  176. //[fibers_asio_async_result_base
  177. // Factor out commonality between async_result<yield_handler<T>> and
  178. // async_result<yield_handler<void>>
  179. class async_result_base {
  180. public:
  181. explicit async_result_base( yield_handler_base & h) :
  182. ycomp_{ new yield_completion{} } {
  183. // Inject ptr to our yield_completion instance into this
  184. // yield_handler<>.
  185. h.ycomp_ = this->ycomp_;
  186. // if yield_t didn't bind an error_code, make yield_handler_base's
  187. // error_code* point to an error_code local to this object so
  188. // yield_handler_base::operator() can unconditionally store through
  189. // its error_code*
  190. if ( ! h.yt_.ec_) {
  191. h.yt_.ec_ = & ec_;
  192. }
  193. }
  194. void get() {
  195. // Unless yield_handler_base::operator() has already been called,
  196. // suspend the calling fiber until that call.
  197. ycomp_->wait();
  198. // The only way our own ec_ member could have a non-default value is
  199. // if our yield_handler did not have a bound error_code AND the
  200. // completion callback passed a non-default error_code.
  201. if ( ec_) {
  202. throw_exception( boost::system::system_error{ ec_ } );
  203. }
  204. }
  205. private:
  206. // If yield_t does not bind an error_code instance, store into here.
  207. boost::system::error_code ec_{};
  208. yield_completion::ptr_t ycomp_;
  209. };
  210. //]
  211. }}}}
  212. namespace boost {
  213. namespace asio {
  214. //[fibers_asio_async_result_T
  215. // asio constructs an async_result<> instance from the yield_handler specified
  216. // by handler_type<>::type. A particular asio async method constructs the
  217. // yield_handler, constructs this async_result specialization from it, then
  218. // returns the result of calling its get() method.
  219. template< typename T >
  220. class async_result< boost::fibers::asio::detail::yield_handler< T > > :
  221. public boost::fibers::asio::detail::async_result_base {
  222. public:
  223. // type returned by get()
  224. typedef T type;
  225. explicit async_result( boost::fibers::asio::detail::yield_handler< T > & h) :
  226. boost::fibers::asio::detail::async_result_base{ h } {
  227. // Inject ptr to our value_ member into yield_handler<>: result will
  228. // be stored here.
  229. h.value_ = & value_;
  230. }
  231. // asio async method returns result of calling get()
  232. type get() {
  233. boost::fibers::asio::detail::async_result_base::get();
  234. return std::move( value_);
  235. }
  236. private:
  237. type value_{};
  238. };
  239. //]
  240. //[fibers_asio_async_result_void
  241. // Without the need to handle a passed value, our yield_handler<void>
  242. // specialization is just like async_result_base.
  243. template<>
  244. class async_result< boost::fibers::asio::detail::yield_handler< void > > :
  245. public boost::fibers::asio::detail::async_result_base {
  246. public:
  247. typedef void type;
  248. explicit async_result( boost::fibers::asio::detail::yield_handler< void > & h):
  249. boost::fibers::asio::detail::async_result_base{ h } {
  250. }
  251. };
  252. //]
  253. // Handler type specialisation for fibers::asio::yield.
  254. // When 'yield' is passed as a completion handler which accepts no parameters,
  255. // use yield_handler<void>.
  256. template< typename ReturnType >
  257. struct handler_type< fibers::asio::yield_t, ReturnType() >
  258. { typedef fibers::asio::detail::yield_handler< void > type; };
  259. // Handler type specialisation for fibers::asio::yield.
  260. // When 'yield' is passed as a completion handler which accepts a data
  261. // parameter, use yield_handler<parameter type> to return that parameter to
  262. // the caller.
  263. template< typename ReturnType, typename Arg1 >
  264. struct handler_type< fibers::asio::yield_t, ReturnType( Arg1) >
  265. { typedef fibers::asio::detail::yield_handler< Arg1 > type; };
  266. //[asio_handler_type
  267. // Handler type specialisation for fibers::asio::yield.
  268. // When 'yield' is passed as a completion handler which accepts only
  269. // error_code, use yield_handler<void>. yield_handler will take care of the
  270. // error_code one way or another.
  271. template< typename ReturnType >
  272. struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code) >
  273. { typedef fibers::asio::detail::yield_handler< void > type; };
  274. //]
  275. // Handler type specialisation for fibers::asio::yield.
  276. // When 'yield' is passed as a completion handler which accepts a data
  277. // parameter and an error_code, use yield_handler<parameter type> to return
  278. // just the parameter to the caller. yield_handler will take care of the
  279. // error_code one way or another.
  280. template< typename ReturnType, typename Arg2 >
  281. struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code, Arg2) >
  282. { typedef fibers::asio::detail::yield_handler< Arg2 > type; };
  283. }}
  284. #ifdef BOOST_HAS_ABI_HEADERS
  285. # include BOOST_ABI_SUFFIX
  286. #endif
  287. #endif // BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP