stabilize build system: depends, installer, boost/bdb fixes, cross targets groundwork

This commit is contained in:
2026-02-24 18:38:47 +00:00
parent da8c28aaeb
commit 65cb2619a7
13106 changed files with 2484322 additions and 1804 deletions
@@ -0,0 +1,114 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_ALGO_ALGORITHM_H
#define BOOST_FIBERS_ALGO_ALGORITHM_H
#include <cstddef>
#include <chrono>
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/fiber/properties.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
class context;
namespace algo {
struct BOOST_FIBERS_DECL algorithm {
virtual ~algorithm() {}
virtual void awakened( context *) noexcept = 0;
virtual context * pick_next() noexcept = 0;
virtual bool has_ready_fibers() const noexcept = 0;
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept = 0;
virtual void notify() noexcept = 0;
};
class BOOST_FIBERS_DECL algorithm_with_properties_base : public algorithm {
public:
// called by fiber_properties::notify() -- don't directly call
virtual void property_change_( context * ctx, fiber_properties * props) noexcept = 0;
protected:
static fiber_properties* get_properties( context * ctx) noexcept;
static void set_properties( context * ctx, fiber_properties * p) noexcept;
};
template< typename PROPS >
struct algorithm_with_properties : public algorithm_with_properties_base {
typedef algorithm_with_properties_base super;
// Mark this override 'final': algorithm_with_properties subclasses
// 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 {
fiber_properties * props = super::get_properties( ctx);
if ( nullptr == props) {
// TODO: would be great if PROPS could be allocated on the new
// fiber's stack somehow
props = new_properties( ctx);
// It is not good for new_properties() to return 0.
BOOST_ASSERT_MSG( props, "new_properties() must return non-NULL");
// new_properties() must return instance of (a subclass of) PROPS
BOOST_ASSERT_MSG( dynamic_cast< PROPS * >( props),
"new_properties() must return properties class");
super::set_properties( ctx, props);
}
// Set algo_ again every time this fiber becomes READY. That
// handles the case of a fiber migrating to a new thread with a new
// algorithm subclass instance.
props->set_algorithm( this);
// Okay, now forward the call to subclass override.
awakened( ctx, properties( ctx) );
}
// subclasses override this method instead of the original awakened()
virtual void awakened( context *, PROPS &) noexcept = 0;
// used for all internal calls
PROPS & properties( context * ctx) noexcept {
return static_cast< PROPS & >( * super::get_properties( ctx) );
}
// override this to be notified by PROPS::notify()
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 {
property_change( ctx, * static_cast< PROPS * >( props) );
}
// Override this to customize instantiation of PROPS, e.g. use a different
// allocator. Each PROPS instance is associated with a particular
// context.
virtual fiber_properties * new_properties( context * ctx) {
return new PROPS( ctx);
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGO_ALGORITHM_H
@@ -0,0 +1,69 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_ALGO_ROUND_ROBIN_H
#define BOOST_FIBERS_ALGO_ROUND_ROBIN_H
#include <condition_variable>
#include <chrono>
#include <mutex>
#include <boost/config.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/scheduler.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
namespace algo {
class BOOST_FIBERS_DECL round_robin : public algorithm {
private:
typedef scheduler::ready_queue_type rqueue_type;
rqueue_type rqueue_{};
std::mutex mtx_{};
std::condition_variable cnd_{};
bool flag_{ false };
public:
round_robin() = default;
round_robin( round_robin const&) = delete;
round_robin & operator=( round_robin const&) = delete;
virtual void awakened( context *) noexcept;
virtual context * pick_next() noexcept;
virtual bool has_ready_fibers() const noexcept;
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept;
virtual void notify() noexcept;
};
}}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGO_ROUND_ROBIN_H
@@ -0,0 +1,86 @@
// Copyright Nat Goodspeed + Oliver Kowalke 2015.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_ALGO_SHARED_WORK_H
#define BOOST_FIBERS_ALGO_SHARED_WORK_H
#include <condition_variable>
#include <chrono>
#include <deque>
#include <mutex>
#include <boost/config.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/scheduler.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
namespace algo {
class BOOST_FIBERS_DECL shared_work : public algorithm {
private:
typedef std::deque< context * > rqueue_type;
typedef scheduler::ready_queue_type lqueue_type;
static rqueue_type rqueue_;
static std::mutex rqueue_mtx_;
lqueue_type lqueue_{};
std::mutex mtx_{};
std::condition_variable cnd_{};
bool flag_{ false };
bool suspend_{ false };
public:
shared_work() = default;
shared_work( bool suspend) :
suspend_{ suspend } {
}
shared_work( shared_work const&) = delete;
shared_work( shared_work &&) = delete;
shared_work & operator=( shared_work const&) = delete;
shared_work & operator=( shared_work &&) = delete;
void awakened( context * ctx) noexcept;
context * pick_next() noexcept;
bool has_ready_fibers() const noexcept {
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 notify() noexcept;
};
}}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGO_SHARED_WORK_H
@@ -0,0 +1,84 @@
// Copyright Oliver Kowalke 2015.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_FIBERS_ALGO_WORK_STEALING_H
#define BOOST_FIBERS_ALGO_WORK_STEALING_H
#include <condition_variable>
#include <chrono>
#include <cstddef>
#include <mutex>
#include <vector>
#include <boost/config.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/detail/context_spinlock_queue.hpp>
#include <boost/fiber/detail/context_spmc_queue.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/scheduler.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace algo {
class work_stealing : public algorithm {
private:
static std::vector< work_stealing * > schedulers_;
std::size_t idx_;
std::size_t max_idx_;
#ifdef BOOST_FIBERS_USE_SPMC_QUEUE
alignas(cache_alignment) detail::context_spmc_queue rqueue_{};
#else
alignas(cache_alignment) detail::context_spinlock_queue rqueue_{};
#endif
std::mutex mtx_{};
std::condition_variable cnd_{};
bool flag_{ false };
bool suspend_;
static void init_( std::size_t max_idx);
public:
work_stealing( std::size_t max_idx, std::size_t idx, bool suspend = false);
work_stealing( work_stealing const&) = delete;
work_stealing( work_stealing &&) = delete;
work_stealing & operator=( work_stealing const&) = delete;
work_stealing & operator=( work_stealing &&) = delete;
void awakened( context * ctx) noexcept;
context * pick_next() noexcept;
context * steal() noexcept {
return rqueue_.steal();
}
bool has_ready_fibers() const noexcept {
return ! rqueue_.empty();
}
void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept;
void notify() noexcept;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGO_WORK_STEALING_H
@@ -0,0 +1,38 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_H
#define BOOST_FIBERS_H
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/algo/round_robin.hpp>
#include <boost/fiber/algo/shared_work.hpp>
#include <boost/fiber/algo/work_stealing.hpp>
#include <boost/fiber/barrier.hpp>
#include <boost/fiber/buffered_channel.hpp>
#include <boost/fiber/channel_op_status.hpp>
#include <boost/fiber/condition_variable.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/fiber.hpp>
#include <boost/fiber/fixedsize_stack.hpp>
#include <boost/fiber/fss.hpp>
#include <boost/fiber/future.hpp>
#include <boost/fiber/mutex.hpp>
#include <boost/fiber/operations.hpp>
#include <boost/fiber/policy.hpp>
#include <boost/fiber/pooled_fixedsize_stack.hpp>
#include <boost/fiber/properties.hpp>
#include <boost/fiber/protected_fixedsize_stack.hpp>
#include <boost/fiber/recursive_mutex.hpp>
#include <boost/fiber/recursive_timed_mutex.hpp>
#include <boost/fiber/scheduler.hpp>
#include <boost/fiber/segmented_stack.hpp>
#include <boost/fiber/timed_mutex.hpp>
#include <boost/fiber/type.hpp>
#include <boost/fiber/unbuffered_channel.hpp>
#endif // BOOST_FIBERS_H
@@ -0,0 +1,48 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_BARRIER_H
#define BOOST_FIBERS_BARRIER_H
#include <cstddef>
#include <boost/config.hpp>
#include <boost/fiber/condition_variable.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/mutex.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
class BOOST_FIBERS_DECL barrier {
private:
std::size_t initial_;
std::size_t current_;
std::size_t cycle_{ 0 };
mutex mtx_{};
condition_variable cond_{};
public:
explicit barrier( std::size_t);
barrier( barrier const&) = delete;
barrier & operator=( barrier const&) = delete;
bool wait();
};
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_BARRIER_H
@@ -0,0 +1,481 @@
// Copyright Oliver Kowalke 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_FIBERS_BUFFERED_CHANNEL_H
#define BOOST_FIBERS_BUFFERED_CHANNEL_H
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <type_traits>
#include <boost/config.hpp>
#include <boost/fiber/channel_op_status.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#include <boost/fiber/exceptions.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
template< typename T >
class buffered_channel {
public:
typedef T value_type;
private:
typedef context::wait_queue_t wait_queue_type;
typedef T slot_type;
alignas(cache_alignment) mutable detail::spinlock splk_{};
wait_queue_type waiting_producers_{};
wait_queue_type waiting_consumers_{};
slot_type * slots_;
std::size_t pidx_{ 0 };
std::size_t cidx_{ 0 };
std::size_t capacity_;
bool closed_{ false };
bool is_full_() const noexcept {
return cidx_ == ((pidx_ + 1) % capacity_);
}
bool is_empty_() const noexcept {
return cidx_ == pidx_;
}
bool is_closed_() const noexcept {
return closed_;
}
public:
explicit buffered_channel( std::size_t capacity) :
capacity_{ capacity } {
if ( 2 > capacity_ || 0 != ( capacity_ & (capacity_ - 1) ) ) {
throw fiber_error{ std::make_error_code( std::errc::invalid_argument),
"boost fiber: buffer capacity is invalid" };
}
slots_ = new slot_type[capacity_];
}
~buffered_channel() {
close();
delete [] slots_;
}
buffered_channel( buffered_channel const&) = delete;
buffered_channel & operator=( buffered_channel const&) = delete;
bool is_closed() const noexcept {
detail::spinlock_lock lk{ splk_ };
return is_closed_();
}
void close() noexcept {
context * active_ctx = context::active();
detail::spinlock_lock lk{ splk_ };
closed_ = true;
// notify all waiting producers
while ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
active_ctx->schedule( producer_ctx);
}
// notify all waiting consumers
while ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
active_ctx->schedule( consumer_ctx);
}
}
channel_op_status try_push( value_type const& value) {
context * active_ctx = context::active();
detail::spinlock_lock lk{ splk_ };
if ( 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
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
lk.unlock();
active_ctx->schedule( consumer_ctx);
}
return channel_op_status::success;
}
}
channel_op_status try_push( value_type && value) {
context * active_ctx = context::active();
detail::spinlock_lock lk{ splk_ };
if ( 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
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
lk.unlock();
active_ctx->schedule( consumer_ctx);
}
return channel_op_status::success;
}
}
channel_op_status push( value_type const& value) {
context * active_ctx = context::active();
for (;;) {
detail::spinlock_lock lk{ splk_ };
if ( is_closed_() ) {
return channel_op_status::closed;
} else if ( is_full_() ) {
active_ctx->wait_link( waiting_producers_);
// suspend this producer
active_ctx->suspend( lk);
} else {
slots_[pidx_] = value;
pidx_ = (pidx_ + 1) % capacity_;
// notify one waiting consumer
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
lk.unlock();
active_ctx->schedule( consumer_ctx);
}
return channel_op_status::success;
}
}
}
channel_op_status push( value_type && value) {
context * active_ctx = context::active();
for (;;) {
detail::spinlock_lock lk{ splk_ };
if ( is_closed_() ) {
return channel_op_status::closed;
} else if ( is_full_() ) {
active_ctx->wait_link( waiting_producers_);
// suspend this producer
active_ctx->suspend( lk);
} else {
slots_[pidx_] = std::move( value);
pidx_ = (pidx_ + 1) % capacity_;
// notify one waiting consumer
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
lk.unlock();
active_ctx->schedule( consumer_ctx);
}
return channel_op_status::success;
}
}
}
template< typename Rep, typename Period >
channel_op_status push_wait_for( value_type const& value,
std::chrono::duration< Rep, Period > const& timeout_duration) {
return push_wait_until( value,
std::chrono::steady_clock::now() + timeout_duration);
}
template< typename Rep, typename Period >
channel_op_status push_wait_for( value_type && value,
std::chrono::duration< Rep, Period > const& timeout_duration) {
return push_wait_until( std::forward< value_type >( value),
std::chrono::steady_clock::now() + timeout_duration);
}
template< typename Clock, typename Duration >
channel_op_status push_wait_until( value_type const& value,
std::chrono::time_point< Clock, Duration > const& timeout_time_) {
context * active_ctx = context::active();
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
for (;;) {
detail::spinlock_lock lk{ splk_ };
if ( is_closed_() ) {
return channel_op_status::closed;
} else if ( is_full_() ) {
active_ctx->wait_link( waiting_producers_);
// 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;
}
} else {
slots_[pidx_] = value;
pidx_ = (pidx_ + 1) % capacity_;
// notify one waiting consumer
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
lk.unlock();
active_ctx->schedule( consumer_ctx);
}
return channel_op_status::success;
}
}
}
template< typename Clock, typename Duration >
channel_op_status push_wait_until( value_type && value,
std::chrono::time_point< Clock, Duration > const& timeout_time_) {
context * active_ctx = context::active();
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
for (;;) {
detail::spinlock_lock lk{ splk_ };
if ( is_closed_() ) {
return channel_op_status::closed;
} else if ( is_full_() ) {
active_ctx->wait_link( waiting_producers_);
// 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;
}
} else {
slots_[pidx_] = std::move( value);
pidx_ = (pidx_ + 1) % capacity_;
// notify one waiting consumer
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
lk.unlock();
active_ctx->schedule( consumer_ctx);
}
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
if ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
lk.unlock();
active_ctx->schedule( producer_ctx);
}
return channel_op_status::success;
}
}
channel_op_status pop( value_type & value) {
context * active_ctx = context::active();
for (;;) {
detail::spinlock_lock lk{ splk_ };
if ( is_empty_() ) {
if ( is_closed_() ) {
return channel_op_status::closed;
} else {
active_ctx->wait_link( waiting_consumers_);
// suspend this consumer
active_ctx->suspend( lk);
}
} else {
value = std::move( slots_[cidx_]);
cidx_ = (cidx_ + 1) % capacity_;
// notify one waiting producer
if ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
lk.unlock();
active_ctx->schedule( producer_ctx);
}
return channel_op_status::success;
}
}
}
value_type value_pop() {
context * active_ctx = context::active();
for (;;) {
detail::spinlock_lock lk{ splk_ };
if ( is_empty_() ) {
if ( is_closed_() ) {
throw fiber_error{
std::make_error_code( std::errc::operation_not_permitted),
"boost fiber: channel is closed" };
} else {
active_ctx->wait_link( waiting_consumers_);
// suspend this consumer
active_ctx->suspend( lk);
}
} else {
value_type value = std::move( slots_[cidx_]);
cidx_ = (cidx_ + 1) % capacity_;
// notify one waiting producer
if ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
lk.unlock();
active_ctx->schedule( producer_ctx);
}
return std::move( value);
}
}
}
template< typename Rep, typename Period >
channel_op_status pop_wait_for( value_type & value,
std::chrono::duration< Rep, Period > const& timeout_duration) {
return pop_wait_until( value,
std::chrono::steady_clock::now() + timeout_duration);
}
template< typename Clock, typename Duration >
channel_op_status pop_wait_until( value_type & value,
std::chrono::time_point< Clock, Duration > const& timeout_time_) {
context * active_ctx = context::active();
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
for (;;) {
detail::spinlock_lock lk{ splk_ };
if ( is_empty_() ) {
if ( is_closed_() ) {
return channel_op_status::closed;
} else {
active_ctx->wait_link( waiting_consumers_);
// 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;
}
}
} else {
value = std::move( slots_[cidx_]);
cidx_ = (cidx_ + 1) % capacity_;
// notify one waiting producer
if ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
lk.unlock();
active_ctx->schedule( producer_ctx);
}
return channel_op_status::success;
}
}
}
class iterator : public std::iterator< std::input_iterator_tag, typename std::remove_reference< value_type >::type > {
private:
typedef typename std::aligned_storage< sizeof( value_type), alignof( value_type) >::type storage_type;
buffered_channel * chan_{ nullptr };
storage_type storage_;
void increment_() {
BOOST_ASSERT( nullptr != chan_);
try {
::new ( static_cast< void * >( std::addressof( storage_) ) ) value_type{ chan_->value_pop() };
} catch ( fiber_error const&) {
chan_ = nullptr;
}
}
public:
typedef typename iterator::pointer pointer_t;
typedef typename iterator::reference reference_t;
iterator() noexcept = default;
explicit iterator( buffered_channel< T > * chan) noexcept :
chan_{ chan } {
increment_();
}
iterator( iterator const& other) noexcept :
chan_{ other.chan_ } {
}
iterator & operator=( iterator const& other) noexcept {
if ( this == & other) return * this;
chan_ = other.chan_;
return * this;
}
bool operator==( iterator const& other) const noexcept {
return other.chan_ == chan_;
}
bool operator!=( iterator const& other) const noexcept {
return other.chan_ != chan_;
}
iterator & operator++() {
increment_();
return * this;
}
iterator operator++( int) = delete;
reference_t operator*() noexcept {
return * reinterpret_cast< value_type * >( std::addressof( storage_) );
}
pointer_t operator->() noexcept {
return reinterpret_cast< value_type * >( std::addressof( storage_) );
}
};
friend class iterator;
};
template< typename T >
typename buffered_channel< T >::iterator
begin( buffered_channel< T > & chan) {
return typename buffered_channel< T >::iterator( & chan);
}
template< typename T >
typename buffered_channel< T >::iterator
end( buffered_channel< T > &) {
return typename buffered_channel< T >::iterator();
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_BUFFERED_CHANNEL_H
@@ -0,0 +1,34 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_CHANNEL_OP_STATUS_H
#define BOOST_FIBERS_CHANNEL_OP_STATUS_H
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
enum class channel_op_status {
success = 0,
empty,
full,
closed,
timeout
};
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_CHANNEL_OP_STATUS_H
@@ -0,0 +1,254 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_CONDITION_VARIABLE_H
#define BOOST_FIBERS_CONDITION_VARIABLE_H
#include <algorithm>
#include <atomic>
#include <chrono>
#include <functional>
#include <mutex>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/mutex.hpp>
#include <boost/fiber/operations.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
//# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
enum class cv_status {
no_timeout = 1,
timeout
};
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_{};
public:
condition_variable_any() = default;
~condition_variable_any() {
BOOST_ASSERT( wait_queue_.empty() );
}
condition_variable_any( condition_variable_any const&) = delete;
condition_variable_any & operator=( condition_variable_any const&) = delete;
void notify_one() noexcept;
void notify_all() noexcept;
template< typename LockType >
void wait( LockType & lt) {
context * active_ctx = context::active();
// 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_);
// unlock external lt
lt.unlock();
// suspend this fiber
active_ctx->suspend( lk);
// relock external again before returning
try {
lt.lock();
} catch (...) {
std::terminate();
}
// post-conditions
BOOST_ASSERT( ! active_ctx->wait_is_linked() );
}
template< typename LockType, typename Pred >
void wait( LockType & lt, Pred pred) {
while ( ! pred() ) {
wait( lt);
}
}
template< typename LockType, typename Clock, typename Duration >
cv_status wait_until( LockType & lt, std::chrono::time_point< Clock, Duration > const& timeout_time_) {
context * active_ctx = context::active();
cv_status status = cv_status::no_timeout;
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
// 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_);
// unlock external lt
lt.unlock();
// suspend this fiber
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
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 {
lt.lock();
} catch (...) {
std::terminate();
}
// post-conditions
BOOST_ASSERT( ! active_ctx->wait_is_linked() );
return status;
}
template< typename LockType, typename Clock, typename Duration, typename Pred >
bool wait_until( LockType & lt,
std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) {
while ( ! pred() ) {
if ( cv_status::timeout == wait_until( lt, timeout_time) ) {
return pred();
}
}
return true;
}
template< typename LockType, typename Rep, typename Period >
cv_status wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration) {
return wait_until( lt,
std::chrono::steady_clock::now() + timeout_duration);
}
template< typename LockType, typename Rep, typename Period, typename Pred >
bool wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) {
return wait_until( lt,
std::chrono::steady_clock::now() + timeout_duration,
pred);
}
};
class BOOST_FIBERS_DECL condition_variable {
private:
condition_variable_any cnd_;
public:
condition_variable() = default;
condition_variable( condition_variable const&) = delete;
condition_variable & operator=( condition_variable const&) = delete;
void notify_one() noexcept {
cnd_.notify_one();
}
void notify_all() noexcept {
cnd_.notify_all();
}
void wait( std::unique_lock< mutex > & lt) {
// pre-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
cnd_.wait( lt);
// post-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
}
template< typename Pred >
void wait( std::unique_lock< mutex > & lt, Pred pred) {
// pre-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
cnd_.wait( lt, pred);
// post-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
}
template< typename Clock, typename Duration >
cv_status wait_until( std::unique_lock< mutex > & lt,
std::chrono::time_point< Clock, Duration > const& timeout_time) {
// pre-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
cv_status result = cnd_.wait_until( lt, timeout_time);
// post-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
return result;
}
template< typename Clock, typename Duration, typename Pred >
bool wait_until( std::unique_lock< mutex > & lt,
std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) {
// pre-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
bool result = cnd_.wait_until( lt, timeout_time, pred);
// post-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
return result;
}
template< typename Rep, typename Period >
cv_status wait_for( std::unique_lock< mutex > & lt,
std::chrono::duration< Rep, Period > const& timeout_duration) {
// pre-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
cv_status result = cnd_.wait_for( lt, timeout_duration);
// post-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
return result;
}
template< typename Rep, typename Period, typename Pred >
bool wait_for( std::unique_lock< mutex > & lt,
std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) {
// pre-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
bool result = cnd_.wait_for( lt, timeout_duration, pred);
// post-condition
BOOST_ASSERT( lt.owns_lock() );
BOOST_ASSERT( context::active() == lt.mutex()->owner_);
return result;
}
};
}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_CONDITION_VARIABLE_H
@@ -0,0 +1,611 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#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>
#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>
#include <boost/fiber/detail/decay_copy.hpp>
#include <boost/fiber/detail/fss.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#include <boost/fiber/detail/wrap.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/fixedsize_stack.hpp>
#include <boost/fiber/policy.hpp>
#include <boost/fiber/properties.hpp>
#include <boost/fiber/segmented_stack.hpp>
#include <boost/fiber/type.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
class context;
class fiber;
class scheduler;
namespace detail {
struct wait_tag;
typedef intrusive::slist_member_hook<
intrusive::tag< wait_tag >,
intrusive::link_mode<
intrusive::safe_link
>
> 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 >,
intrusive::link_mode<
intrusive::auto_unlink
>
> ready_hook;
struct sleep_tag;
typedef intrusive::set_member_hook<
intrusive::tag< sleep_tag >,
intrusive::link_mode<
intrusive::auto_unlink
>
> sleep_hook;
struct worker_tag;
typedef intrusive::list_member_hook<
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 remote_ready_tag;
typedef intrusive::slist_member_hook<
intrusive::tag< remote_ready_tag >,
intrusive::link_mode<
intrusive::safe_link
>
> remote_ready_hook;
}
struct main_context_t {};
const main_context_t main_context{};
struct dispatcher_context_t {};
const dispatcher_context_t dispatcher_context{};
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;
struct fss_data {
void * vp{ nullptr };
detail::fss_cleanup_function::ptr_t cleanup_function{};
fss_data() noexcept {
}
fss_data( void * vp_,
detail::fss_cleanup_function::ptr_t const& fn) noexcept :
vp( vp_),
cleanup_function( fn) {
BOOST_ASSERT( cleanup_function);
}
void do_cleanup() {
( * cleanup_function)( vp);
}
};
typedef std::map< uintptr_t, fss_data > fss_data_t;
#if ! defined(BOOST_FIBERS_NO_ATOMICS)
alignas(cache_alignment) std::atomic< std::size_t > use_count_{ 0 };
#else
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
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_;
#else
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 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 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()->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
terminate();
BOOST_ASSERT_MSG( false, "fiber already terminated");
}
#else
template< typename Fn, typename Tpl >
boost::context::continuation
run_( boost::context::continuation && c, Fn && fn_, Tpl && tpl_) noexcept {
{
// 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_);
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()->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 terminate();
}
#endif
public:
class id {
private:
context * impl_{ nullptr };
public:
id() = default;
explicit id( context * impl) noexcept :
impl_{ impl } {
}
bool operator==( id const& other) const noexcept {
return impl_ == other.impl_;
}
bool operator!=( id const& other) const noexcept {
return impl_ != other.impl_;
}
bool operator<( id const& other) const noexcept {
return impl_ < other.impl_;
}
bool operator>( id const& other) const noexcept {
return other.impl_ < impl_;
}
bool operator<=( id const& other) const noexcept {
return ! ( * this > other);
}
bool operator>=( id const& other) const noexcept {
return ! ( * this < other);
}
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) {
if ( nullptr != other.impl_) {
return os << other.impl_;
} else {
return os << "{not-valid}";
}
}
explicit operator bool() const noexcept {
return nullptr != impl_;
}
bool operator!() const noexcept {
return nullptr == impl_;
}
};
static context * active() noexcept;
static void reset_active() noexcept;
// main fiber context
explicit context( main_context_t) noexcept;
// dispatcher fiber context
context( dispatcher_context_t, boost::context::preallocated const&,
default_stack const&, scheduler *);
// worker fiber context
template< typename StackAlloc,
typename Fn,
typename Tpl
>
context( worker_context_t,
launch policy,
boost::context::preallocated palloc, StackAlloc salloc,
Fn && fn, Tpl && tpl) :
use_count_{ 1 }, // fiber instance or scheduler owner
#if (BOOST_EXECUTION_CONTEXT==1)
# 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 & ctx, void * vp) mutable noexcept {
run_( std::move( fn), std::move( tpl), static_cast< detail::data_t * >( vp) );
},
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)
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
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 {
return scheduler_;
}
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;
void suspend() noexcept;
void suspend( detail::spinlock_lock &) noexcept;
#if (BOOST_EXECUTION_CONTEXT==1)
void terminate() noexcept;
#else
boost::context::continuation suspend_with_cc() noexcept;
boost::context::continuation terminate() noexcept;
#endif
void join();
void yield() noexcept;
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;
void schedule( context *) noexcept;
bool is_context( type t) const noexcept {
return type::none != ( type_ & t);
}
void * get_fss_data( void const * vp) const;
void set_fss_data(
void const * vp,
detail::fss_cleanup_function::ptr_t const& cleanup_fn,
void * data,
bool cleanup_existing);
void set_properties( fiber_properties * props) noexcept;
fiber_properties * get_properties() const noexcept {
return properties_;
}
launch get_policy() const noexcept {
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;
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);
}
void worker_unlink() noexcept;
void ready_unlink() noexcept;
void sleep_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_.fetch_add( 1, std::memory_order_relaxed);
}
friend 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);
#if (BOOST_EXECUTION_CONTEXT==1)
boost::context::execution_context ec = ctx->ctx_;
// destruct context
// deallocates stack (execution_context is ref counted)
ctx->~context();
#else
boost::context::continuation c = std::move( ctx->c_);
// destruct context
ctx->~context();
// deallocated stack
c.resume( nullptr);
#endif
}
}
};
inline
bool operator<( context const& l, context const& r) noexcept {
return l.get_id() < r.get_id();
}
template< typename StackAlloc, typename Fn, typename ... Args >
static intrusive_ptr< context > make_worker_context( launch policy,
StackAlloc salloc,
Fn && fn, Args && ... args) {
boost::context::stack_context sctx = salloc.allocate();
#if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
// reserve space for control structure
const std::size_t size = sctx.size - sizeof( context);
void * sp = static_cast< char * >( sctx.sp) - sizeof( context);
#else
constexpr std::size_t func_alignment = 64; // alignof( context);
constexpr std::size_t func_size = sizeof( context);
// reserve space on stack
void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
// align sp pointer
std::size_t space = func_size + func_alignment;
sp = std::align( func_alignment, func_size, sp, space);
BOOST_ASSERT( nullptr != sp);
// calculate remaining size
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{
worker_context,
policy,
boost::context::preallocated{ sp, size, sctx },
salloc,
std::forward< Fn >( fn),
std::make_tuple( std::forward< Args >( args) ... ) } };
}
namespace detail {
inline
wait_functor::hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type & value) {
return & value.wait_hook_;
}
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)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_CONTEXT_H
@@ -0,0 +1,65 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_CONFIG_H
#define BOOST_FIBERS_DETAIL_CONFIG_H
#include <cstddef>
#include <boost/config.hpp>
#include <boost/predef.h>
#include <boost/detail/workaround.hpp>
#ifdef BOOST_FIBERS_DECL
# undef BOOST_FIBERS_DECL
#endif
#if (defined(BOOST_ALL_DYN_LINK) || defined(BOOST_FIBERS_DYN_LINK) ) && ! defined(BOOST_FIBERS_STATIC_LINK)
# if defined(BOOST_FIBERS_SOURCE)
# define BOOST_FIBERS_DECL BOOST_SYMBOL_EXPORT
# define BOOST_FIBERS_BUILD_DLL
# else
# define BOOST_FIBERS_DECL BOOST_SYMBOL_IMPORT
# endif
#endif
#if ! defined(BOOST_FIBERS_DECL)
# define BOOST_FIBERS_DECL
#endif
#if ! defined(BOOST_FIBERS_SOURCE) && ! defined(BOOST_ALL_NO_LIB) && ! defined(BOOST_FIBERS_NO_LIB)
# define BOOST_LIB_NAME boost_fiber
# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_FIBERS_DYN_LINK)
# define BOOST_DYN_LINK
# endif
# include <boost/config/auto_link.hpp>
#endif
#if BOOST_OS_LINUX || BOOST_OS_WINDOWS
# define BOOST_FIBERS_HAS_FUTEX
#endif
#if (!defined(BOOST_FIBERS_HAS_FUTEX) && \
(defined(BOOST_FIBERS_SPINLOCK_TTAS_FUTEX) || defined(BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX)))
# error "futex not supported on this platform"
#endif
#if !defined(BOOST_FIBERS_SPIN_MAX_COLLISIONS)
# define BOOST_FIBERS_SPIN_MAX_COLLISIONS 16
#endif
#if !defined(BOOST_FIBERS_SPIN_MAX_TESTS)
# define BOOST_FIBERS_SPIN_MAX_TESTS 500
#endif
// modern architectures have cachelines with 64byte length
// ARM Cortex-A15 32/64byte, Cortex-A9 16/32/64bytes
// MIPS 74K: 32byte, 4KEc: 16byte
// ist shoudl be safe to use 64byte for all
static constexpr std::size_t cache_alignment{ 64 };
static constexpr std::size_t cacheline_length{ 64 };
#endif // BOOST_FIBERS_DETAIL_CONFIG_H
@@ -0,0 +1,118 @@
// Copyright Oliver Kowalke 2015.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_FIBERS_DETAIL_SPINLOCK_QUEUE_H
#define BOOST_FIBERS_DETAIL_SPINLOCK_QUEUE_H
#include <cstddef>
#include <cstring>
#include <mutex>
#include <boost/config.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
class context_spinlock_queue {
private:
typedef context * slot_type;
alignas(cache_alignment) mutable spinlock splk_{};
std::size_t pidx_{ 0 };
std::size_t cidx_{ 0 };
std::size_t capacity_;
slot_type * slots_;
void resize_() {
slot_type * old_slots = slots_;
slots_ = new slot_type[2*capacity_];
std::size_t offset = capacity_ - cidx_;
std::memcpy( slots_, old_slots + cidx_, offset * sizeof( slot_type) );
if ( 0 < cidx_) {
std::memcpy( slots_ + offset, old_slots, pidx_ * sizeof( slot_type) );
}
cidx_ = 0;
pidx_ = capacity_ - 1;
capacity_ *= 2;
delete [] old_slots;
}
bool is_full_() const noexcept {
return cidx_ == ((pidx_ + 1) % capacity_);
}
bool is_empty_() const noexcept {
return cidx_ == pidx_;
}
public:
context_spinlock_queue( std::size_t capacity = 4096) :
capacity_{ capacity } {
slots_ = new slot_type[capacity_];
}
~context_spinlock_queue() {
delete [] slots_;
}
context_spinlock_queue( context_spinlock_queue const&) = delete;
context_spinlock_queue & operator=( context_spinlock_queue const&) = delete;
bool empty() const noexcept {
spinlock_lock lk{ splk_ };
return is_empty_();
}
void push( context * c) {
spinlock_lock lk{ splk_ };
if ( is_full_() ) {
resize_();
}
slots_[pidx_] = c;
pidx_ = (pidx_ + 1) % capacity_;
}
context * pop() {
spinlock_lock lk{ splk_ };
context * c = nullptr;
if ( ! is_empty_() ) {
c = slots_[cidx_];
cidx_ = (cidx_ + 1) % capacity_;
}
return c;
}
context * steal() {
spinlock_lock lk{ splk_ };
context * c = nullptr;
if ( ! is_empty_() ) {
c = slots_[cidx_];
if ( c->is_context( type::pinned_context) ) {
return nullptr;
}
cidx_ = (cidx_ + 1) % capacity_;
}
return c;
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_SPINLOCK_QUEUE_H
@@ -0,0 +1,199 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_CONTEXT_SPMC_QUEUE_H
#define BOOST_FIBERS_DETAIL_CONTEXT_SPMC_QUEUE_H
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <type_traits>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/context.hpp>
// David Chase and Yossi Lev. Dynamic circular work-stealing deque.
// In SPAA 05: Proceedings of the seventeenth annual ACM symposium
// on Parallelism in algorithms and architectures, pages 2128,
// New York, NY, USA, 2005. ACM.
//
// Nhat Minh Lê, Antoniu Pop, Albert Cohen, and Francesco Zappa Nardelli. 2013.
// Correct and efficient work-stealing for weak memory models.
// In Proceedings of the 18th ACM SIGPLAN symposium on Principles and practice
// of parallel programming (PPoPP '13). ACM, New York, NY, USA, 69-80.
#if BOOST_COMP_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field"
#endif
namespace boost {
namespace fibers {
namespace detail {
class context_spmc_queue {
private:
class array {
private:
typedef std::atomic< context * > atomic_type;
typedef std::aligned_storage<
sizeof( atomic_type), cache_alignment
>::type storage_type;
std::size_t capacity_;
storage_type * storage_;
public:
array( std::size_t capacity) :
capacity_{ capacity },
storage_{ new storage_type[capacity_] } {
for ( std::size_t i = 0; i < capacity_; ++i) {
::new ( static_cast< void * >( std::addressof( storage_[i]) ) ) atomic_type{ nullptr };
}
}
~array() {
for ( std::size_t i = 0; i < capacity_; ++i) {
reinterpret_cast< atomic_type * >( std::addressof( storage_[i]) )->~atomic_type();
}
delete [] storage_;
}
std::size_t capacity() const noexcept {
return capacity_;
}
void push( std::size_t bottom, context * ctx) noexcept {
reinterpret_cast< atomic_type * >(
std::addressof( storage_[bottom % capacity_]) )
->store( ctx, std::memory_order_relaxed);
}
context * pop( std::size_t top) noexcept {
return reinterpret_cast< atomic_type * >(
std::addressof( storage_[top % capacity_]) )
->load( std::memory_order_relaxed);
}
array * resize( std::size_t bottom, std::size_t top) {
std::unique_ptr< array > tmp{ new array{ 2 * capacity_ } };
for ( std::size_t i = top; i != bottom; ++i) {
tmp->push( i, pop( i) );
}
return tmp.release();
}
};
alignas(cache_alignment) std::atomic< std::size_t > top_{ 0 };
alignas(cache_alignment) std::atomic< std::size_t > bottom_{ 0 };
alignas(cache_alignment) std::atomic< array * > array_;
std::vector< array * > old_arrays_{};
char padding_[cacheline_length];
public:
context_spmc_queue( std::size_t capacity = 4096) :
array_{ new array{ capacity } } {
old_arrays_.reserve( 32);
}
~context_spmc_queue() {
for ( array * a : old_arrays_) {
delete a;
}
delete array_.load();
}
context_spmc_queue( context_spmc_queue const&) = delete;
context_spmc_queue & operator=( context_spmc_queue const&) = delete;
bool empty() const noexcept {
std::size_t bottom = bottom_.load( std::memory_order_relaxed);
std::size_t top = top_.load( std::memory_order_relaxed);
return bottom <= top;
}
void push( context * ctx) {
std::size_t bottom = bottom_.load( std::memory_order_relaxed);
std::size_t top = top_.load( std::memory_order_acquire);
array * a = array_.load( std::memory_order_relaxed);
if ( (a->capacity() - 1) < (bottom - top) ) {
// queue is full
// resize
array * tmp = a->resize( bottom, top);
old_arrays_.push_back( a);
std::swap( a, tmp);
array_.store( a, std::memory_order_relaxed);
}
a->push( bottom, ctx);
std::atomic_thread_fence( std::memory_order_release);
bottom_.store( bottom + 1, std::memory_order_relaxed);
}
context * pop() {
std::size_t bottom = bottom_.load( std::memory_order_relaxed) - 1;
array * a = array_.load( std::memory_order_relaxed);
bottom_.store( bottom, std::memory_order_relaxed);
std::atomic_thread_fence( std::memory_order_seq_cst);
std::size_t top = top_.load( std::memory_order_relaxed);
context * ctx = nullptr;
if ( top <= bottom) {
// queue is not empty
ctx = a->pop( bottom);
BOOST_ASSERT( nullptr != ctx);
if ( top == bottom) {
// last element dequeued
if ( ! top_.compare_exchange_strong( top, top + 1,
std::memory_order_seq_cst,
std::memory_order_relaxed) ) {
// lose the race
ctx = nullptr;
}
bottom_.store( bottom + 1, std::memory_order_relaxed);
}
} else {
// queue is empty
bottom_.store( bottom + 1, std::memory_order_relaxed);
}
return ctx;
}
context * steal() {
std::size_t top = top_.load( std::memory_order_acquire);
std::atomic_thread_fence( std::memory_order_seq_cst);
std::size_t bottom = bottom_.load( std::memory_order_acquire);
context * ctx = nullptr;
if ( top < bottom) {
// queue is not empty
array * a = array_.load( std::memory_order_consume);
ctx = a->pop( top);
BOOST_ASSERT( nullptr != ctx);
// do not steal pinned context (e.g. main-/dispatcher-context)
if ( ctx->is_context( type::pinned_context) ) {
return nullptr;
}
if ( ! top_.compare_exchange_strong( top, top + 1,
std::memory_order_seq_cst,
std::memory_order_relaxed) ) {
// lose the race
return nullptr;
}
}
return ctx;
}
};
}}}
#if BOOST_COMP_CLANG
#pragma clang diagnostic pop
#endif
#endif // BOOST_FIBERS_DETAIL_CONTEXT_SPMC_QUEUE_H
@@ -0,0 +1,59 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_CONVERT_H
#define BOOST_FIBERS_DETAIL_CONVERT_H
#include <chrono>
#include <memory>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
inline
std::chrono::steady_clock::time_point convert(
std::chrono::steady_clock::time_point const& timeout_time) noexcept {
return timeout_time;
}
template< typename Clock, typename Duration >
std::chrono::steady_clock::time_point convert(
std::chrono::time_point< Clock, Duration > const& timeout_time) {
return std::chrono::steady_clock::now() + ( timeout_time - Clock::now() );
}
// suggested by Howard Hinnant
template< typename T >
inline
T * convert( T * p) noexcept {
return p;
}
template< typename Pointer >
inline
typename std::pointer_traits< Pointer >::element_type *
convert( Pointer p) noexcept {
return nullptr != p
? to_raw_pointer( p.operator->() )
: nullptr;
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_CONVERT_H
@@ -0,0 +1,82 @@
// Copyright Oliver Kowalke 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_CPU_RELAX_H
#define BOOST_FIBERS_DETAIL_CPU_RELAX_H
#include <chrono>
#include <thread>
#include <boost/config.hpp>
#include <boost/predef.h>
#include <boost/fiber/detail/config.hpp>
#if BOOST_COMP_MSVC || BOOST_COMP_MSVC_EMULATED
# include <Windows.h>
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
#if BOOST_ARCH_ARM
# if BOOST_COMP_MSVC
# define cpu_relax() YieldProcessor();
# elif (defined(__ARM_ARCH_6K__) || \
defined(__ARM_ARCH_6Z__) || \
defined(__ARM_ARCH_6ZK__) || \
defined(__ARM_ARCH_6T2__) || \
defined(__ARM_ARCH_7__) || \
defined(__ARM_ARCH_7A__) || \
defined(__ARM_ARCH_7R__) || \
defined(__ARM_ARCH_7M__) || \
defined(__ARM_ARCH_7S__) || \
defined(__ARM_ARCH_8A__) || \
defined(__aarch64__))
// http://groups.google.com/a/chromium.org/forum/#!msg/chromium-dev/YGVrZbxYOlU/Vpgy__zeBQAJ
// mnemonic 'yield' is supported from ARMv6k onwards
# define cpu_relax() asm volatile ("yield" ::: "memory");
# else
# define cpu_relax() asm volatile ("nop" ::: "memory");
# endif
#elif BOOST_ARCH_MIPS
# 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
// http://stackoverflow.com/questions/5425506/equivalent-of-x86-pause-instruction-for-ppc
// mnemonic 'or' shared resource hints
// or 27, 27, 27 This form of 'or' provides a hint that performance
// will probably be imrpoved if shared resources dedicated
// to the executing processor are released for use by other
// processors
// extended mnemonics (available with POWER7)
// yield == or 27, 27, 27
# define cpu_relax() asm volatile ("or 27,27,27" ::: "memory");
#elif BOOST_ARCH_X86
# if BOOST_COMP_MSVC || BOOST_COMP_MSVC_EMULATED
# define cpu_relax() YieldProcessor();
# else
# define cpu_relax() asm volatile ("pause" ::: "memory");
# endif
#else
# define cpu_relax() { \
static constexpr std::chrono::microseconds us0{ 0 }; \
std::this_thread::sleep_for( us0); \
}
#endif
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_CPU_RELAX_H
@@ -0,0 +1,71 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_DATA_H
#define BOOST_FIBERS_DETAIL_DATA_H
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
class context;
namespace detail {
#if (BOOST_EXECUTION_CONTEXT==1)
struct data_t {
spinlock_lock * lk{ nullptr };
context * ctx{ nullptr };
data_t() = default;
explicit data_t( spinlock_lock * lk_) noexcept :
lk{ lk_ } {
}
explicit data_t( context * ctx_) noexcept :
ctx{ ctx_ } {
}
};
#else
struct data_t {
spinlock_lock * lk{ nullptr };
context * ctx{ nullptr };
context * from;
explicit data_t( context * from_) noexcept :
from{ from_ } {
}
explicit data_t( spinlock_lock * lk_,
context * from_) noexcept :
lk{ lk_ },
from{ from_ } {
}
explicit data_t( context * ctx_,
context * from_) noexcept :
ctx{ ctx_ },
from{ from_ } {
}
};
#endif
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_DATA_H
@@ -0,0 +1,36 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBER_DETAIL_DECAY_COPY_H
#define BOOST_FIBER_DETAIL_DECAY_COPY_H
#include <type_traits>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
template< typename T >
typename std::decay< T >::type
decay_copy( T && t) {
return std::forward< T >( t);
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBER_DETAIL_DECAY_COPY_H
@@ -0,0 +1,34 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBER_DETAIL_DISABLE_OVERLOAD_H
#define BOOST_FIBER_DETAIL_DISABLE_OVERLOAD_H
#include <type_traits>
#include <boost/config.hpp>
#include <boost/context/detail/disable_overload.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
template< typename X, typename Y >
using disable_overload = boost::context::detail::disable_overload< X, Y >;
}}}
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBER_DETAIL_DISABLE_OVERLOAD_H
@@ -0,0 +1,59 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// based on tss.hpp from boost.thread
#ifndef BOOST_FIBERS_DETAIL_FSS_H
#define BOOST_FIBERS_DETAIL_FSS_H
#include <atomic>
#include <cstddef>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
class fss_cleanup_function {
private:
std::atomic< std::size_t > use_count_{ 0 };
public:
typedef intrusive_ptr< fss_cleanup_function > ptr_t;
fss_cleanup_function() noexcept = default;
virtual ~fss_cleanup_function() = default;
virtual void operator()( void * data) = 0;
friend inline
void intrusive_ptr_add_ref( fss_cleanup_function * p) noexcept {
p->use_count_.fetch_add( 1, std::memory_order_relaxed);
}
friend inline
void intrusive_ptr_release( fss_cleanup_function * p) noexcept {
if ( 1 == p->use_count_.fetch_sub( 1, std::memory_order_release) ) {
std::atomic_thread_fence( std::memory_order_acquire);
delete p;
}
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_FSS_H
@@ -0,0 +1,61 @@
// Copyright Oliver Kowalke 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_FUTEX_H
#define BOOST_FIBERS_DETAIL_FUTEX_H
#include <boost/config.hpp>
#include <boost/predef.h>
#include <boost/fiber/detail/config.hpp>
#if BOOST_OS_LINUX
extern "C" {
#include <linux/futex.h>
#include <sys/syscall.h>
}
#elif BOOST_OS_WINDOWS
#include <Windows.h>
#endif
namespace boost {
namespace fibers {
namespace detail {
#if BOOST_OS_LINUX
inline
int sys_futex( void * addr, std::int32_t op, std::int32_t x) {
return ::syscall( SYS_futex, addr, op, x, nullptr, nullptr, 0);
}
inline
int futex_wake( std::atomic< std::int32_t > * addr) {
return 0 <= sys_futex( static_cast< void * >( addr), FUTEX_WAKE_PRIVATE, 1) ? 0 : -1;
}
inline
int futex_wait( std::atomic< std::int32_t > * addr, std::int32_t x) {
return 0 <= sys_futex( static_cast< void * >( addr), FUTEX_WAIT_PRIVATE, x) ? 0 : -1;
}
#elif BOOST_OS_WINDOWS
inline
int futex_wake( std::atomic< std::int32_t > * addr) {
::WakeByAddressSingle( static_cast< void * >( addr) );
return 0;
}
inline
int futex_wait( std::atomic< std::int32_t > * addr, std::int32_t x) {
::WaitOnAddress( static_cast< volatile void * >( addr), & x, sizeof( x), INFINITE);
return 0;
}
#else
# warn "no futex support on this platform"
#endif
}}}
#endif // BOOST_FIBERS_DETAIL_FUTEX_H
@@ -0,0 +1,65 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_SPINLOCK_H
#define BOOST_FIBERS_SPINLOCK_H
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#if !defined(BOOST_FIBERS_NO_ATOMICS)
# include <mutex>
# include <boost/fiber/detail/spinlock_ttas.hpp>
# include <boost/fiber/detail/spinlock_ttas_adaptive.hpp>
# if defined(BOOST_FIBERS_HAS_FUTEX)
# include <boost/fiber/detail/spinlock_ttas_futex.hpp>
# include <boost/fiber/detail/spinlock_ttas_adaptive_futex.hpp>
# endif
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
#if defined(BOOST_FIBERS_NO_ATOMICS)
struct spinlock {
constexpr spinlock() noexcept {}
void lock() noexcept {}
void unlock() noexcept {}
};
struct spinlock_lock {
constexpr spinlock_lock( spinlock &) noexcept {}
void lock() noexcept {}
void unlock() noexcept {}
};
#else
# if defined(BOOST_FIBERS_SPINLOCK_STD_MUTEX)
using spinlock = std::mutex;
# elif defined(BOOST_FIBERS_SPINLOCK_TTAS_FUTEX)
using spinlock = spinlock_ttas_futex;
# elif defined(BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX)
using spinlock = spinlock_ttas_adaptive_futex;
# elif defined(BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE)
using spinlock = spinlock_ttas_adaptive;
# else
using spinlock = spinlock_ttas;
# endif
using spinlock_lock = std::unique_lock< spinlock >;
#endif
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_SPINLOCK_H
@@ -0,0 +1,115 @@
// Copyright Oliver Kowalke 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_SPINLOCK_TTAS_H
#define BOOST_FIBERS_SPINLOCK_TTAS_H
#include <atomic>
#include <chrono>
#include <random>
#include <thread>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/cpu_relax.hpp>
// based on informations from:
// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops
// https://software.intel.com/en-us/articles/long-duration-spin-wait-loops-on-hyper-threading-technology-enabled-intel-processors
#if BOOST_COMP_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field"
#endif
namespace boost {
namespace fibers {
namespace detail {
class spinlock_ttas {
private:
enum class spinlock_status {
locked = 0,
unlocked
};
std::atomic< spinlock_status > state_{ spinlock_status::unlocked };
public:
spinlock_ttas() noexcept = default;
spinlock_ttas( spinlock_ttas const&) = delete;
spinlock_ttas & operator=( spinlock_ttas const&) = delete;
void lock() noexcept {
std::size_t collisions = 0 ;
for (;;) {
// avoid using multiple pause instructions for a delay of a specific cycle count
// the delay of cpu_relax() (pause on Intel) depends on the processor family
// the cycle count can not guaranteed from one system to the next
// -> check the shared variable 'state_' in between each cpu_relax() to prevent
// unnecessarily long delays on some systems
std::size_t tests = 0;
// test shared variable 'status_'
// first access to 'state_' -> chache miss
// sucessive acccess to 'state_' -> cache hit
// if 'state_' was released by other fiber
// cached 'state_' is invalidated -> cache miss
while ( spinlock_status::locked == state_.load( std::memory_order_relaxed) ) {
#if !defined(BOOST_FIBERS_SPIN_SINGLE_CORE)
if ( BOOST_FIBERS_SPIN_MAX_TESTS > tests) {
++tests;
// give CPU a hint that this thread is in a "spin-wait" loop
// delays the next instruction's execution for a finite period of time (depends on processor family)
// the CPU is not under demand, parts of the pipeline are no longer being used
// -> reduces the power consumed by the CPU
// -> prevent pipeline stalls
cpu_relax();
} else {
// 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
// if and only if a thread of equal or greater priority is ready to run
static constexpr std::chrono::microseconds us0{ 0 };
std::this_thread::sleep_for( us0);
}
#else
std::this_thread::yield();
#endif
}
// test-and-set shared variable 'status_'
// everytime 'status_' is signaled over the bus, even if the test failes
if ( spinlock_status::locked == state_.exchange( spinlock_status::locked, std::memory_order_acquire) ) {
// spinlock now contended
// utilize 'Binary Exponential Backoff' algorithm
// linear_congruential_engine is a random number engine based on Linear congruential generator (LCG)
static thread_local std::minstd_rand generator;
static std::uniform_int_distribution< std::size_t > distribution{ 0, static_cast< std::size_t >( 1) << collisions };
const std::size_t z = distribution( generator);
++collisions;
for ( std::size_t i = 0; i < z; ++i) {
// -> reduces the power consumed by the CPU
// -> prevent pipeline stalls
cpu_relax();
}
} else {
// success, thread has acquired the lock
break;
}
}
}
void unlock() noexcept {
state_.store( spinlock_status::unlocked, std::memory_order_release);
}
};
}}}
#if BOOST_COMP_CLANG
#pragma clang diagnostic pop
#endif
#endif // BOOST_FIBERS_SPINLOCK_TTAS_H
@@ -0,0 +1,112 @@
// Copyright Oliver Kowalke 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_H
#define BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_H
#include <atomic>
#include <chrono>
#include <cmath>
#include <random>
#include <thread>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/cpu_relax.hpp>
// based on informations from:
// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops
// https://software.intel.com/en-us/articles/long-duration-spin-wait-loops-on-hyper-threading-technology-enabled-intel-processors
namespace boost {
namespace fibers {
namespace detail {
class spinlock_ttas_adaptive {
private:
enum class spinlock_status {
locked = 0,
unlocked
};
std::atomic< spinlock_status > state_{ spinlock_status::unlocked };
std::atomic< std::size_t > tests_{ 0 };
public:
spinlock_ttas_adaptive() noexcept = default;
spinlock_ttas_adaptive( spinlock_ttas_adaptive const&) = delete;
spinlock_ttas_adaptive & operator=( spinlock_ttas_adaptive const&) = delete;
void lock() noexcept {
std::size_t collisions = 0 ;
for (;;) {
std::size_t tests = 0;
const std::size_t prev_tests = tests_.load( std::memory_order_relaxed);
const std::size_t max_tests = (std::min)( static_cast< std::size_t >( BOOST_FIBERS_SPIN_MAX_TESTS), 2 * prev_tests + 10);
// avoid using multiple pause instructions for a delay of a specific cycle count
// the delay of cpu_relax() (pause on Intel) depends on the processor family
// the cycle count can not guaranteed from one system to the next
// -> check the shared variable 'state_' in between each cpu_relax() to prevent
// unnecessarily long delays on some systems
// test shared variable 'status_'
// first access to 'state_' -> chache miss
// sucessive acccess to 'state_' -> cache hit
// if 'state_' was released by other fiber
// cached 'state_' is invalidated -> cache miss
while ( spinlock_status::locked == state_.load( std::memory_order_relaxed) ) {
#if !defined(BOOST_FIBERS_SPIN_SINGLE_CORE)
if ( max_tests > tests) {
++tests;
// give CPU a hint that this thread is in a "spin-wait" loop
// delays the next instruction's execution for a finite period of time (depends on processor family)
// the CPU is not under demand, parts of the pipeline are no longer being used
// -> reduces the power consumed by the CPU
// -> prevent pipeline stalls
cpu_relax();
} else {
++tests;
// 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
// if and only if a thread of equal or greater priority is ready to run
static constexpr std::chrono::microseconds us0{ 0 };
std::this_thread::sleep_for( us0);
}
#else
std::this_thread::yield();
#endif
}
// test-and-set shared variable 'status_'
// everytime 'status_' is signaled over the bus, even if the test failes
if ( spinlock_status::locked == state_.exchange( spinlock_status::locked, std::memory_order_acquire) ) {
// spinlock now contended
// utilize 'Binary Exponential Backoff' algorithm
// linear_congruential_engine is a random number engine based on Linear congruential generator (LCG)
static thread_local std::minstd_rand generator;
static std::uniform_int_distribution< std::size_t > distribution{ 0, static_cast< std::size_t >( 1) << collisions };
const std::size_t z = distribution( generator);
++collisions;
for ( std::size_t i = 0; i < z; ++i) {
// -> reduces the power consumed by the CPU
// -> prevent pipeline stalls
cpu_relax();
}
} else {
tests_.store( prev_tests + (tests - prev_tests) / 8, std::memory_order_relaxed);
// success, thread has acquired the lock
break;
}
}
}
void unlock() noexcept {
state_.store( spinlock_status::unlocked, std::memory_order_release);
}
};
}}}
#endif // BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_H
@@ -0,0 +1,111 @@
// Copyright Oliver Kowalke 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX_H
#define BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX_H
#include <atomic>
#include <cmath>
#include <random>
#include <thread>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/cpu_relax.hpp>
#include <boost/fiber/detail/futex.hpp>
// based on informations from:
// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops
// https://software.intel.com/en-us/articles/long-duration-spin-wait-loops-on-hyper-threading-technology-enabled-intel-processors
namespace boost {
namespace fibers {
namespace detail {
class spinlock_ttas_adaptive_futex {
private:
std::atomic< std::int32_t > value_{ 0 };
std::atomic< std::int32_t > tests_{ 0 };
public:
spinlock_ttas_adaptive_futex() noexcept = default;
spinlock_ttas_adaptive_futex( spinlock_ttas_adaptive_futex const&) = delete;
spinlock_ttas_adaptive_futex & operator=( spinlock_ttas_adaptive_futex const&) = delete;
void lock() noexcept {
std::int32_t collisions = 0, tests = 0, expected = 0;
const std::int32_t prev_tests = tests_.load( std::memory_order_relaxed);
const std::int32_t max_tests = (std::min)( static_cast< std::int32_t >( BOOST_FIBERS_SPIN_MAX_TESTS), 2 * prev_tests + 10);
// after max. spins or collisions suspend via futex
while ( max_tests > tests && BOOST_FIBERS_SPIN_MAX_COLLISIONS > collisions) {
// avoid using multiple pause instructions for a delay of a specific cycle count
// the delay of cpu_relax() (pause on Intel) depends on the processor family
// the cycle count can not guaranteed from one system to the next
// -> check the shared variable 'value_' in between each cpu_relax() to prevent
// unnecessarily long delays on some systems
// test shared variable 'status_'
// first access to 'value_' -> chache miss
// sucessive acccess to 'value_' -> cache hit
// if 'value_' was released by other fiber
// cached 'value_' is invalidated -> cache miss
if ( 0 != ( expected = value_.load( std::memory_order_relaxed) ) ) {
++tests;
#if !defined(BOOST_FIBERS_SPIN_SINGLE_CORE)
// give CPU a hint that this thread is in a "spin-wait" loop
// delays the next instruction's execution for a finite period of time (depends on processor family)
// the CPU is not under demand, parts of the pipeline are no longer being used
// -> reduces the power consumed by the CPU
// -> prevent pipeline stalls
cpu_relax();
#else
// std::this_thread::yield() allows this_thread to give up the remaining part of its time slice,
// but only to another thread on the same processor
// instead of constant checking, a thread only checks if no other useful work is pending
std::this_thread::yield();
#endif
} else if ( ! value_.compare_exchange_strong( expected, 1, std::memory_order_acquire, std::memory_order_release) ) {
// spinlock now contended
// utilize 'Binary Exponential Backoff' algorithm
// linear_congruential_engine is a random number engine based on Linear congruential generator (LCG)
static thread_local std::minstd_rand generator;
static std::uniform_int_distribution< std::int32_t > distribution{ 0, static_cast< std::int32_t >( 1) << collisions };
const std::int32_t z = distribution( generator);
++collisions;
for ( std::int32_t i = 0; i < z; ++i) {
// -> reduces the power consumed by the CPU
// -> prevent pipeline stalls
cpu_relax();
}
} else {
// success, lock acquired
tests_.store( prev_tests + (tests - prev_tests) / 8, std::memory_order_relaxed);
return;
}
}
// failure, lock not acquired
// pause via futex
if ( 2 != expected) {
expected = value_.exchange( 2, std::memory_order_acquire);
}
while ( 0 != expected) {
futex_wait( & value_, 2);
expected = value_.exchange( 2, std::memory_order_acquire);
}
// success, lock acquired
tests_.store( prev_tests + (tests - prev_tests) / 8, std::memory_order_relaxed);
}
void unlock() noexcept {
if ( 1 != value_.fetch_sub( 1, std::memory_order_acquire) ) {
value_.store( 0, std::memory_order_release);
futex_wake( & value_);
}
}
};
}}}
#endif // BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX_H
@@ -0,0 +1,104 @@
// Copyright Oliver Kowalke 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_spinlock_ttas_futex_FUTEX_H
#define BOOST_FIBERS_spinlock_ttas_futex_FUTEX_H
#include <atomic>
#include <random>
#include <thread>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/cpu_relax.hpp>
#include <boost/fiber/detail/futex.hpp>
// based on informations from:
// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops
// https://software.intel.com/en-us/articles/long-duration-spin-wait-loops-on-hyper-threading-technology-enabled-intel-processors
namespace boost {
namespace fibers {
namespace detail {
class spinlock_ttas_futex {
private:
std::atomic< std::int32_t > value_{ 0 };
public:
spinlock_ttas_futex() noexcept = default;
spinlock_ttas_futex( spinlock_ttas_futex const&) = delete;
spinlock_ttas_futex & operator=( spinlock_ttas_futex const&) = delete;
void lock() noexcept {
std::int32_t collisions = 0, tests = 0, expected = 0;
// after max. spins or collisions suspend via futex
while ( BOOST_FIBERS_SPIN_MAX_TESTS > tests && BOOST_FIBERS_SPIN_MAX_COLLISIONS > collisions) {
// avoid using multiple pause instructions for a delay of a specific cycle count
// the delay of cpu_relax() (pause on Intel) depends on the processor family
// the cycle count can not guaranteed from one system to the next
// -> check the shared variable 'value_' in between each cpu_relax() to prevent
// unnecessarily long delays on some systems
// test shared variable 'status_'
// first access to 'value_' -> chache miss
// sucessive acccess to 'value_' -> cache hit
// if 'value_' was released by other fiber
// cached 'value_' is invalidated -> cache miss
if ( 0 != ( expected = value_.load( std::memory_order_relaxed) ) ) {
++tests;
#if !defined(BOOST_FIBERS_SPIN_SINGLE_CORE)
// give CPU a hint that this thread is in a "spin-wait" loop
// delays the next instruction's execution for a finite period of time (depends on processor family)
// the CPU is not under demand, parts of the pipeline are no longer being used
// -> reduces the power consumed by the CPU
// -> prevent pipeline stalls
cpu_relax();
#else
// std::this_thread::yield() allows this_thread to give up the remaining part of its time slice,
// but only to another thread on the same processor
// instead of constant checking, a thread only checks if no other useful work is pending
std::this_thread::yield();
#endif
} else if ( ! value_.compare_exchange_strong( expected, 1, std::memory_order_acquire, std::memory_order_release) ) {
// spinlock now contended
// utilize 'Binary Exponential Backoff' algorithm
// linear_congruential_engine is a random number engine based on Linear congruential generator (LCG)
static thread_local std::minstd_rand generator;
static std::uniform_int_distribution< std::int32_t > distribution{ 0, static_cast< std::int32_t >( 1) << collisions };
const std::int32_t z = distribution( generator);
++collisions;
for ( std::int32_t i = 0; i < z; ++i) {
// -> reduces the power consumed by the CPU
// -> prevent pipeline stalls
cpu_relax();
}
} else {
// success, lock acquired
return;
}
}
// failure, lock not acquired
// pause via futex
if ( 2 != expected) {
expected = value_.exchange( 2, std::memory_order_acquire);
}
while ( 0 != expected) {
futex_wait( & value_, 2);
expected = value_.exchange( 2, std::memory_order_acquire);
}
}
void unlock() noexcept {
if ( 1 != value_.fetch_sub( 1, std::memory_order_acquire) ) {
value_.store( 0, std::memory_order_release);
futex_wake( & value_);
}
}
};
}}}
#endif // BOOST_FIBERS_spinlock_ttas_futex_FUTEX_H
@@ -0,0 +1,131 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBER_DETAIL_WRAP_H
#define BOOST_FIBER_DETAIL_WRAP_H
#include <type_traits>
#include <boost/config.hpp>
#if defined(BOOST_NO_CXX17_STD_INVOKE)
#include <boost/context/detail/invoke.hpp>
#endif
#if (BOOST_EXECUTION_CONTEXT==1)
# include <boost/context/execution_context.hpp>
#else
# include <boost/context/continuation.hpp>
#endif
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/data.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
#if (BOOST_EXECUTION_CONTEXT==1)
template< typename Fn1, typename Fn2, typename Tpl >
class wrapper {
private:
typename std::decay< Fn1 >::type fn1_;
typename std::decay< Fn2 >::type fn2_;
typename std::decay< Tpl >::type tpl_;
boost::context::execution_context ctx_;
public:
wrapper( Fn1 && fn1, Fn2 && fn2, Tpl && tpl,
boost::context::execution_context const& ctx) :
fn1_{ std::move( fn1) },
fn2_{ std::move( fn2) },
tpl_{ std::move( tpl) },
ctx_{ ctx } {
}
wrapper( wrapper const&) = delete;
wrapper & operator=( wrapper const&) = delete;
wrapper( wrapper && other) = default;
wrapper & operator=( wrapper && other) = default;
void operator()( void * vp) {
#if defined(BOOST_NO_CXX17_STD_INVOKE)
boost::context::detail::invoke( std::move( fn1_), fn2_, tpl_, ctx_, vp);
#else
std::invoke( std::move( fn1_), fn2_, tpl_, ctx_, vp);
#endif
}
};
template< typename Fn1, typename Fn2, typename Tpl >
wrapper< Fn1, Fn2, Tpl >
wrap( Fn1 && fn1, Fn2 && fn2, Tpl && tpl,
boost::context::execution_context const& ctx) {
return wrapper< Fn1, Fn2, Tpl >{
std::forward< Fn1 >( fn1),
std::forward< Fn2 >( fn2),
std::forward< Tpl >( tpl),
ctx };
}
#else
template< typename Fn1, typename Fn2, typename Tpl >
class wrapper {
private:
typename std::decay< Fn1 >::type fn1_;
typename std::decay< Fn2 >::type fn2_;
typename std::decay< Tpl >::type tpl_;
public:
wrapper( Fn1 && fn1, Fn2 && fn2, Tpl && tpl) :
fn1_{ std::move( fn1) },
fn2_{ std::move( fn2) },
tpl_{ std::move( tpl) } {
}
wrapper( wrapper const&) = delete;
wrapper & operator=( wrapper const&) = delete;
wrapper( wrapper && other) = default;
wrapper & operator=( wrapper && other) = default;
boost::context::continuation
operator()( boost::context::continuation && c) {
#if defined(BOOST_NO_CXX17_STD_INVOKE)
return boost::context::detail::invoke(
std::move( fn1_),
fn2_,
tpl_,
std::forward< boost::context::continuation >( c) );
#else
return std::invoke(
std::move( fn1_),
fn2_,
tpl_,
std::forward< boost::context::continuation >( c) );
#endif
}
};
template< typename Fn1, typename Fn2, typename Tpl >
wrapper< Fn1, Fn2, Tpl >
wrap( Fn1 && fn1, Fn2 && fn2, Tpl && tpl) {
return wrapper< Fn1, Fn2, Tpl >{
std::forward< Fn1 >( fn1),
std::forward< Fn2 >( fn2),
std::forward< Tpl >( tpl) };
}
#endif
}}}
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBER_DETAIL_WRAP_H
@@ -0,0 +1,148 @@
//
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// based on boost.thread
#ifndef BOOST_fiber_errorS_H
#define BOOST_fiber_errorS_H
#include <future>
#include <stdexcept>
#include <string>
#include <system_error>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
class fiber_error : public std::system_error {
public:
fiber_error( std::error_code ec) :
std::system_error{ ec } {
}
fiber_error( std::error_code ec, const char * what_arg) :
std::system_error{ ec, what_arg } {
}
fiber_error( std::error_code ec, std::string const& what_arg) :
std::system_error{ ec, what_arg } {
}
virtual ~fiber_error() = default;
};
class lock_error : public fiber_error {
public:
lock_error( std::error_code ec) :
fiber_error{ ec } {
}
lock_error( std::error_code ec, const char * what_arg) :
fiber_error{ ec, what_arg } {
}
lock_error( std::error_code ec, std::string const& what_arg) :
fiber_error{ ec, what_arg } {
}
};
enum class future_errc {
broken_promise = 1,
future_already_retrieved,
promise_already_satisfied,
no_state
};
BOOST_FIBERS_DECL
std::error_category const& future_category() noexcept;
}}
namespace std {
template<>
struct is_error_code_enum< boost::fibers::future_errc > : public true_type {
};
inline
std::error_code make_error_code( boost::fibers::future_errc e) noexcept {
return std::error_code{ static_cast< int >( e), boost::fibers::future_category() };
}
inline
std::error_condition make_error_condition( boost::fibers::future_errc e) noexcept {
return std::error_condition{ static_cast< int >( e), boost::fibers::future_category() };
}
}
namespace boost {
namespace fibers {
class future_error : public fiber_error {
public:
future_error( std::error_code ec) :
fiber_error{ ec } {
}
};
class future_uninitialized : public future_error {
public:
future_uninitialized() :
future_error{ std::make_error_code( future_errc::no_state) } {
}
};
class future_already_retrieved : public future_error {
public:
future_already_retrieved() :
future_error{ std::make_error_code( future_errc::future_already_retrieved) } {
}
};
class broken_promise : public future_error {
public:
broken_promise() :
future_error{ std::make_error_code( future_errc::broken_promise) } {
}
};
class promise_already_satisfied : public future_error {
public:
promise_already_satisfied() :
future_error{ std::make_error_code( future_errc::promise_already_satisfied) } {
}
};
class promise_uninitialized : public future_error {
public:
promise_uninitialized() :
future_error{ std::make_error_code( future_errc::no_state) } {
}
};
class packaged_task_uninitialized : public future_error {
public:
packaged_task_uninitialized() :
future_error{ std::make_error_code( future_errc::no_state) } {
}
};
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_fiber_errorS_H
@@ -0,0 +1,162 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_FIBER_H
#define BOOST_FIBERS_FIBER_H
#include <algorithm>
#include <exception>
#include <memory>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/disable_overload.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/fixedsize_stack.hpp>
#include <boost/fiber/policy.hpp>
#include <boost/fiber/properties.hpp>
#include <boost/fiber/segmented_stack.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
class BOOST_FIBERS_DECL fiber {
private:
friend class context;
typedef intrusive_ptr< context > ptr_t;
ptr_t impl_{};
void start_() noexcept;
public:
typedef context::id id;
fiber() = default;
template< typename Fn,
typename ... Args,
typename = detail::disable_overload< fiber, Fn >
>
fiber( Fn && fn, Args && ... args) :
fiber{ launch::post,
std::allocator_arg, default_stack(),
std::forward< Fn >( fn), std::forward< Args >( args) ... } {
}
template< typename Fn,
typename ... Args,
typename = detail::disable_overload< fiber, Fn >
>
fiber( launch policy, Fn && fn, Args && ... args) :
fiber{ policy,
std::allocator_arg, default_stack(),
std::forward< Fn >( fn), std::forward< Args >( args) ... } {
}
template< typename StackAllocator,
typename Fn,
typename ... Args
>
fiber( std::allocator_arg_t, StackAllocator salloc, Fn && fn, Args && ... args) :
fiber{ launch::post,
std::allocator_arg, salloc,
std::forward< Fn >( fn), std::forward< Args >( args) ... } {
}
template< typename StackAllocator,
typename Fn,
typename ... Args
>
fiber( launch policy, std::allocator_arg_t, StackAllocator salloc, Fn && fn, Args && ... args) :
impl_{ make_worker_context( policy, salloc, std::forward< Fn >( fn), std::forward< Args >( args) ... ) } {
start_();
}
~fiber() {
if ( joinable() ) {
std::terminate();
}
}
fiber( fiber const&) = delete;
fiber & operator=( fiber const&) = delete;
fiber( fiber && other) noexcept :
impl_{} {
impl_.swap( other.impl_);
}
fiber & operator=( fiber && other) noexcept {
if ( joinable() ) {
std::terminate();
}
if ( this == & other) {
return * this;
}
impl_.swap( other.impl_);
return * this;
}
void swap( fiber & other) noexcept {
impl_.swap( other.impl_);
}
id get_id() const noexcept {
return impl_ ? impl_->get_id() : id();
}
bool joinable() const noexcept {
return nullptr != impl_;
}
void join();
void detach();
template< typename PROPS >
PROPS & properties() {
auto props = impl_->get_properties();
BOOST_ASSERT_MSG( props, "fiber::properties not set");
return dynamic_cast< PROPS & >( * props );
}
};
inline
bool operator<( fiber const& l, fiber const& r) noexcept {
return l.get_id() < r.get_id();
}
inline
void swap( fiber & l, fiber & r) noexcept {
return l.swap( r);
}
}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_FIBER_H
@@ -0,0 +1,33 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_FIXEDSIZE_STACK_H
#define BOOST_FIBERS_FIXEDSIZE_STACK_H
#include <boost/config.hpp>
#include <boost/context/fixedsize_stack.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
using fixedsize_stack = boost::context::fixedsize_stack;
#if !defined(BOOST_USE_SEGMENTED_STACKS)
using default_stack = boost::context::default_stack;
#endif
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_FIXEDSIZE_STACK_H
@@ -0,0 +1,107 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// based on tss.hpp from boost.thread
#ifndef BOOST_FIBERS_FSS_H
#define BOOST_FIBERS_FSS_H
#include <boost/config.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/fss.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
template< typename T >
class fiber_specific_ptr {
private:
struct default_cleanup_function : public detail::fss_cleanup_function {
void operator()( void * data) noexcept {
delete static_cast< T * >( data);
}
};
struct custom_cleanup_function : public detail::fss_cleanup_function {
void (*fn)(T*);
explicit custom_cleanup_function( void(*fn_)(T*) ) noexcept :
fn{ fn_ } {
}
void operator()( void* data) {
if ( fn) {
fn( static_cast< T * >( data) );
}
}
};
detail::fss_cleanup_function::ptr_t cleanup_fn_;
public:
typedef T element_type;
fiber_specific_ptr() :
cleanup_fn_{ new default_cleanup_function() } {
}
explicit fiber_specific_ptr( void(*fn)(T*) ) :
cleanup_fn_{ new custom_cleanup_function( fn) } {
}
~fiber_specific_ptr() {
context * active_ctx = context::active();
if ( nullptr != active_ctx) {
active_ctx->set_fss_data(
this, cleanup_fn_, nullptr, true);
}
}
fiber_specific_ptr( fiber_specific_ptr const&) = delete;
fiber_specific_ptr & operator=( fiber_specific_ptr const&) = delete;
T * get() const noexcept {
BOOST_ASSERT( context::active() );
void * vp = context::active()->get_fss_data( this);
return static_cast< T * >( vp);
}
T * operator->() const noexcept {
return get();
}
T & operator*() const noexcept {
return * get();
}
T * release() {
T * tmp = get();
context::active()->set_fss_data(
this, cleanup_fn_, nullptr, false);
return tmp;
}
void reset( T * t) {
T * c = get();
if ( c != t) {
context::active()->set_fss_data(
this, cleanup_fn_, t, true);
}
}
};
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_FSS_H
@@ -0,0 +1,10 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/fiber/future/async.hpp>
#include <boost/fiber/future/future.hpp>
#include <boost/fiber/future/packaged_task.hpp>
#include <boost/fiber/future/promise.hpp>
@@ -0,0 +1,112 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_ASYNC_HPP
#define BOOST_FIBERS_ASYNC_HPP
#include <algorithm>
#include <memory>
#include <type_traits>
#include <utility>
#include <boost/config.hpp>
#include <boost/fiber/future/future.hpp>
#include <boost/fiber/future/packaged_task.hpp>
#include <boost/fiber/policy.hpp>
namespace boost {
namespace fibers {
template< typename Fn, typename ... Args >
future<
typename std::result_of<
typename std::enable_if<
! detail::is_launch_policy< typename std::decay< Fn >::type >::value,
typename std::decay< Fn >::type
>::type( typename std::decay< Args >::type ... )
>::type
>
async( Fn && fn, Args && ... args) {
typedef typename std::result_of<
typename std::decay< Fn >::type( typename std::decay< Args >::type ... )
>::type result_type;
packaged_task< result_type( typename std::decay< Args >::type ... ) > pt{
std::forward< Fn >( fn) };
future< result_type > f{ pt.get_future() };
fiber{ std::move( pt), std::forward< Args >( args) ... }.detach();
return f;
}
template< typename Policy, typename Fn, typename ... Args >
future<
typename std::result_of<
typename std::enable_if<
detail::is_launch_policy< Policy >::value,
typename std::decay< Fn >::type
>::type( typename std::decay< Args >::type ...)
>::type
>
async( Policy policy, Fn && fn, Args && ... args) {
typedef typename std::result_of<
typename std::decay< Fn >::type( typename std::decay< Args >::type ... )
>::type result_type;
packaged_task< result_type( typename std::decay< Args >::type ... ) > pt{
std::forward< Fn >( fn) };
future< result_type > f{ pt.get_future() };
fiber{ policy, std::move( pt), std::forward< Args >( args) ... }.detach();
return f;
}
template< typename Policy, typename StackAllocator, typename Fn, typename ... Args >
future<
typename std::result_of<
typename std::enable_if<
detail::is_launch_policy< Policy >::value,
typename std::decay< Fn >::type
>::type( typename std::decay< Args >::type ... )
>::type
>
async( Policy policy, std::allocator_arg_t, StackAllocator salloc, Fn && fn, Args && ... args) {
typedef typename std::result_of<
typename std::decay< Fn >::type( typename std::decay< Args >::type ... )
>::type result_type;
packaged_task< result_type( typename std::decay< Args >::type ... ) > pt{
std::forward< Fn >( fn) };
future< result_type > f{ pt.get_future() };
fiber{ policy, std::allocator_arg, salloc,
std::move( pt), std::forward< Args >( args) ... }.detach();
return f;
}
template< typename Policy, typename StackAllocator, typename Allocator, typename Fn, typename ... Args >
future<
typename std::result_of<
typename std::enable_if<
detail::is_launch_policy< Policy >::value,
typename std::decay< Fn >::type
>::type( typename std::decay< Args >::type ... )
>::type
>
async( Policy policy, std::allocator_arg_t, StackAllocator salloc, Allocator alloc, Fn && fn, Args && ... args) {
typedef typename std::result_of<
typename std::decay< Fn >::type( typename std::decay< Args >::type ... )
>::type result_type;
packaged_task< result_type( typename std::decay< Args >::type ... ) > pt{
std::allocator_arg, alloc, std::forward< Fn >( fn) };
future< result_type > f{ pt.get_future() };
fiber{ policy, std::allocator_arg, salloc,
std::move( pt), std::forward< Args >( args) ... }.detach();
return f;
}
}}
#endif // BOOST_FIBERS_ASYNC_HPP
@@ -0,0 +1,313 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_SHARED_STATE_H
#define BOOST_FIBERS_DETAIL_SHARED_STATE_H
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstddef>
#include <exception>
#include <memory>
#include <mutex>
#include <type_traits>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/future/future_status.hpp>
#include <boost/fiber/condition_variable.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/mutex.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
class shared_state_base {
private:
std::atomic< std::size_t > use_count_{ 0 };
mutable condition_variable waiters_{};
protected:
mutable mutex mtx_{};
bool ready_{ false };
std::exception_ptr except_{};
void mark_ready_and_notify_( std::unique_lock< mutex > & lk) noexcept {
BOOST_ASSERT( lk.owns_lock() );
ready_ = true;
lk.unlock();
waiters_.notify_all();
}
void owner_destroyed_( std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
if ( ! ready_) {
set_exception_(
std::make_exception_ptr( broken_promise() ),
lk);
}
}
void set_exception_( std::exception_ptr except, std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
if ( ready_) {
throw promise_already_satisfied();
}
except_ = except;
mark_ready_and_notify_( lk);
}
std::exception_ptr get_exception_ptr_( std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
wait_( lk);
return except_;
}
void wait_( std::unique_lock< mutex > & lk) const {
BOOST_ASSERT( lk.owns_lock() );
waiters_.wait( lk, [this](){ return ready_; });
}
template< typename Rep, typename Period >
future_status wait_for_( std::unique_lock< mutex > & lk,
std::chrono::duration< Rep, Period > const& timeout_duration) const {
BOOST_ASSERT( lk.owns_lock() );
return waiters_.wait_for( lk, timeout_duration, [this](){ return ready_; })
? future_status::ready
: future_status::timeout;
}
template< typename Clock, typename Duration >
future_status wait_until_( std::unique_lock< mutex > & lk,
std::chrono::time_point< Clock, Duration > const& timeout_time) const {
BOOST_ASSERT( lk.owns_lock() );
return waiters_.wait_until( lk, timeout_time, [this](){ return ready_; })
? future_status::ready
: future_status::timeout;
}
virtual void deallocate_future() noexcept = 0;
public:
shared_state_base() = default;
virtual ~shared_state_base() = default;
shared_state_base( shared_state_base const&) = delete;
shared_state_base & operator=( shared_state_base const&) = delete;
void owner_destroyed() {
std::unique_lock< mutex > lk{ mtx_ };
owner_destroyed_( lk);
}
void set_exception( std::exception_ptr except) {
std::unique_lock< mutex > lk{ mtx_ };
set_exception_( except, lk);
}
std::exception_ptr get_exception_ptr() {
std::unique_lock< mutex > lk{ mtx_ };
return get_exception_ptr_( lk);
}
void wait() const {
std::unique_lock< mutex > lk{ mtx_ };
wait_( lk);
}
template< typename Rep, typename Period >
future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const {
std::unique_lock< mutex > lk{ mtx_ };
return wait_for_( lk, timeout_duration);
}
template< typename Clock, typename Duration >
future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const {
std::unique_lock< mutex > lk{ mtx_ };
return wait_until_( lk, timeout_time);
}
friend inline
void intrusive_ptr_add_ref( shared_state_base * p) noexcept {
p->use_count_.fetch_add( 1, std::memory_order_relaxed);
}
friend inline
void intrusive_ptr_release( shared_state_base * p) noexcept {
if ( 1 == p->use_count_.fetch_sub( 1, std::memory_order_release) ) {
std::atomic_thread_fence( std::memory_order_acquire);
p->deallocate_future();
}
}
};
template< typename R >
class shared_state : public shared_state_base {
private:
typename std::aligned_storage< sizeof( R), alignof( R) >::type storage_{};
void set_value_( R const& value, std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
if ( ready_) {
throw promise_already_satisfied{};
}
::new ( static_cast< void * >( std::addressof( storage_) ) ) R{ value };
mark_ready_and_notify_( lk);
}
void set_value_( R && value, std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
if ( ready_) {
throw promise_already_satisfied{};
}
::new ( static_cast< void * >( std::addressof( storage_) ) ) R{ std::move( value) };
mark_ready_and_notify_( lk);
}
R & get_( std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
wait_( lk);
if ( except_) {
std::rethrow_exception( except_);
}
return * reinterpret_cast< R * >( std::addressof( storage_) );
}
public:
typedef intrusive_ptr< shared_state > ptr_type;
shared_state() = default;
virtual ~shared_state() {
if ( ready_ && ! except_) {
reinterpret_cast< R * >( std::addressof( storage_) )->~R();
}
}
shared_state( shared_state const&) = delete;
shared_state & operator=( shared_state const&) = delete;
void set_value( R const& value) {
std::unique_lock< mutex > lk{ mtx_ };
set_value_( value, lk);
}
void set_value( R && value) {
std::unique_lock< mutex > lk{ mtx_ };
set_value_( std::move( value), lk);
}
R & get() {
std::unique_lock< mutex > lk{ mtx_ };
return get_( lk);
}
};
template< typename R >
class shared_state< R & > : public shared_state_base {
private:
R * value_{ nullptr };
void set_value_( R & value, std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
if ( ready_) {
throw promise_already_satisfied();
}
value_ = std::addressof( value);
mark_ready_and_notify_( lk);
}
R & get_( std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
wait_( lk);
if ( except_) {
std::rethrow_exception( except_);
}
return * value_;
}
public:
typedef intrusive_ptr< shared_state > ptr_type;
shared_state() = default;
virtual ~shared_state() = default;
shared_state( shared_state const&) = delete;
shared_state & operator=( shared_state const&) = delete;
void set_value( R & value) {
std::unique_lock< mutex > lk{ mtx_ };
set_value_( value, lk);
}
R & get() {
std::unique_lock< mutex > lk{ mtx_ };
return get_( lk);
}
};
template<>
class shared_state< void > : public shared_state_base {
private:
inline
void set_value_( std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
if ( ready_) {
throw promise_already_satisfied();
}
mark_ready_and_notify_( lk);
}
inline
void get_( std::unique_lock< mutex > & lk) {
BOOST_ASSERT( lk.owns_lock() );
wait_( lk);
if ( except_) {
std::rethrow_exception( except_);
}
}
public:
typedef intrusive_ptr< shared_state > ptr_type;
shared_state() = default;
virtual ~shared_state() = default;
shared_state( shared_state const&) = delete;
shared_state & operator=( shared_state const&) = delete;
inline
void set_value() {
std::unique_lock< mutex > lk{ mtx_ };
set_value_( lk);
}
inline
void get() {
std::unique_lock< mutex > lk{ mtx_ };
get_( lk);
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_SHARED_STATE_H
@@ -0,0 +1,58 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_SHARED_STATE_OBJECT_H
#define BOOST_FIBERS_DETAIL_SHARED_STATE_OBJECT_H
#include <memory>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/future/detail/shared_state.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
template< typename R, typename Allocator >
class shared_state_object : public shared_state< R > {
public:
typedef typename std::allocator_traits< Allocator >::template rebind_alloc<
shared_state_object
> allocator_type;
shared_state_object( allocator_type const& alloc) :
shared_state< R >{},
alloc_{ alloc } {
}
protected:
void deallocate_future() noexcept override final {
destroy_( alloc_, this);
}
private:
allocator_type alloc_;
static void destroy_( allocator_type const& alloc, shared_state_object * p) noexcept {
allocator_type a{ alloc };
a.destroy( p);
a.deallocate( p, 1);
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_SHARED_STATE_OBJECT_H
@@ -0,0 +1,41 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_TASK_BASE_H
#define BOOST_FIBERS_DETAIL_TASK_BASE_H
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/future/detail/shared_state.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
template< typename R, typename ... Args >
struct task_base : public shared_state< R > {
typedef intrusive_ptr< task_base > ptr_type;
virtual ~task_base() {
}
virtual void run( Args && ... args) = 0;
virtual ptr_type reset() = 0;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_TASK_BASE_H
@@ -0,0 +1,170 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DETAIL_TASK_OBJECT_H
#define BOOST_FIBERS_DETAIL_TASK_OBJECT_H
#include <exception>
#include <memory>
#include <tuple>
#include <utility>
#include <boost/config.hpp>
#if defined(BOOST_NO_CXX17_STD_APPLY)
#include <boost/context/detail/apply.hpp>
#endif
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/future/detail/task_base.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
template< typename Fn, typename Allocator, typename R, typename ... Args >
class task_object : public task_base< R, Args ... > {
private:
typedef task_base< R, Args ... > base_type;
public:
typedef typename std::allocator_traits< Allocator >::template rebind_alloc<
task_object
> allocator_type;
task_object( allocator_type const& alloc, Fn const& fn) :
base_type{},
fn_{ fn },
alloc_{ alloc } {
}
task_object( allocator_type const& alloc, Fn && fn) :
base_type{},
fn_{ std::move( fn) },
alloc_{ alloc } {
}
void run( Args && ... args) override final {
try {
this->set_value(
#if defined(BOOST_NO_CXX17_STD_APPLY)
boost::context::detail::apply(
fn_, std::make_tuple( std::forward< Args >( args) ... ) )
#else
std::apply(
fn_, std::make_tuple( std::forward< Args >( args) ... ) )
#endif
);
} catch (...) {
this->set_exception( std::current_exception() );
}
}
typename base_type::ptr_type reset() override final {
typedef std::allocator_traits< allocator_type > traity_type;
typename traity_type::pointer ptr{ traity_type::allocate( alloc_, 1) };
try {
traity_type::construct( alloc_, ptr, alloc_, std::move( fn_) );
} catch (...) {
traity_type::deallocate( alloc_, ptr, 1);
throw;
}
return { convert( ptr) };
}
protected:
void deallocate_future() noexcept override final {
destroy_( alloc_, this);
}
private:
Fn fn_;
allocator_type alloc_;
static void destroy_( allocator_type const& alloc, task_object * p) noexcept {
allocator_type a{ alloc };
a.destroy( p);
a.deallocate( p, 1);
}
};
template< typename Fn, typename Allocator, typename ... Args >
class task_object< Fn, Allocator, void, Args ... > : public task_base< void, Args ... > {
private:
typedef task_base< void, Args ... > base_type;
public:
typedef typename Allocator::template rebind<
task_object< Fn, Allocator, void, Args ... >
>::other allocator_type;
task_object( allocator_type const& alloc, Fn const& fn) :
base_type{},
fn_{ fn },
alloc_{ alloc } {
}
task_object( allocator_type const& alloc, Fn && fn) :
base_type{},
fn_{ std::move( fn) },
alloc_{ alloc } {
}
void run( Args && ... args) override final {
try {
#if defined(BOOST_NO_CXX17_STD_APPLY)
boost::context::detail::apply(
fn_, std::make_tuple( std::forward< Args >( args) ... ) );
#else
std::apply(
fn_, std::make_tuple( std::forward< Args >( args) ... ) );
#endif
this->set_value();
} catch (...) {
this->set_exception( std::current_exception() );
}
}
typename base_type::ptr_type reset() override final {
typedef std::allocator_traits< allocator_type > traity_type;
typename traity_type::pointer ptr{ traity_type::allocate( alloc_, 1) };
try {
traity_type::construct( alloc_, ptr, alloc_, std::move( fn_) );
} catch (...) {
traity_type::deallocate( alloc_, ptr, 1);
throw;
}
return { convert( ptr) };
}
protected:
void deallocate_future() noexcept override final {
destroy_( alloc_, this);
}
private:
Fn fn_;
allocator_type alloc_;
static void destroy_( allocator_type const& alloc, task_object * p) noexcept {
allocator_type a{ alloc };
a.destroy( p);
a.deallocate( p, 1);
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_TASK_OBJECT_H
@@ -0,0 +1,474 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_FUTURE_HPP
#define BOOST_FIBERS_FUTURE_HPP
#include <algorithm>
#include <chrono>
#include <exception>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/future/detail/shared_state.hpp>
#include <boost/fiber/future/future_status.hpp>
namespace boost {
namespace fibers {
namespace detail {
template< typename R >
struct future_base {
typedef typename shared_state< R >::ptr_type ptr_type;
ptr_type state_{};
future_base() = default;
explicit future_base( ptr_type const& p) noexcept :
state_{ p } {
}
~future_base() = default;
future_base( future_base const& other) :
state_{ other.state_ } {
}
future_base( future_base && other) noexcept :
state_{ other.state_ } {
other.state_.reset();
}
future_base & operator=( future_base const& other) noexcept {
if ( this != & other) {
state_ = other.state_;
}
return * this;
}
future_base & operator=( future_base && other) noexcept {
if ( this != & other) {
state_ = other.state_;
other.state_.reset();
}
return * this;
}
bool valid() const noexcept {
return nullptr != state_.get();
}
std::exception_ptr get_exception_ptr() {
if ( ! valid() ) {
throw future_uninitialized{};
}
return state_->get_exception_ptr();
}
void wait() const {
if ( ! valid() ) {
throw future_uninitialized{};
}
state_->wait();
}
template< typename Rep, typename Period >
future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const {
if ( ! valid() ) {
throw future_uninitialized{};
}
return state_->wait_for( timeout_duration);
}
template< typename Clock, typename Duration >
future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const {
if ( ! valid() ) {
throw future_uninitialized{};
}
return state_->wait_until( timeout_time);
}
};
template< typename R >
struct promise_base;
}
template< typename R >
class shared_future;
template< typename Signature >
class packaged_task;
template< typename R >
class future : private detail::future_base< R > {
private:
typedef detail::future_base< R > base_type;
friend struct detail::promise_base< R >;
friend class shared_future< R >;
template< typename Signature >
friend class packaged_task;
explicit future( typename base_type::ptr_type const& p) noexcept :
base_type{ p } {
}
public:
future() = default;
future( future const&) = delete;
future & operator=( future const&) = delete;
future( future && other) noexcept :
base_type{ std::move( other) } {
}
future & operator=( future && other) noexcept {
if ( this != & other) {
base_type::operator=( std::move( other) );
}
return * this;
}
shared_future< R > share();
R get() {
if ( ! base_type::valid() ) {
throw future_uninitialized{};
}
typename base_type::ptr_type tmp{};
tmp.swap( base_type::state_);
return std::move( tmp->get() );
}
using base_type::valid;
using base_type::get_exception_ptr;
using base_type::wait;
using base_type::wait_for;
using base_type::wait_until;
};
template< typename R >
class future< R & > : private detail::future_base< R & > {
private:
typedef detail::future_base< R & > base_type;
friend struct detail::promise_base< R & >;
friend class shared_future< R & >;
template< typename Signature >
friend class packaged_task;
explicit future( typename base_type::ptr_type const& p) noexcept :
base_type{ p } {
}
public:
future() = default;
future( future const&) = delete;
future & operator=( future const&) = delete;
future( future && other) noexcept :
base_type{ std::move( other) } {
}
future & operator=( future && other) noexcept {
if ( this != & other) {
base_type::operator=( std::move( other) );
}
return * this;
}
shared_future< R & > share();
R & get() {
if ( ! base_type::valid() ) {
throw future_uninitialized{};
}
typename base_type::ptr_type tmp{};
tmp.swap( base_type::state_);
return tmp->get();
}
using base_type::valid;
using base_type::get_exception_ptr;
using base_type::wait;
using base_type::wait_for;
using base_type::wait_until;
};
template<>
class future< void > : private detail::future_base< void > {
private:
typedef detail::future_base< void > base_type;
friend struct detail::promise_base< void >;
friend class shared_future< void >;
template< typename Signature >
friend class packaged_task;
explicit future( base_type::ptr_type const& p) noexcept :
base_type{ p } {
}
public:
future() = default;
future( future const&) = delete;
future & operator=( future const&) = delete;
inline
future( future && other) noexcept :
base_type{ std::move( other) } {
}
inline
future & operator=( future && other) noexcept {
if ( this != & other) {
base_type::operator=( std::move( other) );
}
return * this;
}
shared_future< void > share();
inline
void get() {
if ( ! base_type::valid() ) {
throw future_uninitialized{};
}
base_type::ptr_type tmp{};
tmp.swap( base_type::state_);
tmp->get();
}
using base_type::valid;
using base_type::get_exception_ptr;
using base_type::wait;
using base_type::wait_for;
using base_type::wait_until;
};
template< typename R >
class shared_future : private detail::future_base< R > {
private:
typedef detail::future_base< R > base_type;
explicit shared_future( typename base_type::ptr_type const& p) noexcept :
base_type{ p } {
}
public:
shared_future() = default;
~shared_future() = default;
shared_future( shared_future const& other) :
base_type{ other } {
}
shared_future( shared_future && other) noexcept :
base_type{ std::move( other) } {
}
shared_future( future< R > && other) noexcept :
base_type{ std::move( other) } {
}
shared_future & operator=( shared_future const& other) noexcept {
if ( this != & other) {
base_type::operator=( other);
}
return * this;
}
shared_future & operator=( shared_future && other) noexcept {
if ( this != & other) {
base_type::operator=( std::move( other) );
}
return * this;
}
shared_future & operator=( future< R > && other) noexcept {
base_type::operator=( std::move( other) );
return * this;
}
R const& get() const {
if ( ! valid() ) {
throw future_uninitialized{};
}
return base_type::state_->get();
}
using base_type::valid;
using base_type::get_exception_ptr;
using base_type::wait;
using base_type::wait_for;
using base_type::wait_until;
};
template< typename R >
class shared_future< R & > : private detail::future_base< R & > {
private:
typedef detail::future_base< R & > base_type;
explicit shared_future( typename base_type::ptr_type const& p) noexcept :
base_type{ p } {
}
public:
shared_future() = default;
~shared_future() = default;
shared_future( shared_future const& other) :
base_type{ other } {
}
shared_future( shared_future && other) noexcept :
base_type{ std::move( other) } {
}
shared_future( future< R & > && other) noexcept :
base_type{ std::move( other) } {
}
shared_future & operator=( shared_future const& other) noexcept {
if ( this != & other) {
base_type::operator=( other);
}
return * this;
}
shared_future & operator=( shared_future && other) noexcept {
if ( this != & other) {
base_type::operator=( std::move( other) );
}
return * this;
}
shared_future & operator=( future< R & > && other) noexcept {
base_type::operator=( std::move( other) );
return * this;
}
R & get() const {
if ( ! valid() ) {
throw future_uninitialized{};
}
return base_type::state_->get();
}
using base_type::valid;
using base_type::get_exception_ptr;
using base_type::wait;
using base_type::wait_for;
using base_type::wait_until;
};
template<>
class shared_future< void > : private detail::future_base< void > {
private:
typedef detail::future_base< void > base_type;
explicit shared_future( base_type::ptr_type const& p) noexcept :
base_type{ p } {
}
public:
shared_future() = default;
~shared_future() = default;
inline
shared_future( shared_future const& other) :
base_type{ other } {
}
inline
shared_future( shared_future && other) noexcept :
base_type{ std::move( other) } {
}
inline
shared_future( future< void > && other) noexcept :
base_type{ std::move( other) } {
}
inline
shared_future & operator=( shared_future const& other) noexcept {
if ( this != & other) {
base_type::operator=( other);
}
return * this;
}
inline
shared_future & operator=( shared_future && other) noexcept {
if ( this != & other) {
base_type::operator=( std::move( other) );
}
return * this;
}
inline
shared_future & operator=( future< void > && other) noexcept {
base_type::operator=( std::move( other) );
return * this;
}
inline
void get() const {
if ( ! valid() ) {
throw future_uninitialized{};
}
base_type::state_->get();
}
using base_type::valid;
using base_type::get_exception_ptr;
using base_type::wait;
using base_type::wait_for;
using base_type::wait_until;
};
template< typename R >
shared_future< R >
future< R >::share() {
if ( ! base_type::valid() ) {
throw future_uninitialized{};
}
return shared_future< R >{ std::move( * this) };
}
template< typename R >
shared_future< R & >
future< R & >::share() {
if ( ! base_type::valid() ) {
throw future_uninitialized{};
}
return shared_future< R & >{ std::move( * this) };
}
inline
shared_future< void >
future< void >::share() {
if ( ! base_type::valid() ) {
throw future_uninitialized{};
}
return shared_future< void >{ std::move( * this) };
}
}}
#endif
@@ -0,0 +1,27 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_FUTURE_STATUS_HPP
#define BOOST_FIBERS_FUTURE_STATUS_HPP
#include <future>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
namespace boost {
namespace fibers {
enum class future_status {
ready = 1,
timeout,
deferred
};
}}
#endif // BOOST_FIBERS_FUTURE_STATUS_HPP
@@ -0,0 +1,141 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_PACKAGED_TASK_HPP
#define BOOST_FIBERS_PACKAGED_TASK_HPP
#include <algorithm>
#include <memory>
#include <type_traits>
#include <utility>
#include <boost/config.hpp>
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/detail/disable_overload.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/future/detail/task_base.hpp>
#include <boost/fiber/future/detail/task_object.hpp>
#include <boost/fiber/future/future.hpp>
namespace boost {
namespace fibers {
template< typename Signature >
class packaged_task;
template< typename R, typename ... Args >
class packaged_task< R( Args ... ) > {
private:
typedef typename detail::task_base< R, Args ... >::ptr_type ptr_type;
bool obtained_{ false };
ptr_type task_{};
public:
packaged_task() = default;
template< typename Fn,
typename = detail::disable_overload< packaged_task, Fn >
>
explicit packaged_task( Fn && fn) :
packaged_task{ std::allocator_arg,
std::allocator< packaged_task >{},
std::forward< Fn >( fn) } {
}
template< typename Fn,
typename Allocator
>
explicit packaged_task( std::allocator_arg_t, Allocator const& alloc, Fn && fn) {
typedef detail::task_object<
typename std::decay< Fn >::type, Allocator, R, Args ...
> object_type;
typedef std::allocator_traits<
typename object_type::allocator_type
> traits_type;
typename object_type::allocator_type a{ alloc };
typename traits_type::pointer ptr{ traits_type::allocate( a, 1) };
try {
traits_type::construct( a, ptr, a, std::forward< Fn >( fn) );
} catch (...) {
traits_type::deallocate( a, ptr, 1);
throw;
}
task_.reset( convert( ptr) );
}
~packaged_task() {
if ( task_) {
task_->owner_destroyed();
}
}
packaged_task( packaged_task const&) = delete;
packaged_task & operator=( packaged_task const&) = delete;
packaged_task( packaged_task && other) noexcept :
obtained_{ other.obtained_ },
task_{ std::move( other.task_) } {
other.obtained_ = false;
}
packaged_task & operator=( packaged_task && other) noexcept {
if ( this != & other) {
packaged_task tmp{ std::move( other) };
swap( tmp);
}
return * this;
}
void swap( packaged_task & other) noexcept {
std::swap( obtained_, other.obtained_);
task_.swap( other.task_);
}
bool valid() const noexcept {
return nullptr != task_.get();
}
future< R > get_future() {
if ( obtained_) {
throw future_already_retrieved{};
}
if ( ! valid() ) {
throw packaged_task_uninitialized{};
}
obtained_ = true;
return future< R >{
boost::static_pointer_cast< detail::shared_state< R > >( task_) };
}
void operator()( Args ... args) {
if ( ! valid() ) {
throw packaged_task_uninitialized{};
}
task_->run( std::forward< Args >( args) ... );
}
void reset() {
if ( ! valid() ) {
throw packaged_task_uninitialized{};
}
packaged_task tmp;
tmp.task_ = task_;
task_ = tmp.task_->reset();
obtained_ = false;
}
};
template< typename Signature >
void swap( packaged_task< Signature > & l, packaged_task< Signature > & r) noexcept {
l.swap( r);
}
}}
#endif // BOOST_FIBERS_PACKAGED_TASK_HPP
@@ -0,0 +1,220 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_PROMISE_HPP
#define BOOST_FIBERS_PROMISE_HPP
#include <algorithm>
#include <memory>
#include <utility>
#include <boost/config.hpp>
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/future/detail/shared_state.hpp>
#include <boost/fiber/future/detail/shared_state_object.hpp>
#include <boost/fiber/future/future.hpp>
namespace boost {
namespace fibers {
namespace detail {
template< typename R >
struct promise_base {
typedef typename shared_state< R >::ptr_type ptr_type;
bool obtained_{ false };
ptr_type future_{};
promise_base() :
promise_base{ std::allocator_arg, std::allocator< promise_base >{} } {
}
template< typename Allocator >
promise_base( std::allocator_arg_t, Allocator alloc) {
typedef detail::shared_state_object< R, Allocator > object_type;
typedef std::allocator_traits< typename object_type::allocator_type > traits_type;
typename object_type::allocator_type a{ alloc };
typename traits_type::pointer ptr{ traits_type::allocate( a, 1) };
try {
traits_type::construct( a, ptr, a);
} catch (...) {
traits_type::deallocate( a, ptr, 1);
throw;
}
future_.reset( convert( ptr) );
}
~promise_base() {
if ( future_) {
future_->owner_destroyed();
}
}
promise_base( promise_base const&) = delete;
promise_base & operator=( promise_base const&) = delete;
promise_base( promise_base && other) noexcept :
obtained_{ other.obtained_ },
future_{ std::move( other.future_) } {
other.obtained_ = false;
}
promise_base & operator=( promise_base && other) noexcept {
if ( this != & other) {
promise_base tmp{ std::move( other) };
swap( tmp);
}
return * this;
}
future< R > get_future() {
if ( obtained_) {
throw future_already_retrieved{};
}
if ( ! future_) {
throw promise_uninitialized{};
}
obtained_ = true;
return future< R >{ future_ };
}
void swap( promise_base & other) noexcept {
std::swap( obtained_, other.obtained_);
future_.swap( other.future_);
}
void set_exception( std::exception_ptr p) {
if ( ! future_) {
throw promise_uninitialized{};
}
future_->set_exception( p);
}
};
}
template< typename R >
class promise : private detail::promise_base< R > {
private:
typedef detail::promise_base< R > base_type;
public:
promise() = default;
template< typename Allocator >
promise( std::allocator_arg_t, Allocator alloc) :
base_type{ std::allocator_arg, alloc } {
}
promise( promise const&) = delete;
promise & operator=( promise const&) = delete;
promise( promise && other) noexcept = default;
promise & operator=( promise && other) = default;
void set_value( R const& value) {
if ( ! base_type::future_) {
throw promise_uninitialized{};
}
base_type::future_->set_value( value);
}
void set_value( R && value) {
if ( ! base_type::future_) {
throw promise_uninitialized{};
}
base_type::future_->set_value( std::move( value) );
}
void swap( promise & other) noexcept {
base_type::swap( other);
}
using base_type::get_future;
using base_type::set_exception;
};
template< typename R >
class promise< R & > : private detail::promise_base< R & > {
private:
typedef detail::promise_base< R & > base_type;
public:
promise() = default;
template< typename Allocator >
promise( std::allocator_arg_t, Allocator alloc) :
base_type{ std::allocator_arg, alloc } {
}
promise( promise const&) = delete;
promise & operator=( promise const&) = delete;
promise( promise && other) noexcept = default;
promise & operator=( promise && other) noexcept = default;
void set_value( R & value) {
if ( ! base_type::future_) {
throw promise_uninitialized{};
}
base_type::future_->set_value( value);
}
void swap( promise & other) noexcept {
base_type::swap( other);
}
using base_type::get_future;
using base_type::set_exception;
};
template<>
class promise< void > : private detail::promise_base< void > {
private:
typedef detail::promise_base< void > base_type;
public:
promise() = default;
template< typename Allocator >
promise( std::allocator_arg_t, Allocator alloc) :
base_type{ std::allocator_arg, alloc } {
}
promise( promise const&) = delete;
promise & operator=( promise const&) = delete;
promise( promise && other) noexcept = default;
promise & operator=( promise && other) noexcept = default;
inline
void set_value() {
if ( ! base_type::future_) {
throw promise_uninitialized{};
}
base_type::future_->set_value();
}
inline
void swap( promise & other) noexcept {
base_type::swap( other);
}
using base_type::get_future;
using base_type::set_exception;
};
template< typename R >
void swap( promise< R > & l, promise< R > & r) noexcept {
l.swap( r);
}
}}
#endif // BOOST_FIBERS_PROMISE_HPP
@@ -0,0 +1,70 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_MUTEX_H
#define BOOST_FIBERS_MUTEX_H
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
class condition_variable;
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_{};
context * owner_{ nullptr };
public:
mutex() = default;
~mutex() {
BOOST_ASSERT( nullptr == owner_);
BOOST_ASSERT( wait_queue_.empty() );
}
mutex( mutex const&) = delete;
mutex & operator=( mutex const&) = delete;
void lock();
bool try_lock();
void unlock();
};
}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_MUTEX_H
@@ -0,0 +1,90 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_THIS_FIBER_OPERATIONS_H
#define BOOST_THIS_FIBER_OPERATIONS_H
#include <chrono>
#include <boost/config.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/fiber.hpp>
#include <boost/fiber/scheduler.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace this_fiber {
inline
fibers::fiber::id get_id() noexcept {
return fibers::context::active()->get_id();
}
inline
void yield() noexcept {
fibers::context::active()->yield();
}
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()->wait_until( sleep_time);
}
template< typename Rep, typename Period >
void sleep_for( std::chrono::duration< Rep, Period > const& timeout_duration) {
fibers::context::active()->wait_until(
std::chrono::steady_clock::now() + timeout_duration);
}
template< typename PROPS >
PROPS & properties() {
fibers::fiber_properties * props = fibers::context::active()->get_properties();
if ( ! props) {
// props could be nullptr if the thread's main fiber has not yet
// yielded (not yet passed through algorithm_with_properties::
// awakened()). Address that by yielding right now.
yield();
// Try again to obtain the fiber_properties subclass instance ptr.
// Walk through the whole chain again because who knows WHAT might
// have happened while we were yielding!
props = fibers::context::active()->get_properties();
// Could still be hosed if the running manager isn't a subclass of
// algorithm_with_properties.
BOOST_ASSERT_MSG( props, "this_fiber::properties not set");
}
return dynamic_cast< PROPS & >( * props );
}
}
namespace fibers {
inline
bool has_ready_fibers() noexcept {
return boost::fibers::context::active()->get_scheduler()->has_ready_fibers();
}
template< typename SchedAlgo, typename ... Args >
void use_scheduling_algorithm( Args && ... args) noexcept {
boost::fibers::context::active()->get_scheduler()
->set_algo(
std::unique_ptr< SchedAlgo >(
new SchedAlgo( std::forward< Args >( args) ... ) ) );
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_THIS_FIBER_OPERATIONS_H
@@ -0,0 +1,46 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_POLICY_H
#define BOOST_FIBERS_POLICY_H
#include <type_traits>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
enum class launch {
dispatch,
post
};
namespace detail {
template< typename Fn >
struct is_launch_policy : public std::false_type {
};
template<>
struct is_launch_policy< boost::fibers::launch > : public std::true_type {
};
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_POLICY_H
@@ -0,0 +1,30 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_POOLED_FIXEDSIZE_STACK_H
#define BOOST_FIBERS_POOLED_FIXEDSIZE_STACK_H
#include <boost/config.hpp>
#include <boost/context/pooled_fixedsize_stack.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
using pooled_fixedsize_stack = boost::context::pooled_fixedsize_stack;
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_POOLED_FIXEDSIZE_STACK_H
@@ -0,0 +1,79 @@
// Copyright Nat Goodspeed 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Define fiber_properties, a base class from which a library consumer can
// derive a subclass with specific properties important to a user-coded
// scheduler.
#ifndef BOOST_FIBERS_PROPERTIES_HPP
#define BOOST_FIBERS_PROPERTIES_HPP
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
# if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable:4275)
# endif
namespace boost {
namespace fibers {
class context;
namespace algo {
struct algorithm;
}
class BOOST_FIBERS_DECL fiber_properties {
protected:
// initialized by constructor
context * ctx_;
// set every time this fiber becomes READY
algo::algorithm * algo_{ nullptr };
// Inform the relevant algorithm instance that something important
// has changed, so it can (presumably) adjust its data structures
// accordingly.
void notify() noexcept;
public:
// Any specific property setter method, after updating the relevant
// instance variable, can/should call notify().
// fiber_properties, and by implication every subclass, must accept a back
// pointer to its context.
fiber_properties( context * ctx) noexcept :
ctx_{ ctx } {
}
// We need a virtual destructor (hence a vtable) because fiber_properties
// is stored polymorphically (as fiber_properties*) in context, and
// destroyed via that pointer.
virtual ~fiber_properties() = default;
// not really intended for public use, but algorithm_with_properties
// must be able to call this
void set_algorithm( algo::algorithm * algo) noexcept {
algo_ = algo;
}
};
}} // namespace boost::fibers
# if defined(BOOST_MSVC)
# pragma warning(pop)
# endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_PROPERTIES_HPP
@@ -0,0 +1,30 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_PROTECTED_FIXEDSIZE_STACK_H
#define BOOST_FIBERS_PROTECTED_FIXEDSIZE_STACK_H
#include <boost/config.hpp>
#include <boost/context/protected_fixedsize_stack.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
using protected_fixedsize_stack = boost::context::protected_fixedsize_stack;
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_PROTECTED_FIXEDSIZE_STACK_H
@@ -0,0 +1,76 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// based on boost::interprocess::sync::interprocess_spinlock
#ifndef BOOST_FIBERS_RECURSIVE_MUTEX_H
#define BOOST_FIBERS_RECURSIVE_MUTEX_H
#include <cstddef>
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
class condition_variable;
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_{};
context * owner_{ nullptr };
std::size_t count_{ 0 };
public:
recursive_mutex() = default;
~recursive_mutex() {
BOOST_ASSERT( nullptr == owner_);
BOOST_ASSERT( 0 == count_);
BOOST_ASSERT( wait_queue_.empty() );
}
recursive_mutex( recursive_mutex const&) = delete;
recursive_mutex & operator=( recursive_mutex const&) = delete;
void lock();
bool try_lock() noexcept;
void unlock();
};
}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_RECURSIVE_MUTEX_H
@@ -0,0 +1,91 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// based on boost::interprocess::sync::interprocess_spinlock
#ifndef BOOST_FIBERS_RECURSIVE_TIMED_MUTEX_H
#define BOOST_FIBERS_RECURSIVE_TIMED_MUTEX_H
#include <chrono>
#include <cstddef>
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
class condition_variable;
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_{};
context * owner_{ nullptr };
std::size_t count_{ 0 };
bool try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time) noexcept;
public:
recursive_timed_mutex() = default;
~recursive_timed_mutex() {
BOOST_ASSERT( nullptr == owner_);
BOOST_ASSERT( 0 == count_);
BOOST_ASSERT( wait_queue_.empty() );
}
recursive_timed_mutex( recursive_timed_mutex const&) = delete;
recursive_timed_mutex & operator=( recursive_timed_mutex const&) = delete;
void lock();
bool try_lock() noexcept;
template< typename Clock, typename Duration >
bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time_) {
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
return try_lock_until_( timeout_time);
}
template< typename Rep, typename Period >
bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration) {
return try_lock_until_( std::chrono::steady_clock::now() + timeout_duration);
}
void unlock();
};
}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_RECURSIVE_TIMED_MUTEX_H
@@ -0,0 +1,173 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_FIBER_MANAGER_H
#define BOOST_FIBERS_FIBER_MANAGER_H
#include <chrono>
#include <functional>
#include <memory>
#include <mutex>
#include <vector>
#include <boost/config.hpp>
#if (BOOST_EXECUTION_CONTEXT==1)
# include <boost/context/execution_context.hpp>
#else
# include <boost/context/continuation.hpp>
#endif
#include <boost/intrusive/list.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/intrusive/slist.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/data.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
class BOOST_FIBERS_DECL scheduler {
public:
struct timepoint_less {
bool operator()( context const& l, context const& r) const noexcept {
return l.tp_ < r.tp_;
}
};
typedef intrusive::list<
context,
intrusive::member_hook<
context, detail::ready_hook, & context::ready_hook_ >,
intrusive::constant_time_size< false >
> ready_queue_type;
private:
typedef intrusive::multiset<
context,
intrusive::member_hook<
context, detail::sleep_hook, & context::sleep_hook_ >,
intrusive::constant_time_size< false >,
intrusive::compare< timepoint_less >
> sleep_queue_type;
typedef intrusive::list<
context,
intrusive::member_hook<
context, detail::worker_hook, & context::worker_hook_ >,
intrusive::constant_time_size< false >
> worker_queue_type;
typedef intrusive::slist<
context,
intrusive::member_hook<
context, detail::terminated_hook, & context::terminated_hook_ >,
intrusive::linear< true >,
intrusive::cache_last< true >
> terminated_queue_type;
typedef intrusive::slist<
context,
intrusive::member_hook<
context, detail::remote_ready_hook, & context::remote_ready_hook_ >,
intrusive::linear< true >,
intrusive::cache_last< true >
> remote_ready_queue_type;
#if ! defined(BOOST_FIBERS_NO_ATOMICS)
// remote ready-queue contains context' signaled by schedulers
// running in other threads
detail::spinlock remote_ready_splk_{};
remote_ready_queue_type remote_ready_queue_{};
#endif
alignas(cache_alignment) std::unique_ptr< algo::algorithm > algo_;
// 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
// except main-context and dispatcher-context
// unlink happens on destruction of a context
worker_queue_type worker_queue_{};
// terminated-queue contains context' which have been terminated
terminated_queue_type terminated_queue_{};
intrusive_ptr< context > dispatcher_ctx_{};
context * main_ctx_{ nullptr };
bool shutdown_{ false };
void release_terminated_() noexcept;
#if ! defined(BOOST_FIBERS_NO_ATOMICS)
void remote_ready2ready_() noexcept;
#endif
void sleep2ready_() noexcept;
public:
scheduler() noexcept;
scheduler( scheduler const&) = delete;
scheduler & operator=( scheduler const&) = delete;
virtual ~scheduler();
void schedule( context *) noexcept;
#if ! defined(BOOST_FIBERS_NO_ATOMICS)
void schedule_from_remote( context *) noexcept;
#endif
#if (BOOST_EXECUTION_CONTEXT==1)
void dispatch() noexcept;
void terminate( detail::spinlock_lock &, context *) noexcept;
#else
boost::context::continuation dispatch() noexcept;
boost::context::continuation terminate( detail::spinlock_lock &, context *) noexcept;
#endif
void yield( context *) noexcept;
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;
void suspend() noexcept;
void suspend( detail::spinlock_lock &) noexcept;
bool has_ready_fibers() const noexcept;
void set_algo( std::unique_ptr< algo::algorithm >) noexcept;
void attach_main_context( context *) noexcept;
void attach_dispatcher_context( intrusive_ptr< context >) noexcept;
void attach_worker_context( context *) noexcept;
void detach_worker_context( context *) noexcept;
};
}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_FIBER_MANAGER_H
@@ -0,0 +1,35 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_SEGMENTED_STACK_H
#define BOOST_FIBERS_SEGMENTED_STACK_H
#include <boost/config.hpp>
#include <boost/context/segmented_stack.hpp>
#include <boost/fiber/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
#if defined(BOOST_USE_SEGMENTED_STACKS)
# if ! defined(BOOST_WINDOWS)
using segmented_stack = boost::context::segmented_stack;
using default_stack = boost::context::default_stack;
# endif
#endif
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_SEGMENTED_STACK_H
@@ -0,0 +1,85 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_TIMED_MUTEX_H
#define BOOST_FIBERS_TIMED_MUTEX_H
#include <chrono>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4251)
#endif
namespace boost {
namespace fibers {
class condition_variable;
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_{};
context * owner_{ nullptr };
bool try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time) noexcept;
public:
timed_mutex() = default;
~timed_mutex() {
BOOST_ASSERT( nullptr == owner_);
BOOST_ASSERT( wait_queue_.empty() );
}
timed_mutex( timed_mutex const&) = delete;
timed_mutex & operator=( timed_mutex const&) = delete;
void lock();
bool try_lock();
template< typename Clock, typename Duration >
bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time_) {
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
return try_lock_until_( timeout_time);
}
template< typename Rep, typename Period >
bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration) {
return try_lock_until_( std::chrono::steady_clock::now() + timeout_duration);
}
void unlock();
};
}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_TIMED_MUTEX_H
@@ -0,0 +1,107 @@
// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_TYPE_H
#define BOOST_FIBERS_TYPE_H
#include <atomic>
#include <chrono>
#include <exception>
#include <functional>
#include <map>
#include <memory>
#include <type_traits>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/detail/apply.hpp>
#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/fiber/detail/config.hpp>
#include <boost/fiber/detail/data.hpp>
#include <boost/fiber/detail/decay_copy.hpp>
#include <boost/fiber/detail/fss.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#include <boost/fiber/detail/wrap.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/fixedsize_stack.hpp>
#include <boost/fiber/properties.hpp>
#include <boost/fiber/segmented_stack.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
enum class type {
none = 0,
main_context = 1 << 1,
dispatcher_context = 1 << 2,
worker_context = 1 << 3,
pinned_context = main_context | dispatcher_context
};
inline
constexpr type
operator&( type l, type r) {
return static_cast< type >(
static_cast< unsigned int >( l) & static_cast< unsigned int >( r) );
}
inline
constexpr type
operator|( type l, type r) {
return static_cast< type >(
static_cast< unsigned int >( l) | static_cast< unsigned int >( r) );
}
inline
constexpr type
operator^( type l, type r) {
return static_cast< type >(
static_cast< unsigned int >( l) ^ static_cast< unsigned int >( r) );
}
inline
constexpr type
operator~( type l) {
return static_cast< type >( ~static_cast< unsigned int >( l) );
}
inline
type &
operator&=( type & l, type r) {
l = l & r;
return l;
}
inline
type &
operator|=( type & l, type r) {
l = l | r;
return l;
}
inline
type &
operator^=( type & l, type r) {
l = l ^ r;
return l;
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_TYPE_H
@@ -0,0 +1,527 @@
// Copyright Oliver Kowalke 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_UNBUFFERED_CHANNEL_H
#define BOOST_FIBERS_UNBUFFERED_CHANNEL_H
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
#include <boost/config.hpp>
#include <boost/fiber/channel_op_status.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#include <boost/fiber/exceptions.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
template< typename T >
class unbuffered_channel {
public:
typedef T value_type;
private:
typedef context::wait_queue_t wait_queue_type;
struct alignas(cache_alignment) slot {
value_type value;
context * ctx;
slot( value_type const& value_, context * ctx_) :
value{ value_ },
ctx{ ctx_ } {
}
slot( value_type && value_, context * ctx_) :
value{ std::move( value_) },
ctx{ ctx_ } {
}
};
// shared cacheline
alignas(cache_alignment) std::atomic< slot * > slot_{ nullptr };
// shared cacheline
alignas(cache_alignment) std::atomic_bool closed_{ false };
alignas(cache_alignment) mutable detail::spinlock splk_producers_{};
wait_queue_type waiting_producers_{};
alignas( cache_alignment) mutable detail::spinlock splk_consumers_{};
wait_queue_type waiting_consumers_{};
char pad_[cacheline_length];
bool is_empty_() {
return nullptr == slot_.load( std::memory_order_acquire);
}
bool try_push_( slot * own_slot) {
for (;;) {
slot * s = slot_.load( std::memory_order_acquire);
if ( nullptr == s) {
if ( ! slot_.compare_exchange_strong( s, own_slot, std::memory_order_acq_rel) ) {
continue;
}
return true;
} else {
return false;
}
}
}
slot * try_pop_() {
slot * nil_slot = nullptr;
for (;;) {
slot * s = slot_.load( std::memory_order_acquire);
if ( nullptr != s) {
if ( ! slot_.compare_exchange_strong( s, nil_slot, std::memory_order_acq_rel) ) {
continue;}
}
return s;
}
}
public:
unbuffered_channel() = default;
~unbuffered_channel() {
close();
slot * s = nullptr;
if ( nullptr != ( s = try_pop_() ) ) {
BOOST_ASSERT( nullptr != s);
BOOST_ASSERT( nullptr != s->ctx);
// value will be destructed in the context of the waiting fiber
context::active()->schedule( s->ctx);
}
}
unbuffered_channel( unbuffered_channel const&) = delete;
unbuffered_channel & operator=( unbuffered_channel const&) = delete;
bool is_closed() const noexcept {
return closed_.load( std::memory_order_acquire);
}
void close() noexcept {
context * active_ctx = context::active();
// notify all waiting producers
closed_.store( true, std::memory_order_release);
detail::spinlock_lock lk1{ splk_producers_ };
while ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
active_ctx->schedule( producer_ctx);
}
// notify all waiting consumers
detail::spinlock_lock lk2{ splk_consumers_ };
while ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
active_ctx->schedule( consumer_ctx);
}
}
channel_op_status push( value_type const& value) {
context * active_ctx = context::active();
slot s{ value, active_ctx };
for (;;) {
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( try_push_( & s) ) {
detail::spinlock_lock lk{ splk_consumers_ };
// notify one waiting consumer
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
active_ctx->schedule( consumer_ctx);
}
// suspend till value has been consumed
active_ctx->suspend( lk);
// resumed, value has been consumed
return channel_op_status::success;
} else {
detail::spinlock_lock lk{ splk_producers_ };
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( is_empty_() ) {
continue;
}
active_ctx->wait_link( waiting_producers_);
// suspend this producer
active_ctx->suspend( lk);
// resumed, slot mabye free
}
}
}
channel_op_status push( value_type && value) {
context * active_ctx = context::active();
slot s{ std::move( value), active_ctx };
for (;;) {
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( try_push_( & s) ) {
detail::spinlock_lock lk{ splk_consumers_ };
// notify one waiting consumer
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
active_ctx->schedule( consumer_ctx);
}
// suspend till value has been consumed
active_ctx->suspend( lk);
// resumed, value has been consumed
return channel_op_status::success;
} else {
detail::spinlock_lock lk{ splk_producers_ };
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( is_empty_() ) {
continue;
}
active_ctx->wait_link( waiting_producers_);
// suspend this producer
active_ctx->suspend( lk);
// resumed, slot mabye free
}
}
}
template< typename Rep, typename Period >
channel_op_status push_wait_for( value_type const& value,
std::chrono::duration< Rep, Period > const& timeout_duration) {
return push_wait_until( value,
std::chrono::steady_clock::now() + timeout_duration);
}
template< typename Rep, typename Period >
channel_op_status push_wait_for( value_type && value,
std::chrono::duration< Rep, Period > const& timeout_duration) {
return push_wait_until( std::forward< value_type >( value),
std::chrono::steady_clock::now() + timeout_duration);
}
template< typename Clock, typename Duration >
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 };
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
for (;;) {
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( try_push_( & s) ) {
detail::spinlock_lock lk{ splk_consumers_ };
// notify one waiting consumer
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
active_ctx->schedule( consumer_ctx);
}
// suspend this producer
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
// clear slot
slot * nil_slot = nullptr, * own_slot = & s;
slot_.compare_exchange_strong( own_slot, nil_slot, std::memory_order_acq_rel);
// resumed, value has not been consumed
return channel_op_status::timeout;
}
// resumed, value has been consumed
return channel_op_status::success;
} else {
detail::spinlock_lock lk{ splk_producers_ };
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( is_empty_() ) {
continue;
}
active_ctx->wait_link( waiting_producers_);
// 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
}
}
}
template< typename Clock, typename Duration >
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 };
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
for (;;) {
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( try_push_( & s) ) {
detail::spinlock_lock lk{ splk_consumers_ };
// notify one waiting consumer
if ( ! waiting_consumers_.empty() ) {
context * consumer_ctx = & waiting_consumers_.front();
waiting_consumers_.pop_front();
active_ctx->schedule( consumer_ctx);
}
// suspend this producer
if ( ! active_ctx->wait_until( timeout_time, lk) ) {
// clear slot
slot * nil_slot = nullptr, * own_slot = & s;
slot_.compare_exchange_strong( own_slot, nil_slot, std::memory_order_acq_rel);
// resumed, value has not been consumed
return channel_op_status::timeout;
}
// resumed, value has been consumed
return channel_op_status::success;
} else {
detail::spinlock_lock lk{ splk_producers_ };
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( is_empty_() ) {
continue;
}
active_ctx->wait_link( waiting_producers_);
// 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
}
}
}
channel_op_status pop( value_type & value) {
context * active_ctx = context::active();
slot * s = nullptr;
for (;;) {
if ( nullptr != ( s = try_pop_() ) ) {
{
detail::spinlock_lock lk{ splk_producers_ };
// notify one waiting producer
if ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
lk.unlock();
active_ctx->schedule( producer_ctx);
}
}
// consume value
value = std::move( s->value);
// resume suspended producer
active_ctx->schedule( s->ctx);
return channel_op_status::success;
} else {
detail::spinlock_lock lk{ splk_consumers_ };
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( ! is_empty_() ) {
continue;
}
active_ctx->wait_link( waiting_consumers_);
// suspend this consumer
active_ctx->suspend( lk);
// resumed, slot mabye set
}
}
}
value_type value_pop() {
context * active_ctx = context::active();
slot * s = nullptr;
for (;;) {
if ( nullptr != ( s = try_pop_() ) ) {
{
detail::spinlock_lock lk{ splk_producers_ };
// notify one waiting producer
if ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
lk.unlock();
active_ctx->schedule( producer_ctx);
}
}
// consume value
value_type value{ std::move( s->value) };
// resume suspended producer
active_ctx->schedule( s->ctx);
return std::move( value);
} else {
detail::spinlock_lock lk{ splk_consumers_ };
if ( 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_);
// suspend this consumer
active_ctx->suspend( lk);
// resumed, slot mabye set
}
}
}
template< typename Rep, typename Period >
channel_op_status pop_wait_for( value_type & value,
std::chrono::duration< Rep, Period > const& timeout_duration) {
return pop_wait_until( value,
std::chrono::steady_clock::now() + timeout_duration);
}
template< typename Clock, typename Duration >
channel_op_status pop_wait_until( value_type & value,
std::chrono::time_point< Clock, Duration > const& timeout_time_) {
context * active_ctx = context::active();
slot * s = nullptr;
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
for (;;) {
if ( nullptr != ( s = try_pop_() ) ) {
{
detail::spinlock_lock lk{ splk_producers_ };
// notify one waiting producer
if ( ! waiting_producers_.empty() ) {
context * producer_ctx = & waiting_producers_.front();
waiting_producers_.pop_front();
lk.unlock();
active_ctx->schedule( producer_ctx);
}
}
// consume value
value = std::move( s->value);
// resume suspended producer
active_ctx->schedule( s->ctx);
return channel_op_status::success;
} else {
detail::spinlock_lock lk{ splk_consumers_ };
if ( is_closed() ) {
return channel_op_status::closed;
}
if ( ! is_empty_() ) {
continue;
}
active_ctx->wait_link( waiting_consumers_);
// 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;
}
}
}
}
class iterator : public std::iterator< std::input_iterator_tag, typename std::remove_reference< value_type >::type > {
private:
typedef typename std::aligned_storage< sizeof( value_type), alignof( value_type) >::type storage_type;
unbuffered_channel * chan_{ nullptr };
storage_type storage_;
void increment_() {
BOOST_ASSERT( nullptr != chan_);
try {
::new ( static_cast< void * >( std::addressof( storage_) ) ) value_type{ chan_->value_pop() };
} catch ( fiber_error const&) {
chan_ = nullptr;
}
}
public:
typedef typename iterator::pointer pointer_t;
typedef typename iterator::reference reference_t;
iterator() noexcept = default;
explicit iterator( unbuffered_channel< T > * chan) noexcept :
chan_{ chan } {
increment_();
}
iterator( iterator const& other) noexcept :
chan_{ other.chan_ } {
}
iterator & operator=( iterator const& other) noexcept {
if ( this == & other) return * this;
chan_ = other.chan_;
return * this;
}
bool operator==( iterator const& other) const noexcept {
return other.chan_ == chan_;
}
bool operator!=( iterator const& other) const noexcept {
return other.chan_ != chan_;
}
iterator & operator++() {
increment_();
return * this;
}
iterator operator++( int) = delete;
reference_t operator*() noexcept {
return * reinterpret_cast< value_type * >( std::addressof( storage_) );
}
pointer_t operator->() noexcept {
return reinterpret_cast< value_type * >( std::addressof( storage_) );
}
};
friend class iterator;
};
template< typename T >
typename unbuffered_channel< T >::iterator
begin( unbuffered_channel< T > & chan) {
return typename unbuffered_channel< T >::iterator( & chan);
}
template< typename T >
typename unbuffered_channel< T >::iterator
end( unbuffered_channel< T > &) {
return typename unbuffered_channel< T >::iterator();
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_UNBUFFERED_CHANNEL_H