Imported Upstream version 1.64.0
[platform/upstream/boost.git] / boost / fiber / context.hpp
index d873343..773528e 100644 (file)
@@ -7,23 +7,32 @@
 #ifndef BOOST_FIBERS_CONTEXT_H
 #define BOOST_FIBERS_CONTEXT_H
 
+#include <iostream>
 #include <atomic>
 #include <chrono>
 #include <exception>
 #include <functional>
 #include <map>
 #include <memory>
+#include <tuple>
 #include <type_traits>
 
 #include <boost/assert.hpp>
 #include <boost/config.hpp>
+#if defined(BOOST_NO_CXX17_STD_APPLY)
 #include <boost/context/detail/apply.hpp>
-#include <boost/context/execution_context.hpp>
+#endif
+#if (BOOST_EXECUTION_CONTEXT==1)
+# include <boost/context/execution_context.hpp>
+#else
+# include <boost/context/continuation.hpp>
+#endif
 #include <boost/context/stack_context.hpp>
 #include <boost/intrusive/list.hpp>
 #include <boost/intrusive/parent_from_member.hpp>
 #include <boost/intrusive_ptr.hpp>
 #include <boost/intrusive/set.hpp>
+#include <boost/intrusive/slist.hpp>
 
 #include <boost/fiber/detail/config.hpp>
 #include <boost/fiber/detail/data.hpp>
@@ -57,10 +66,10 @@ class scheduler;
 namespace detail {
 
 struct wait_tag;
-typedef intrusive::list_member_hook<
+typedef intrusive::slist_member_hook<
     intrusive::tag< wait_tag >,
     intrusive::link_mode<
-        intrusive::auto_unlink
+        intrusive::safe_link
     >
 >                                 wait_hook;
 // declaration of the functor that converts between
@@ -97,21 +106,29 @@ typedef intrusive::set_member_hook<
     >
 >                                       sleep_hook;
 
-struct terminated_tag;
+struct worker_tag;
 typedef intrusive::list_member_hook<
-    intrusive::tag< terminated_tag >,
+    intrusive::tag< worker_tag >,
     intrusive::link_mode<
         intrusive::auto_unlink
     >
+>                                       worker_hook;
+
+struct terminated_tag;
+typedef intrusive::slist_member_hook<
+    intrusive::tag< terminated_tag >,
+    intrusive::link_mode<
+        intrusive::safe_link
+    >
 >                                       terminated_hook;
 
-struct worker_tag;
-typedef intrusive::list_member_hook<
-    intrusive::tag< worker_tag >,
+struct remote_ready_tag;
+typedef intrusive::slist_member_hook<
+    intrusive::tag< remote_ready_tag >,
     intrusive::link_mode<
-        intrusive::auto_unlink
+        intrusive::safe_link
     >
->                                       worker_hook;
+>                                       remote_ready_hook;
 
 }
 
@@ -125,13 +142,17 @@ struct worker_context_t {};
 const worker_context_t worker_context{};
 
 class BOOST_FIBERS_DECL context {
+public:
+    typedef intrusive::slist<
+                context,
+                intrusive::function_hook< detail::wait_functor >,
+                intrusive::linear< true >,
+                intrusive::cache_last< true > 
+            >   wait_queue_t;
+
 private:
     friend class scheduler;
 
-    enum flag_t {
-        flag_terminated = 1 << 1
-    };
-
     struct fss_data {
         void                                *   vp{ nullptr };
         detail::fss_cleanup_function::ptr_t     cleanup_function{};
@@ -151,100 +172,102 @@ private:
         }
     };
 
-    typedef std::map< uintptr_t, fss_data >     fss_data_t;
+    typedef std::map< uintptr_t, fss_data >             fss_data_t;
 
 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
