continuation_winfib.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. #ifndef BOOST_ASSERT_IS_VOID
  167. bool caught{ false };
  168. #endif
  169. explicit forced_unwind( activation_record * from_) :
  170. from{ from_ } {
  171. }
  172. #ifndef BOOST_ASSERT_IS_VOID
  173. ~forced_unwind() {
  174. BOOST_ASSERT( caught);
  175. }
  176. #endif
  177. };
  178. template< typename Ctx, typename StackAlloc, typename Fn >
  179. class capture_record : public activation_record {
  180. private:
  181. typename std::decay< StackAlloc >::type salloc_;
  182. typename std::decay< Fn >::type fn_;
  183. static void destroy( capture_record * p) noexcept {
  184. typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
  185. stack_context sctx = p->sctx;
  186. // deallocate activation record
  187. p->~capture_record();
  188. // destroy stack with stack allocator
  189. salloc.deallocate( sctx);
  190. }
  191. public:
  192. capture_record( stack_context sctx, StackAlloc && salloc, Fn && fn) noexcept :
  193. activation_record( sctx),
  194. salloc_( std::forward< StackAlloc >( salloc)),
  195. fn_( std::forward< Fn >( fn) ) {
  196. }
  197. void deallocate() noexcept override final {
  198. BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
  199. destroy( this);
  200. }
  201. void run() {
  202. Ctx c{ from };
  203. try {
  204. // invoke context-function
  205. #if defined(BOOST_NO_CXX17_STD_INVOKE)
  206. c = boost::context::detail::invoke( fn_, std::move( c) );
  207. #else
  208. c = std::invoke( fn_, std::move( c) );
  209. #endif
  210. } catch ( forced_unwind const& ex) {
  211. c = Ctx{ ex.from };
  212. #ifndef BOOST_ASSERT_IS_VOID
  213. const_cast< forced_unwind & >( ex).caught = true;
  214. #endif
  215. }
  216. // this context has finished its task
  217. from = nullptr;
  218. ontop = nullptr;
  219. terminated = true;
  220. force_unwind = false;
  221. c.resume();
  222. BOOST_ASSERT_MSG( false, "continuation already terminated");
  223. }
  224. };
  225. template< typename Ctx, typename StackAlloc, typename Fn >
  226. static activation_record * create_context1( StackAlloc && salloc, Fn && fn) {
  227. typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
  228. auto sctx = salloc.allocate();
  229. BOOST_ASSERT( ( sizeof( capture_t) ) < sctx.size);
  230. // reserve space for control structure
  231. void * storage = reinterpret_cast< void * >(
  232. ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
  233. & ~ static_cast< uintptr_t >( 0xff) );
  234. // placment new for control structure on context stack
  235. capture_t * record = new ( storage) capture_t{
  236. sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
  237. // create user-context
  238. record->fiber = ::CreateFiber( sctx.size, & detail::entry_func< capture_t >, record);
  239. return record;
  240. }
  241. template< typename Ctx, typename StackAlloc, typename Fn >
  242. static activation_record * create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
  243. typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
  244. BOOST_ASSERT( ( sizeof( capture_t) ) < palloc.size);
  245. // reserve space for control structure
  246. void * storage = reinterpret_cast< void * >(
  247. ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
  248. & ~ static_cast< uintptr_t >( 0xff) );
  249. // placment new for control structure on context stack
  250. capture_t * record = new ( storage) capture_t{
  251. palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
  252. // create user-context
  253. record->fiber = ::CreateFiber( palloc.sctx.size, & detail::entry_func< capture_t >, record);
  254. return record;
  255. }
  256. }
  257. class BOOST_CONTEXT_DECL continuation {
  258. private:
  259. friend struct detail::activation_record;
  260. template< typename Ctx, typename StackAlloc, typename Fn >
  261. friend class detail::capture_record;
  262. template< typename Ctx, typename StackAlloc, typename Fn >
  263. friend detail::activation_record * detail::create_context1( StackAlloc &&, Fn &&);
  264. template< typename Ctx, typename StackAlloc, typename Fn >
  265. friend detail::activation_record * detail::create_context2( preallocated, StackAlloc &&, Fn &&);
  266. template< typename StackAlloc, typename Fn >
  267. friend continuation
  268. callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);
  269. template< typename StackAlloc, typename Fn >
  270. friend continuation
  271. callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);
  272. detail::activation_record * ptr_{ nullptr };
  273. continuation( detail::activation_record * ptr) noexcept :
  274. ptr_{ ptr } {
  275. }
  276. public:
  277. continuation() = default;
  278. ~continuation() {
  279. if ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) {
  280. if ( BOOST_LIKELY( ! ptr_->terminated) ) {
  281. ptr_->force_unwind = true;
  282. ptr_->resume();
  283. BOOST_ASSERT( ptr_->terminated);
  284. }
  285. ptr_->deallocate();
  286. }
  287. }
  288. continuation( continuation const&) = delete;
  289. continuation & operator=( continuation const&) = delete;
  290. continuation( continuation && other) noexcept {
  291. swap( other);
  292. }
  293. continuation & operator=( continuation && other) noexcept {
  294. if ( BOOST_LIKELY( this != & other) ) {
  295. continuation tmp = std::move( other);
  296. swap( tmp);
  297. }
  298. return * this;
  299. }
  300. continuation resume() & {
  301. return std::move( * this).resume();
  302. }
  303. continuation resume() && {
  304. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  305. detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume();
  306. #else
  307. detail::activation_record * ptr = std::exchange( ptr_, nullptr)->resume();
  308. #endif
  309. if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
  310. throw detail::forced_unwind{ ptr};
  311. } else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
  312. ptr = detail::activation_record::current()->ontop( ptr);
  313. detail::activation_record::current()->ontop = nullptr;
  314. }
  315. return { ptr };
  316. }
  317. template< typename Fn >
  318. continuation resume_with( Fn && fn) & {
  319. return std::move( * this).resume_with( std::forward< Fn >( fn) );
  320. }
  321. template< typename Fn >
  322. continuation resume_with( Fn && fn) && {
  323. #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
  324. detail::activation_record * ptr =
  325. detail::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
  326. #else
  327. detail::activation_record * ptr =
  328. std::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
  329. #endif
  330. if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
  331. throw detail::forced_unwind{ ptr};
  332. } else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
  333. ptr = detail::activation_record::current()->ontop( ptr);
  334. detail::activation_record::current()->ontop = nullptr;
  335. }
  336. return { ptr };
  337. }
  338. explicit operator bool() const noexcept {
  339. return nullptr != ptr_ && ! ptr_->terminated;
  340. }
  341. bool operator!() const noexcept {
  342. return nullptr == ptr_ || ptr_->terminated;
  343. }
  344. bool operator<( continuation const& other) const noexcept {
  345. return ptr_ < other.ptr_;
  346. }
  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. if ( nullptr != other.ptr_) {
  351. return os << other.ptr_;
  352. } else {
  353. return os << "{not-a-context}";
  354. }
  355. }
  356. void swap( continuation & other) noexcept {
  357. std::swap( ptr_, other.ptr_);
  358. }
  359. };
  360. template<
  361. typename Fn,
  362. typename = detail::disable_overload< continuation, Fn >
  363. >
  364. continuation
  365. callcc( Fn && fn) {
  366. return callcc(
  367. std::allocator_arg,
  368. fixedsize_stack(),
  369. std::forward< Fn >( fn) );
  370. }
  371. template< typename StackAlloc, typename Fn >
  372. continuation
  373. callcc( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) {
  374. return continuation{
  375. detail::create_context1< continuation >(
  376. std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
  377. }
  378. template< typename StackAlloc, typename Fn >
  379. continuation
  380. callcc( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) {
  381. return continuation{
  382. detail::create_context2< continuation >(
  383. palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
  384. }
  385. inline
  386. void swap( continuation & l, continuation & r) noexcept {
  387. l.swap( r);
  388. }
  389. }}
  390. #if defined(BOOST_MSVC)
  391. # pragma warning(pop)
  392. #endif
  393. #ifdef BOOST_HAS_ABI_HEADERS
  394. # include BOOST_ABI_SUFFIX
  395. #endif
  396. #endif // BOOST_CONTEXT_CONTINUATION_H