context.hpp 17 KB

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