-    std::atomic< std::size_t >                      use_count_{ 0 };
-    std::atomic< unsigned int >                     flags_;
-    std::atomic< type >                             type_;
-    std::atomic< scheduler * >                      scheduler_{ nullptr };
+    alignas(cache_alignment) std::atomic< std::size_t > use_count_{ 0 };
 #else
-    std::size_t                                     use_count_{ 0 };
-    unsigned int                                    flags_;
-    type                                            type_;
-    scheduler                                   *   scheduler_{ nullptr };
+    alignas(cache_alignment) std::size_t                use_count_{ 0 };
+#endif
+#if ! defined(BOOST_FIBERS_NO_ATOMICS)
+    alignas(cache_alignment) detail::remote_ready_hook  remote_ready_hook_{};
+    std::atomic< context * >                            remote_nxt_{ nullptr };
 #endif
-    launch                                          policy_{ launch::post };
+    alignas(cache_alignment) detail::spinlock           splk_{};
+    bool                                                terminated_{ false };
+    wait_queue_t                                        wait_queue_{};
+public:
+    detail::wait_hook                                   wait_hook_{};
+private:
+    alignas(cache_alignment) scheduler              *   scheduler_{ nullptr };
+    fss_data_t                                          fss_data_{};
+    detail::sleep_hook                                  sleep_hook_{};
+    detail::ready_hook                                  ready_hook_{};
+    detail::terminated_hook                             terminated_hook_{};
+    detail::worker_hook                                 worker_hook_{};
 #if (BOOST_EXECUTION_CONTEXT==1)
-    boost::context::execution_context               ctx_;
+    boost::context::execution_context                   ctx_;
 #else
-    boost::context::execution_context< detail::data_t * >   ctx_;
+    boost::context::continuation                        c_;
 #endif
+    fiber_properties                                *   properties_{ nullptr };
+    std::chrono::steady_clock::time_point               tp_{ (std::chrono::steady_clock::time_point::max)() };
+    type                                                type_;
+    launch                                              policy_;
 
     void resume_( detail::data_t &) noexcept;
-    void set_ready_( context *) noexcept;
+    void schedule_( context *) noexcept;
 
 #if (BOOST_EXECUTION_CONTEXT==1)
     template< typename Fn, typename Tpl >
     void run_( Fn && fn_, Tpl && tpl_, detail::data_t * dp) noexcept {
         {
-            // fn and tpl must be destroyed before calling set_terminated()
+            // fn and tpl must be destroyed before calling terminate()
             typename std::decay< Fn >::type fn = std::forward< Fn >( fn_);
             typename std::decay< Tpl >::type tpl = std::forward< Tpl >( tpl_);
             if ( nullptr != dp->lk) {
                 dp->lk->unlock();
             } else if ( nullptr != dp->ctx) {
-                active()->set_ready_( dp->ctx);
+                active()->schedule_( dp->ctx);
             }
+#if defined(BOOST_NO_CXX17_STD_APPLY)
             boost::context::detail::apply( std::move( fn), std::move( tpl) );
+#else
+            std::apply( std::move( fn), std::move( tpl) );
+#endif
         }
         // terminate context
-        set_terminated();
+        terminate();
         BOOST_ASSERT_MSG( false, "fiber already terminated");
     }
 #else
     template< typename Fn, typename Tpl >
