update boost
This commit is contained in:
@@ -35,7 +35,7 @@ private:
|
||||
public:
|
||||
typedef intrusive_ptr< algorithm > ptr_t;
|
||||
|
||||
virtual ~algorithm() {}
|
||||
virtual ~algorithm() = default;
|
||||
|
||||
virtual void awakened( context *) noexcept = 0;
|
||||
|
||||
@@ -47,6 +47,8 @@ public:
|
||||
|
||||
virtual void notify() noexcept = 0;
|
||||
|
||||
#if !defined(BOOST_EMBTC)
|
||||
|
||||
friend void intrusive_ptr_add_ref( algorithm * algo) noexcept {
|
||||
BOOST_ASSERT( nullptr != algo);
|
||||
algo->use_count_.fetch_add( 1, std::memory_order_relaxed);
|
||||
@@ -59,8 +61,33 @@ public:
|
||||
delete algo;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
friend void intrusive_ptr_add_ref( algorithm * algo) noexcept;
|
||||
friend void intrusive_ptr_release( algorithm * algo) noexcept;
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#if defined(BOOST_EMBTC)
|
||||
|
||||
inline void intrusive_ptr_add_ref( algorithm * algo) noexcept {
|
||||
BOOST_ASSERT( nullptr != algo);
|
||||
algo->use_count_.fetch_add( 1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_release( algorithm * algo) noexcept {
|
||||
BOOST_ASSERT( nullptr != algo);
|
||||
if ( 1 == algo->use_count_.fetch_sub( 1, std::memory_order_release) ) {
|
||||
std::atomic_thread_fence( std::memory_order_acquire);
|
||||
delete algo;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class BOOST_FIBERS_DECL algorithm_with_properties_base : public algorithm {
|
||||
public:
|
||||
// called by fiber_properties::notify() -- don't directly call
|
||||
@@ -79,7 +106,7 @@ struct algorithm_with_properties : public algorithm_with_properties_base {
|
||||
// must override awakened() with properties parameter instead. Otherwise
|
||||
// you'd have to remember to start every subclass awakened() override
|
||||
// with: algorithm_with_properties<PROPS>::awakened(fb);
|
||||
virtual void awakened( context * ctx) noexcept override final {
|
||||
void awakened( context * ctx) noexcept final {
|
||||
fiber_properties * props = super::get_properties( ctx);
|
||||
if ( BOOST_LIKELY( nullptr == props) ) {
|
||||
// TODO: would be great if PROPS could be allocated on the new
|
||||
@@ -110,11 +137,11 @@ struct algorithm_with_properties : public algorithm_with_properties_base {
|
||||
}
|
||||
|
||||
// override this to be notified by PROPS::notify()
|
||||
virtual void property_change( context * ctx, PROPS & props) noexcept {
|
||||
virtual void property_change( context * /* ctx */, PROPS & /* props */) noexcept {
|
||||
}
|
||||
|
||||
// implementation for algorithm_with_properties_base method
|
||||
void property_change_( context * ctx, fiber_properties * props) noexcept override final {
|
||||
void property_change_( context * ctx, fiber_properties * props) noexcept final {
|
||||
property_change( ctx, * static_cast< PROPS * >( props) );
|
||||
}
|
||||
|
||||
|
||||
@@ -45,15 +45,15 @@ public:
|
||||
round_robin( round_robin const&) = delete;
|
||||
round_robin & operator=( round_robin const&) = delete;
|
||||
|
||||
virtual void awakened( context *) noexcept;
|
||||
void awakened( context *) noexcept override;
|
||||
|
||||
virtual context * pick_next() noexcept;
|
||||
context * pick_next() noexcept override;
|
||||
|
||||
virtual bool has_ready_fibers() const noexcept;
|
||||
bool has_ready_fibers() const noexcept override;
|
||||
|
||||
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept;
|
||||
void suspend_until( std::chrono::steady_clock::time_point const&) noexcept override;
|
||||
|
||||
virtual void notify() noexcept;
|
||||
void notify() noexcept override;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
@@ -59,18 +59,18 @@ public:
|
||||
shared_work & operator=( shared_work const&) = delete;
|
||||
shared_work & operator=( shared_work &&) = delete;
|
||||
|
||||
void awakened( context * ctx) noexcept;
|
||||
void awakened( context * ctx) noexcept override;
|
||||
|
||||
context * pick_next() noexcept;
|
||||
context * pick_next() noexcept override;
|
||||
|
||||
bool has_ready_fibers() const noexcept {
|
||||
bool has_ready_fibers() const noexcept override {
|
||||
std::unique_lock< std::mutex > lock{ rqueue_mtx_ };
|
||||
return ! rqueue_.empty() || ! lqueue_.empty();
|
||||
}
|
||||
|
||||
void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept;
|
||||
void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept override;
|
||||
|
||||
void notify() noexcept;
|
||||
void notify() noexcept override;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
@@ -62,21 +62,21 @@ public:
|
||||
work_stealing & operator=( work_stealing const&) = delete;
|
||||
work_stealing & operator=( work_stealing &&) = delete;
|
||||
|
||||
virtual void awakened( context *) noexcept;
|
||||
void awakened( context *) noexcept override;
|
||||
|
||||
virtual context * pick_next() noexcept;
|
||||
context * pick_next() noexcept override;
|
||||
|
||||
virtual context * steal() noexcept {
|
||||
return rqueue_.steal();
|
||||
}
|
||||
|
||||
virtual bool has_ready_fibers() const noexcept {
|
||||
bool has_ready_fibers() const noexcept override {
|
||||
return ! rqueue_.empty();
|
||||
}
|
||||
|
||||
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept;
|
||||
void suspend_until( std::chrono::steady_clock::time_point const&) noexcept override;
|
||||
|
||||
virtual void notify() noexcept;
|
||||
void notify() noexcept override;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <boost/fiber/channel_op_status.hpp>
|
||||
#include <boost/fiber/context.hpp>
|
||||
#include <boost/fiber/waker.hpp>
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/convert.hpp>
|
||||
#include <boost/fiber/detail/spinlock.hpp>
|
||||
@@ -34,15 +35,14 @@ namespace fibers {
|
||||
template< typename T >
|
||||
class buffered_channel {
|
||||
public:
|
||||
typedef typename std::remove_reference< T >::type value_type;
|
||||
using value_type = typename std::remove_reference<T>::type;
|
||||
|
||||
private:
|
||||
typedef context::wait_queue_t wait_queue_type;
|
||||
typedef value_type slot_type;
|
||||
using slot_type = value_type;
|
||||
|
||||
mutable detail::spinlock splk_{};
|
||||
wait_queue_type waiting_producers_{};
|
||||
wait_queue_type waiting_consumers_{};
|
||||
wait_queue waiting_producers_{};
|
||||
wait_queue waiting_consumers_{};
|
||||
slot_type * slots_;
|
||||
std::size_t pidx_{ 0 };
|
||||
std::size_t cidx_{ 0 };
|
||||
@@ -85,103 +85,40 @@ public:
|
||||
}
|
||||
|
||||
void close() noexcept {
|
||||
context * active_ctx = context::active();
|
||||
detail::spinlock_lock lk{ splk_ };
|
||||
if ( ! closed_) {
|
||||
closed_ = true;
|
||||
// notify all waiting producers
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
}
|
||||
}
|
||||
// notify all waiting consumers
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
}
|
||||
}
|
||||
waiting_producers_.notify_all();
|
||||
waiting_consumers_.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
channel_op_status try_push( value_type const& value) {
|
||||
context * active_ctx = context::active();
|
||||
detail::spinlock_lock lk{ splk_ };
|
||||
if ( BOOST_UNLIKELY( is_closed_() ) ) {
|
||||
return channel_op_status::closed;
|
||||
} else if ( is_full_() ) {
|
||||
return channel_op_status::full;
|
||||
} else {
|
||||
slots_[pidx_] = value;
|
||||
pidx_ = (pidx_ + 1) % capacity_;
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return channel_op_status::success;
|
||||
}
|
||||
if ( is_full_() ) {
|
||||
return channel_op_status::full;
|
||||
}
|
||||
slots_[pidx_] = value;
|
||||
pidx_ = (pidx_ + 1) % capacity_;
|
||||
waiting_consumers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
|
||||
channel_op_status try_push( value_type && value) {
|
||||
context * active_ctx = context::active();
|
||||
detail::spinlock_lock lk{ splk_ };
|
||||
if ( BOOST_UNLIKELY( is_closed_() ) ) {
|
||||
return channel_op_status::closed;
|
||||
} else if ( is_full_() ) {
|
||||
return channel_op_status::full;
|
||||
} else {
|
||||
slots_[pidx_] = std::move( value);
|
||||
pidx_ = (pidx_ + 1) % capacity_;
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return channel_op_status::success;
|
||||
}
|
||||
if ( is_full_() ) {
|
||||
return channel_op_status::full;
|
||||
}
|
||||
slots_[pidx_] = std::move( value);
|
||||
pidx_ = (pidx_ + 1) % capacity_;
|
||||
waiting_consumers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
|
||||
channel_op_status push( value_type const& value) {
|
||||
@@ -190,32 +127,13 @@ public:
|
||||
detail::spinlock_lock lk{ splk_ };
|
||||
if ( BOOST_UNLIKELY( is_closed_() ) ) {
|
||||
return channel_op_status::closed;
|
||||
} else if ( is_full_() ) {
|
||||
active_ctx->wait_link( waiting_producers_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// suspend this producer
|
||||
active_ctx->suspend( lk);
|
||||
}
|
||||
if ( is_full_() ) {
|
||||
waiting_producers_.suspend_and_wait( lk, active_ctx);
|
||||
} else {
|
||||
slots_[pidx_] = value;
|
||||
pidx_ = (pidx_ + 1) % capacity_;
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_consumers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
}
|
||||
@@ -227,32 +145,14 @@ public:
|
||||
detail::spinlock_lock lk{ splk_ };
|
||||
if ( BOOST_UNLIKELY( is_closed_() ) ) {
|
||||
return channel_op_status::closed;
|
||||
} else if ( is_full_() ) {
|
||||
active_ctx->wait_link( waiting_producers_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// suspend this producer
|
||||
active_ctx->suspend( lk);
|
||||
}
|
||||
if ( is_full_() ) {
|
||||
waiting_producers_.suspend_and_wait( lk, active_ctx);
|
||||
} else {
|
||||
slots_[pidx_] = std::move( value);
|
||||
pidx_ = (pidx_ + 1) % capacity_;
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
waiting_consumers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
}
|
||||
@@ -281,38 +181,15 @@ public:
|
||||
detail::spinlock_lock lk{ splk_ };
|
||||
if ( BOOST_UNLIKELY( is_closed_() ) ) {
|
||||
return channel_op_status::closed;
|
||||
} else if ( is_full_() ) {
|
||||
active_ctx->wait_link( waiting_producers_);
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
// suspend this producer
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
// relock local lk
|
||||
lk.lock();
|
||||
// remove from waiting-queue
|
||||
waiting_producers_.remove( * active_ctx);
|
||||
}
|
||||
if ( is_full_() ) {
|
||||
if ( ! waiting_producers_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
} else {
|
||||
slots_[pidx_] = value;
|
||||
pidx_ = (pidx_ + 1) % capacity_;
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_consumers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
}
|
||||
@@ -327,73 +204,32 @@ public:
|
||||
detail::spinlock_lock lk{ splk_ };
|
||||
if ( BOOST_UNLIKELY( is_closed_() ) ) {
|
||||
return channel_op_status::closed;
|
||||
} else if ( is_full_() ) {
|
||||
active_ctx->wait_link( waiting_producers_);
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
// suspend this producer
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
// relock local lk
|
||||
lk.lock();
|
||||
// remove from waiting-queue
|
||||
waiting_producers_.remove( * active_ctx);
|
||||
}
|
||||
if ( is_full_() ) {
|
||||
if ( ! waiting_producers_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
} else {
|
||||
slots_[pidx_] = std::move( value);
|
||||
pidx_ = (pidx_ + 1) % capacity_;
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_consumers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
channel_op_status try_pop( value_type & value) {
|
||||
context * active_ctx = context::active();
|
||||
detail::spinlock_lock lk{ splk_ };
|
||||
if ( is_empty_() ) {
|
||||
return is_closed_()
|
||||
? channel_op_status::closed
|
||||
: channel_op_status::empty;
|
||||
} else {
|
||||
value = std::move( slots_[cidx_]);
|
||||
cidx_ = (cidx_ + 1) % capacity_;
|
||||
// notify one waiting producer
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return channel_op_status::success;
|
||||
}
|
||||
value = std::move( slots_[cidx_]);
|
||||
cidx_ = (cidx_ + 1) % capacity_;
|
||||
waiting_producers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
|
||||
channel_op_status pop( value_type & value) {
|
||||
@@ -403,33 +239,12 @@ public:
|
||||
if ( is_empty_() ) {
|
||||
if ( BOOST_UNLIKELY( is_closed_() ) ) {
|
||||
return channel_op_status::closed;
|
||||
} else {
|
||||
active_ctx->wait_link( waiting_consumers_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// suspend this consumer
|
||||
active_ctx->suspend( lk);
|
||||
}
|
||||
waiting_consumers_.suspend_and_wait( lk, active_ctx);
|
||||
} else {
|
||||
value = std::move( slots_[cidx_]);
|
||||
cidx_ = (cidx_ + 1) % capacity_;
|
||||
// notify one waiting producer
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_producers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
}
|
||||
@@ -444,34 +259,13 @@ public:
|
||||
throw fiber_error{
|
||||
std::make_error_code( std::errc::operation_not_permitted),
|
||||
"boost fiber: channel is closed" };
|
||||
} else {
|
||||
active_ctx->wait_link( waiting_consumers_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// suspend this consumer
|
||||
active_ctx->suspend( lk);
|
||||
}
|
||||
waiting_consumers_.suspend_and_wait( lk, active_ctx);
|
||||
} else {
|
||||
value_type value = std::move( slots_[cidx_]);
|
||||
cidx_ = (cidx_ + 1) % capacity_;
|
||||
// notify one waiting producer
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::move( value);
|
||||
waiting_producers_.notify_one();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,39 +287,14 @@ public:
|
||||
if ( is_empty_() ) {
|
||||
if ( BOOST_UNLIKELY( is_closed_() ) ) {
|
||||
return channel_op_status::closed;
|
||||
} else {
|
||||
active_ctx->wait_link( waiting_consumers_);
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
// suspend this consumer
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
// relock local lk
|
||||
lk.lock();
|
||||
// remove from waiting-queue
|
||||
waiting_consumers_.remove( * active_ctx);
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
}
|
||||
if ( ! waiting_consumers_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
} else {
|
||||
value = std::move( slots_[cidx_]);
|
||||
cidx_ = (cidx_ + 1) % capacity_;
|
||||
// notify one waiting producer
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_producers_.notify_one();
|
||||
return channel_op_status::success;
|
||||
}
|
||||
}
|
||||
@@ -538,9 +307,12 @@ public:
|
||||
buffered_channel * chan_{ nullptr };
|
||||
storage_type storage_;
|
||||
|
||||
void increment_() {
|
||||
void increment_( bool initial = false) {
|
||||
BOOST_ASSERT( nullptr != chan_);
|
||||
try {
|
||||
if ( ! initial) {
|
||||
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
|
||||
}
|
||||
::new ( static_cast< void * >( std::addressof( storage_) ) ) value_type{ chan_->value_pop() };
|
||||
} catch ( fiber_error const&) {
|
||||
chan_ = nullptr;
|
||||
@@ -548,19 +320,19 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef value_type * pointer;
|
||||
typedef value_type & reference;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
typedef pointer pointer_t;
|
||||
typedef reference reference_t;
|
||||
using pointer_t = pointer;
|
||||
using reference_t = reference;
|
||||
|
||||
iterator() noexcept = default;
|
||||
iterator() = default;
|
||||
|
||||
explicit iterator( buffered_channel< T > * chan) noexcept :
|
||||
chan_{ chan } {
|
||||
increment_();
|
||||
increment_( true);
|
||||
}
|
||||
|
||||
iterator( iterator const& other) noexcept :
|
||||
@@ -583,11 +355,12 @@ public:
|
||||
}
|
||||
|
||||
iterator & operator++() {
|
||||
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
|
||||
increment_();
|
||||
return * this;
|
||||
}
|
||||
|
||||
iterator operator++( int) = delete;
|
||||
const iterator operator++( int) = delete;
|
||||
|
||||
reference_t operator*() noexcept {
|
||||
return * reinterpret_cast< value_type * >( std::addressof( storage_) );
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <boost/fiber/exceptions.hpp>
|
||||
#include <boost/fiber/mutex.hpp>
|
||||
#include <boost/fiber/operations.hpp>
|
||||
#include <boost/fiber/waker.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
@@ -44,10 +45,8 @@ enum class cv_status {
|
||||
|
||||
class BOOST_FIBERS_DECL condition_variable_any {
|
||||
private:
|
||||
typedef context::wait_queue_t wait_queue_t;
|
||||
|
||||
detail::spinlock wait_queue_splk_{};
|
||||
wait_queue_t wait_queue_{};
|
||||
wait_queue wait_queue_{};
|
||||
|
||||
public:
|
||||
condition_variable_any() = default;
|
||||
@@ -69,13 +68,9 @@ public:
|
||||
// atomically call lt.unlock() and block on *this
|
||||
// store this fiber in waiting-queue
|
||||
detail::spinlock_lock lk{ wait_queue_splk_ };
|
||||
BOOST_ASSERT( ! active_ctx->wait_is_linked() );
|
||||
active_ctx->wait_link( wait_queue_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// unlock external lt
|
||||
lt.unlock();
|
||||
// suspend this fiber
|
||||
active_ctx->suspend( lk);
|
||||
wait_queue_.suspend_and_wait( lk, active_ctx);
|
||||
|
||||
// relock external again before returning
|
||||
try {
|
||||
lt.lock();
|
||||
@@ -86,8 +81,6 @@ public:
|
||||
} catch (...) {
|
||||
std::terminate();
|
||||
}
|
||||
// post-conditions
|
||||
BOOST_ASSERT( ! active_ctx->wait_is_linked() );
|
||||
}
|
||||
|
||||
template< typename LockType, typename Pred >
|
||||
@@ -105,20 +98,10 @@ public:
|
||||
// atomically call lt.unlock() and block on *this
|
||||
// store this fiber in waiting-queue
|
||||
detail::spinlock_lock lk{ wait_queue_splk_ };
|
||||
BOOST_ASSERT( ! active_ctx->wait_is_linked() );
|
||||
active_ctx->wait_link( wait_queue_);
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
// unlock external lt
|
||||
lt.unlock();
|
||||
// suspend this fiber
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
if ( ! wait_queue_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
|
||||
status = cv_status::timeout;
|
||||
// relock local lk
|
||||
lk.lock();
|
||||
// remove from waiting-queue
|
||||
wait_queue_.remove( * active_ctx);
|
||||
// unlock local lk
|
||||
lk.unlock();
|
||||
}
|
||||
// relock external again before returning
|
||||
try {
|
||||
@@ -130,8 +113,6 @@ public:
|
||||
} catch (...) {
|
||||
std::terminate();
|
||||
}
|
||||
// post-conditions
|
||||
BOOST_ASSERT( ! active_ctx->wait_is_linked() );
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
@@ -43,6 +44,7 @@
|
||||
#include <boost/fiber/properties.hpp>
|
||||
#include <boost/fiber/segmented_stack.hpp>
|
||||
#include <boost/fiber/type.hpp>
|
||||
#include <boost/fiber/waker.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
@@ -62,31 +64,6 @@ class scheduler;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct wait_tag;
|
||||
typedef intrusive::list_member_hook<
|
||||
intrusive::tag< wait_tag >,
|
||||
intrusive::link_mode<
|
||||
intrusive::auto_unlink
|
||||
>
|
||||
> wait_hook;
|
||||
// declaration of the functor that converts between
|
||||
// the context class and the wait-hook
|
||||
struct wait_functor {
|
||||
// required types
|
||||
typedef wait_hook hook_type;
|
||||
typedef hook_type * hook_ptr;
|
||||
typedef const hook_type * const_hook_ptr;
|
||||
typedef context value_type;
|
||||
typedef value_type * pointer;
|
||||
typedef const value_type * const_pointer;
|
||||
|
||||
// required static functions
|
||||
static hook_ptr to_hook_ptr( value_type &value);
|
||||
static const_hook_ptr to_hook_ptr( value_type const& value);
|
||||
static pointer to_value_ptr( hook_ptr n);
|
||||
static const_pointer to_value_ptr( const_hook_ptr n);
|
||||
};
|
||||
|
||||
struct ready_tag;
|
||||
typedef intrusive::list_member_hook<
|
||||
intrusive::tag< ready_tag >,
|
||||
@@ -130,13 +107,6 @@ typedef intrusive::slist_member_hook<
|
||||
}
|
||||
|
||||
class BOOST_FIBERS_DECL context {
|
||||
public:
|
||||
typedef intrusive::list<
|
||||
context,
|
||||
intrusive::function_hook< detail::wait_functor >,
|
||||
intrusive::constant_time_size< false >
|
||||
> wait_queue_t;
|
||||
|
||||
private:
|
||||
friend class dispatcher_context;
|
||||
friend class main_context;
|
||||
@@ -147,13 +117,12 @@ private:
|
||||
void * vp{ nullptr };
|
||||
detail::fss_cleanup_function::ptr_t cleanup_function{};
|
||||
|
||||
fss_data() noexcept {
|
||||
}
|
||||
fss_data() = default;
|
||||
|
||||
fss_data( void * vp_,
|
||||
detail::fss_cleanup_function::ptr_t const& fn) noexcept :
|
||||
detail::fss_cleanup_function::ptr_t fn) noexcept :
|
||||
vp( vp_),
|
||||
cleanup_function( fn) {
|
||||
cleanup_function(std::move( fn)) {
|
||||
BOOST_ASSERT( cleanup_function);
|
||||
}
|
||||
|
||||
@@ -174,16 +143,16 @@ private:
|
||||
#endif
|
||||
detail::spinlock splk_{};
|
||||
bool terminated_{ false };
|
||||
wait_queue_t wait_queue_{};
|
||||
wait_queue wait_queue_{};
|
||||
public:
|
||||
detail::wait_hook wait_hook_{};
|
||||
#if ! defined(BOOST_FIBERS_NO_ATOMICS)
|
||||
std::atomic< std::intptr_t > twstatus{ 0 };
|
||||
std::atomic<size_t> waker_epoch_{ 0 };
|
||||
#endif
|
||||
private:
|
||||
scheduler * scheduler_{ nullptr };
|
||||
fss_data_t fss_data_{};
|
||||
detail::sleep_hook sleep_hook_{};
|
||||
waker sleep_waker_{};
|
||||
detail::ready_hook ready_hook_{};
|
||||
detail::terminated_hook terminated_hook_{};
|
||||
detail::worker_hook worker_hook_{};
|
||||
@@ -241,9 +210,8 @@ public:
|
||||
operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) {
|
||||
if ( nullptr != other.impl_) {
|
||||
return os << other.impl_;
|
||||
} else {
|
||||
return os << "{not-valid}";
|
||||
}
|
||||
return os << "{not-valid}";
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
@@ -264,11 +232,20 @@ public:
|
||||
context & operator=( context const&) = delete;
|
||||
context & operator=( context &&) = delete;
|
||||
|
||||
#if !defined(BOOST_EMBTC)
|
||||
|
||||
friend bool
|
||||
operator==( context const& lhs, context const& rhs) noexcept {
|
||||
return & lhs == & rhs;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
friend bool
|
||||
operator==( context const& lhs, context const& rhs) noexcept;
|
||||
|
||||
#endif
|
||||
|
||||
virtual ~context();
|
||||
|
||||
scheduler * get_scheduler() const noexcept {
|
||||
@@ -278,8 +255,7 @@ public:
|
||||
id get_id() const noexcept;
|
||||
|
||||
bool is_resumable() const noexcept {
|
||||
if ( c_) return true;
|
||||
else return false;
|
||||
return static_cast<bool>(c_);
|
||||
}
|
||||
|
||||
void resume() noexcept;
|
||||
@@ -298,7 +274,15 @@ public:
|
||||
|
||||
bool wait_until( std::chrono::steady_clock::time_point const&) noexcept;
|
||||
bool wait_until( std::chrono::steady_clock::time_point const&,
|
||||
detail::spinlock_lock &) noexcept;
|
||||
detail::spinlock_lock &,
|
||||
waker &&) noexcept;
|
||||
|
||||
bool wake(const size_t) noexcept;
|
||||
|
||||
waker create_waker() noexcept {
|
||||
// this operation makes all previously created wakers to be outdated
|
||||
return { this, ++waker_epoch_ };
|
||||
}
|
||||
|
||||
void schedule( context *) noexcept;
|
||||
|
||||
@@ -334,8 +318,6 @@ public:
|
||||
|
||||
bool terminated_is_linked() const noexcept;
|
||||
|
||||
bool wait_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");
|
||||
@@ -371,25 +353,18 @@ public:
|
||||
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);
|
||||
}
|
||||
|
||||
void worker_unlink() noexcept;
|
||||
|
||||
void ready_unlink() noexcept;
|
||||
|
||||
void sleep_unlink() noexcept;
|
||||
|
||||
void wait_unlink() noexcept;
|
||||
|
||||
void detach() noexcept;
|
||||
|
||||
void attach( context *) noexcept;
|
||||
|
||||
#if !defined(BOOST_EMBTC)
|
||||
|
||||
friend void intrusive_ptr_add_ref( context * ctx) noexcept {
|
||||
BOOST_ASSERT( nullptr != ctx);
|
||||
ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
|
||||
@@ -406,8 +381,42 @@ public:
|
||||
std::move( c).resume();
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
friend void intrusive_ptr_add_ref( context * ctx) noexcept;
|
||||
friend void intrusive_ptr_release( context * ctx) noexcept;
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#if defined(BOOST_EMBTC)
|
||||
|
||||
inline bool
|
||||
operator==( context const& lhs, context const& rhs) noexcept {
|
||||
return & lhs == & rhs;
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_add_ref( context * ctx) noexcept {
|
||||
BOOST_ASSERT( nullptr != ctx);
|
||||
ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_release( context * ctx) noexcept {
|
||||
BOOST_ASSERT( nullptr != ctx);
|
||||
if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
|
||||
std::atomic_thread_fence( std::memory_order_acquire);
|
||||
boost::context::fiber c = std::move( ctx->c_);
|
||||
// destruct context
|
||||
ctx->~context();
|
||||
// deallocated stack
|
||||
std::move( c).resume();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline
|
||||
bool operator<( context const& l, context const& r) noexcept {
|
||||
return l.get_id() < r.get_id();
|
||||
@@ -443,22 +452,35 @@ private:
|
||||
public:
|
||||
template< typename StackAlloc >
|
||||
worker_context( launch policy,
|
||||
fiber_properties* properties,
|
||||
boost::context::preallocated const& palloc, StackAlloc && salloc,
|
||||
Fn && fn, Arg ... arg) :
|
||||
context{ 1, type::worker_context, policy },
|
||||
fn_( std::forward< Fn >( fn) ),
|
||||
arg_( std::forward< Arg >( arg) ... ) {
|
||||
if ( properties != nullptr ) {
|
||||
set_properties(properties);
|
||||
properties->set_context(this);
|
||||
}
|
||||
c_ = boost::context::fiber{ std::allocator_arg, palloc, std::forward< StackAlloc >( salloc),
|
||||
std::bind( & worker_context::run_, this, std::placeholders::_1) };
|
||||
#if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
|
||||
c_ = std::move( c_).resume();
|
||||
#endif
|
||||
}
|
||||
|
||||
template< typename StackAlloc >
|
||||
worker_context( launch policy,
|
||||
boost::context::preallocated const& palloc, StackAlloc && salloc,
|
||||
Fn && fn, Arg ... arg) :
|
||||
worker_context( policy, palloc, salloc, nullptr, std::forward<Fn>( fn ), std::forward<Arg>( arg ) ... ){
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template< typename StackAlloc, typename Fn, typename ... Arg >
|
||||
static intrusive_ptr< context > make_worker_context( launch policy,
|
||||
static intrusive_ptr< context > make_worker_context_with_properties( launch policy,
|
||||
fiber_properties* properties,
|
||||
StackAlloc && salloc,
|
||||
Fn && fn, Arg ... arg) {
|
||||
typedef worker_context< Fn, Arg ... > context_t;
|
||||
@@ -475,35 +497,23 @@ static intrusive_ptr< context > make_worker_context( launch policy,
|
||||
return intrusive_ptr< context >{
|
||||
new ( storage) context_t{
|
||||
policy,
|
||||
properties,
|
||||
boost::context::preallocated{ storage, size, sctx },
|
||||
std::forward< StackAlloc >( salloc),
|
||||
std::forward< Fn >( fn),
|
||||
std::forward< Arg >( arg) ... } };
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
wait_functor::hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type & value) {
|
||||
return & value.wait_hook_;
|
||||
template< typename StackAlloc, typename Fn, typename ... Arg >
|
||||
static intrusive_ptr< context > make_worker_context( launch policy,
|
||||
StackAlloc && salloc,
|
||||
Fn && fn, Arg ... arg){
|
||||
return make_worker_context_with_properties( policy, nullptr, std::forward<StackAlloc>(salloc),
|
||||
std::forward<Fn>( fn ), std::forward<Arg>( arg ) ... );
|
||||
}
|
||||
|
||||
inline
|
||||
wait_functor::const_hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type const& value) {
|
||||
return & value.wait_hook_;
|
||||
}
|
||||
|
||||
inline
|
||||
wait_functor::pointer wait_functor::to_value_ptr( wait_functor::hook_ptr n) {
|
||||
return intrusive::get_parent_from_member< context >( n, & context::wait_hook_);
|
||||
}
|
||||
|
||||
inline
|
||||
wait_functor::const_pointer wait_functor::to_value_ptr( wait_functor::const_hook_ptr n) {
|
||||
return intrusive::get_parent_from_member< context >( n, & context::wait_hook_);
|
||||
}
|
||||
|
||||
}}}
|
||||
}}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace detail {
|
||||
# else
|
||||
# define cpu_relax() asm volatile ("nop" ::: "memory");
|
||||
# endif
|
||||
#elif BOOST_ARCH_MIPS
|
||||
#elif BOOST_ARCH_MIPS && (((__mips_isa_rev > 1) && defined(__mips32)) || ((__mips_isa_rev > 2) && defined(__mips64)))
|
||||
# define cpu_relax() asm volatile ("pause" ::: "memory");
|
||||
#elif BOOST_ARCH_PPC
|
||||
// http://code.metager.de/source/xref/gnu/glibc/sysdeps/powerpc/sys/platform/ppc.h
|
||||
|
||||
@@ -30,7 +30,7 @@ private:
|
||||
public:
|
||||
typedef intrusive_ptr< fss_cleanup_function > ptr_t;
|
||||
|
||||
fss_cleanup_function() noexcept = default;
|
||||
fss_cleanup_function() = default;
|
||||
|
||||
virtual ~fss_cleanup_function() = default;
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
|
||||
#ifndef SYS_futex
|
||||
#define SYS_futex SYS_futex_time64
|
||||
#endif
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
extern "C" {
|
||||
#include <linux/futex.h>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef BOOST_FIBERS_SPINLOCK_RTM_H
|
||||
#define BOOST_FIBERS_SPINLOCK_RTM_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef BOOST_FIBERS_SPINLOCK_TTAS_H
|
||||
#define BOOST_FIBERS_SPINLOCK_TTAS_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
@@ -64,6 +65,7 @@ public:
|
||||
// -> prevent pipeline stalls
|
||||
cpu_relax();
|
||||
} else if ( BOOST_FIBERS_SPIN_BEFORE_YIELD > retries) {
|
||||
++retries;
|
||||
// std::this_thread::sleep_for( 0us) has a fairly long instruction path length,
|
||||
// combined with an expensive ring3 to ring 0 transition costing about 1000 cycles
|
||||
// std::this_thread::sleep_for( 0us) lets give up this_thread the remaining part of its time slice
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_H
|
||||
#define BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX_H
|
||||
#define BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef BOOST_FIBERS_spinlock_ttas_futex_FUTEX_H
|
||||
#define BOOST_FIBERS_spinlock_ttas_futex_FUTEX_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
|
||||
@@ -51,9 +51,8 @@ public:
|
||||
lk.unlock(); // no pessimization
|
||||
cond_.notify_all();
|
||||
return true;
|
||||
} else {
|
||||
cond_.wait( lk, [&](){ return cycle != cycle_; });
|
||||
}
|
||||
cond_.wait( lk, [&](){ return cycle != cycle_; });
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace fibers {
|
||||
|
||||
class fiber_error : public std::system_error {
|
||||
public:
|
||||
fiber_error( std::error_code ec) :
|
||||
explicit fiber_error( std::error_code ec) :
|
||||
std::system_error{ ec } {
|
||||
}
|
||||
|
||||
@@ -39,12 +39,12 @@ public:
|
||||
std::system_error{ ec, what_arg } {
|
||||
}
|
||||
|
||||
virtual ~fiber_error() = default;
|
||||
~fiber_error() override = default;
|
||||
};
|
||||
|
||||
class lock_error : public fiber_error {
|
||||
public:
|
||||
lock_error( std::error_code ec) :
|
||||
explicit lock_error( std::error_code ec) :
|
||||
fiber_error{ ec } {
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace fibers {
|
||||
|
||||
class future_error : public fiber_error {
|
||||
public:
|
||||
future_error( std::error_code ec) :
|
||||
explicit future_error( std::error_code ec) :
|
||||
fiber_error{ ec } {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,14 +41,14 @@ class BOOST_FIBERS_DECL fiber {
|
||||
private:
|
||||
friend class context;
|
||||
|
||||
typedef intrusive_ptr< context > ptr_t;
|
||||
using ptr_t = intrusive_ptr<context>;
|
||||
|
||||
ptr_t impl_{};
|
||||
|
||||
void start_() noexcept;
|
||||
|
||||
public:
|
||||
typedef context::id id;
|
||||
using id = context::id;
|
||||
|
||||
fiber() = default;
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
typename = detail::disable_overload< std::allocator_arg_t, Fn >
|
||||
>
|
||||
#if BOOST_COMP_GNUC < 50000000
|
||||
fiber( Fn && fn, Arg && ... arg) :
|
||||
explicit fiber( Fn && fn, Arg && ... arg) :
|
||||
#else
|
||||
fiber( Fn && fn, Arg ... arg) :
|
||||
#endif
|
||||
@@ -105,7 +105,69 @@ public:
|
||||
#else
|
||||
fiber( launch policy, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg ... arg) :
|
||||
#endif
|
||||
impl_{ make_worker_context( policy, std::forward< StackAllocator >( salloc), std::forward< Fn >( fn), std::forward< Arg >( arg) ... ) } {
|
||||
fiber{ policy,
|
||||
static_cast<fiber_properties*>(nullptr),
|
||||
std::allocator_arg, std::forward< StackAllocator >( salloc),
|
||||
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
|
||||
}
|
||||
|
||||
template< typename Fn,
|
||||
typename ... Arg,
|
||||
typename = detail::disable_overload< fiber, Fn >,
|
||||
typename = detail::disable_overload< launch, Fn >,
|
||||
typename = detail::disable_overload< std::allocator_arg_t, Fn >
|
||||
>
|
||||
#if BOOST_COMP_GNUC < 50000000
|
||||
explicit fiber( fiber_properties* properties, Fn && fn, Arg && ... arg) :
|
||||
#else
|
||||
fiber( fiber_properties* properties, Fn && fn, Arg ... arg) :
|
||||
#endif
|
||||
fiber{ launch::post,
|
||||
properties,
|
||||
std::allocator_arg, default_stack(),
|
||||
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
|
||||
}
|
||||
|
||||
template< typename Fn,
|
||||
typename ... Arg,
|
||||
typename = detail::disable_overload< fiber, Fn >
|
||||
>
|
||||
#if BOOST_COMP_GNUC < 50000000
|
||||
fiber( launch policy, fiber_properties* properties, Fn && fn, Arg && ... arg) :
|
||||
#else
|
||||
fiber( launch policy, fiber_properties* properties, Fn && fn, Arg ... arg) :
|
||||
#endif
|
||||
fiber{ policy,
|
||||
properties,
|
||||
std::allocator_arg, default_stack(),
|
||||
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
|
||||
}
|
||||
|
||||
template< typename StackAllocator,
|
||||
typename Fn,
|
||||
typename ... Arg
|
||||
>
|
||||
#if BOOST_COMP_GNUC < 50000000
|
||||
fiber( fiber_properties* properties, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg && ... arg) :
|
||||
#else
|
||||
fiber( fiber_properties* properties, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg ... arg) :
|
||||
#endif
|
||||
fiber{ launch::post,
|
||||
properties,
|
||||
std::allocator_arg, std::forward< StackAllocator >( salloc),
|
||||
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
|
||||
}
|
||||
|
||||
template< typename StackAllocator,
|
||||
typename Fn,
|
||||
typename ... Arg
|
||||
>
|
||||
#if BOOST_COMP_GNUC < 50000000
|
||||
fiber( launch policy, fiber_properties* properties, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg && ... arg) :
|
||||
#else
|
||||
fiber( launch policy, fiber_properties* properties, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg ... arg) :
|
||||
#endif
|
||||
impl_{ make_worker_context_with_properties( policy, properties, std::forward< StackAllocator >( salloc), std::forward< Fn >( fn), std::forward< Arg >( arg) ... ) } {
|
||||
start_();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ template< typename T >
|
||||
class fiber_specific_ptr {
|
||||
private:
|
||||
struct default_cleanup_function : public detail::fss_cleanup_function {
|
||||
void operator()( void * data) noexcept {
|
||||
void operator()( void * data) noexcept override {
|
||||
delete static_cast< T * >( data);
|
||||
}
|
||||
};
|
||||
@@ -37,7 +37,7 @@ private:
|
||||
fn{ fn_ } {
|
||||
}
|
||||
|
||||
void operator()( void * data) {
|
||||
void operator()( void * data) override {
|
||||
if ( BOOST_LIKELY( nullptr != fn) ) {
|
||||
fn( static_cast< T * >( data) );
|
||||
}
|
||||
@@ -47,7 +47,7 @@ private:
|
||||
detail::fss_cleanup_function::ptr_t cleanup_fn_;
|
||||
|
||||
public:
|
||||
typedef T element_type;
|
||||
using element_type = T;
|
||||
|
||||
fiber_specific_ptr() :
|
||||
cleanup_fn_{ new default_cleanup_function() } {
|
||||
|
||||
@@ -21,9 +21,19 @@
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
|
||||
#if (defined(BOOST_MSVC) && (_MSC_VER >= 1911 && _MSVC_LANG >= 201703)) || __cplusplus >= 202002L
|
||||
template <typename>
|
||||
struct result_of;
|
||||
template <typename F, typename... Args>
|
||||
struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
|
||||
#else
|
||||
using std::result_of;
|
||||
#endif
|
||||
|
||||
|
||||
template< typename Fn, typename ... Args >
|
||||
future<
|
||||
typename std::result_of<
|
||||
typename result_of<
|
||||
typename std::enable_if<
|
||||
! detail::is_launch_policy< typename std::decay< Fn >::type >::value,
|
||||
typename std::decay< Fn >::type
|
||||
@@ -31,7 +41,7 @@ future<
|
||||
>::type
|
||||
>
|
||||
async( Fn && fn, Args ... args) {
|
||||
typedef typename std::result_of<
|
||||
typedef typename result_of<
|
||||
typename std::decay< Fn >::type( typename std::decay< Args >::type ... )
|
||||
>::type result_type;
|
||||
|
||||
@@ -44,7 +54,7 @@ async( Fn && fn, Args ... args) {
|
||||
|
||||
template< typename Policy, typename Fn, typename ... Args >
|
||||
future<
|
||||
typename std::result_of<
|
||||
typename result_of<
|
||||
typename std::enable_if<
|
||||
detail::is_launch_policy< Policy >::value,
|
||||
typename std::decay< Fn >::type
|
||||
@@ -52,7 +62,7 @@ future<
|
||||
>::type
|
||||
>
|
||||
async( Policy policy, Fn && fn, Args ... args) {
|
||||
typedef typename std::result_of<
|
||||
typedef typename result_of<
|
||||
typename std::decay< Fn >::type( typename std::decay< Args >::type ... )
|
||||
>::type result_type;
|
||||
|
||||
@@ -65,7 +75,7 @@ async( Policy policy, Fn && fn, Args ... args) {
|
||||
|
||||
template< typename Policy, typename StackAllocator, typename Fn, typename ... Args >
|
||||
future<
|
||||
typename std::result_of<
|
||||
typename result_of<
|
||||
typename std::enable_if<
|
||||
detail::is_launch_policy< Policy >::value,
|
||||
typename std::decay< Fn >::type
|
||||
@@ -73,7 +83,7 @@ future<
|
||||
>::type
|
||||
>
|
||||
async( Policy policy, std::allocator_arg_t, StackAllocator salloc, Fn && fn, Args ... args) {
|
||||
typedef typename std::result_of<
|
||||
typedef typename result_of<
|
||||
typename std::decay< Fn >::type( typename std::decay< Args >::type ... )
|
||||
>::type result_type;
|
||||
|
||||
@@ -87,7 +97,7 @@ async( Policy policy, std::allocator_arg_t, StackAllocator salloc, Fn && fn, Arg
|
||||
|
||||
template< typename Policy, typename StackAllocator, typename Allocator, typename Fn, typename ... Args >
|
||||
future<
|
||||
typename std::result_of<
|
||||
typename result_of<
|
||||
typename std::enable_if<
|
||||
detail::is_launch_policy< Policy >::value,
|
||||
typename std::decay< Fn >::type
|
||||
@@ -95,7 +105,7 @@ future<
|
||||
>::type
|
||||
>
|
||||
async( Policy policy, std::allocator_arg_t, StackAllocator salloc, Allocator alloc, Fn && fn, Args ... args) {
|
||||
typedef typename std::result_of<
|
||||
typedef typename result_of<
|
||||
typename std::decay< Fn >::type( typename std::decay< Args >::type ... )
|
||||
>::type result_type;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
@@ -30,8 +31,8 @@ struct future_base {
|
||||
|
||||
future_base() = default;
|
||||
|
||||
explicit future_base( ptr_type const& p) noexcept :
|
||||
state_{ p } {
|
||||
explicit future_base( ptr_type p) noexcept :
|
||||
state_{std::move( p )} {
|
||||
}
|
||||
|
||||
~future_base() = default;
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
promise( promise const&) = delete;
|
||||
promise & operator=( promise const&) = delete;
|
||||
|
||||
promise( promise && other) noexcept = default;
|
||||
promise( promise && other) = default;
|
||||
promise & operator=( promise && other) = default;
|
||||
|
||||
void set_value( R const& value) {
|
||||
@@ -158,8 +158,8 @@ public:
|
||||
promise( promise const&) = delete;
|
||||
promise & operator=( promise const&) = delete;
|
||||
|
||||
promise( promise && other) noexcept = default;
|
||||
promise & operator=( promise && other) noexcept = default;
|
||||
promise( promise && other) = default;
|
||||
promise & operator=( promise && other) = default;
|
||||
|
||||
void set_value( R & value) {
|
||||
if ( BOOST_UNLIKELY( ! base_type::future_) ) {
|
||||
@@ -192,8 +192,8 @@ public:
|
||||
promise( promise const&) = delete;
|
||||
promise & operator=( promise const&) = delete;
|
||||
|
||||
promise( promise && other) noexcept = default;
|
||||
promise & operator=( promise && other) noexcept = default;
|
||||
promise( promise && other) = default;
|
||||
promise & operator=( promise && other) = default;
|
||||
|
||||
inline
|
||||
void set_value() {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <boost/fiber/context.hpp>
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/spinlock.hpp>
|
||||
#include <boost/fiber/waker.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
@@ -33,10 +34,8 @@ class BOOST_FIBERS_DECL mutex {
|
||||
private:
|
||||
friend class condition_variable;
|
||||
|
||||
typedef context::wait_queue_t wait_queue_type;
|
||||
|
||||
detail::spinlock wait_queue_splk_{};
|
||||
wait_queue_type wait_queue_{};
|
||||
wait_queue wait_queue_{};
|
||||
context * owner_{ nullptr };
|
||||
|
||||
public:
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace fibers {
|
||||
namespace numa {
|
||||
namespace algo {
|
||||
|
||||
class work_stealing : public boost::fibers::algo::algorithm {
|
||||
class BOOST_FIBERS_DECL work_stealing : public boost::fibers::algo::algorithm {
|
||||
private:
|
||||
static std::vector< intrusive_ptr< work_stealing > > schedulers_;
|
||||
|
||||
|
||||
@@ -38,14 +38,12 @@ template< typename Clock, typename Duration >
|
||||
void sleep_until( std::chrono::time_point< Clock, Duration > const& sleep_time_) {
|
||||
std::chrono::steady_clock::time_point sleep_time = boost::fibers::detail::convert( sleep_time_);
|
||||
fibers::context * active_ctx = fibers::context::active();
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
active_ctx->wait_until( sleep_time);
|
||||
}
|
||||
|
||||
template< typename Rep, typename Period >
|
||||
void sleep_for( std::chrono::duration< Rep, Period > const& timeout_duration) {
|
||||
fibers::context * active_ctx = fibers::context::active();
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
active_ctx->wait_until( std::chrono::steady_clock::now() + timeout_duration);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef BOOST_FIBERS_PROPERTIES_HPP
|
||||
#define BOOST_FIBERS_PROPERTIES_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
@@ -50,7 +51,10 @@ public:
|
||||
|
||||
// fiber_properties, and by implication every subclass, must accept a back
|
||||
// pointer to its context.
|
||||
fiber_properties( context * ctx) noexcept :
|
||||
|
||||
// For fiber_properties passed to fiber constructors, nullptr must be
|
||||
// used here.
|
||||
explicit fiber_properties( context * ctx) noexcept :
|
||||
ctx_{ ctx } {
|
||||
}
|
||||
|
||||
@@ -64,6 +68,14 @@ public:
|
||||
void set_algorithm( algo::algorithm * algo) noexcept {
|
||||
algo_ = algo;
|
||||
}
|
||||
|
||||
// not really intended for public use, but required to set properties
|
||||
// on fiber/context construction.
|
||||
void set_context( context* ctx ) noexcept {
|
||||
BOOST_ASSERT( ctx_ == nullptr );
|
||||
BOOST_ASSERT( ctx != nullptr );
|
||||
ctx_ = ctx;
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace boost::fibers
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <boost/fiber/context.hpp>
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/spinlock.hpp>
|
||||
#include <boost/fiber/waker.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
@@ -37,10 +38,8 @@ class BOOST_FIBERS_DECL recursive_mutex {
|
||||
private:
|
||||
friend class condition_variable;
|
||||
|
||||
typedef context::wait_queue_t wait_queue_type;
|
||||
|
||||
detail::spinlock wait_queue_splk_{};
|
||||
wait_queue_type wait_queue_{};
|
||||
wait_queue wait_queue_{};
|
||||
context * owner_{ nullptr };
|
||||
std::size_t count_{ 0 };
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/convert.hpp>
|
||||
#include <boost/fiber/detail/spinlock.hpp>
|
||||
#include <boost/fiber/waker.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
@@ -39,10 +40,8 @@ class BOOST_FIBERS_DECL recursive_timed_mutex {
|
||||
private:
|
||||
friend class condition_variable;
|
||||
|
||||
typedef context::wait_queue_t wait_queue_type;
|
||||
|
||||
detail::spinlock wait_queue_splk_{};
|
||||
wait_queue_type wait_queue_{};
|
||||
wait_queue wait_queue_{};
|
||||
context * owner_{ nullptr };
|
||||
std::size_t count_{ 0 };
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ private:
|
||||
// sleep-queue contains context' which have been called
|
||||
// scheduler::wait_until()
|
||||
sleep_queue_type sleep_queue_{};
|
||||
// worker-queue contains all context' mananged by this scheduler
|
||||
// worker-queue contains all context' managed by this scheduler
|
||||
// except main-context and dispatcher-context
|
||||
// unlink happens on destruction of a context
|
||||
worker_queue_type worker_queue_{};
|
||||
@@ -130,9 +130,11 @@ public:
|
||||
|
||||
bool wait_until( context *,
|
||||
std::chrono::steady_clock::time_point const&) noexcept;
|
||||
|
||||
bool wait_until( context *,
|
||||
std::chrono::steady_clock::time_point const&,
|
||||
detail::spinlock_lock &) noexcept;
|
||||
detail::spinlock_lock &,
|
||||
waker &&) noexcept;
|
||||
|
||||
void suspend() noexcept;
|
||||
void suspend( detail::spinlock_lock &) noexcept;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/convert.hpp>
|
||||
#include <boost/fiber/detail/spinlock.hpp>
|
||||
#include <boost/fiber/waker.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
@@ -35,10 +36,8 @@ class BOOST_FIBERS_DECL timed_mutex {
|
||||
private:
|
||||
friend class condition_variable;
|
||||
|
||||
typedef context::wait_queue_t wait_queue_type;
|
||||
|
||||
detail::spinlock wait_queue_splk_{};
|
||||
wait_queue_type wait_queue_{};
|
||||
wait_queue wait_queue_{};
|
||||
context * owner_{ nullptr };
|
||||
|
||||
bool try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time) noexcept;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
#include <boost/fiber/detail/spinlock.hpp>
|
||||
#include <boost/fiber/exceptions.hpp>
|
||||
#include <boost/fiber/waker.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
@@ -36,23 +37,21 @@ namespace fibers {
|
||||
template< typename T >
|
||||
class unbuffered_channel {
|
||||
public:
|
||||
typedef typename std::remove_reference< T >::type value_type;
|
||||
using value_type = typename std::remove_reference<T>::type;
|
||||
|
||||
private:
|
||||
typedef context::wait_queue_t wait_queue_type;
|
||||
|
||||
struct slot {
|
||||
value_type value;
|
||||
context * ctx;
|
||||
waker w;
|
||||
|
||||
slot( value_type const& value_, context * ctx_) :
|
||||
slot( value_type const& value_, waker && w) :
|
||||
value{ value_ },
|
||||
ctx{ ctx_ } {
|
||||
w{ std::move(w) } {
|
||||
}
|
||||
|
||||
slot( value_type && value_, context * ctx_) :
|
||||
slot( value_type && value_, waker && w) :
|
||||
value{ std::move( value_) },
|
||||
ctx{ ctx_ } {
|
||||
w{ std::move(w) } {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -61,9 +60,9 @@ private:
|
||||
// shared cacheline
|
||||
std::atomic_bool closed_{ false };
|
||||
mutable detail::spinlock splk_producers_{};
|
||||
wait_queue_type waiting_producers_{};
|
||||
wait_queue waiting_producers_{};
|
||||
mutable detail::spinlock splk_consumers_{};
|
||||
wait_queue_type waiting_consumers_{};
|
||||
wait_queue waiting_consumers_{};
|
||||
char pad_[cacheline_length];
|
||||
|
||||
bool is_empty_() {
|
||||
@@ -78,9 +77,8 @@ private:
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +95,7 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
unbuffered_channel() {
|
||||
}
|
||||
unbuffered_channel() = default;
|
||||
|
||||
~unbuffered_channel() {
|
||||
close();
|
||||
@@ -112,149 +109,86 @@ public:
|
||||
}
|
||||
|
||||
void close() noexcept {
|
||||
context * active_ctx = context::active();
|
||||
// set flag
|
||||
if ( ! closed_.exchange( true, std::memory_order_acquire) ) {
|
||||
// notify current waiting
|
||||
slot * s = slot_.load( std::memory_order_acquire);
|
||||
if ( nullptr != s) {
|
||||
// notify context
|
||||
active_ctx->schedule( s->ctx);
|
||||
s->w.wake();
|
||||
}
|
||||
// notify all waiting producers
|
||||
detail::spinlock_lock lk1{ splk_producers_ };
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
}
|
||||
}
|
||||
// notify all waiting consumers
|
||||
waiting_producers_.notify_all();
|
||||
|
||||
detail::spinlock_lock lk2{ splk_consumers_ };
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
}
|
||||
}
|
||||
waiting_consumers_.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
channel_op_status push( value_type const& value) {
|
||||
context * active_ctx = context::active();
|
||||
slot s{ value, active_ctx };
|
||||
slot s{ value, {} };
|
||||
for (;;) {
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
s.w = active_ctx->create_waker();
|
||||
if ( try_push_( & s) ) {
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_consumers_.notify_one();
|
||||
// suspend till value has been consumed
|
||||
active_ctx->suspend( lk);
|
||||
// resumed
|
||||
if ( nullptr == s.ctx) {
|
||||
// value has been consumed
|
||||
return channel_op_status::success;
|
||||
} else {
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
// channel was closed before value was consumed
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
} else {
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
active_ctx->wait_link( waiting_producers_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// suspend this producer
|
||||
active_ctx->suspend( lk);
|
||||
// resumed, slot mabye free
|
||||
// value has been consumed
|
||||
return channel_op_status::success;
|
||||
}
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
waiting_producers_.suspend_and_wait( lk, active_ctx);
|
||||
// resumed, slot mabye free
|
||||
}
|
||||
}
|
||||
|
||||
channel_op_status push( value_type && value) {
|
||||
context * active_ctx = context::active();
|
||||
slot s{ std::move( value), active_ctx };
|
||||
slot s{ std::move( value), {} };
|
||||
for (;;) {
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
s.w = active_ctx->create_waker();
|
||||
if ( try_push_( & s) ) {
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_consumers_.notify_one();
|
||||
// suspend till value has been consumed
|
||||
active_ctx->suspend( lk);
|
||||
// resumed
|
||||
if ( nullptr == s.ctx) {
|
||||
// value has been consumed
|
||||
return channel_op_status::success;
|
||||
} else {
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
// channel was closed before value was consumed
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
} else {
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
active_ctx->wait_link( waiting_producers_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// suspend this producer
|
||||
active_ctx->suspend( lk);
|
||||
// resumed, slot mabye free
|
||||
// value has been consumed
|
||||
return channel_op_status::success;
|
||||
}
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
waiting_producers_.suspend_and_wait( lk, active_ctx);
|
||||
// resumed, slot mabye free
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,33 +210,18 @@ public:
|
||||
channel_op_status push_wait_until( value_type const& value,
|
||||
std::chrono::time_point< Clock, Duration > const& timeout_time_) {
|
||||
context * active_ctx = context::active();
|
||||
slot s{ value, active_ctx };
|
||||
slot s{ value, {} };
|
||||
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
|
||||
for (;;) {
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
s.w = active_ctx->create_waker();
|
||||
if ( try_push_( & s) ) {
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_consumers_.notify_one();
|
||||
// suspend this producer
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
if ( ! active_ctx->wait_until(timeout_time, lk, waker(s.w))) {
|
||||
// clear slot
|
||||
slot * nil_slot = nullptr, * own_slot = & s;
|
||||
slot_.compare_exchange_strong( own_slot, nil_slot, std::memory_order_acq_rel);
|
||||
@@ -310,33 +229,26 @@ public:
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
// resumed
|
||||
if ( nullptr == s.ctx) {
|
||||
// value has been consumed
|
||||
return channel_op_status::success;
|
||||
} else {
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
// channel was closed before value was consumed
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
} else {
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
active_ctx->wait_link( waiting_producers_);
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
// suspend this producer
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
// relock local lk
|
||||
lk.lock();
|
||||
// remove from waiting-queue
|
||||
waiting_producers_.remove( * active_ctx);
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
// resumed, slot maybe free
|
||||
// value has been consumed
|
||||
return channel_op_status::success;
|
||||
}
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! waiting_producers_.suspend_and_wait_until( lk, active_ctx, timeout_time))
|
||||
{
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
// resumed, slot maybe free
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,33 +256,18 @@ public:
|
||||
channel_op_status push_wait_until( value_type && value,
|
||||
std::chrono::time_point< Clock, Duration > const& timeout_time_) {
|
||||
context * active_ctx = context::active();
|
||||
slot s{ std::move( value), active_ctx };
|
||||
slot s{ std::move( value), {} };
|
||||
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
|
||||
for (;;) {
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
s.w = active_ctx->create_waker();
|
||||
if ( try_push_( & s) ) {
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
// notify one waiting consumer
|
||||
while ( ! waiting_consumers_.empty() ) {
|
||||
context * consumer_ctx = & waiting_consumers_.front();
|
||||
waiting_consumers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( consumer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( consumer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_consumers_.notify_one();
|
||||
// suspend this producer
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
if ( ! active_ctx->wait_until(timeout_time, lk, waker(s.w))) {
|
||||
// clear slot
|
||||
slot * nil_slot = nullptr, * own_slot = & s;
|
||||
slot_.compare_exchange_strong( own_slot, nil_slot, std::memory_order_acq_rel);
|
||||
@@ -378,33 +275,25 @@ public:
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
// resumed
|
||||
if ( nullptr == s.ctx) {
|
||||
// value has been consumed
|
||||
return channel_op_status::success;
|
||||
} else {
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
// channel was closed before value was consumed
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
} else {
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
active_ctx->wait_link( waiting_producers_);
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
// suspend this producer
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
// relock local lk
|
||||
lk.lock();
|
||||
// remove from waiting-queue
|
||||
waiting_producers_.remove( * active_ctx);
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
// resumed, slot maybe free
|
||||
// value has been consumed
|
||||
return channel_op_status::success;
|
||||
}
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
if (! waiting_producers_.suspend_and_wait_until( lk, active_ctx, timeout_time))
|
||||
{
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
// resumed, slot maybe free
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,47 +304,22 @@ public:
|
||||
if ( nullptr != ( s = try_pop_() ) ) {
|
||||
{
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
// notify one waiting producer
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_producers_.notify_one();
|
||||
}
|
||||
value = std::move( s->value);
|
||||
// notify context
|
||||
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
|
||||
active_ctx->schedule( detail::exchange( s->ctx, nullptr) );
|
||||
#else
|
||||
active_ctx->schedule( std::exchange( s->ctx, nullptr) );
|
||||
#endif
|
||||
s->w.wake();
|
||||
return channel_op_status::success;
|
||||
} else {
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( ! is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
active_ctx->wait_link( waiting_consumers_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// suspend this consumer
|
||||
active_ctx->suspend( lk);
|
||||
// resumed, slot mabye set
|
||||
}
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( ! is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
waiting_consumers_.suspend_and_wait( lk, active_ctx);
|
||||
// resumed, slot mabye set
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,50 +330,25 @@ public:
|
||||
if ( nullptr != ( s = try_pop_() ) ) {
|
||||
{
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
// notify one waiting producer
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_producers_.notify_one();
|
||||
}
|
||||
// consume value
|
||||
value_type value = std::move( s->value);
|
||||
// notify context
|
||||
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
|
||||
active_ctx->schedule( detail::exchange( s->ctx, nullptr) );
|
||||
#else
|
||||
active_ctx->schedule( std::exchange( s->ctx, nullptr) );
|
||||
#endif
|
||||
s->w.wake();
|
||||
return std::move( value);
|
||||
} else {
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
throw fiber_error{
|
||||
std::make_error_code( std::errc::operation_not_permitted),
|
||||
"boost fiber: channel is closed" };
|
||||
}
|
||||
if ( ! is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
active_ctx->wait_link( waiting_consumers_);
|
||||
active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
|
||||
// suspend this consumer
|
||||
active_ctx->suspend( lk);
|
||||
// resumed, slot mabye set
|
||||
}
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
throw fiber_error{
|
||||
std::make_error_code( std::errc::operation_not_permitted),
|
||||
"boost fiber: channel is closed" };
|
||||
}
|
||||
if ( ! is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
waiting_consumers_.suspend_and_wait( lk, active_ctx);
|
||||
// resumed, slot mabye set
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,52 +369,23 @@ public:
|
||||
if ( nullptr != ( s = try_pop_() ) ) {
|
||||
{
|
||||
detail::spinlock_lock lk{ splk_producers_ };
|
||||
// notify one waiting producer
|
||||
while ( ! waiting_producers_.empty() ) {
|
||||
context * producer_ctx = & waiting_producers_.front();
|
||||
waiting_producers_.pop_front();
|
||||
std::intptr_t expected = reinterpret_cast< std::intptr_t >( this);
|
||||
if ( producer_ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) {
|
||||
lk.unlock();
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
} else if ( static_cast< std::intptr_t >( 0) == expected) {
|
||||
lk.unlock();
|
||||
// no timed-wait op.
|
||||
// notify context
|
||||
active_ctx->schedule( producer_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
waiting_producers_.notify_one();
|
||||
}
|
||||
// consume value
|
||||
value = std::move( s->value);
|
||||
// notify context
|
||||
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
|
||||
active_ctx->schedule( detail::exchange( s->ctx, nullptr) );
|
||||
#else
|
||||
active_ctx->schedule( std::exchange( s->ctx, nullptr) );
|
||||
#endif
|
||||
s->w.wake();
|
||||
return channel_op_status::success;
|
||||
} else {
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( ! is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
active_ctx->wait_link( waiting_consumers_);
|
||||
active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
|
||||
// suspend this consumer
|
||||
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
|
||||
// relock local lk
|
||||
lk.lock();
|
||||
// remove from waiting-queue
|
||||
waiting_consumers_.remove( * active_ctx);
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
}
|
||||
detail::spinlock_lock lk{ splk_consumers_ };
|
||||
if ( BOOST_UNLIKELY( is_closed() ) ) {
|
||||
return channel_op_status::closed;
|
||||
}
|
||||
if ( ! is_empty_() ) {
|
||||
continue;
|
||||
}
|
||||
if ( ! waiting_consumers_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
|
||||
return channel_op_status::timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -587,9 +397,12 @@ public:
|
||||
unbuffered_channel * chan_{ nullptr };
|
||||
storage_type storage_;
|
||||
|
||||
void increment_() {
|
||||
void increment_( bool initial = false) {
|
||||
BOOST_ASSERT( nullptr != chan_);
|
||||
try {
|
||||
if ( ! initial) {
|
||||
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
|
||||
}
|
||||
::new ( static_cast< void * >( std::addressof( storage_) ) ) value_type{ chan_->value_pop() };
|
||||
} catch ( fiber_error const&) {
|
||||
chan_ = nullptr;
|
||||
@@ -597,19 +410,19 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef value_type * pointer;
|
||||
typedef value_type & reference;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
typedef pointer pointer_t;
|
||||
typedef reference reference_t;
|
||||
using pointer_t = pointer;
|
||||
using reference_t = reference;
|
||||
|
||||
iterator() noexcept = default;
|
||||
iterator() = default;
|
||||
|
||||
explicit iterator( unbuffered_channel< T > * chan) noexcept :
|
||||
chan_{ chan } {
|
||||
increment_();
|
||||
increment_( true);
|
||||
}
|
||||
|
||||
iterator( iterator const& other) noexcept :
|
||||
@@ -631,11 +444,12 @@ public:
|
||||
}
|
||||
|
||||
iterator & operator++() {
|
||||
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
|
||||
increment_();
|
||||
return * this;
|
||||
}
|
||||
|
||||
iterator operator++( int) = delete;
|
||||
const iterator operator++( int) = delete;
|
||||
|
||||
reference_t operator*() noexcept {
|
||||
return * reinterpret_cast< value_type * >( std::addressof( storage_) );
|
||||
|
||||
88
linx64/include/boost/fiber/waker.hpp
Normal file
88
linx64/include/boost/fiber/waker.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef BOOST_FIBERS_WAKER_H
|
||||
#define BOOST_FIBERS_WAKER_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/spinlock.hpp>
|
||||
#include <boost/intrusive/slist.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
|
||||
class context;
|
||||
|
||||
namespace detail {
|
||||
|
||||
typedef intrusive::slist_member_hook<> waker_queue_hook;
|
||||
|
||||
} // detail
|
||||
|
||||
|
||||
class BOOST_FIBERS_DECL waker {
|
||||
private:
|
||||
context *ctx_{};
|
||||
size_t epoch_{};
|
||||
|
||||
public:
|
||||
friend class context;
|
||||
|
||||
waker() = default;
|
||||
|
||||
waker(context * ctx, const size_t epoch)
|
||||
: ctx_{ ctx }
|
||||
, epoch_{ epoch }
|
||||
{}
|
||||
|
||||
bool wake() const noexcept;
|
||||
};
|
||||
|
||||
|
||||
class BOOST_FIBERS_DECL waker_with_hook : public waker {
|
||||
public:
|
||||
explicit waker_with_hook(waker && w)
|
||||
: waker{ std::move(w) }
|
||||
{}
|
||||
|
||||
bool is_linked() const noexcept {
|
||||
return waker_queue_hook_.is_linked();
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator==( waker const& lhs, waker const& rhs) noexcept {
|
||||
return & lhs == & rhs;
|
||||
}
|
||||
|
||||
public:
|
||||
detail::waker_queue_hook waker_queue_hook_{};
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
typedef intrusive::slist<
|
||||
waker_with_hook,
|
||||
intrusive::member_hook<
|
||||
waker_with_hook, detail::waker_queue_hook, & waker_with_hook::waker_queue_hook_ >,
|
||||
intrusive::constant_time_size< false >,
|
||||
intrusive::cache_last< true >
|
||||
> waker_slist_t;
|
||||
}
|
||||
|
||||
class BOOST_FIBERS_DECL wait_queue {
|
||||
private:
|
||||
detail::waker_slist_t slist_{};
|
||||
|
||||
public:
|
||||
void suspend_and_wait( detail::spinlock_lock &, context *);
|
||||
bool suspend_and_wait_until( detail::spinlock_lock &,
|
||||
context *,
|
||||
std::chrono::steady_clock::time_point const&);
|
||||
void notify_one();
|
||||
void notify_all();
|
||||
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // BOOST_FIBERS_WAKER_H
|
||||
Reference in New Issue
Block a user