execution_context_v2_void.ipp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Copyright Oliver Kowalke 2014.
  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. namespace detail {
  6. template< typename Ctx, typename Fn >
  7. transfer_t ecv2_context_ontop_void( transfer_t);
  8. template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
  9. fcontext_t ecv2_context_create_void( StackAlloc &&, Fn &&, Params && ...);
  10. template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
  11. fcontext_t ecv2_context_create_void( preallocated, StackAlloc &&, Fn &&, Params && ...);
  12. template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
  13. class ecv2_record_void {
  14. private:
  15. typename std::decay< StackAlloc >::type salloc_;
  16. stack_context sctx_;
  17. typename std::decay< Fn >::type fn_;
  18. std::tuple< typename std::decay< Params >::type ... > params_;
  19. static void destroy( ecv2_record_void * p) noexcept {
  20. typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
  21. stack_context sctx = p->sctx_;
  22. // deallocate record
  23. p->~ecv2_record_void();
  24. // destroy stack with stack allocator
  25. salloc.deallocate( sctx);
  26. }
  27. public:
  28. ecv2_record_void( stack_context sctx, StackAlloc && salloc,
  29. Fn && fn, Params && ... params) noexcept :
  30. salloc_( std::forward< StackAlloc >( salloc) ),
  31. sctx_( sctx),
  32. fn_( std::forward< Fn >( fn) ),
  33. params_( std::forward< Params >( params) ... ) {
  34. }
  35. ecv2_record_void( ecv2_record_void const&) = delete;
  36. ecv2_record_void & operator=( ecv2_record_void const&) = delete;
  37. void deallocate() noexcept {
  38. destroy( this);
  39. }
  40. transfer_t run( transfer_t t) {
  41. Ctx from{ t.fctx };
  42. // invoke context-function
  43. #if defined(BOOST_NO_CXX17_STD_APPLY)
  44. Ctx cc = boost::context::detail::apply( fn_, std::tuple_cat( params_, std::forward_as_tuple( std::move( from) ) ) );
  45. #else
  46. Ctx cc = std::apply( fn_, std::tuple_cat( params_, std::forward_as_tuple( std::move( from) ) ) );
  47. #endif
  48. return { exchange( cc.fctx_, nullptr), nullptr };
  49. }
  50. };
  51. }
  52. inline namespace v2 {
  53. template<>
  54. class execution_context< void > {
  55. private:
  56. friend class ontop_error;
  57. template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
  58. friend class detail::ecv2_record_void;
  59. template< typename Ctx, typename Fn >
  60. friend detail::transfer_t detail::ecv2_context_ontop_void( detail::transfer_t);
  61. detail::fcontext_t fctx_{ nullptr };
  62. execution_context( detail::fcontext_t fctx) noexcept :
  63. fctx_( fctx) {
  64. }
  65. public:
  66. execution_context() noexcept = default;
  67. #if defined(BOOST_USE_SEGMENTED_STACKS)
  68. // segmented-stack requires to preserve the segments of the `current` context
  69. // which is not possible (no global pointer to current context)
  70. template< typename Fn, typename ... Params >
  71. execution_context( std::allocator_arg_t, segmented_stack, Fn &&, Params && ...) = delete;
  72. template< typename Fn, typename ... Params >
  73. execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete;
  74. #else
  75. template< typename Fn,
  76. typename ... Params,
  77. typename = detail::disable_overload< execution_context, Fn >
  78. >
  79. execution_context( Fn && fn, Params && ... params) :
  80. // deferred execution of fn and its arguments
  81. // arguments are stored in std::tuple<>
  82. // non-type template parameter pack via std::index_sequence_for<>
  83. // preserves the number of arguments
  84. // used to extract the function arguments from std::tuple<>
  85. fctx_( detail::ecv2_context_create_void< execution_context >(
  86. fixedsize_stack(),
  87. std::forward< Fn >( fn),
  88. std::forward< Params >( params) ... ) ) {
  89. }
  90. template< typename StackAlloc,
  91. typename Fn,
  92. typename ... Params
  93. >
  94. execution_context( std::allocator_arg_t, StackAlloc && salloc, Fn && fn, Params && ... params) :
  95. // deferred execution of fn and its arguments
  96. // arguments are stored in std::tuple<>
  97. // non-type template parameter pack via std::index_sequence_for<>
  98. // preserves the number of arguments
  99. // used to extract the function arguments from std::tuple<>
  100. fctx_( detail::ecv2_context_create_void< execution_context >(
  101. std::forward< StackAlloc >( salloc),
  102. std::forward< Fn >( fn),
  103. std::forward< Params >( params) ... ) ) {
  104. }
  105. template< typename StackAlloc,
  106. typename Fn,
  107. typename ... Params
  108. >
  109. execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) :
  110. // deferred execution of fn and its arguments
  111. // arguments are stored in std::tuple<>
  112. // non-type template parameter pack via std::index_sequence_for<>
  113. // preserves the number of arguments
  114. // used to extract the function arguments from std::tuple<>
  115. fctx_( detail::ecv2_context_create_void< execution_context >(
  116. palloc, std::forward< StackAlloc >( salloc),
  117. std::forward< Fn >( fn),
  118. std::forward< Params >( params) ... ) ) {
  119. }
  120. #endif
  121. ~execution_context() {
  122. if ( nullptr != fctx_) {
  123. detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::ecv2_context_unwind);
  124. }
  125. }
  126. execution_context( execution_context && other) noexcept :
  127. fctx_( other.fctx_) {
  128. other.fctx_ = nullptr;
  129. }
  130. execution_context & operator=( execution_context && other) noexcept {
  131. if ( this != & other) {
  132. execution_context tmp = std::move( other);
  133. swap( tmp);
  134. }
  135. return * this;
  136. }
  137. execution_context( execution_context const& other) noexcept = delete;
  138. execution_context & operator=( execution_context const& other) noexcept = delete;
  139. execution_context operator()() {
  140. BOOST_ASSERT( nullptr != fctx_);
  141. detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), nullptr);
  142. if ( nullptr != t.data) {
  143. std::exception_ptr * eptr = static_cast< std::exception_ptr * >( t.data);
  144. try {
  145. std::rethrow_exception( * eptr);
  146. } catch (...) {
  147. std::throw_with_nested( ontop_error{ t.fctx } );
  148. }
  149. }
  150. return execution_context( t.fctx);
  151. }
  152. template< typename Fn >
  153. execution_context operator()( exec_ontop_arg_t, Fn && fn) {
  154. BOOST_ASSERT( nullptr != fctx_);
  155. auto p = std::make_tuple( fn, std::exception_ptr{} );
  156. detail::transfer_t t = detail::ontop_fcontext(
  157. detail::exchange( fctx_, nullptr),
  158. & p,
  159. detail::ecv2_context_ontop_void< execution_context, Fn >);
  160. if ( nullptr != t.data) {
  161. std::exception_ptr * eptr = static_cast< std::exception_ptr * >( t.data);
  162. try {
  163. std::rethrow_exception( * eptr);
  164. } catch (...) {
  165. std::throw_with_nested( ontop_error{ t.fctx } );
  166. }
  167. }
  168. return execution_context( t.fctx);
  169. }
  170. explicit operator bool() const noexcept {
  171. return nullptr != fctx_;
  172. }
  173. bool operator!() const noexcept {
  174. return nullptr == fctx_;
  175. }
  176. bool operator<( execution_context const& other) const noexcept {
  177. return fctx_ < other.fctx_;
  178. }
  179. template< typename charT, class traitsT >
  180. friend std::basic_ostream< charT, traitsT > &
  181. operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
  182. if ( nullptr != other.fctx_) {
  183. return os << other.fctx_;
  184. } else {
  185. return os << "{not-a-context}";
  186. }
  187. }
  188. void swap( execution_context & other) noexcept {
  189. std::swap( fctx_, other.fctx_);
  190. }
  191. };
  192. }
  193. namespace detail {
  194. template< typename Ctx, typename Fn >
  195. transfer_t ecv2_context_ontop_void( transfer_t t) {
  196. auto p = static_cast< std::tuple< Fn, std::exception_ptr > * >( t.data);
  197. BOOST_ASSERT( nullptr != p);
  198. typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
  199. try {
  200. // execute function
  201. fn();
  202. #if defined( BOOST_CONTEXT_HAS_CXXABI_H )
  203. } catch ( abi::__forced_unwind const&) {
  204. throw;
  205. #endif
  206. } catch (...) {
  207. std::get< 1 >( * p) = std::current_exception();
  208. return { t.fctx, & std::get< 1 >( * p ) };
  209. }
  210. return { exchange( t.fctx, nullptr), nullptr };
  211. }
  212. template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
  213. fcontext_t ecv2_context_create_void( StackAlloc && salloc, Fn && fn, Params && ... params) {
  214. typedef ecv2_record_void< Ctx, StackAlloc, Fn, Params ... > record_t;
  215. auto sctx = salloc.allocate();
  216. // reserve space for control structure
  217. #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
  218. const std::size_t size = sctx.size - sizeof( record_t);
  219. void * sp = static_cast< char * >( sctx.sp) - sizeof( record_t);
  220. #else
  221. constexpr std::size_t func_alignment = 64; // alignof( record_t);
  222. constexpr std::size_t func_size = sizeof( record_t);
  223. // reserve space on stack
  224. void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
  225. // align sp pointer
  226. std::size_t space = func_size + func_alignment;
  227. sp = std::align( func_alignment, func_size, sp, space);
  228. BOOST_ASSERT( nullptr != sp);
  229. // calculate remaining size
  230. const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
  231. #endif
  232. // create fast-context
  233. const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< record_t >);
  234. BOOST_ASSERT( nullptr != fctx);
  235. // placment new for control structure on context-stack
  236. auto rec = ::new ( sp) record_t{
  237. sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... };
  238. // transfer control structure to context-stack
  239. return jump_fcontext( fctx, rec).fctx;
  240. }
  241. template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
  242. fcontext_t ecv2_context_create_void( preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) {
  243. typedef ecv2_record_void< Ctx, StackAlloc, Fn, Params ... > record_t;
  244. // reserve space for control structure
  245. #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
  246. const std::size_t size = palloc.size - sizeof( record_t);
  247. void * sp = static_cast< char * >( palloc.sp) - sizeof( record_t);
  248. #else
  249. constexpr std::size_t func_alignment = 64; // alignof( record_t);
  250. constexpr std::size_t func_size = sizeof( record_t);
  251. // reserve space on stack
  252. void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
  253. // align sp pointer
  254. std::size_t space = func_size + func_alignment;
  255. sp = std::align( func_alignment, func_size, sp, space);
  256. BOOST_ASSERT( nullptr != sp);
  257. // calculate remaining size
  258. const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
  259. #endif
  260. // create fast-context
  261. const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< record_t >);
  262. BOOST_ASSERT( nullptr != fctx);
  263. // placment new for control structure on context-stack
  264. auto rec = ::new ( sp) record_t{
  265. palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... };
  266. // transfer control structure to context-stack
  267. return jump_fcontext( fctx, rec).fctx;
  268. }
  269. }