-    boost::context::execution_context< detail::data_t * >
-    run_( boost::context::execution_context< detail::data_t * > && ctx, Fn && fn_, Tpl && tpl_, detail::data_t * dp) noexcept {
+    boost::context::continuation
+    run_( boost::context::continuation && c, Fn && fn_, Tpl && tpl_) noexcept {
         {
-            // fn and tpl must be destroyed before calling set_terminated()
+            // fn and tpl must be destroyed before calling terminate()
             typename std::decay< Fn >::type fn = std::forward< Fn >( fn_);
             typename std::decay< Tpl >::type tpl = std::forward< Tpl >( tpl_);
-            // update execution_context of calling fiber
-            dp->from->ctx_ = std::move( ctx);
+            c = c.resume();
+            detail::data_t * dp = c.get_data< detail::data_t * >();
+            // update contiunation of calling fiber
+            dp->from->c_ = std::move( c);
             if ( nullptr != dp->lk) {
                 dp->lk->unlock();
             } else if ( nullptr != dp->ctx) {
-                active()->set_ready_( dp->ctx);
+                active()->schedule_( dp->ctx);
             }
+#if defined(BOOST_NO_CXX17_STD_APPLY)
             boost::context::detail::apply( std::move( fn), std::move( tpl) );
+#else
+            std::apply( std::move( fn), std::move( tpl) );
+#endif
         }
         // terminate context
-        return set_terminated();
+        return terminate();
     }
 #endif
 
 public:
-    detail::ready_hook                      ready_hook_{};
-    detail::sleep_hook                      sleep_hook_{};
-    detail::terminated_hook                 terminated_hook_{};
-    detail::wait_hook                       wait_hook_{};
-    detail::worker_hook                     worker_hook_{};
-    std::atomic< context * >                remote_nxt_{ nullptr };
-    std::chrono::steady_clock::time_point   tp_{ (std::chrono::steady_clock::time_point::max)() };
-
-    typedef intrusive::list<
-        context,
-        intrusive::function_hook< detail::wait_functor >,
-        intrusive::constant_time_size< false > >   wait_queue_t;
-
-private:
-    fss_data_t                              fss_data_{};
-    wait_queue_t                            wait_queue_{};
-    detail::spinlock                        splk_{};
-    fiber_properties                    *   properties_{ nullptr };
-
-public:
     class id {
     private:
         context  *   impl_{ nullptr };
 
     public:
-        id() noexcept {
-        }
+        id() = default;
 
         explicit id( context * impl) noexcept :
-            impl_( impl) {
+            impl_{ impl } {
         }
 
         bool operator==( id const& other) const noexcept {
@@ -311,9 +334,6 @@ public:
              boost::context::preallocated palloc, StackAlloc salloc,
              Fn && fn, Tpl && tpl) :
         use_count_{ 1 }, // fiber instance or scheduler owner
-        flags_{ 0 },
-        type_{ type::worker_context },
-        policy_{ policy },
 #if (BOOST_EXECUTION_CONTEXT==1)
 # if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
         ctx_{ std::allocator_arg, palloc, salloc,
@@ -325,50 +345,66 @@ public:
                   std::forward< Fn >( fn),
                   std::forward< Tpl >( tpl),
                   boost::context::execution_context::current() )
-              }
+              },
+        type_{ type::worker_context },
+        policy_{ policy }
 # else
         ctx_{ std::allocator_arg, palloc, salloc,
               [this,fn=detail::decay_copy( std::forward< Fn >( fn) ),tpl=std::forward< Tpl >( tpl),
                ctx=boost::context::execution_context::current()] (void * vp) mutable noexcept {
                     run_( std::move( fn), std::move( tpl), static_cast< detail::data_t * >( vp) );
-              }}
+              }},
+        type_{ type::worker_context },
+        policy_{ policy }
 # endif
+        {}
 #else
