context.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. // Copyright Oliver Kowalke 2013.
  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_CONTEXT_H
  6. #define BOOST_FIBERS_CONTEXT_H
  7. #include <atomic>
  8. #include <chrono>
  9. #include <cstdint>
  10. #include <exception>
  11. #include <functional>
  12. #include <iostream>
  13. #include <map>
  14. #include <memory>
  15. #include <tuple>
  16. #include <type_traits>
  17. #include <boost/assert.hpp>
  18. #include <boost/config.hpp>
  19. #include <boost/core/ignore_unused.hpp>
  20. #if defined(BOOST_NO_CXX17_STD_APPLY)
  21. #include <boost/context/detail/apply.hpp>
  22. #endif
  23. #include <boost/context/fiber.hpp>
  24. #include <boost/context/stack_context.hpp>
  25. #include <boost/intrusive/list.hpp>
  26. #include <boost/intrusive/parent_from_member.hpp>
  27. #include <boost/intrusive_ptr.hpp>
  28. #include <boost/intrusive/set.hpp>
  29. #include <boost/intrusive/slist.hpp>
  30. #include <boost/fiber/detail/config.hpp>
  31. #include <boost/fiber/detail/data.hpp>
  32. #include <boost/fiber/detail/decay_copy.hpp>
  33. #include <boost/fiber/detail/fss.hpp>
  34. #include <boost/fiber/detail/spinlock.hpp>
  35. #include <boost/fiber/exceptions.hpp>
  36. #include <boost/fiber/fixedsize_stack.hpp>
  37. #include <boost/fiber/policy.hpp>
  38. #include <boost/fiber/properties.hpp>
  39. #include <boost/fiber/segmented_stack.hpp>
  40. #include <boost/fiber/type.hpp>
  41. #ifdef BOOST_HAS_ABI_HEADERS
  42. # include BOOST_ABI_PREFIX
  43. #endif
  44. #ifdef _MSC_VER
  45. # pragma warning(push)
  46. # pragma warning(disable:4251)
  47. #endif
  48. namespace boost {
  49. namespace fibers {
  50. class context;
  51. class fiber;
  52. class scheduler;
  53. namespace detail {
  54. struct wait_tag;
  55. typedef intrusive::list_member_hook<
  56. intrusive::tag< wait_tag >,
  57. intrusive::link_mode<
  58. intrusive::auto_unlink
  59. >
  60. > wait_hook;
  61. // declaration of the functor that converts between
  62. // the context class and the wait-hook
  63. struct wait_functor {
  64. // required types
  65. typedef wait_hook hook_type;
  66. typedef hook_type * hook_ptr;
  67. typedef const hook_type * const_hook_ptr;
  68. typedef context value_type;
  69. typedef value_type * pointer;
  70. typedef const value_type * const_pointer;
  71. // required static functions
  72. static hook_ptr to_hook_ptr( value_type &value);
  73. static const_hook_ptr to_hook_ptr( value_type const& value);
  74. static pointer to_value_ptr( hook_ptr n);
  75. static const_pointer to_value_ptr( const_hook_ptr n);
  76. };
  77. struct ready_tag;
  78. typedef intrusive::list_member_hook<
  79. intrusive::tag< ready_tag >,
  80. intrusive::link_mode<
  81. intrusive::auto_unlink
  82. >
  83. > ready_hook;
  84. struct sleep_tag;
  85. typedef intrusive::set_member_hook<
  86. intrusive::tag< sleep_tag >,
  87. intrusive::link_mode<
  88. intrusive::auto_unlink
  89. >
  90. > sleep_hook;
  91. struct worker_tag;
  92. typedef intrusive::list_member_hook<
  93. intrusive::tag< worker_tag >,
  94. intrusive::link_mode<
  95. intrusive::auto_unlink
  96. >
  97. > worker_hook;
  98. struct terminated_tag;
  99. typedef intrusive::slist_member_hook<
  100. intrusive::tag< terminated_tag >,
  101. intrusive::link_mode<
  102. intrusive::safe_link
  103. >
  104. > terminated_hook;
  105. struct remote_ready_tag;
  106. typedef intrusive::slist_member_hook<
  107. intrusive::tag< remote_ready_tag >,
  108. intrusive::link_mode<
  109. intrusive::safe_link
  110. >
  111. > remote_ready_hook;
  112. }
  113. class BOOST_FIBERS_DECL context {
  114. public:
  115. typedef intrusive::list<
  116. context,
  117. intrusive::function_hook< detail::wait_functor >,
  118. intrusive::constant_time_size< false >
  119. > wait_queue_t;
  120. private:
  121. friend class dispatcher_context;
  122. friend class main_context;
  123. template< typename Fn, typename ... Arg > friend class worker_context;
  124. friend class scheduler;
  125. struct fss_data {
  126. void * vp{ nullptr };
  127. detail::fss_cleanup_function::ptr_t cleanup_function{};
  128. fss_data() noexcept {
  129. }
  130. fss_data( void * vp_,
  131. detail::fss_cleanup_function::ptr_t const& fn) noexcept :
  132. vp( vp_),
  133. cleanup_function( fn) {
  134. BOOST_ASSERT( cleanup_function);
  135. }
  136. void do_cleanup() {
  137. ( * cleanup_function)( vp);
  138. }
  139. };
  140. typedef std::map< uintptr_t, fss_data > fss_data_t;
  141. #if ! defined(BOOST_FIBERS_NO_ATOMICS)
  142. std::atomic< std::size_t > use_count_;
  143. #else
  144. std::size_t use_count_;
  145. #endif
  146. #if ! defined(BOOST_FIBERS_NO_ATOMICS)
  147. detail::remote_ready_hook remote_ready_hook_{};
  148. #endif
  149. detail::spinlock splk_{};
  150. bool terminated_{ false };
  151. wait_queue_t wait_queue_{};
  152. public:
  153. detail::wait_hook wait_hook_{};
  154. #if ! defined(BOOST_FIBERS_NO_ATOMICS)
  155. std::atomic< std::intptr_t > twstatus{ 0 };
  156. #endif
  157. private:
  158. scheduler * scheduler_{ nullptr };
  159. fss_data_t fss_data_{};
  160. detail::sleep_hook sleep_hook_{};
  161. detail::ready_hook ready_hook_{};
  162. detail::terminated_hook terminated_hook_{};
  163. detail::worker_hook worker_hook_{};
  164. fiber_properties * properties_{ nullptr };
  165. boost::context::fiber c_{};
  166. std::chrono::steady_clock::time_point tp_;
  167. type type_;
  168. launch policy_;
  169. context( std::size_t initial_count, type t, launch policy) noexcept :
  170. use_count_{ initial_count },
  171. tp_{ (std::chrono::steady_clock::time_point::max)() },
  172. type_{ t },
  173. policy_{ policy } {
  174. }
  175. public:
  176. class id {
  177. private:
  178. context * impl_{ nullptr };
  179. public:
  180. id() = default;
  181. explicit id( context * impl) noexcept :
  182. impl_{ impl } {
  183. }
  184. bool operator==( id const& other) const noexcept {
  185. return impl_ == other.impl_;
  186. }
  187. bool operator!=( id const& other) const noexcept {
  188. return impl_ != other.impl_;
  189. }
  190. bool operator<( id const& other) const noexcept {
  191. return impl_ < other.impl_;
  192. }
  193. bool operator>( id const& other) const noexcept {
  194. return other.impl_ < impl_;
  195. }
  196. bool operator<=( id const& other) const noexcept {
  197. return ! ( * this > other);
  198. }
  199. bool operator>=( id const& other) const noexcept {
  200. return ! ( * this < other);
  201. }
  202. template< typename charT, class traitsT >
  203. friend std::basic_ostream< charT, traitsT > &
  204. operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) {
  205. if ( nullptr != other.impl_) {
  206. return os << other.impl_;
  207. } else {
  208. return os << "{not-valid}";
  209. }
  210. }
  211. explicit operator bool() const noexcept {
  212. return nullptr != impl_;
  213. }
  214. bool operator!() const noexcept {
  215. return nullptr == impl_;
  216. }
  217. };
  218. static context * active() noexcept;
  219. static void reset_active() noexcept;
  220. context( context const&) = delete;
  221. context( context &&) = delete;
  222. context & operator=( context const&) = delete;
  223. context & operator=( context &&) = delete;
  224. friend bool
  225. operator==( context const& lhs, context const& rhs) noexcept {
  226. return & lhs == & rhs;
  227. }
  228. virtual ~context();
  229. scheduler * get_scheduler() const noexcept {
  230. return scheduler_;
  231. }
  232. id get_id() const noexcept;
  233. bool is_resumable() const noexcept {
  234. if ( c_) return true;
  235. else return false;
  236. }
  237. void resume() noexcept;
  238. void resume( detail::spinlock_lock &) noexcept;
  239. void resume( context *) noexcept;
  240. void suspend() noexcept;
  241. void suspend( detail::spinlock_lock &) noexcept;
  242. boost::context::fiber suspend_with_cc() noexcept;
  243. boost::context::fiber terminate() noexcept;
  244. void join();
  245. void yield() noexcept;
  246. bool wait_until( std::chrono::steady_clock::time_point const&) noexcept;
  247. bool wait_until( std::chrono::steady_clock::time_point const&,
  248. detail::spinlock_lock &) noexcept;
  249. void schedule( context *) noexcept;
  250. bool is_context( type t) const noexcept {
  251. return type::none != ( type_ & t);
  252. }
  253. void * get_fss_data( void const * vp) const;
  254. void set_fss_data(
  255. void const * vp,
  256. detail::fss_cleanup_function::ptr_t const& cleanup_fn,
  257. void * data,
  258. bool cleanup_existing);
  259. void set_properties( fiber_properties * props) noexcept;
  260. fiber_properties * get_properties() const noexcept {
  261. return properties_;
  262. }
  263. launch get_policy() const noexcept {
  264. return policy_;
  265. }
  266. bool worker_is_linked() const noexcept;
  267. bool ready_is_linked() const noexcept;
  268. bool remote_ready_is_linked() const noexcept;
  269. bool sleep_is_linked() const noexcept;
  270. bool terminated_is_linked() const noexcept;
  271. bool wait_is_linked() const noexcept;
  272. template< typename List >
  273. void worker_link( List & lst) noexcept {
  274. static_assert( std::is_same< typename List::value_traits::hook_type, detail::worker_hook >::value, "not a worker-queue");
  275. BOOST_ASSERT( ! worker_is_linked() );
  276. lst.push_back( * this);
  277. }
  278. template< typename List >
  279. void ready_link( List & lst) noexcept {
  280. static_assert( std::is_same< typename List::value_traits::hook_type, detail::ready_hook >::value, "not a ready-queue");
  281. BOOST_ASSERT( ! ready_is_linked() );
  282. lst.push_back( * this);
  283. }
  284. template< typename List >
  285. void remote_ready_link( List & lst) noexcept {
  286. static_assert( std::is_same< typename List::value_traits::hook_type, detail::remote_ready_hook >::value, "not a remote-ready-queue");
  287. BOOST_ASSERT( ! remote_ready_is_linked() );
  288. lst.push_back( * this);
  289. }
  290. template< typename Set >
  291. void sleep_link( Set & set) noexcept {
  292. static_assert( std::is_same< typename Set::value_traits::hook_type,detail::sleep_hook >::value, "not a sleep-queue");
  293. BOOST_ASSERT( ! sleep_is_linked() );
  294. set.insert( * this);
  295. }
  296. template< typename List >
  297. void terminated_link( List & lst) noexcept {
  298. static_assert( std::is_same< typename List::value_traits::hook_type, detail::terminated_hook >::value, "not a terminated-queue");
  299. BOOST_ASSERT( ! terminated_is_linked() );
  300. lst.push_back( * this);
  301. }
  302. template< typename List >
  303. void wait_link( List & lst) noexcept {
  304. static_assert( std::is_same< typename List::value_traits::hook_type, detail::wait_hook >::value, "not a wait-queue");
  305. BOOST_ASSERT( ! wait_is_linked() );
  306. lst.push_back( * this);
  307. }
  308. void worker_unlink() noexcept;
  309. void ready_unlink() noexcept;
  310. void sleep_unlink() noexcept;
  311. void wait_unlink() noexcept;
  312. void detach() noexcept;
  313. void attach( context *) noexcept;
  314. friend void intrusive_ptr_add_ref( context * ctx) noexcept {
  315. BOOST_ASSERT( nullptr != ctx);
  316. ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
  317. }
  318. friend void intrusive_ptr_release( context * ctx) noexcept {
  319. BOOST_ASSERT( nullptr != ctx);
  320. if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
  321. std::atomic_thread_fence( std::memory_order_acquire);
  322. boost::context::fiber c = std::move( ctx->c_);
  323. // destruct context
  324. ctx->~context();
  325. // deallocated stack
  326. std::move( c).resume();
  327. }
  328. }
  329. };
  330. inline
  331. bool operator<( context const& l, context const& r) noexcept {
  332. return l.get_id() < r.get_id();
  333. }
  334. template< typename Fn, typename ... Arg >
  335. class worker_context final : public context {
  336. private:
  337. typename std::decay< Fn >::type fn_;
  338. std::tuple< Arg ... > arg_;
  339. boost::context::fiber
  340. run_( boost::context::fiber && c) {
  341. {
  342. // fn and tpl must be destroyed before calling terminate()
  343. auto fn = std::move( fn_);
  344. auto arg = std::move( arg_);
  345. #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
  346. std::move( c).resume();
  347. #else
  348. boost::ignore_unused(c);
  349. #endif
  350. #if defined(BOOST_NO_CXX17_STD_APPLY)
  351. boost::context::detail::apply( std::move( fn), std::move( arg) );
  352. #else
  353. std::apply( std::move( fn), std::move( arg) );
  354. #endif
  355. }
  356. // terminate context
  357. return terminate();
  358. }
  359. public:
  360. template< typename StackAlloc >
  361. worker_context( launch policy,
  362. boost::context::preallocated const& palloc, StackAlloc && salloc,
  363. Fn && fn, Arg ... arg) :
  364. context{ 1, type::worker_context, policy },
  365. fn_( std::forward< Fn >( fn) ),
  366. arg_( std::forward< Arg >( arg) ... ) {
  367. c_ = boost::context::fiber{ std::allocator_arg, palloc, std::forward< StackAlloc >( salloc),
  368. std::bind( & worker_context::run_, this, std::placeholders::_1) };
  369. #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
  370. c_ = std::move( c_).resume();
  371. #endif
  372. }
  373. };
  374. template< typename StackAlloc, typename Fn, typename ... Arg >
  375. static intrusive_ptr< context > make_worker_context( launch policy,
  376. StackAlloc && salloc,
  377. Fn && fn, Arg ... arg) {
  378. typedef worker_context< Fn, Arg ... > context_t;
  379. auto sctx = salloc.allocate();
  380. // reserve space for control structure
  381. void * storage = reinterpret_cast< void * >(
  382. ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( context_t) ) )
  383. & ~ static_cast< uintptr_t >( 0xff) );
  384. void * stack_bottom = reinterpret_cast< void * >(
  385. reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
  386. const std::size_t size = reinterpret_cast< uintptr_t >( storage) - reinterpret_cast< uintptr_t >( stack_bottom);
  387. // placement new of context on top of fiber's stack
  388. return intrusive_ptr< context >{
  389. new ( storage) context_t{
  390. policy,
  391. boost::context::preallocated{ storage, size, sctx },
  392. std::forward< StackAlloc >( salloc),
  393. std::forward< Fn >( fn),
  394. std::forward< Arg >( arg) ... } };
  395. }
  396. namespace detail {
  397. inline
  398. wait_functor::hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type & value) {
  399. return & value.wait_hook_;
  400. }
  401. inline
  402. wait_functor::const_hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type const& value) {
  403. return & value.wait_hook_;
  404. }
  405. inline
  406. wait_functor::pointer wait_functor::to_value_ptr( wait_functor::hook_ptr n) {
  407. return intrusive::get_parent_from_member< context >( n, & context::wait_hook_);
  408. }
  409. inline
  410. wait_functor::const_pointer wait_functor::to_value_ptr( wait_functor::const_hook_ptr n) {
  411. return intrusive::get_parent_from_member< context >( n, & context::wait_hook_);
  412. }
  413. }}}
  414. #ifdef _MSC_VER
  415. # pragma warning(pop)
  416. #endif
  417. #ifdef BOOST_HAS_ABI_HEADERS
  418. # include BOOST_ABI_SUFFIX
  419. #endif
  420. #endif // BOOST_FIBERS_CONTEXT_H