continuation_winfib.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. // Copyright Oliver Kowalke 2017.
  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_CONTINUATION_H
  6. #define BOOST_CONTEXT_CONTINUATION_H
  7. #include <windows.h>
  8. #include <boost/context/detail/config.hpp>
  9. #include <algorithm>
  10. #include <cstddef>
  11. #include <cstdint>
  12. #include <cstdlib>
  13. #include <cstring>
  14. #include <functional>
  15. #include <memory>
  16. #include <ostream>
  17. #include <system_error>
  18. #include <tuple>
  19. #include <utility>
  20. #include <boost/assert.hpp>
  21. #include <boost/config.hpp>
  22. #include <boost/context/detail/disable_overload.hpp>
  23. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  24. #include <boost/context/detail/exchange.hpp>
  25. #endif
  26. #if defined(BOOST_NO_CXX17_STD_INVOKE)
  27. #include <boost/context/detail/invoke.hpp>
  28. #endif
  29. #include <boost/context/fixedsize_stack.hpp>
  30. #include <boost/context/flags.hpp>
  31. #include <boost/context/preallocated.hpp>
  32. #include <boost/context/stack_context.hpp>
  33. #ifdef BOOST_HAS_ABI_HEADERS
  34. # include BOOST_ABI_PREFIX
  35. #endif
  36. #if defined(BOOST_MSVC)
  37. # pragma warning(push)
  38. # pragma warning(disable: 4702)
  39. #endif
  40. namespace boost {
  41. namespace context {
  42. namespace detail {
  43. // tampoline function
  44. // entered if the execution context
  45. // is resumed for the first time
  46. template< typename Record >
  47. static VOID WINAPI entry_func( LPVOID data) noexcept {
  48. Record * record = static_cast< Record * >( data);
  49. BOOST_ASSERT( nullptr != record);
  50. // start execution of toplevel context-function
  51. record->run();
  52. }
  53. struct BOOST_CONTEXT_DECL activation_record {
  54. LPVOID fiber{ nullptr };
  55. stack_context sctx{};
  56. bool main_ctx{ true };
  57. activation_record * from{ nullptr };
  58. std::function< activation_record*(activation_record*&) > ontop{};
  59. bool terminated{ false };
  60. bool force_unwind{ false };
  61. static activation_record *& current() noexcept;
  62. // used for toplevel-context
  63. // (e.g. main context, thread-entry context)
  64. activation_record() noexcept {
  65. #if ( _WIN32_WINNT > 0x0600)
  66. if ( ::IsThreadAFiber() ) {
  67. fiber = ::GetCurrentFiber();
  68. } else {
  69. fiber = ::ConvertThreadToFiber( nullptr);
  70. }
  71. #else
  72. fiber = ::ConvertThreadToFiber( nullptr);
  73. if ( BOOST_UNLIKELY( nullptr == fiber) ) {
  74. DWORD err = ::GetLastError();
  75. BOOST_ASSERT( ERROR_ALREADY_FIBER == err);
  76. fiber = ::GetCurrentFiber();
  77. BOOST_ASSERT( nullptr != fiber);
  78. BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != fiber);
  79. }
  80. #endif
  81. }
  82. activation_record( stack_context sctx_) noexcept :
  83. sctx{ sctx_ },
  84. main_ctx{ false } {
  85. }
  86. virtual ~activation_record() {
  87. if ( BOOST_UNLIKELY( main_ctx) ) {
  88. ::ConvertFiberToThread();
  89. } else {
  90. ::DeleteFiber( fiber);
  91. }
  92. }
  93. activation_record( activation_record const&) = delete;
  94. activation_record & operator=( activation_record const&) = delete;
  95. bool is_main_context() const noexcept {
  96. return main_ctx;
  97. }
  98. activation_record * resume() {
  99. from = current();
  100. // store `this` in static, thread local pointer
  101. // `this` will become the active (running) context
  102. current() = this;
  103. // context switch from parent context to `this`-context
  104. // context switch
  105. ::SwitchToFiber( fiber);
  106. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  107. return detail::exchange( current()->from, nullptr);
  108. #else
  109. return std::exchange( current()->from, nullptr);
  110. #endif
  111. }
  112. template< typename Ctx, typename Fn >
  113. activation_record * resume_with( Fn && fn) {
  114. from = current();
  115. // store `this` in static, thread local pointer
  116. // `this` will become the active (running) context
  117. // returned by continuation::current()
  118. current() = this;
  119. #if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
  120. current()->ontop = std::bind(
  121. [](typename std::decay< Fn >::type & fn, activation_record *& ptr){
  122. Ctx c{ ptr };
  123. c = fn( std::move( c) );
  124. if ( ! c) {
  125. ptr = nullptr;
  126. }
  127. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  128. return exchange( c.ptr_, nullptr);
  129. #else
  130. return std::exchange( c.ptr_, nullptr);
  131. #endif
  132. },
  133. std::forward< Fn >( fn),
  134. std::placeholders::_1);
  135. #else
  136. current()->ontop = [fn=std::forward<Fn>(fn)](activation_record *& ptr){
  137. Ctx c{ ptr };
  138. c = fn( std::move( c) );
  139. if ( ! c) {
  140. ptr = nullptr;
  141. }
  142. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  143. return exchange( c.ptr_, nullptr);
  144. #else
  145. return std::exchange( c.ptr_, nullptr);
  146. #endif
  147. };
  148. #endif
  149. // context switch
  150. ::SwitchToFiber( fiber);
  151. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  152. return detail::exchange( current()->from, nullptr);
  153. #else
  154. return std::exchange( current()->from, nullptr);
  155. #endif
  156. }
  157. virtual void deallocate() noexcept {
  158. }
  159. };
  160. struct BOOST_CONTEXT_DECL activation_record_initializer {
  161. activation_record_initializer() noexcept;
  162. ~activation_record_initializer();
  163. };
  164. struct forced_unwind {
  165. activation_record * from{ nullptr };
  166. explicit forced_unwind( activation_record * from_) :
  167. from{ from_ } {
  168. }
  169. };
  170. template< typename Ctx, typename StackAlloc, typename Fn >
  171. class capture_record : public activation_record {
  172. private:
  173. typename std::decay< StackAlloc >::type salloc_;
  174. typename std::decay< Fn >::type fn_;
  175. static void destroy( capture_record * p) noexcept {
  176. typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
  177. stack_context sctx = p->sctx;
  178. // deallocate activation record
  179. p->~capture_record();
  180. // destroy stack with stack allocator
  181. salloc.deallocate( sctx);
  182. }
  183. public:
  184. capture_record( stack_context sctx, StackAlloc && salloc, Fn && fn) noexcept :
  185. activation_record( sctx),
  186. salloc_( std::forward< StackAlloc >( salloc)),
  187. fn_( std::forward< Fn >( fn) ) {
  188. }
  189. void deallocate() noexcept override final {
  190. BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
  191. destroy( this);
  192. }
  193. void run() {
  194. Ctx c{ from };
  195. try {
  196. // invoke context-function
  197. #if defined(BOOST_NO_CXX17_STD_INVOKE)
  198. c = boost::context::detail::invoke( fn_, std::move( c) );
  199. #else
  200. c = std::invoke( fn_, std::move( c) );
  201. #endif
  202. } catch ( forced_unwind const& ex) {
  203. c = Ctx{ ex.from };
  204. }
  205. // this context has finished its task
  206. from = nullptr;
  207. ontop = nullptr;
  208. terminated = true;
  209. force_unwind = false;
  210. c.resume();
  211. BOOST_ASSERT_MSG( false, "continuation already terminated");
  212. }
  213. };
  214. template< typename Ctx, typename StackAlloc, typename Fn >
  215. static activation_record * create_context1( StackAlloc && salloc, Fn && fn) {
  216. typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
  217. auto sctx = salloc.allocate();
  218. BOOST_ASSERT( ( sizeof( capture_t) ) < sctx.size);
  219. // reserve space for control structure
  220. void * storage = reinterpret_cast< void * >(
  221. ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
  222. & ~ static_cast< uintptr_t >( 0xff) );
  223. // placment new for control structure on context stack
  224. capture_t * record = new ( storage) capture_t{
  225. sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
  226. // create user-context
  227. record->fiber = ::CreateFiber( sctx.size, & detail::entry_func< capture_t >, record);
  228. return record;
  229. }
  230. template< typename Ctx, typename StackAlloc, typename Fn >
  231. static activation_record * create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
  232. typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
  233. BOOST_ASSERT( ( sizeof( capture_t) ) < palloc.size);
  234. // reserve space for control structure
  235. void * storage = reinterpret_cast< void * >(
  236. ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
  237. & ~ static_cast< uintptr_t >( 0xff) );
  238. // placment new for control structure on context stack
  239. capture_t * record = new ( storage) capture_t{
  240. palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
  241. // create user-context
  242. record->fiber = ::CreateFiber( palloc.sctx.size, & detail::entry_func< capture_t >, record);
  243. return record;
  244. }
  245. }
  246. class BOOST_CONTEXT_DECL continuation {
  247. private:
  248. friend struct detail::activation_record;
  249. template< typename Ctx, typename StackAlloc, typename Fn >
  250. friend class detail::capture_record;
  251. template< typename Ctx, typename StackAlloc, typename Fn >
  252. friend detail::activation_record * detail::create_context1( StackAlloc &&, Fn &&);
  253. template< typename Ctx, typename StackAlloc, typename Fn >
  254. friend detail::activation_record * detail::create_context2( preallocated, StackAlloc &&, Fn &&);
  255. template< typename StackAlloc, typename Fn >
  256. friend continuation
  257. callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);
  258. template< typename StackAlloc, typename Fn >
  259. friend continuation
  260. callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);
  261. detail::activation_record * ptr_{ nullptr };
  262. continuation( detail::activation_record * ptr) noexcept :
  263. ptr_{ ptr } {
  264. }
  265. public:
  266. continuation() = default;
  267. ~continuation() {
  268. if ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) {
  269. if ( BOOST_LIKELY( ! ptr_->terminated) ) {
  270. ptr_->force_unwind = true;
  271. ptr_->resume();
  272. BOOST_ASSERT( ptr_->terminated);
  273. }
  274. ptr_->deallocate();
  275. }
  276. }
  277. continuation( continuation const&) = delete;
  278. continuation & operator=( continuation const&) = delete;
  279. continuation( continuation && other) noexcept {
  280. swap( other);
  281. }
  282. continuation & operator=( continuation && other) noexcept {
  283. if ( BOOST_LIKELY( this != & other) ) {
  284. continuation tmp = std::move( other);
  285. swap( tmp);
  286. }
  287. return * this;
  288. }
  289. continuation resume() & {
  290. return std::move( * this).resume();
  291. }
  292. continuation resume() && {
  293. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  294. detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume();
  295. #else
  296. detail::activation_record * ptr = std::exchange( ptr_, nullptr)->resume();
  297. #endif
  298. if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
  299. throw detail::forced_unwind{ ptr};
  300. } else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
  301. ptr = detail::activation_record::current()->ontop( ptr);
  302. detail::activation_record::current()->ontop = nullptr;
  303. }
  304. return { ptr };
  305. }
  306. template< typename Fn >
  307. continuation resume_with( Fn && fn) & {
  308. return std::move( * this).resume_with( std::forward< Fn >( fn) );
  309. }
  310. template< typename Fn >
  311. continuation resume_with( Fn && fn) && {
  312. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  313. detail::activation_record * ptr =
  314. detail::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
  315. #else
  316. detail::activation_record * ptr =
  317. std::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
  318. #endif
  319. if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
  320. throw detail::forced_unwind{ ptr};
  321. } else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
  322. ptr = detail::activation_record::current()->ontop( ptr);
  323. detail::activation_record::current()->ontop = nullptr;
  324. }
  325. return { ptr };
  326. }
  327. explicit operator bool() const noexcept {
  328. return nullptr != ptr_ && ! ptr_->terminated;
  329. }
  330. bool operator!() const noexcept {
  331. return nullptr == ptr_ || ptr_->terminated;
  332. }
  333. bool operator<( continuation const& other) const noexcept {
  334. return ptr_ < other.ptr_;
  335. }
  336. #if !defined(BOOST_EMBTC)
  337. template< typename charT, class traitsT >
  338. friend std::basic_ostream< charT, traitsT > &
  339. operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
  340. if ( nullptr != other.ptr_) {
  341. return os << other.ptr_;
  342. } else {
  343. return os << "{not-a-context}";
  344. }
  345. }
  346. #else
  347. template< typename charT, class traitsT >
  348. friend std::basic_ostream< charT, traitsT > &
  349. operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other);
  350. #endif
  351. void swap( continuation & other) noexcept {
  352. std::swap( ptr_, other.ptr_);
  353. }
  354. };
  355. #if defined(BOOST_EMBTC)
  356. template< typename charT, class traitsT >
  357. inline std::basic_ostream< charT, traitsT > &
  358. operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
  359. if ( nullptr != other.ptr_) {
  360. return os << other.ptr_;
  361. } else {
  362. return os << "{not-a-context}";
  363. }
  364. }
  365. #endif
  366. template<
  367. typename Fn,
  368. typename = detail::disable_overload< continuation, Fn >
  369. >
  370. continuation
  371. callcc( Fn && fn) {
  372. return callcc(
  373. std::allocator_arg,
  374. fixedsize_stack(),
  375. std::forward< Fn >( fn) );
  376. }
  377. template< typename StackAlloc, typename Fn >
  378. continuation
  379. callcc( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) {
  380. return continuation{
  381. detail::create_context1< continuation >(
  382. std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
  383. }
  384. template< typename StackAlloc, typename Fn >
  385. continuation
  386. callcc( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) {
  387. return continuation{
  388. detail::create_context2< continuation >(
  389. palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
  390. }
  391. inline
  392. void swap( continuation & l, continuation & r) noexcept {
  393. l.swap( r);
  394. }
  395. }}
  396. #if defined(BOOST_MSVC)
  397. # pragma warning(pop)
  398. #endif
  399. #ifdef BOOST_HAS_ABI_HEADERS
  400. # include BOOST_ABI_SUFFIX
  401. #endif
  402. #endif // BOOST_CONTEXT_CONTINUATION_H