execution_context_v1.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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. #ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_V1_H
  6. #define BOOST_CONTEXT_EXECUTION_CONTEXT_V1_H
  7. #include <boost/context/detail/config.hpp>
  8. #include <algorithm>
  9. #include <atomic>
  10. #include <cstddef>
  11. #include <cstdint>
  12. #include <cstdlib>
  13. #include <functional>
  14. #include <memory>
  15. #include <ostream>
  16. #include <tuple>
  17. #include <utility>
  18. #include <boost/assert.hpp>
  19. #include <boost/config.hpp>
  20. #include <boost/intrusive_ptr.hpp>
  21. #if defined(BOOST_NO_CXX17_STD_APPLY)
  22. #include <boost/context/detail/apply.hpp>
  23. #endif
  24. #include <boost/context/detail/disable_overload.hpp>
  25. #include <boost/context/detail/externc.hpp>
  26. #include <boost/context/detail/fcontext.hpp>
  27. #include <boost/context/fixedsize_stack.hpp>
  28. #include <boost/context/flags.hpp>
  29. #include <boost/context/preallocated.hpp>
  30. #include <boost/context/segmented_stack.hpp>
  31. #include <boost/context/stack_context.hpp>
  32. #ifdef BOOST_HAS_ABI_HEADERS
  33. # include BOOST_ABI_PREFIX
  34. #endif
  35. namespace boost {
  36. namespace context {
  37. namespace detail {
  38. template< typename Fn >
  39. transfer_t ecv1_context_ontop( transfer_t);
  40. struct ecv1_activation_record;
  41. struct ecv1_data_t {
  42. ecv1_activation_record * from;
  43. void * data;
  44. };
  45. struct BOOST_CONTEXT_DECL ecv1_activation_record {
  46. typedef boost::intrusive_ptr< ecv1_activation_record > ptr_t;
  47. static ptr_t & current() noexcept;
  48. std::atomic< std::size_t > use_count{ 0 };
  49. fcontext_t fctx{ nullptr };
  50. stack_context sctx{};
  51. bool main_ctx{ true };
  52. // used for toplevel-context
  53. // (e.g. main context, thread-entry context)
  54. ecv1_activation_record() = default;
  55. ecv1_activation_record( fcontext_t fctx_, stack_context sctx_) noexcept :
  56. fctx{ fctx_ },
  57. sctx( sctx_ ), // sctx{ sctx_ } - clang-3.6: no viable conversion from 'boost::context::stack_context' to 'std::size_t'
  58. main_ctx{ false } {
  59. }
  60. virtual ~ecv1_activation_record() = default;
  61. bool is_main_context() const noexcept {
  62. return main_ctx;
  63. }
  64. void * resume( void * vp) {
  65. // store current activation record in local variable
  66. auto from = current().get();
  67. // store `this` in static, thread local pointer
  68. // `this` will become the active (running) context
  69. // returned by execution_context::current()
  70. current() = this;
  71. #if defined(BOOST_USE_SEGMENTED_STACKS)
  72. // adjust segmented stack properties
  73. __splitstack_getcontext( from->sctx.segments_ctx);
  74. __splitstack_setcontext( sctx.segments_ctx);
  75. #endif
  76. ecv1_data_t d = { from, vp };
  77. // context switch from parent context to `this`-context
  78. transfer_t t = jump_fcontext( fctx, & d);
  79. ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data);
  80. dp->from->fctx = t.fctx;
  81. // parent context resumed
  82. return dp->data;
  83. }
  84. template< typename Fn >
  85. void * resume_ontop( void * data, Fn && fn) {
  86. // store current activation record in local variable
  87. ecv1_activation_record * from = current().get();
  88. // store `this` in static, thread local pointer
  89. // `this` will become the active (running) context
  90. // returned by execution_context::current()
  91. current() = this;
  92. #if defined(BOOST_USE_SEGMENTED_STACKS)
  93. // adjust segmented stack properties
  94. __splitstack_getcontext( from->sctx.segments_ctx);
  95. __splitstack_setcontext( sctx.segments_ctx);
  96. #endif
  97. std::tuple< void *, Fn > p = std::forward_as_tuple( data, fn);
  98. ecv1_data_t d = { from, & p };
  99. // context switch from parent context to `this`-context
  100. // execute Fn( Tpl) on top of `this`
  101. transfer_t t = ontop_fcontext( fctx, & d, ecv1_context_ontop< Fn >);
  102. ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data);
  103. dp->from->fctx = t.fctx;
  104. // parent context resumed
  105. return dp->data;
  106. }
  107. virtual void deallocate() noexcept {
  108. }
  109. friend void intrusive_ptr_add_ref( ecv1_activation_record * ar) noexcept {
  110. ++ar->use_count;
  111. }
  112. friend void intrusive_ptr_release( ecv1_activation_record * ar) noexcept {
  113. BOOST_ASSERT( nullptr != ar);
  114. if ( 0 == --ar->use_count) {
  115. ar->deallocate();
  116. }
  117. }
  118. };
  119. struct BOOST_CONTEXT_DECL ecv1_activation_record_initializer {
  120. ecv1_activation_record_initializer() noexcept;
  121. ~ecv1_activation_record_initializer();
  122. };
  123. template< typename Fn >
  124. transfer_t ecv1_context_ontop( transfer_t t) {
  125. ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data);
  126. dp->from->fctx = t.fctx;
  127. auto tpl = reinterpret_cast< std::tuple< void *, Fn > * >( dp->data);
  128. BOOST_ASSERT( nullptr != tpl);
  129. auto data = std::get< 0 >( * tpl);
  130. typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 1 >( * tpl) );
  131. #if defined(BOOST_NO_CXX17_STD_APPLY)
  132. dp->data = boost::context::detail::apply( fn, std::tie( data) );
  133. #else
  134. dp->data = std::apply( fn, std::tie( data) );
  135. #endif
  136. return { t.fctx, dp };
  137. }
  138. template< typename StackAlloc, typename Fn, typename ... Args >
  139. class ecv1_capture_record : public ecv1_activation_record {
  140. private:
  141. typename std::decay< StackAlloc >::type salloc_;
  142. typename std::decay< Fn >::type fn_;
  143. std::tuple< typename std::decay< Args >::type ... > args_;
  144. ecv1_activation_record * caller_;
  145. static void destroy( ecv1_capture_record * p) noexcept {
  146. typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
  147. stack_context sctx = p->sctx;
  148. // deallocate activation record
  149. p->~ecv1_capture_record();
  150. // destroy stack with stack allocator
  151. salloc.deallocate( sctx);
  152. }
  153. public:
  154. ecv1_capture_record( stack_context sctx, StackAlloc && salloc,
  155. fcontext_t fctx,
  156. ecv1_activation_record * caller,
  157. Fn && fn, Args && ... args) noexcept :
  158. ecv1_activation_record{ fctx, sctx },
  159. salloc_{ std::forward< StackAlloc >( salloc) },
  160. fn_( std::forward< Fn >( fn) ),
  161. args_( std::forward< Args >( args) ... ),
  162. caller_{ caller } {
  163. }
  164. void deallocate() noexcept override final {
  165. destroy( this);
  166. }
  167. void run() {
  168. auto data = caller_->resume( nullptr);
  169. #if defined(BOOST_NO_CXX17_STD_APPLY)
  170. boost::context::detail::apply( fn_, std::tuple_cat( args_, std::tie( data) ) );
  171. #else
  172. std::apply( fn_, std::tuple_cat( args_, std::tie( data) ) );
  173. #endif
  174. BOOST_ASSERT_MSG( ! main_ctx, "main-context does not execute activation-record::run()");
  175. }
  176. };
  177. }
  178. namespace v1 {
  179. class BOOST_CONTEXT_DECL execution_context {
  180. private:
  181. // tampoline function
  182. // entered if the execution context
  183. // is resumed for the first time
  184. template< typename AR >
  185. static void entry_func( detail::transfer_t t) noexcept {
  186. detail::ecv1_data_t * dp = reinterpret_cast< detail::ecv1_data_t * >( t.data);
  187. AR * ar = static_cast< AR * >( dp->data);
  188. BOOST_ASSERT( nullptr != ar);
  189. dp->from->fctx = t.fctx;
  190. // start execution of toplevel context-function
  191. ar->run();
  192. }
  193. typedef boost::intrusive_ptr< detail::ecv1_activation_record > ptr_t;
  194. ptr_t ptr_;
  195. template< typename StackAlloc, typename Fn, typename ... Args >
  196. static detail::ecv1_activation_record * create_context( StackAlloc && salloc,
  197. Fn && fn, Args && ... args) {
  198. typedef detail::ecv1_capture_record<
  199. StackAlloc, Fn, Args ...
  200. > capture_t;
  201. auto sctx = salloc.allocate();
  202. // reserve space for control structure
  203. #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
  204. const std::size_t size = sctx.size - sizeof( capture_t);
  205. void * sp = static_cast< char * >( sctx.sp) - sizeof( capture_t);
  206. #else
  207. constexpr std::size_t func_alignment = 64; // alignof( capture_t);
  208. constexpr std::size_t func_size = sizeof( capture_t);
  209. // reserve space on stack
  210. void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
  211. // align sp pointer
  212. std::size_t space = func_size + func_alignment;
  213. sp = std::align( func_alignment, func_size, sp, space);
  214. BOOST_ASSERT( nullptr != sp);
  215. // calculate remaining size
  216. const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
  217. #endif
  218. // create fast-context
  219. const detail::fcontext_t fctx = detail::make_fcontext( sp, size, & execution_context::entry_func< capture_t >);
  220. BOOST_ASSERT( nullptr != fctx);
  221. // get current activation record
  222. auto curr = execution_context::current().ptr_;
  223. // placment new for control structure on fast-context stack
  224. return ::new ( sp) capture_t{
  225. sctx, std::forward< StackAlloc >( salloc), fctx, curr.get(), std::forward< Fn >( fn), std::forward< Args >( args) ... };
  226. }
  227. template< typename StackAlloc, typename Fn, typename ... Args >
  228. static detail::ecv1_activation_record * create_context( preallocated palloc, StackAlloc && salloc,
  229. Fn && fn, Args && ... args) {
  230. typedef detail::ecv1_capture_record<
  231. StackAlloc, Fn, Args ...
  232. > capture_t;
  233. // reserve space for control structure
  234. #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
  235. const std::size_t size = palloc.size - sizeof( capture_t);
  236. void * sp = static_cast< char * >( palloc.sp) - sizeof( capture_t);
  237. #else
  238. constexpr std::size_t func_alignment = 64; // alignof( capture_t);
  239. constexpr std::size_t func_size = sizeof( capture_t);
  240. // reserve space on stack
  241. void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
  242. // align sp pointer
  243. std::size_t space = func_size + func_alignment;
  244. sp = std::align( func_alignment, func_size, sp, space);
  245. BOOST_ASSERT( nullptr != sp);
  246. // calculate remaining size
  247. const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
  248. #endif
  249. // create fast-context
  250. const detail::fcontext_t fctx = detail::make_fcontext( sp, size, & execution_context::entry_func< capture_t >);
  251. BOOST_ASSERT( nullptr != fctx);
  252. // get current activation record
  253. auto curr = execution_context::current().ptr_;
  254. // placment new for control structure on fast-context stack
  255. return ::new ( sp) capture_t{
  256. palloc.sctx, std::forward< StackAlloc >( salloc), fctx, curr.get(), std::forward< Fn >( fn), std::forward< Args >( args) ... };
  257. }
  258. execution_context() noexcept :
  259. // default constructed with current ecv1_activation_record
  260. ptr_{ detail::ecv1_activation_record::current() } {
  261. }
  262. public:
  263. static execution_context current() noexcept;
  264. #if defined(BOOST_USE_SEGMENTED_STACKS)
  265. template< typename Fn,
  266. typename ... Args,
  267. typename = detail::disable_overload< execution_context, Fn >
  268. >
  269. execution_context( Fn && fn, Args && ... args) :
  270. // deferred execution of fn and its arguments
  271. // arguments are stored in std::tuple<>
  272. // non-type template parameter pack via std::index_sequence_for<>
  273. // preserves the number of arguments
  274. // used to extract the function arguments from std::tuple<>
  275. ptr_{ create_context( segmented_stack(),
  276. std::forward< Fn >( fn),
  277. std::forward< Args >( args) ...) } {
  278. ptr_->resume( ptr_.get() );
  279. }
  280. template< typename Fn,
  281. typename ... Args
  282. >
  283. execution_context( std::allocator_arg_t, segmented_stack salloc, Fn && fn, Args && ... args) :
  284. // deferred execution of fn and its arguments
  285. // arguments are stored in std::tuple<>
  286. // non-type template parameter pack via std::index_sequence_for<>
  287. // preserves the number of arguments
  288. // used to extract the function arguments from std::tuple<>
  289. ptr_{ create_context( salloc,
  290. std::forward< Fn >( fn),
  291. std::forward< Args >( args) ...) } {
  292. ptr_->resume( ptr_.get() );
  293. }
  294. template< typename Fn,
  295. typename ... Args
  296. >
  297. execution_context( std::allocator_arg_t, preallocated palloc, segmented_stack salloc, Fn && fn, Args && ... args) :
  298. // deferred execution of fn and its arguments
  299. // arguments are stored in std::tuple<>
  300. // non-type template parameter pack via std::index_sequence_for<>
  301. // preserves the number of arguments
  302. // used to extract the function arguments from std::tuple<>
  303. ptr_{ create_context( palloc, salloc,
  304. std::forward< Fn >( fn),
  305. std::forward< Args >( args) ...) } {
  306. ptr_->resume( ptr_.get() );
  307. }
  308. #else
  309. template< typename Fn,
  310. typename ... Args,
  311. typename = detail::disable_overload< execution_context, Fn >
  312. >
  313. execution_context( Fn && fn, Args && ... args) :
  314. // deferred execution of fn and its arguments
  315. // arguments are stored in std::tuple<>
  316. // non-type template parameter pack via std::index_sequence_for<>
  317. // preserves the number of arguments
  318. // used to extract the function arguments from std::tuple<>
  319. ptr_{ create_context( fixedsize_stack(),
  320. std::forward< Fn >( fn),
  321. std::forward< Args >( args) ...) } {
  322. ptr_->resume( ptr_.get() );
  323. }
  324. template< typename StackAlloc,
  325. typename Fn,
  326. typename ... Args
  327. >
  328. execution_context( std::allocator_arg_t, StackAlloc && salloc, Fn && fn, Args && ... args) :
  329. // deferred execution of fn and its arguments
  330. // arguments are stored in std::tuple<>
  331. // non-type template parameter pack via std::index_sequence_for<>
  332. // preserves the number of arguments
  333. // used to extract the function arguments from std::tuple<>
  334. ptr_{ create_context( std::forward< StackAlloc >( salloc),
  335. std::forward< Fn >( fn),
  336. std::forward< Args >( args) ...) } {
  337. ptr_->resume( ptr_.get() );
  338. }
  339. template< typename StackAlloc,
  340. typename Fn,
  341. typename ... Args
  342. >
  343. execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn, Args && ... args) :
  344. // deferred execution of fn and its arguments
  345. // arguments are stored in std::tuple<>
  346. // non-type template parameter pack via std::index_sequence_for<>
  347. // preserves the number of arguments
  348. // used to extract the function arguments from std::tuple<>
  349. ptr_{ create_context( palloc, std::forward< StackAlloc >( salloc),
  350. std::forward< Fn >( fn),
  351. std::forward< Args >( args) ...) } {
  352. ptr_->resume( ptr_.get() );
  353. }
  354. #endif
  355. execution_context( execution_context const& other) noexcept :
  356. ptr_{ other.ptr_ } {
  357. }
  358. execution_context( execution_context && other) noexcept :
  359. ptr_{ other.ptr_ } {
  360. other.ptr_.reset();
  361. }
  362. execution_context & operator=( execution_context const& other) noexcept {
  363. // intrusive_ptr<> does not test for self-assignment
  364. if ( this == & other) return * this;
  365. ptr_ = other.ptr_;
  366. return * this;
  367. }
  368. execution_context & operator=( execution_context && other) noexcept {
  369. if ( this == & other) return * this;
  370. execution_context tmp{ std::move( other) };
  371. swap( tmp);
  372. return * this;
  373. }
  374. void * operator()( void * vp = nullptr) {
  375. return ptr_->resume( vp);
  376. }
  377. template< typename Fn >
  378. void * operator()( exec_ontop_arg_t, Fn && fn, void * vp = nullptr) {
  379. return ptr_->resume_ontop( vp,
  380. std::forward< Fn >( fn) );
  381. }
  382. explicit operator bool() const noexcept {
  383. return nullptr != ptr_.get();
  384. }
  385. bool operator!() const noexcept {
  386. return nullptr == ptr_.get();
  387. }
  388. bool operator<( execution_context const& other) const noexcept {
  389. return ptr_ < other.ptr_;
  390. }
  391. template< typename charT, class traitsT >
  392. friend std::basic_ostream< charT, traitsT > &
  393. operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
  394. if ( nullptr != other.ptr_) {
  395. return os << other.ptr_;
  396. } else {
  397. return os << "{not-a-context}";
  398. }
  399. }
  400. void swap( execution_context & other) noexcept {
  401. ptr_.swap( other.ptr_);
  402. }
  403. };
  404. inline
  405. void swap( execution_context & l, execution_context & r) noexcept {
  406. l.swap( r);
  407. }
  408. }}}
  409. #ifdef BOOST_HAS_ABI_HEADERS
  410. # include BOOST_ABI_SUFFIX
  411. #endif
  412. #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_V1_H