+        c_{},
+        type_{ type::worker_context },
+        policy_{ policy }
+        {
 # if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
-        ctx_{ std::allocator_arg, palloc, salloc,
-              detail::wrap(
-                  [this]( typename std::decay< Fn >::type & fn, typename std::decay< Tpl >::type & tpl,
-                          boost::context::execution_context< detail::data_t * > && ctx, detail::data_t * dp) mutable noexcept {
-                        return run_( std::forward< boost::context::execution_context< detail::data_t * > >( ctx), std::move( fn), std::move( tpl), dp);
-                  },
-                  std::forward< Fn >( fn),
-                  std::forward< Tpl >( tpl) )}
-
+            c_ = boost::context::callcc(
+                    std::allocator_arg, palloc, salloc,
+                      detail::wrap(
+                          [this]( typename std::decay< Fn >::type & fn, typename std::decay< Tpl >::type & tpl,
+                                  boost::context::continuation && c) mutable noexcept {
+                                return run_( std::forward< boost::context::continuation >( c), std::move( fn), std::move( tpl) );
+                          },
+                          std::forward< Fn >( fn),
+                          std::forward< Tpl >( tpl) ) );
 # else
-        ctx_{ std::allocator_arg, palloc, salloc,
-              [this,fn=detail::decay_copy( std::forward< Fn >( fn) ),tpl=std::forward< Tpl >( tpl)]
-               (boost::context::execution_context< detail::data_t * > && ctx, detail::data_t * dp) mutable noexcept {
-                    return run_( std::forward< boost::context::execution_context< detail::data_t * > >( ctx), std::move( fn), std::move( tpl), dp);
-              }}
+            c_ = boost::context::callcc(
+                    std::allocator_arg, palloc, salloc,
+                    [this,fn=detail::decay_copy( std::forward< Fn >( fn) ),tpl=std::forward< Tpl >( tpl)]
+                    (boost::context::continuation && c) mutable noexcept {
+                          return run_( std::forward< boost::context::continuation >( c), std::move( fn), std::move( tpl) );
+                    });
 # endif
+        }
 #endif
-    {}
 
     context( context const&) = delete;
     context & operator=( context const&) = delete;
 
+    friend bool
+    operator==( context const& lhs, context const& rhs) noexcept {
+        return & lhs == & rhs;
+    }
+
     virtual ~context();
 
     scheduler * get_scheduler() const noexcept {
-#if ! defined(BOOST_FIBERS_NO_ATOMICS)
-        return scheduler_.load( std::memory_order_relaxed);
-#else
         return scheduler_;
-#endif
     }
 
     id get_id() const noexcept;
 
+    bool is_resumable() const noexcept {
+        if ( c_) return true;
+        else return false;
+    }
+
     void resume() noexcept;
     void resume( detail::spinlock_lock &) noexcept;
     void resume( context *) noexcept;
@@ -377,10 +413,10 @@ public:
     void suspend( detail::spinlock_lock &) noexcept;
 
 #if (BOOST_EXECUTION_CONTEXT==1)
-    void set_terminated() noexcept;
+    void terminate() noexcept;
 #else
-    boost::context::execution_context< detail::data_t * > suspend_with_cc() noexcept;
-    boost::context::execution_context< detail::data_t * > set_terminated() noexcept;
+    boost::context::continuation suspend_with_cc() noexcept;
+    boost::context::continuation terminate() noexcept;
 #endif
     void join();
 
@@ -390,16 +426,12 @@ public:
     bool wait_until( std::chrono::steady_clock::time_point const&,
                      detail::spinlock_lock &) noexcept;
 
-    void set_ready( context *) noexcept;
+    void schedule( context *) noexcept;
 
     bool is_context( type t) const noexcept {
         return type::none != ( type_ & t);
     }
 
-    bool is_terminated() const noexcept {
-        return 0 != ( flags_ & flag_terminated);
-    }
-
     void * get_fss_data( void const * vp) const;
 
     void set_fss_data(
@@ -418,77 +450,90 @@ public:
         return policy_;
     }
 
+    bool worker_is_linked() const noexcept;
+
     bool ready_is_linked() const noexcept;
 
+    bool remote_ready_is_linked() const noexcept;
+
     bool sleep_is_linked() const noexcept;
 
     bool terminated_is_linked() const noexcept;
 
     bool wait_is_linked() const noexcept;
 
-    bool worker_is_linked() const noexcept;
+    template< typename List >
+    void worker_link( List & lst) noexcept {
+        static_assert( std::is_same< typename List::value_traits::hook_type, detail::worker_hook >::value, "not a worker-queue");
+        BOOST_ASSERT( ! worker_is_linked() );
+        lst.push_back( * this);
+    }
 
     template< typename List >
     void ready_link( List & lst) noexcept {
         static_assert( std::is_same< typename List::value_traits::hook_type, detail::ready_hook >::value, "not a ready-queue");
+        BOOST_ASSERT( ! ready_is_linked() );
+        lst.push_back( * this);
+    }
+
+    template< typename List >
+    void remote_ready_link( List & lst) noexcept {
+        static_assert( std::is_same< typename List::value_traits::hook_type, detail::remote_ready_hook >::value, "not a remote-ready-queue");
+        BOOST_ASSERT( ! remote_ready_is_linked() );
         lst.push_back( * this);
     }
 
     template< typename Set >
     void sleep_link( Set & set) noexcept {
         static_assert( std::is_same< typename Set::value_traits::hook_type,detail::sleep_hook >::value, "not a sleep-queue");
+        BOOST_ASSERT( ! sleep_is_linked() );
         set.insert( * this);
     }
 
     template< typename List >
     void terminated_link( List & lst) noexcept {
         static_assert( std::is_same< typename List::value_traits::hook_type, detail::terminated_hook >::value, "not a terminated-queue");
+        BOOST_ASSERT( ! terminated_is_linked() );
         lst.push_back( * this);
     }
 
     template< typename List >
     void wait_link( List & lst) noexcept {
         static_assert( std::is_same< typename List::value_traits::hook_type, detail::wait_hook >::value, "not a wait-queue");
+        BOOST_ASSERT( ! wait_is_linked() );
         lst.push_back( * this);
     }
 
-    template< typename List >
-    void worker_link( List & lst) noexcept {
-        static_assert( std::is_same< typename List::value_traits::hook_type, detail::worker_hook >::value, "not a worker-queue");
-        lst.push_back( * this);
-    }
+    void worker_unlink() noexcept;
 
     void ready_unlink() noexcept;
 
     void sleep_unlink() noexcept;
 
-    void wait_unlink() noexcept;
-
-    void worker_unlink() noexcept;
-
     void detach() noexcept;
 
     void attach( context *) noexcept;
 
     friend void intrusive_ptr_add_ref( context * ctx) noexcept {
         BOOST_ASSERT( nullptr != ctx);
-        ++ctx->use_count_;
+        ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
     }
 
     friend void intrusive_ptr_release( context * ctx) noexcept {
         BOOST_ASSERT( nullptr != ctx);
-        if ( 0 == --ctx->use_count_) {
+        if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
+            std::atomic_thread_fence( std::memory_order_acquire);
 #if (BOOST_EXECUTION_CONTEXT==1)
-            boost::context::execution_context ec( ctx->ctx_);
+            boost::context::execution_context ec = ctx->ctx_;
             // destruct context
             // deallocates stack (execution_context is ref counted)
             ctx->~context();
 #else
-            boost::context::execution_context< detail::data_t * > cc( std::move( ctx->ctx_) );
+            boost::context::continuation c = std::move( ctx->c_);
             // destruct context
             ctx->~context();
             // deallocated stack
-            cc( nullptr);
+            c.resume( nullptr);
 #endif
         }
     }
@@ -521,14 +566,14 @@ static intrusive_ptr< context > make_worker_context( launch policy,
     const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
 #endif
     // placement new of context on top of fiber's stack
-    return intrusive_ptr< context >( 
-            ::new ( sp) context(
+    return intrusive_ptr< context >{ 
+            ::new ( sp) context{
                 worker_context,
                 policy,
-                boost::context::preallocated( sp, size, sctx),
+                boost::context::preallocated{ sp, size, sctx },
                 salloc,
                 std::forward< Fn >( fn),
-                std::make_tuple( std::forward< Args >( args) ... ) ) );
+                std::make_tuple( std::forward< Args >( args) ... ) } };
 }
 
 namespace detail {