Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+162
View File
@@ -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_ALGO_ALGORITHM_H
#define BOOST_FIBERS_ALGO_ALGORITHM_H
#include <atomic>
#include <chrono>
#include <cstddef>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.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 {
class BOOST_FIBERS_DECL algorithm {
private:
std::atomic< std::size_t > use_count_{ 0 };
public:
typedef intrusive_ptr< algorithm > ptr_t;
virtual ~algorithm() = default;
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;
#if !defined(BOOST_EMBTC)
friend void intrusive_ptr_add_ref( algorithm * algo) noexcept {
BOOST_ASSERT( nullptr != algo);
algo->use_count_.fetch_add( 1, std::memory_order_relaxed);
}
friend void intrusive_ptr_release( algorithm * algo) noexcept {
BOOST_ASSERT( nullptr != algo);
if ( 1 == algo->use_count_.fetch_sub( 1, std::memory_order_release) ) {
std::atomic_thread_fence( std::memory_order_acquire);
delete algo;
}
}
#else
friend void intrusive_ptr_add_ref( algorithm * algo) noexcept;
friend void intrusive_ptr_release( algorithm * algo) noexcept;
#endif
};
#if defined(BOOST_EMBTC)
inline void intrusive_ptr_add_ref( algorithm * algo) noexcept {
BOOST_ASSERT( nullptr != algo);
algo->use_count_.fetch_add( 1, std::memory_order_relaxed);
}
inline void intrusive_ptr_release( algorithm * algo) noexcept {
BOOST_ASSERT( nullptr != algo);
if ( 1 == algo->use_count_.fetch_sub( 1, std::memory_order_release) ) {
std::atomic_thread_fence( std::memory_order_acquire);
delete algo;
}
}
#endif
class BOOST_FIBERS_DECL algorithm_with_properties_base : public algorithm {
public:
// called by fiber_properties::notify() -- don't directly call
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);
void awakened( context * ctx) noexcept final {
fiber_properties * props = super::get_properties( ctx);
if ( BOOST_LIKELY( nullptr == props) ) {
// TODO: would be great if PROPS could be allocated on the new
// 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 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
+69
View File
@@ -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;
void awakened( context *) noexcept override;
context * pick_next() noexcept override;
bool has_ready_fibers() const noexcept override;
void suspend_until( std::chrono::steady_clock::time_point const&) noexcept override;
void notify() noexcept override;
};
}}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGO_ROUND_ROBIN_H
+86
View File
@@ -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 override;
context * pick_next() noexcept override;
bool has_ready_fibers() const noexcept override {
std::unique_lock< std::mutex > lock{ rqueue_mtx_ };
return ! rqueue_.empty() || ! lqueue_.empty();
}
void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept override;
void notify() noexcept override;
};
}}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGO_SHARED_WORK_H
+88
View File
@@ -0,0 +1,88 @@
// 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 <atomic>
#include <condition_variable>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <vector>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/context_spinlock_queue.hpp>
#include <boost/fiber/detail/context_spmc_queue.hpp>
#include <boost/fiber/scheduler.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace algo {
class BOOST_FIBERS_DECL work_stealing : public algorithm {
private:
static std::atomic< std::uint32_t > counter_;
static std::vector< intrusive_ptr< work_stealing > > schedulers_;
std::uint32_t id_;
std::uint32_t thread_count_;
#ifdef BOOST_FIBERS_USE_SPMC_QUEUE
detail::context_spmc_queue rqueue_{};
#else
detail::context_spinlock_queue rqueue_{};
#endif
std::mutex mtx_{};
std::condition_variable cnd_{};
bool flag_{ false };
bool suspend_;
static void init_( std::uint32_t, std::vector< intrusive_ptr< work_stealing > > &);
public:
work_stealing( std::uint32_t, bool = 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 *) noexcept override;
context * pick_next() noexcept override;
virtual context * steal() noexcept {
return rqueue_.steal();
}
bool has_ready_fibers() const noexcept override {
return ! rqueue_.empty();
}
void suspend_until( std::chrono::steady_clock::time_point const&) noexcept override;
void notify() noexcept override;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGO_WORK_STEALING_H
+38
View File
@@ -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
+48
View File
@@ -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
+449
View File
@@ -0,0 +1,449 @@
// 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/waker.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:
using value_type = typename std::remove_reference<T>::type;
private:
using slot_type = value_type;
mutable detail::spinlock splk_{};
wait_queue waiting_producers_{};
wait_queue 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 ( BOOST_UNLIKELY( 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_, std::defer_lock};
for(;;) {
if(lk.try_lock())
break;
context::active()->yield();
}
return is_closed_();
}
void close() noexcept {
detail::spinlock_lock lk{splk_, std::defer_lock};
for(;;) {
if(lk.try_lock())
break;
context::active()->yield();
}
if ( ! closed_) {
closed_ = true;
waiting_producers_.notify_all();
waiting_consumers_.notify_all();
}
}
channel_op_status try_push( value_type const& value) {
detail::spinlock_lock lk{splk_, std::defer_lock};
for(;;) {
if(lk.try_lock())
break;
context::active()->yield();
}
if ( BOOST_UNLIKELY( is_closed_() ) ) {
return channel_op_status::closed;
}
if ( is_full_() ) {
return channel_op_status::full;
}
slots_[pidx_] = value;
pidx_ = (pidx_ + 1) % capacity_;
waiting_consumers_.notify_one();
return channel_op_status::success;
}
channel_op_status try_push( value_type && value) {
detail::spinlock_lock lk{splk_, std::defer_lock};
for(;;) {
if(lk.try_lock())
break;
context::active()->yield();
}
if ( BOOST_UNLIKELY( is_closed_() ) ) {
return channel_op_status::closed;
}
if ( is_full_() ) {
return channel_op_status::full;
}
slots_[pidx_] = std::move( value);
pidx_ = (pidx_ + 1) % capacity_;
waiting_consumers_.notify_one();
return channel_op_status::success;
}
channel_op_status push( value_type const& value) {
context * active_ctx = context::active();
for (;;) {
detail::spinlock_lock lk{splk_, std::try_to_lock};
if (!lk) {
active_ctx->yield();
continue;
}
if ( BOOST_UNLIKELY( is_closed_() ) ) {
return channel_op_status::closed;
}
if ( is_full_() ) {
waiting_producers_.suspend_and_wait( lk, active_ctx);
} else {
slots_[pidx_] = value;
pidx_ = (pidx_ + 1) % capacity_;
waiting_consumers_.notify_one();
return channel_op_status::success;
}
}
}
channel_op_status push( value_type && value) {
context * active_ctx = context::active();
for (;;) {
detail::spinlock_lock lk{splk_, std::try_to_lock};
if (!lk) {
active_ctx->yield();
continue;
}
if ( BOOST_UNLIKELY( is_closed_() ) ) {
return channel_op_status::closed;
}
if ( is_full_() ) {
waiting_producers_.suspend_and_wait( lk, active_ctx);
} else {
slots_[pidx_] = std::move( value);
pidx_ = (pidx_ + 1) % capacity_;
waiting_consumers_.notify_one();
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_, std::try_to_lock};
if (!lk) {
active_ctx->yield();
continue;
}
if ( BOOST_UNLIKELY( is_closed_() ) ) {
return channel_op_status::closed;
}
if ( is_full_() ) {
if ( ! waiting_producers_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
return channel_op_status::timeout;
}
} else {
slots_[pidx_] = value;
pidx_ = (pidx_ + 1) % capacity_;
waiting_consumers_.notify_one();
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_, std::try_to_lock};
if (!lk) {
active_ctx->yield();
continue;
}
if ( BOOST_UNLIKELY( is_closed_() ) ) {
return channel_op_status::closed;
}
if ( is_full_() ) {
if ( ! waiting_producers_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
return channel_op_status::timeout;
}
} else {
slots_[pidx_] = std::move( value);
pidx_ = (pidx_ + 1) % capacity_;
// notify one waiting consumer
waiting_consumers_.notify_one();
return channel_op_status::success;
}
}
}
channel_op_status try_pop( value_type & value) {
detail::spinlock_lock lk{splk_, std::defer_lock};
for(;;) {
if(lk.try_lock())
break;
context::active()->yield();
}
if ( is_empty_() ) {
return is_closed_()
? channel_op_status::closed
: channel_op_status::empty;
}
value = std::move( slots_[cidx_]);
cidx_ = (cidx_ + 1) % capacity_;
waiting_producers_.notify_one();
return channel_op_status::success;
}
channel_op_status pop( value_type & value) {
context * active_ctx = context::active();
for (;;) {
detail::spinlock_lock lk{splk_, std::try_to_lock};
if (!lk) {
active_ctx->yield();
continue;
}
if ( is_empty_() ) {
if ( BOOST_UNLIKELY( is_closed_() ) ) {
return channel_op_status::closed;
}
waiting_consumers_.suspend_and_wait( lk, active_ctx);
} else {
value = std::move( slots_[cidx_]);
cidx_ = (cidx_ + 1) % capacity_;
waiting_producers_.notify_one();
return channel_op_status::success;
}
}
}
value_type value_pop() {
context * active_ctx = context::active();
for (;;) {
detail::spinlock_lock lk{splk_, std::try_to_lock};
if (!lk) {
active_ctx->yield();
continue;
}
if ( is_empty_() ) {
if ( BOOST_UNLIKELY( is_closed_() ) ) {
throw fiber_error{
std::make_error_code( std::errc::operation_not_permitted),
"boost fiber: channel is closed" };
}
waiting_consumers_.suspend_and_wait( lk, active_ctx);
} else {
value_type value = std::move( slots_[cidx_]);
cidx_ = (cidx_ + 1) % capacity_;
waiting_producers_.notify_one();
return 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_, std::try_to_lock};
if (!lk) {
active_ctx->yield();
continue;
}
if ( is_empty_() ) {
if ( BOOST_UNLIKELY( is_closed_() ) ) {
return channel_op_status::closed;
}
if ( ! waiting_consumers_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
return channel_op_status::timeout;
}
} else {
value = std::move( slots_[cidx_]);
cidx_ = (cidx_ + 1) % capacity_;
waiting_producers_.notify_one();
return channel_op_status::success;
}
}
}
class iterator {
private:
typedef typename std::aligned_storage< sizeof( value_type), alignof( value_type) >::type storage_type;
buffered_channel * chan_{ nullptr };
storage_type storage_;
void increment_( bool initial = false) {
BOOST_ASSERT( nullptr != chan_);
try {
if ( ! initial) {
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
}
::new ( static_cast< void * >( std::addressof( storage_) ) ) value_type{ chan_->value_pop() };
} catch ( fiber_error const&) {
chan_ = nullptr;
}
}
public:
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
using pointer_t = pointer;
using reference_t = reference;
iterator() = default;
explicit iterator( buffered_channel< T > * chan) noexcept :
chan_{ chan } {
increment_( true);
}
iterator( iterator const& other) noexcept :
chan_{ other.chan_ } {
}
iterator & operator=( iterator const& other) noexcept {
if ( BOOST_LIKELY( this != & other) ) {
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++() {
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
increment_();
return * this;
}
const 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
+34
View File
@@ -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
+246
View File
@@ -0,0 +1,246 @@
// 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/context/detail/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>
#include <boost/fiber/waker.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:
detail::spinlock wait_queue_splk_{};
wait_queue 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_ };
lt.unlock();
wait_queue_.suspend_and_wait( lk, active_ctx);
// relock external again before returning
try {
lt.lock();
#if defined(BOOST_CONTEXT_HAS_CXXABI_H)
} catch ( abi::__forced_unwind const&) {
throw;
#endif
} catch (...) {
std::terminate();
}
}
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_ };
// unlock external lt
lt.unlock();
if ( ! wait_queue_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
status = cv_status::timeout;
}
// relock external again before returning
try {
lt.lock();
#if defined(BOOST_CONTEXT_HAS_CXXABI_H)
} catch ( abi::__forced_unwind const&) {
throw;
#endif
} catch (...) {
std::terminate();
}
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
+531
View File
@@ -0,0 +1,531 @@
// 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 <atomic>
#include <chrono>
#include <cstdint>
#include <exception>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/core/ignore_unused.hpp>
#if defined(BOOST_NO_CXX17_STD_APPLY)
#include <boost/context/detail/apply.hpp>
#endif
#include <boost/context/fiber.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/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/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>
#include <boost/fiber/waker.hpp>
#include <boost/fiber/stack_allocator_wrapper.hpp>
#include <boost/fiber/algo/algorithm.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 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;
}
class BOOST_FIBERS_DECL context {
private:
friend class dispatcher_context;
friend class main_context;
template< typename Fn, typename ... Arg > friend class worker_context;
friend class scheduler;
struct fss_data {
void * vp{ nullptr };
detail::fss_cleanup_function::ptr_t cleanup_function{};
fss_data() = default;
fss_data( void * vp_,
detail::fss_cleanup_function::ptr_t fn) noexcept :
vp( vp_),
cleanup_function(std::move( 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)
std::atomic< std::size_t > use_count_;
#else
std::size_t use_count_;
#endif
#if ! defined(BOOST_FIBERS_NO_ATOMICS)
detail::remote_ready_hook remote_ready_hook_{};
#endif
detail::spinlock splk_{};
bool terminated_{ false };
wait_queue wait_queue_{};
public:
#if ! defined(BOOST_FIBERS_NO_ATOMICS)
std::atomic<size_t> waker_epoch_{ 0 };
#endif
private:
scheduler * scheduler_{ nullptr };
fss_data_t fss_data_{};
detail::sleep_hook sleep_hook_{};
waker sleep_waker_{};
detail::ready_hook ready_hook_{};
detail::terminated_hook terminated_hook_{};
detail::worker_hook worker_hook_{};
fiber_properties * properties_{ nullptr };
boost::context::fiber c_{};
std::chrono::steady_clock::time_point tp_;
type type_;
launch policy_;
context( std::size_t initial_count, type t, launch policy) noexcept :
use_count_{ initial_count },
tp_{ (std::chrono::steady_clock::time_point::max)() },
type_{ t },
policy_{ policy } {
}
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_;
}
return os << "{not-valid}";
}
explicit operator bool() const noexcept {
return nullptr != impl_;
}
bool operator!() const noexcept {
return nullptr == impl_;
}
};
// Returns true if the thread could be initialize, false otherwise (it was already initialized previously).
static bool initialize_thread(algo::algorithm::ptr_t algo, stack_allocator_wrapper&& salloc) noexcept;
static context * active() noexcept;
static void reset_active() noexcept;
context( context const&) = delete;
context( context &&) = delete;
context & operator=( context const&) = delete;
context & operator=( context &&) = delete;
#if !defined(BOOST_EMBTC)
friend bool
operator==( context const& lhs, context const& rhs) noexcept {
return & lhs == & rhs;
}
#else
friend bool
operator==( context const& lhs, context const& rhs) noexcept;
#endif
virtual ~context();
scheduler * get_scheduler() const noexcept {
return scheduler_;
}
id get_id() const noexcept;
bool is_resumable() const noexcept {
return static_cast<bool>(c_);
}
void resume() noexcept;
void resume( detail::spinlock_lock &) noexcept;
void resume( context *) noexcept;
void suspend() noexcept;
void suspend( detail::spinlock_lock &) noexcept;
boost::context::fiber suspend_with_cc() noexcept;
boost::context::fiber terminate() noexcept;
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 &,
waker &&) noexcept;
bool wake(const size_t) noexcept;
waker create_waker() noexcept {
// this operation makes all previously created wakers to be outdated
return { this, ++waker_epoch_ };
}
void schedule( context *) noexcept;
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;
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);
}
void worker_unlink() noexcept;
void ready_unlink() noexcept;
void sleep_unlink() noexcept;
void detach() noexcept;
void attach( context *) noexcept;
#if !defined(BOOST_EMBTC)
friend void intrusive_ptr_add_ref( context * ctx) noexcept {
BOOST_ASSERT( nullptr != ctx);
ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
}
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);
boost::context::fiber c = std::move( ctx->c_);
// destruct context
ctx->~context();
// deallocated stack
std::move( c).resume();
}
}
#else
friend void intrusive_ptr_add_ref( context * ctx) noexcept;
friend void intrusive_ptr_release( context * ctx) noexcept;
#endif
};
#if defined(BOOST_EMBTC)
inline bool
operator==( context const& lhs, context const& rhs) noexcept {
return & lhs == & rhs;
}
inline void intrusive_ptr_add_ref( context * ctx) noexcept {
BOOST_ASSERT( nullptr != ctx);
ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
}
inline void intrusive_ptr_release( context * ctx) noexcept {
BOOST_ASSERT( nullptr != ctx);
if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
std::atomic_thread_fence( std::memory_order_acquire);
boost::context::fiber c = std::move( ctx->c_);
// destruct context
ctx->~context();
// deallocated stack
std::move( c).resume();
}
}
#endif
inline
bool operator<( context const& l, context const& r) noexcept {
return l.get_id() < r.get_id();
}
template< typename Fn, typename ... Arg >
class worker_context final : public context {
private:
typename std::decay< Fn >::type fn_;
std::tuple< Arg ... > arg_;
boost::context::fiber
run_( boost::context::fiber && c) {
{
// fn and tpl must be destroyed before calling terminate()
auto fn = std::move( fn_);
auto arg = std::move( arg_);
#if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
std::move( c).resume();
#else
boost::ignore_unused(c);
#endif
#if defined(BOOST_NO_CXX17_STD_APPLY)
boost::context::detail::apply( std::move( fn), std::move( arg) );
#else
std::apply( std::move( fn), std::move( arg) );
#endif
}
// terminate context
return terminate();
}
public:
template< typename StackAlloc >
worker_context( launch policy,
fiber_properties* properties,
boost::context::preallocated const& palloc, StackAlloc && salloc,
Fn && fn, Arg ... arg) :
context{ 1, type::worker_context, policy },
fn_( std::forward< Fn >( fn) ),
arg_( std::forward< Arg >( arg) ... ) {
if ( properties != nullptr ) {
set_properties(properties);
properties->set_context(this);
}
c_ = boost::context::fiber{ std::allocator_arg, palloc, std::forward< StackAlloc >( salloc),
std::bind( & worker_context::run_, this, std::placeholders::_1) };
#if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
c_ = std::move( c_).resume();
#endif
}
template< typename StackAlloc >
worker_context( launch policy,
boost::context::preallocated const& palloc, StackAlloc && salloc,
Fn && fn, Arg ... arg) :
worker_context( policy, palloc, salloc, nullptr, std::forward<Fn>( fn ), std::forward<Arg>( arg ) ... ){
}
};
template< typename StackAlloc, typename Fn, typename ... Arg >
static intrusive_ptr< context > make_worker_context_with_properties( launch policy,
fiber_properties* properties,
StackAlloc && salloc,
Fn && fn, Arg ... arg) {
typedef worker_context< Fn, Arg ... > context_t;
auto sctx = salloc.allocate();
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( context_t) ) )
& ~ static_cast< uintptr_t >( 0xff) );
void * stack_bottom = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
const std::size_t size = reinterpret_cast< uintptr_t >( storage) - reinterpret_cast< uintptr_t >( stack_bottom);
// placement new of context on top of fiber's stack
return intrusive_ptr< context >{
new ( storage) context_t{
policy,
properties,
boost::context::preallocated{ storage, size, sctx },
std::forward< StackAlloc >( salloc),
std::forward< Fn >( fn),
std::forward< Arg >( arg) ... } };
}
template< typename StackAlloc, typename Fn, typename ... Arg >
static intrusive_ptr< context > make_worker_context( launch policy,
StackAlloc && salloc,
Fn && fn, Arg ... arg){
return make_worker_context_with_properties( policy, nullptr, std::forward<StackAlloc>(salloc),
std::forward<Fn>( fn ), std::forward<Arg>( arg ) ... );
}
}}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_CONTEXT_H
+139
View File
@@ -0,0 +1,139 @@
// Copyright Oliver Kowalke 2017.
// 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_CUDA_WAITFOR_H
#define BOOST_FIBERS_CUDA_WAITFOR_H
#include <initializer_list>
#include <mutex>
#include <iostream>
#include <set>
#include <tuple>
#include <vector>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <cuda.h>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/is_all_same.hpp>
#include <boost/fiber/condition_variable.hpp>
#include <boost/fiber/mutex.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace cuda {
namespace detail {
template< typename Rendezvous >
static void trampoline( cudaStream_t st, cudaError_t status, void * vp) {
Rendezvous * data = static_cast< Rendezvous * >( vp);
data->notify( st, status);
}
class single_stream_rendezvous {
public:
single_stream_rendezvous( cudaStream_t st) {
unsigned int flags = 0;
cudaError_t status = ::cudaStreamAddCallback( st, trampoline< single_stream_rendezvous >, this, flags);
if ( cudaSuccess != status) {
st_ = st;
status_ = status;
done_ = true;
}
}
void notify( cudaStream_t st, cudaError_t status) noexcept {
std::unique_lock< mutex > lk{ mtx_ };
st_ = st;
status_ = status;
done_ = true;
lk.unlock();
cv_.notify_one();
}
std::tuple< cudaStream_t, cudaError_t > wait() {
std::unique_lock< mutex > lk{ mtx_ };
cv_.wait( lk, [this]{ return done_; });
return std::make_tuple( st_, status_);
}
private:
mutex mtx_{};
condition_variable cv_{};
cudaStream_t st_{};
cudaError_t status_{ cudaErrorUnknown };
bool done_{ false };
};
class many_streams_rendezvous {
public:
many_streams_rendezvous( std::initializer_list< cudaStream_t > l) :
stx_{ l } {
results_.reserve( stx_.size() );
for ( cudaStream_t st : stx_) {
unsigned int flags = 0;
cudaError_t status = ::cudaStreamAddCallback( st, trampoline< many_streams_rendezvous >, this, flags);
if ( cudaSuccess != status) {
std::unique_lock< mutex > lk{ mtx_ };
stx_.erase( st);
results_.push_back( std::make_tuple( st, status) );
}
}
}
void notify( cudaStream_t st, cudaError_t status) noexcept {
std::unique_lock< mutex > lk{ mtx_ };
stx_.erase( st);
results_.push_back( std::make_tuple( st, status) );
if ( stx_.empty() ) {
lk.unlock();
cv_.notify_one();
}
}
std::vector< std::tuple< cudaStream_t, cudaError_t > > wait() {
std::unique_lock< mutex > lk{ mtx_ };
cv_.wait( lk, [this]{ return stx_.empty(); });
return results_;
}
private:
mutex mtx_{};
condition_variable cv_{};
std::set< cudaStream_t > stx_;
std::vector< std::tuple< cudaStream_t, cudaError_t > > results_;
};
}
void waitfor_all();
inline
std::tuple< cudaStream_t, cudaError_t > waitfor_all( cudaStream_t st) {
detail::single_stream_rendezvous rendezvous( st);
return rendezvous.wait();
}
template< typename ... STP >
std::vector< std::tuple< cudaStream_t, cudaError_t > > waitfor_all( cudaStream_t st0, STP ... stx) {
static_assert( boost::fibers::detail::is_all_same< cudaStream_t, STP ...>::value, "all arguments must be of type `CUstream*`.");
detail::many_streams_rendezvous rendezvous{ st0, stx ... };
return rendezvous.wait();
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_CUDA_WAITFOR_H
+66
View File
@@ -0,0 +1,66 @@
// 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_CONTENTION_WINDOW_THRESHOLD)
# define BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD 16
#endif
#if !defined(BOOST_FIBERS_RETRY_THRESHOLD)
# define BOOST_FIBERS_RETRY_THRESHOLD 64
#endif
#if !defined(BOOST_FIBERS_SPIN_BEFORE_SLEEP0)
# define BOOST_FIBERS_SPIN_BEFORE_SLEEP0 32
#endif
#if !defined(BOOST_FIBERS_SPIN_BEFORE_YIELD)
# define BOOST_FIBERS_SPIN_BEFORE_YIELD 64
#endif
#endif // BOOST_FIBERS_DETAIL_CONFIG_H
+118
View File
@@ -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;
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
+197
View File
@@ -0,0 +1,197 @@
// 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 atomic_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();
}
};
std::atomic< std::size_t > top_{ 0 };
std::atomic< std::size_t > bottom_{ 0 };
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
+43
View File
@@ -0,0 +1,43 @@
// 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() );
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_CONVERT_H
+86
View File
@@ -0,0 +1,86 @@
// 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 && (((__mips_isa_rev > 1) && defined(__mips32)) || ((__mips_isa_rev > 2) && defined(__mips64)))
# define cpu_relax() asm volatile ("pause" ::: "memory");
#elif BOOST_ARCH_PPC
// http://code.metager.de/source/xref/gnu/glibc/sysdeps/powerpc/sys/platform/ppc.h
// 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
# if defined(__POWERPC__) // Darwin PPC
# define cpu_relax() asm volatile ("or r27,r27,r27" ::: "memory");
# else
# define cpu_relax() asm volatile ("or 27,27,27" ::: "memory");
# endif
#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
+54
View File
@@ -0,0 +1,54 @@
// 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 {
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_ } {
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_DATA_H
+36
View File
@@ -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
+34
View File
@@ -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
+36
View File
@@ -0,0 +1,36 @@
// Copyright Oliver Kowalke 2018.
// 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_EXCHANGE_H
#define BOOST_FIBER_DETAIL_EXCHANGE_H
#include <algorithm>
#include <utility>
#include <boost/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace detail {
template< typename T, typename U = T >
T exchange( T & t, U && nv) {
T ov = std::move( t);
t = std::forward< U >( nv);
return ov;
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBER_DETAIL_EXCHANGE_H
+59
View File
@@ -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() = 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
+65
View File
@@ -0,0 +1,65 @@
// 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>
#ifndef SYS_futex
#define SYS_futex SYS_futex_time64
#endif
#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
BOOST_FORCEINLINE
int sys_futex( void * addr, std::int32_t op, std::int32_t x) {
return ::syscall( SYS_futex, addr, op, x, nullptr, nullptr, 0);
}
BOOST_FORCEINLINE
int futex_wake( std::atomic< std::int32_t > * addr) {
return 0 <= sys_futex( static_cast< void * >( addr), FUTEX_WAKE_PRIVATE, 1) ? 0 : -1;
}
BOOST_FORCEINLINE
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
BOOST_FORCEINLINE
int futex_wake( std::atomic< std::int32_t > * addr) {
::WakeByAddressSingle( static_cast< void * >( addr) );
return 0;
}
BOOST_FORCEINLINE
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
+44
View File
@@ -0,0 +1,44 @@
// Copyright Oliver Kowalke 2017.
// 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_IS_ALL_SAME_H
#define BOOST_FIBERS_DETAIL_IS_ALL_SAME_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 X, typename ... Y >
struct is_all_same;
template< typename X, typename Y0, typename ... Y >
struct is_all_same< X, Y0, Y ... > {
static constexpr bool value =
std::is_same< X, Y0 >::value && is_all_same< X, Y ... >::value;
};
template< typename X, typename Y0 >
struct is_all_same< X, Y0 > {
static constexpr bool value = std::is_same< X, Y0 >::value;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_IS_ALL_SAME_H
+94
View File
@@ -0,0 +1,94 @@
// Copyright Oliver Kowalke 2017.
// 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_RTM_H
#define BOOST_FIBER_DETAIL_RTM_H
#include <cstdint>
#include <boost/assert.hpp>
#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 {
struct rtm_status {
enum {
none = 0,
explicit_abort = 1 << 0,
may_retry = 1 << 1,
memory_conflict = 1 << 2,
buffer_overflow = 1 << 3,
debug_hit = 1 << 4,
nested_abort = 1 << 5
};
static constexpr std::uint32_t success = ~std::uint32_t{ 0 };
};
static BOOST_FORCEINLINE
std::uint32_t rtm_begin() noexcept {
std::uint32_t result = rtm_status::success;
__asm__ __volatile__
(
".byte 0xc7,0xf8 ; .long 0"
: "+a" (result)
:
: "memory"
);
return result;
}
static BOOST_FORCEINLINE
void rtm_end() noexcept {
__asm__ __volatile__
(
".byte 0x0f,0x01,0xd5"
:
:
: "memory"
);
}
static BOOST_FORCEINLINE
void rtm_abort_lock_not_free() noexcept {
__asm__ __volatile__
(
".byte 0xc6,0xf8,0xff"
:
:
: "memory"
);
}
static BOOST_FORCEINLINE
bool rtm_test() noexcept {
bool result;
__asm__ __volatile__
(
".byte 0x0f,0x01,0xd6; setz %0"
: "=q" (result)
:
: "memory"
);
return result;
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBER_DETAIL_RTM_H
+84
View File
@@ -0,0 +1,84 @@
// 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_adaptive.hpp>
# include <boost/fiber/detail/spinlock_ttas.hpp>
# if defined(BOOST_FIBERS_HAS_FUTEX)
# include <boost/fiber/detail/spinlock_ttas_adaptive_futex.hpp>
# include <boost/fiber/detail/spinlock_ttas_futex.hpp>
# endif
# if defined(BOOST_USE_TSX)
# include <boost/fiber/detail/spinlock_rtm.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)
# if defined(BOOST_USE_TSX)
using spinlock = spinlock_rtm< spinlock_ttas_futex >;
# else
using spinlock = spinlock_ttas_futex;
# endif
# elif defined(BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX)
# if defined(BOOST_USE_TSX)
using spinlock = spinlock_rtm< spinlock_ttas_adaptive_futex >;
# else
using spinlock = spinlock_ttas_adaptive_futex;
# endif
# elif defined(BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE)
# if defined(BOOST_USE_TSX)
using spinlock = spinlock_rtm< spinlock_ttas_adaptive >;
# else
using spinlock = spinlock_ttas_adaptive;
# endif
# else
# if defined(BOOST_USE_TSX)
using spinlock = spinlock_rtm< spinlock_ttas >;
# else
using spinlock = spinlock_ttas;
# endif
# endif
using spinlock_lock = std::unique_lock< spinlock >;
#endif
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_SPINLOCK_H
+127
View File
@@ -0,0 +1,127 @@
// Copyright Oliver Kowalke 2017.
// 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_RTM_H
#define BOOST_FIBERS_SPINLOCK_RTM_H
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <random>
#include <thread>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/cpu_relax.hpp>
#include <boost/fiber/detail/rtm.hpp>
#include <boost/fiber/detail/spinlock_status.hpp>
namespace boost {
namespace fibers {
namespace detail {
template< typename FBSplk >
class spinlock_rtm {
private:
FBSplk splk_{};
public:
spinlock_rtm() = default;
spinlock_rtm( spinlock_rtm const&) = delete;
spinlock_rtm & operator=( spinlock_rtm const&) = delete;
void lock() noexcept {
static thread_local std::minstd_rand generator{ std::random_device{}() };
std::size_t collisions = 0 ;
for ( std::size_t retries = 0; retries < BOOST_FIBERS_RETRY_THRESHOLD; ++retries) {
std::uint32_t status;
if ( rtm_status::success == ( status = rtm_begin() ) ) {
// add lock to read-set
if ( spinlock_status::unlocked == splk_.state_.load( std::memory_order_relaxed) ) {
// lock is free, enter critical section
return;
}
// lock was acquired by another thread
// explicit abort of transaction with abort argument 'lock not free'
rtm_abort_lock_not_free();
}
// transaction aborted
if ( rtm_status::none != (status & rtm_status::may_retry) ||
rtm_status::none != (status & rtm_status::memory_conflict) ) {
// another logical processor conflicted with a memory address that was
// part or the read-/write-set
if ( BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD > collisions) {
std::uniform_int_distribution< std::size_t > distribution{
0, static_cast< std::size_t >( 1) << (std::min)(collisions, static_cast< std::size_t >( BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD)) };
const std::size_t z = distribution( generator);
++collisions;
for ( std::size_t i = 0; i < z; ++i) {
cpu_relax();
}
} else {
std::this_thread::yield();
}
} else if ( rtm_status::none != (status & rtm_status::explicit_abort) &&
rtm_status::none == (status & rtm_status::nested_abort) ) {
// another logical processor has acquired the lock and
// abort was not caused by a nested transaction
// wait till lock becomes free again
std::size_t count = 0;
while ( spinlock_status::locked == splk_.state_.load( std::memory_order_relaxed) ) {
if ( BOOST_FIBERS_SPIN_BEFORE_SLEEP0 > count) {
++count;
cpu_relax();
} else if ( BOOST_FIBERS_SPIN_BEFORE_YIELD > count) {
++count;
static constexpr std::chrono::microseconds us0{ 0 };
std::this_thread::sleep_for( us0);
#if 0
using namespace std::chrono_literals;
std::this_thread::sleep_for( 0ms);
#endif
} else {
std::this_thread::yield();
}
}
} else {
// transaction aborted due:
// - internal buffer to track transactional state overflowed
// - debug exception or breakpoint exception was hit
// - abort during execution of nested transactions (max nesting limit exceeded)
// -> use fallback path
break;
}
}
splk_.lock();
}
bool try_lock() noexcept {
if ( rtm_status::success != rtm_begin() ) {
return false;
}
// add lock to read-set
if ( spinlock_status::unlocked != splk_.state_.load( std::memory_order_relaxed) ) {
// lock was acquired by another thread
// explicit abort of transaction with abort argument 'lock not free'
rtm_abort_lock_not_free();
}
return true;
}
void unlock() noexcept {
if ( spinlock_status::unlocked == splk_.state_.load( std::memory_order_acquire) ) {
rtm_end();
} else {
splk_.unlock();
}
}
};
}}}
#endif // BOOST_FIBERS_SPINLOCK_RTM_H
+21
View File
@@ -0,0 +1,21 @@
// Copyright Oliver Kowalke 2017.
// 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_STATUS_H
#define BOOST_FIBERS_SPINLOCK_STATUS_H
namespace boost {
namespace fibers {
namespace detail {
enum class spinlock_status {
locked = 0,
unlocked
};
}}}
#endif // BOOST_FIBERS_SPINLOCK_STATUS_H
+118
View File
@@ -0,0 +1,118 @@
// 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 <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <random>
#include <thread>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/cpu_relax.hpp>
#include <boost/fiber/detail/spinlock_status.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 {
private:
template< typename FBSplk >
friend class spinlock_rtm;
std::atomic< spinlock_status > state_{ spinlock_status::unlocked };
public:
spinlock_ttas() = default;
spinlock_ttas( spinlock_ttas const&) = delete;
spinlock_ttas & operator=( spinlock_ttas const&) = delete;
void lock() noexcept {
static thread_local std::minstd_rand generator{ std::random_device{}() };
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 retries = 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_BEFORE_SLEEP0 > retries) {
++retries;
// 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 if ( BOOST_FIBERS_SPIN_BEFORE_YIELD > retries) {
++retries;
// std::this_thread::sleep_for( 0us) has a fairly long instruction path length,
// combined with an expensive ring3 to ring 0 transition costing about 1000 cycles
// std::this_thread::sleep_for( 0us) lets give up this_thread the remaining part of its time slice
// 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() 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();
}
#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)
std::uniform_int_distribution< std::size_t > distribution{
0, static_cast< std::size_t >( 1) << (std::min)(collisions, static_cast< std::size_t >( BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD)) };
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;
}
}
}
bool try_lock() noexcept {
return spinlock_status::unlocked == state_.exchange( spinlock_status::locked, std::memory_order_acquire);
}
void unlock() noexcept {
state_.store( spinlock_status::unlocked, std::memory_order_release);
}
};
}}}
#endif // BOOST_FIBERS_SPINLOCK_TTAS_H
+125
View File
@@ -0,0 +1,125 @@
// 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 <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <random>
#include <thread>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/cpu_relax.hpp>
#include <boost/fiber/detail/spinlock_status.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:
template< typename FBSplk >
friend class spinlock_rtm;
std::atomic< spinlock_status > state_{ spinlock_status::unlocked };
std::atomic< std::size_t > retries_{ 0 };
public:
spinlock_ttas_adaptive() = default;
spinlock_ttas_adaptive( spinlock_ttas_adaptive const&) = delete;
spinlock_ttas_adaptive & operator=( spinlock_ttas_adaptive const&) = delete;
void lock() noexcept {
static thread_local std::minstd_rand generator{ std::random_device{}() };
std::size_t collisions = 0 ;
for (;;) {
std::size_t retries = 0;
const std::size_t prev_retries = retries_.load( std::memory_order_relaxed);
const std::size_t max_relax_retries = (std::min)(
static_cast< std::size_t >( BOOST_FIBERS_SPIN_BEFORE_SLEEP0), 2 * prev_retries + 10);
const std::size_t max_sleep_retries = (std::min)(
static_cast< std::size_t >( BOOST_FIBERS_SPIN_BEFORE_YIELD), 2 * prev_retries + 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_relax_retries > retries) {
++retries;
// 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 if ( max_sleep_retries > retries) {
++retries;
// std::this_thread::sleep_for( 0us) has a fairly long instruction path length,
// combined with an expensive ring3 to ring 0 transition costing about 1000 cycles
// std::this_thread::sleep_for( 0us) lets give up this_thread the remaining part of its time slice
// 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() 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();
}
#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)
std::uniform_int_distribution< std::size_t > distribution{
0, static_cast< std::size_t >( 1) << (std::min)(collisions, static_cast< std::size_t >( BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD)) };
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 {
retries_.store( prev_retries + (retries - prev_retries) / 8, std::memory_order_relaxed);
// success, thread has acquired the lock
break;
}
}
}
bool try_lock() noexcept {
return spinlock_status::unlocked == state_.exchange( spinlock_status::locked, std::memory_order_acquire);
}
void unlock() noexcept {
state_.store( spinlock_status::unlocked, std::memory_order_release);
}
};
}}}
#endif // BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_H
+137
View File
@@ -0,0 +1,137 @@
// 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 <algorithm>
#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:
template< typename FBSplk >
friend class spinlock_rtm;
std::atomic< std::int32_t > value_{ 0 };
std::atomic< std::int32_t > retries_{ 0 };
public:
spinlock_ttas_adaptive_futex() = 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 {
static thread_local std::minstd_rand generator{ std::random_device{}() };
std::int32_t collisions = 0, retries = 0, expected = 0;
const std::int32_t prev_retries = retries_.load( std::memory_order_relaxed);
const std::int32_t max_relax_retries = (std::min)(
static_cast< std::int32_t >( BOOST_FIBERS_SPIN_BEFORE_SLEEP0), 2 * prev_retries + 10);
const std::int32_t max_sleep_retries = (std::min)(
static_cast< std::int32_t >( BOOST_FIBERS_SPIN_BEFORE_YIELD), 2 * prev_retries + 10);
// after max. spins or collisions suspend via futex
while ( retries++ < BOOST_FIBERS_RETRY_THRESHOLD) {
// 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) ) ) {
#if !defined(BOOST_FIBERS_SPIN_SINGLE_CORE)
if ( max_relax_retries > retries) {
// 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 if ( max_sleep_retries > retries) {
// std::this_thread::sleep_for( 0us) has a fairly long instruction path length,
// combined with an expensive ring3 to ring 0 transition costing about 1000 cycles
// std::this_thread::sleep_for( 0us) lets give up this_thread the remaining part of its time slice
// 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() 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();
}
#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) ) {
// spinlock now contended
// utilize 'Binary Exponential Backoff' algorithm
// linear_congruential_engine is a random number engine based on Linear congruential generator (LCG)
std::uniform_int_distribution< std::int32_t > distribution{
0, static_cast< std::int32_t >( 1) << (std::min)(collisions, static_cast< std::int32_t >( BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD)) };
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
retries_.store( prev_retries + (retries - prev_retries) / 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
retries_.store( prev_retries + (retries - prev_retries) / 8, std::memory_order_relaxed);
}
bool try_lock() noexcept {
std::int32_t expected = 0;
return value_.compare_exchange_strong( expected, 1, 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_ADAPTIVE_FUTEX_H
+128
View File
@@ -0,0 +1,128 @@
// 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 <algorithm>
#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_futex {
private:
template< typename FBSplk >
friend class spinlock_rtm;
std::atomic< std::int32_t > value_{ 0 };
public:
spinlock_ttas_futex() = default;
spinlock_ttas_futex( spinlock_ttas_futex const&) = delete;
spinlock_ttas_futex & operator=( spinlock_ttas_futex const&) = delete;
void lock() noexcept {
static thread_local std::minstd_rand generator{ std::random_device{}() };
std::int32_t collisions = 0, retries = 0, expected = 0;
// after max. spins or collisions suspend via futex
while ( retries++ < BOOST_FIBERS_RETRY_THRESHOLD) {
// 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) ) ) {
#if !defined(BOOST_FIBERS_SPIN_SINGLE_CORE)
if ( BOOST_FIBERS_SPIN_BEFORE_SLEEP0 > retries) {
// 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 if ( BOOST_FIBERS_SPIN_BEFORE_YIELD > retries) {
// std::this_thread::sleep_for( 0us) has a fairly long instruction path length,
// combined with an expensive ring3 to ring 0 transition costing about 1000 cycles
// std::this_thread::sleep_for( 0us) lets give up this_thread the remaining part of its time slice
// 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() 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();
}
#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) ) {
// spinlock now contended
// utilize 'Binary Exponential Backoff' algorithm
// linear_congruential_engine is a random number engine based on Linear congruential generator (LCG)
std::uniform_int_distribution< std::int32_t > distribution{
0, static_cast< std::int32_t >( 1) << (std::min)(collisions, static_cast< std::int32_t >( BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD)) };
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);
}
}
bool try_lock() noexcept {
std::int32_t expected = 0;
return value_.compare_exchange_strong( expected, 1, 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
+62
View File
@@ -0,0 +1,62 @@
// 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_FIBER_DETAIL_THREAD_BARRIER_H
#define BOOST_FIBER_DETAIL_THREAD_BARRIER_H
#include <cstddef>
#include <condition_variable>
#include <mutex>
#include <boost/assert.hpp>
#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 {
class thread_barrier {
private:
std::size_t initial_;
std::size_t current_;
bool cycle_{ true };
std::mutex mtx_{};
std::condition_variable cond_{};
public:
explicit thread_barrier( std::size_t initial) :
initial_{ initial },
current_{ initial_ } {
BOOST_ASSERT ( 0 != initial);
}
thread_barrier( thread_barrier const&) = delete;
thread_barrier & operator=( thread_barrier const&) = delete;
bool wait() {
std::unique_lock< std::mutex > lk( mtx_);
const bool cycle = cycle_;
if ( 0 == --current_) {
cycle_ = ! cycle_;
current_ = initial_;
lk.unlock(); // no pessimization
cond_.notify_all();
return true;
}
cond_.wait( lk, [&](){ return cycle != cycle_; });
return false;
}
};
}}}
#endif // BOOST_FIBER_DETAIL_THREAD_BARRIER_H
+148
View File
@@ -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:
explicit 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 } {
}
~fiber_error() override = default;
};
class lock_error : public fiber_error {
public:
explicit 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:
explicit 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
+243
View File
@@ -0,0 +1,243 @@
// 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/predef.h>
#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;
using ptr_t = intrusive_ptr<context>;
ptr_t impl_{};
void start_() noexcept;
public:
using id = context::id;
fiber() = default;
template< typename Fn,
typename ... Arg,
typename = detail::disable_overload< fiber, Fn >,
typename = detail::disable_overload< launch, Fn >,
typename = detail::disable_overload< std::allocator_arg_t, Fn >
>
#if BOOST_COMP_GNUC < 50000000
explicit fiber( Fn && fn, Arg && ... arg) :
#else
fiber( Fn && fn, Arg ... arg) :
#endif
fiber{ launch::post,
std::allocator_arg, default_stack(),
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
}
template< typename Fn,
typename ... Arg,
typename = detail::disable_overload< fiber, Fn >
>
#if BOOST_COMP_GNUC < 50000000
fiber( launch policy, Fn && fn, Arg && ... arg) :
#else
fiber( launch policy, Fn && fn, Arg ... arg) :
#endif
fiber{ policy,
std::allocator_arg, default_stack(),
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
}
template< typename StackAllocator,
typename Fn,
typename ... Arg
>
#if BOOST_COMP_GNUC < 50000000
fiber( std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg && ... arg) :
#else
fiber( std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg ... arg) :
#endif
fiber{ launch::post,
std::allocator_arg, std::forward< StackAllocator >( salloc),
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
}
template< typename StackAllocator,
typename Fn,
typename ... Arg
>
#if BOOST_COMP_GNUC < 50000000
fiber( launch policy, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg && ... arg) :
#else
fiber( launch policy, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg ... arg) :
#endif
fiber{ policy,
static_cast<fiber_properties*>(nullptr),
std::allocator_arg, std::forward< StackAllocator >( salloc),
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
}
template< typename Fn,
typename ... Arg,
typename = detail::disable_overload< fiber, Fn >,
typename = detail::disable_overload< launch, Fn >,
typename = detail::disable_overload< std::allocator_arg_t, Fn >
>
#if BOOST_COMP_GNUC < 50000000
explicit fiber( fiber_properties* properties, Fn && fn, Arg && ... arg) :
#else
fiber( fiber_properties* properties, Fn && fn, Arg ... arg) :
#endif
fiber{ launch::post,
properties,
std::allocator_arg, default_stack(),
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
}
template< typename Fn,
typename ... Arg,
typename = detail::disable_overload< fiber, Fn >
>
#if BOOST_COMP_GNUC < 50000000
fiber( launch policy, fiber_properties* properties, Fn && fn, Arg && ... arg) :
#else
fiber( launch policy, fiber_properties* properties, Fn && fn, Arg ... arg) :
#endif
fiber{ policy,
properties,
std::allocator_arg, default_stack(),
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
}
template< typename StackAllocator,
typename Fn,
typename ... Arg
>
#if BOOST_COMP_GNUC < 50000000
fiber( fiber_properties* properties, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg && ... arg) :
#else
fiber( fiber_properties* properties, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg ... arg) :
#endif
fiber{ launch::post,
properties,
std::allocator_arg, std::forward< StackAllocator >( salloc),
std::forward< Fn >( fn), std::forward< Arg >( arg) ... } {
}
template< typename StackAllocator,
typename Fn,
typename ... Arg
>
#if BOOST_COMP_GNUC < 50000000
fiber( launch policy, fiber_properties* properties, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg && ... arg) :
#else
fiber( launch policy, fiber_properties* properties, std::allocator_arg_t, StackAllocator && salloc, Fn && fn, Arg ... arg) :
#endif
impl_{ make_worker_context_with_properties( policy, properties, std::forward< StackAllocator >( salloc), std::forward< Fn >( fn), std::forward< Arg >( arg) ... ) } {
start_();
}
~fiber() {
if ( joinable() ) {
std::terminate();
}
}
fiber( fiber const&) = delete;
fiber & operator=( fiber const&) = delete;
fiber( fiber && other) noexcept :
impl_{} {
swap( other);
}
fiber & operator=( fiber && other) noexcept {
if ( joinable() ) {
std::terminate();
}
if ( BOOST_UNLIKELY( 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
+33
View File
@@ -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
+107
View File
@@ -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 override {
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) override {
if ( BOOST_LIKELY( nullptr != fn) ) {
fn( static_cast< T * >( data) );
}
}
};
detail::fss_cleanup_function::ptr_t cleanup_fn_;
public:
using element_type = T;
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 ( BOOST_LIKELY( 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
+10
View File
@@ -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>
+122
View File
@@ -0,0 +1,122 @@
// 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 {
#if (defined(BOOST_MSVC) && (_MSC_VER >= 1911 && _MSVC_LANG >= 201703)) || __cplusplus >= 202002L
template <typename>
struct result_of;
template <typename F, typename... Args>
struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
#else
using std::result_of;
#endif
template< typename Fn, typename ... Args >
future<
typename 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 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 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 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 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 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 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 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
+313
View File
@@ -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 ( BOOST_UNLIKELY( 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 ( BOOST_UNLIKELY( 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 ( BOOST_UNLIKELY( 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 ( BOOST_UNLIKELY( 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 ( BOOST_UNLIKELY( 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
+59
View File
@@ -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_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 };
typedef std::allocator_traits< allocator_type > traity_type;
traity_type::destroy( a, p);
traity_type::deallocate( a, p, 1);
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_SHARED_STATE_OBJECT_H
+41
View File
@@ -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
+188
View File
@@ -0,0 +1,188 @@
// 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>
#include <boost/context/detail/config.hpp>
#if defined(BOOST_NO_CXX17_STD_APPLY)
#include <boost/context/detail/apply.hpp>
#endif
#include <boost/core/pointer_traits.hpp>
#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;
typedef std::allocator_traits< Allocator > allocator_traits;
public:
typedef typename allocator_traits::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
);
#if defined(BOOST_CONTEXT_HAS_CXXABI_H)
} catch ( abi::__forced_unwind const&) {
throw;
#endif
} catch (...) {
this->set_exception( std::current_exception() );
}
}
typename base_type::ptr_type reset() override final {
typedef std::allocator_traits< allocator_type > traity_type;
typedef pointer_traits< typename traity_type::pointer> ptrait_type;
typename traity_type::pointer ptr{ traity_type::allocate( alloc_, 1) };
typename ptrait_type::element_type* p = boost::to_address(ptr);
try {
traity_type::construct( alloc_, p, alloc_, std::move( fn_) );
} catch (...) {
traity_type::deallocate( alloc_, ptr, 1);
throw;
}
return { p };
}
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 };
typedef std::allocator_traits< allocator_type > traity_type;
traity_type::destroy( a, p);
traity_type::deallocate( a, 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;
typedef std::allocator_traits< Allocator > allocator_traits;
public:
typedef typename allocator_traits::template rebind_alloc<
task_object< Fn, Allocator, void, Args ... >
> 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();
#if defined(BOOST_CONTEXT_HAS_CXXABI_H)
} catch ( abi::__forced_unwind const&) {
throw;
#endif
} catch (...) {
this->set_exception( std::current_exception() );
}
}
typename base_type::ptr_type reset() override final {
typedef std::allocator_traits< allocator_type > traity_type;
typedef pointer_traits< typename traity_type::pointer> ptrait_type;
typename traity_type::pointer ptr{ traity_type::allocate( alloc_, 1) };
typename ptrait_type::element_type* p = boost::to_address(ptr);
try {
traity_type::construct( alloc_, p, alloc_, std::move( fn_) );
} catch (...) {
traity_type::deallocate( alloc_, ptr, 1);
throw;
}
return { p };
}
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 };
typedef std::allocator_traits< allocator_type > traity_type;
traity_type::destroy( a, p);
traity_type::deallocate( a, p, 1);
}
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DETAIL_TASK_OBJECT_H
+475
View File
@@ -0,0 +1,475 @@
// 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 <utility>
#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 p) noexcept :
state_{std::move( 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 ( BOOST_LIKELY( this != & other) ) {
state_ = other.state_;
}
return * this;
}
future_base & operator=( future_base && other) noexcept {
if ( BOOST_LIKELY( 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 ( BOOST_UNLIKELY( ! valid() ) ) {
throw future_uninitialized{};
}
return state_->get_exception_ptr();
}
void wait() const {
if ( BOOST_UNLIKELY( ! 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 ( BOOST_UNLIKELY( ! 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 ( BOOST_UNLIKELY( ! 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 ( BOOST_LIKELY( this != & other) ) {
base_type::operator=( std::move( other) );
}
return * this;
}
shared_future< R > share();
R get() {
if ( BOOST_UNLIKELY( ! 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 ( BOOST_LIKELY( this != & other) ) {
base_type::operator=( std::move( other) );
}
return * this;
}
shared_future< R & > share();
R & get() {
if ( BOOST_UNLIKELY( ! 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 ( BOOST_LIKELY( this != & other) ) {
base_type::operator=( std::move( other) );
}
return * this;
}
shared_future< void > share();
inline
void get() {
if ( BOOST_UNLIKELY( ! 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 ( BOOST_LIKELY( this != & other) ) {
base_type::operator=( other);
}
return * this;
}
shared_future & operator=( shared_future && other) noexcept {
if ( BOOST_LIKELY( 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 ( BOOST_UNLIKELY( ! 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 ( BOOST_LIKELY( this != & other) ) {
base_type::operator=( other);
}
return * this;
}
shared_future & operator=( shared_future && other) noexcept {
if ( BOOST_LIKELY( 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 ( BOOST_UNLIKELY( ! 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 ( BOOST_LIKELY( this != & other) ) {
base_type::operator=( other);
}
return * this;
}
inline
shared_future & operator=( shared_future && other) noexcept {
if ( BOOST_LIKELY( 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 ( BOOST_UNLIKELY( ! 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 ( BOOST_UNLIKELY( ! base_type::valid() ) ) {
throw future_uninitialized{};
}
return shared_future< R >{ std::move( * this) };
}
template< typename R >
shared_future< R & >
future< R & >::share() {
if ( BOOST_UNLIKELY( ! base_type::valid() ) ) {
throw future_uninitialized{};
}
return shared_future< R & >{ std::move( * this) };
}
inline
shared_future< void >
future< void >::share() {
if ( BOOST_UNLIKELY( ! base_type::valid() ) ) {
throw future_uninitialized{};
}
return shared_future< void >{ std::move( * this) };
}
}}
#endif
+27
View File
@@ -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
+142
View File
@@ -0,0 +1,142 @@
// 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/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;
typedef pointer_traits< typename traits_type::pointer > ptrait_type;
typename object_type::allocator_type a{ alloc };
typename traits_type::pointer ptr{ traits_type::allocate( a, 1) };
typename ptrait_type::element_type* p = boost::to_address(ptr);
try {
traits_type::construct( a, p, a, std::forward< Fn >( fn) );
} catch (...) {
traits_type::deallocate( a, ptr, 1);
throw;
}
task_.reset(p);
}
~packaged_task() {
if ( task_ && obtained_) {
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 ( BOOST_LIKELY( 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 ( BOOST_UNLIKELY( ! valid() ) ) {
throw packaged_task_uninitialized{};
}
obtained_ = true;
return future< R >{
boost::static_pointer_cast< detail::shared_state< R > >( task_) };
}
void operator()( Args ... args) {
if ( BOOST_UNLIKELY( ! valid() ) ) {
throw packaged_task_uninitialized{};
}
task_->run( std::forward< Args >( args) ... );
}
void reset() {
if ( BOOST_UNLIKELY( ! 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
+222
View File
@@ -0,0 +1,222 @@
// 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/core/pointer_traits.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;
typedef pointer_traits< typename traits_type::pointer > ptrait_type;
typename object_type::allocator_type a{ alloc };
typename traits_type::pointer ptr{ traits_type::allocate( a, 1) };
typename ptrait_type::element_type* p = boost::to_address(ptr);
try {
traits_type::construct( a, p, a);
} catch (...) {
traits_type::deallocate( a, ptr, 1);
throw;
}
future_.reset(p);
}
~promise_base() {
if ( future_ && obtained_) {
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 ( BOOST_LIKELY( this != & other) ) {
promise_base tmp{ std::move( other) };
swap( tmp);
}
return * this;
}
future< R > get_future() {
if ( BOOST_UNLIKELY( obtained_) ) {
throw future_already_retrieved{};
}
if ( BOOST_UNLIKELY( ! 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 ( BOOST_UNLIKELY( ! 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) = default;
promise & operator=( promise && other) = default;
void set_value( R const& value) {
if ( BOOST_UNLIKELY( ! base_type::future_) ) {
throw promise_uninitialized{};
}
base_type::future_->set_value( value);
}
void set_value( R && value) {
if ( BOOST_UNLIKELY( ! 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) = default;
promise & operator=( promise && other) = default;
void set_value( R & value) {
if ( BOOST_UNLIKELY( ! 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) = default;
promise & operator=( promise && other) = default;
inline
void set_value() {
if ( BOOST_UNLIKELY( ! 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
+139
View File
@@ -0,0 +1,139 @@
// Copyright Oliver Kowalke 2017.
// 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_CUDA_WAITFOR_H
#define BOOST_FIBERS_CUDA_WAITFOR_H
#include <initializer_list>
#include <mutex>
#include <iostream>
#include <set>
#include <tuple>
#include <vector>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <hip/hip_runtime.h>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/is_all_same.hpp>
#include <boost/fiber/condition_variable.hpp>
#include <boost/fiber/mutex.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace cuda {
namespace detail {
template< typename Rendezvous >
static void trampoline( hipStream_t st, hipError_t status, void * vp) {
Rendezvous * data = static_cast< Rendezvous * >( vp);
data->notify( st, status);
}
class single_stream_rendezvous {
public:
single_stream_rendezvous( hipStream_t st) {
unsigned int flags = 0;
hipError_t status = ::hipStreamAddCallback( st, trampoline< single_stream_rendezvous >, this, flags);
if ( hipSuccess != status) {
st_ = st;
status_ = status;
done_ = true;
}
}
void notify( hipStream_t st, hipError_t status) noexcept {
std::unique_lock< mutex > lk{ mtx_ };
st_ = st;
status_ = status;
done_ = true;
lk.unlock();
cv_.notify_one();
}
std::tuple< hipStream_t, hipError_t > wait() {
std::unique_lock< mutex > lk{ mtx_ };
cv_.wait( lk, [this]{ return done_; });
return std::make_tuple( st_, status_);
}
private:
mutex mtx_{};
condition_variable cv_{};
hipStream_t st_{};
hipError_t status_{ hipErrorUnknown };
bool done_{ false };
};
class many_streams_rendezvous {
public:
many_streams_rendezvous( std::initializer_list< hipStream_t > l) :
stx_{ l } {
results_.reserve( stx_.size() );
for ( hipStream_t st : stx_) {
unsigned int flags = 0;
hipError_t status = ::hipStreamAddCallback( st, trampoline< many_streams_rendezvous >, this, flags);
if ( hipSuccess != status) {
std::unique_lock< mutex > lk{ mtx_ };
stx_.erase( st);
results_.push_back( std::make_tuple( st, status) );
}
}
}
void notify( hipStream_t st, hipError_t status) noexcept {
std::unique_lock< mutex > lk{ mtx_ };
stx_.erase( st);
results_.push_back( std::make_tuple( st, status) );
if ( stx_.empty() ) {
lk.unlock();
cv_.notify_one();
}
}
std::vector< std::tuple< hipStream_t, hipError_t > > wait() {
std::unique_lock< mutex > lk{ mtx_ };
cv_.wait( lk, [this]{ return stx_.empty(); });
return results_;
}
private:
mutex mtx_{};
condition_variable cv_{};
std::set< hipStream_t > stx_;
std::vector< std::tuple< hipStream_t, hipError_t > > results_;
};
}
void waitfor_all();
inline
std::tuple< hipStream_t, hipError_t > waitfor_all( hipStream_t st) {
detail::single_stream_rendezvous rendezvous( st);
return rendezvous.wait();
}
template< typename ... STP >
std::vector< std::tuple< hipStream_t, hipError_t > > waitfor_all( hipStream_t st0, STP ... stx) {
static_assert( boost::fibers::detail::is_all_same< hipStream_t, STP ...>::value, "all arguments must be of type `CUstream*`.");
detail::many_streams_rendezvous rendezvous{ st0, stx ... };
return rendezvous.wait();
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_CUDA_WAITFOR_H
+69
View File
@@ -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_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>
#include <boost/fiber/waker.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;
detail::spinlock wait_queue_splk_{};
wait_queue 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
+93
View File
@@ -0,0 +1,93 @@
// Copyright Oliver Kowalke 2017.
// 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_NUMA_ALGO_WORK_STEALING_H
#define BOOST_FIBERS_NUMA_ALGO_WORK_STEALING_H
#include <condition_variable>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <vector>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/context_spinlock_queue.hpp>
#include <boost/fiber/detail/context_spmc_queue.hpp>
#include <boost/fiber/numa/pin_thread.hpp>
#include <boost/fiber/numa/topology.hpp>
#include <boost/fiber/scheduler.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace numa {
namespace algo {
class BOOST_FIBERS_DECL work_stealing : public boost::fibers::algo::algorithm {
private:
static std::vector< intrusive_ptr< work_stealing > > schedulers_;
std::uint32_t cpu_id_;
std::vector< std::uint32_t > local_cpus_;
std::vector< std::uint32_t > remote_cpus_;
#ifdef BOOST_FIBERS_USE_SPMC_QUEUE
detail::context_spmc_queue rqueue_{};
#else
detail::context_spinlock_queue rqueue_{};
#endif
std::mutex mtx_{};
std::condition_variable cnd_{};
bool flag_{ false };
bool suspend_;
static void init_( std::vector< boost::fibers::numa::node > const&,
std::vector< intrusive_ptr< work_stealing > > &);
public:
work_stealing( std::uint32_t, std::uint32_t,
std::vector< boost::fibers::numa::node > const&,
bool = 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;
virtual void awakened( context *) noexcept;
virtual context * pick_next() noexcept;
virtual context * steal() noexcept {
return rqueue_.steal();
}
virtual bool has_ready_fibers() const noexcept {
return ! rqueue_.empty();
}
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept;
virtual void notify() noexcept;
};
}}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_NUMA_ALGO_WORK_STEALING_H
+15
View File
@@ -0,0 +1,15 @@
// Copyright Oliver Kowalke 2018.
// 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_NUMA_H
#define BOOST_FIBERS_NUMA_H
#include <boost/fiber/numa/algo/work_stealing.hpp>
#include <boost/fiber/numa/pin_thread.hpp>
#include <boost/fiber/numa/topology.hpp>
#endif // BOOST_FIBERS_NUMA_H
+37
View File
@@ -0,0 +1,37 @@
// Copyright Oliver Kowalke 2017.
// 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_NUMA_PIN_THREAD_H
#define BOOST_FIBERS_NUMA_PIN_THREAD_H
#include <cstdint>
#include <thread>
#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 numa {
BOOST_FIBERS_DECL
void pin_thread( std::uint32_t, std::thread::native_handle_type);
BOOST_FIBERS_DECL
void pin_thread( std::uint32_t cpuid);
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_NUMA_PIN_THREAD_H
+46
View File
@@ -0,0 +1,46 @@
// Copyright Oliver Kowalke 2017.
// 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_NUMA_TOPOLOGY_H
#define BOOST_FIBERS_NUMA_TOPOLOGY_H
#include <cstdint>
#include <set>
#include <vector>
#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 numa {
struct node {
std::uint32_t id;
std::set< std::uint32_t > logical_cpus;
std::vector< std::uint32_t > distance;
};
inline
bool operator<( node const& lhs, node const& rhs) noexcept {
return lhs.id < rhs.id;
}
BOOST_FIBERS_DECL
std::vector< node > topology();
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_NUMA_TOPOLOGY_H
+95
View File
@@ -0,0 +1,95 @@
// 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/algo/algorithm.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>
#include <boost/fiber/stack_allocator_wrapper.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_ctx = fibers::context::active();
active_ctx->wait_until( sleep_time);
}
template< typename Rep, typename Period >
void sleep_for( std::chrono::duration< Rep, Period > const& timeout_duration) {
fibers::context * active_ctx = fibers::context::active();
active_ctx->wait_until( std::chrono::steady_clock::now() + timeout_duration);
}
template< typename PROPS >
PROPS & properties() {
fibers::fiber_properties * props = fibers::context::active()->get_properties();
if ( BOOST_LIKELY( nullptr == 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();
}
// Returns true if the thread could be initialize, false otherwise (it was already initialized previously).
inline bool initialize_thread(algo::algorithm::ptr_t algo, stack_allocator_wrapper&& salloc) noexcept {
return boost::fibers::context::initialize_thread(algo, std::move(salloc));
}
template< typename SchedAlgo, typename ... Args >
void use_scheduling_algorithm( Args && ... args) noexcept {
initialize_thread(new SchedAlgo(std::forward< Args >( args) ... ), make_stack_allocator_wrapper<boost::fibers::default_stack>());
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_THIS_FIBER_OPERATIONS_H
+46
View File
@@ -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
+30
View File
@@ -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
+91
View File
@@ -0,0 +1,91 @@
// 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/assert.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 {
class 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.
// For fiber_properties passed to fiber constructors, nullptr must be
// used here.
explicit 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;
}
// not really intended for public use, but required to set properties
// on fiber/context construction.
void set_context( context* ctx ) noexcept {
BOOST_ASSERT( ctx_ == nullptr );
BOOST_ASSERT( ctx != nullptr );
ctx_ = ctx;
}
};
}} // namespace boost::fibers
# if defined(BOOST_MSVC)
# pragma warning(pop)
# endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_PROPERTIES_HPP
+30
View File
@@ -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
+75
View File
@@ -0,0 +1,75 @@
// 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>
#include <boost/fiber/waker.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;
detail::spinlock wait_queue_splk_{};
wait_queue 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
+90
View File
@@ -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)
//
// 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>
#include <boost/fiber/waker.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;
detail::spinlock wait_queue_splk_{};
wait_queue 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
+165
View File
@@ -0,0 +1,165 @@
// 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>
#include <boost/context/fiber.hpp>
#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
algo::algorithm::ptr_t algo_;
// sleep-queue contains context' which have been called
// scheduler::wait_until()
sleep_queue_type sleep_queue_{};
// worker-queue contains all context' managed by this scheduler
// except main-context and dispatcher-context
// unlink happens on destruction of a context
worker_queue_type worker_queue_{};
// 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(algo::algorithm::ptr_t algo) 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
boost::context::fiber dispatch() noexcept;
boost::context::fiber terminate( detail::spinlock_lock &, context *) noexcept;
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 &,
waker &&) noexcept;
void suspend() noexcept;
void suspend( detail::spinlock_lock &) noexcept;
bool has_ready_fibers() const noexcept;
void set_algo( algo::algorithm::ptr_t) 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
+35
View File
@@ -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
+94
View File
@@ -0,0 +1,94 @@
#ifndef BOOST_FIBERS_STACK_ALLOCATOR_WRAPPER_H
#define BOOST_FIBERS_STACK_ALLOCATOR_WRAPPER_H
#include <memory>
#include <boost/fiber/detail/config.hpp>
#include <boost/context/stack_context.hpp>
#include <boost/fiber/fixedsize_stack.hpp>
#include <boost/fiber/segmented_stack.hpp>
namespace boost {
namespace fibers {
namespace detail {
class BOOST_FIBERS_DECL polymorphic_stack_allocator_base {
public:
polymorphic_stack_allocator_base() = default;
virtual ~polymorphic_stack_allocator_base() = default;
polymorphic_stack_allocator_base(const polymorphic_stack_allocator_base&) = delete;
polymorphic_stack_allocator_base& operator=(const polymorphic_stack_allocator_base&) = delete;
polymorphic_stack_allocator_base(polymorphic_stack_allocator_base&&) = delete;
polymorphic_stack_allocator_base& operator=(polymorphic_stack_allocator_base&&) = delete;
virtual boost::context::stack_context allocate() = 0;
virtual void deallocate(boost::context::stack_context& sctx) = 0;
};
template< typename StackAllocator >
class BOOST_FIBERS_DECL polymorphic_stack_allocator_impl final : public polymorphic_stack_allocator_base {
public:
template<typename ... Args >
polymorphic_stack_allocator_impl( Args && ... args )
:_allocator(std::forward< Args >( args) ... )
{}
~polymorphic_stack_allocator_impl() = default;
boost::context::stack_context allocate() override
{
return _allocator.allocate();
}
void deallocate(boost::context::stack_context& sctx) override
{
_allocator.deallocate(sctx);
}
private:
StackAllocator _allocator;
};
}
class BOOST_FIBERS_DECL stack_allocator_wrapper final {
public:
stack_allocator_wrapper(std::unique_ptr<detail::polymorphic_stack_allocator_base> allocator)
:_allocator(std::move(allocator))
{}
~stack_allocator_wrapper() = default;
stack_allocator_wrapper(const stack_allocator_wrapper&) = delete;
stack_allocator_wrapper& operator=(const stack_allocator_wrapper&) = delete;
stack_allocator_wrapper(stack_allocator_wrapper&&) = default;
stack_allocator_wrapper& operator=(stack_allocator_wrapper&&) = default;
boost::context::stack_context allocate()
{
return _allocator->allocate();
}
void deallocate(boost::context::stack_context& sctx)
{
_allocator->deallocate(sctx);
}
private:
std::unique_ptr<detail::polymorphic_stack_allocator_base> _allocator;
};
template <typename StackAllocator, typename ... Args>
BOOST_FIBERS_DECL stack_allocator_wrapper make_stack_allocator_wrapper(Args && ... args)
{
return stack_allocator_wrapper(
std::unique_ptr<detail::polymorphic_stack_allocator_base>(
new detail::polymorphic_stack_allocator_impl<StackAllocator>(std::forward< Args >( args) ... )));
}
}
}
#endif
+84
View File
@@ -0,0 +1,84 @@
// 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>
#include <boost/fiber/waker.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;
detail::spinlock wait_queue_splk_{};
wait_queue 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
+106
View File
@@ -0,0 +1,106 @@
// 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/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
+484
View File
@@ -0,0 +1,484 @@
// 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>
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
#include <boost/fiber/detail/exchange.hpp>
#endif
#include <boost/fiber/detail/spinlock.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/waker.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
template< typename T >
class unbuffered_channel {
public:
using value_type = typename std::remove_reference<T>::type;
private:
struct slot {
value_type value;
waker w;
slot( value_type const& value_, waker && w) :
value{ value_ },
w{ std::move(w) } {
}
slot( value_type && value_, waker && w) :
value{ std::move( value_) },
w{ std::move(w) } {
}
};
// shared cacheline
std::atomic< slot * > slot_{ nullptr };
// shared cacheline
std::atomic_bool closed_{ false };
mutable detail::spinlock splk_producers_{};
wait_queue waiting_producers_{};
mutable detail::spinlock splk_consumers_{};
wait_queue 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;
}
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();
}
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 {
// set flag
if ( ! closed_.exchange( true, std::memory_order_acquire) ) {
// notify current waiting
slot * s = slot_.load( std::memory_order_acquire);
if ( nullptr != s) {
// notify context
s->w.wake();
}
detail::spinlock_lock lk1{ splk_producers_ };
waiting_producers_.notify_all();
detail::spinlock_lock lk2{ splk_consumers_ };
waiting_consumers_.notify_all();
}
}
channel_op_status push( value_type const& value) {
context * active_ctx = context::active();
slot s{ value, {} };
for (;;) {
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
s.w = active_ctx->create_waker();
if ( try_push_( & s) ) {
detail::spinlock_lock lk{ splk_consumers_ };
waiting_consumers_.notify_one();
// suspend till value has been consumed
active_ctx->suspend( lk);
// resumed
if ( BOOST_UNLIKELY( is_closed() ) ) {
// channel was closed before value was consumed
return channel_op_status::closed;
}
// value has been consumed
return channel_op_status::success;
}
detail::spinlock_lock lk{ splk_producers_ };
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
if ( is_empty_() ) {
continue;
}
waiting_producers_.suspend_and_wait( lk, active_ctx);
// resumed, slot mabye free
}
}
channel_op_status push( value_type && value) {
context * active_ctx = context::active();
slot s{ std::move( value), {} };
for (;;) {
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
s.w = active_ctx->create_waker();
if ( try_push_( & s) ) {
detail::spinlock_lock lk{ splk_consumers_ };
waiting_consumers_.notify_one();
// suspend till value has been consumed
active_ctx->suspend( lk);
// resumed
if ( BOOST_UNLIKELY( is_closed() ) ) {
// channel was closed before value was consumed
return channel_op_status::closed;
}
// value has been consumed
return channel_op_status::success;
}
detail::spinlock_lock lk{ splk_producers_ };
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
if ( is_empty_() ) {
continue;
}
waiting_producers_.suspend_and_wait( lk, active_ctx);
// resumed, slot mabye free
}
}
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, {} };
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
for (;;) {
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
s.w = active_ctx->create_waker();
if ( try_push_( & s) ) {
detail::spinlock_lock lk{ splk_consumers_ };
waiting_consumers_.notify_one();
// suspend this producer
if ( ! active_ctx->wait_until(timeout_time, lk, waker(s.w))) {
// clear slot
slot * nil_slot = nullptr, * own_slot = & s;
slot_.compare_exchange_strong( own_slot, nil_slot, std::memory_order_acq_rel);
// resumed, value has not been consumed
return channel_op_status::timeout;
}
// resumed
if ( BOOST_UNLIKELY( is_closed() ) ) {
// channel was closed before value was consumed
return channel_op_status::closed;
}
// value has been consumed
return channel_op_status::success;
}
detail::spinlock_lock lk{ splk_producers_ };
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
if ( is_empty_() ) {
continue;
}
if (! waiting_producers_.suspend_and_wait_until( lk, active_ctx, timeout_time))
{
return channel_op_status::timeout;
}
// resumed, slot maybe free
}
}
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), {} };
std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
for (;;) {
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
s.w = active_ctx->create_waker();
if ( try_push_( & s) ) {
detail::spinlock_lock lk{ splk_consumers_ };
waiting_consumers_.notify_one();
// suspend this producer
if ( ! active_ctx->wait_until(timeout_time, lk, waker(s.w))) {
// clear slot
slot * nil_slot = nullptr, * own_slot = & s;
slot_.compare_exchange_strong( own_slot, nil_slot, std::memory_order_acq_rel);
// resumed, value has not been consumed
return channel_op_status::timeout;
}
// resumed
if ( BOOST_UNLIKELY( is_closed() ) ) {
// channel was closed before value was consumed
return channel_op_status::closed;
}
// value has been consumed
return channel_op_status::success;
}
detail::spinlock_lock lk{ splk_producers_ };
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
if ( is_empty_() ) {
continue;
}
if (! waiting_producers_.suspend_and_wait_until( lk, active_ctx, timeout_time))
{
return channel_op_status::timeout;
}
// resumed, slot maybe free
}
}
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_ };
waiting_producers_.notify_one();
}
value = std::move( s->value);
// notify context
s->w.wake();
return channel_op_status::success;
}
detail::spinlock_lock lk{ splk_consumers_ };
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
if ( ! is_empty_() ) {
continue;
}
waiting_consumers_.suspend_and_wait( lk, active_ctx);
// resumed, slot mabye set
}
}
value_type value_pop() {
context * active_ctx = context::active();
slot * s = nullptr;
for (;;) {
if ( nullptr != ( s = try_pop_() ) ) {
{
detail::spinlock_lock lk{ splk_producers_ };
waiting_producers_.notify_one();
}
// consume value
value_type value = std::move( s->value);
// notify context
s->w.wake();
return std::move( value);
}
detail::spinlock_lock lk{ splk_consumers_ };
if ( BOOST_UNLIKELY( is_closed() ) ) {
throw fiber_error{
std::make_error_code( std::errc::operation_not_permitted),
"boost fiber: channel is closed" };
}
if ( ! is_empty_() ) {
continue;
}
waiting_consumers_.suspend_and_wait( lk, active_ctx);
// resumed, slot mabye set
}
}
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_ };
waiting_producers_.notify_one();
}
// consume value
value = std::move( s->value);
// notify context
s->w.wake();
return channel_op_status::success;
}
detail::spinlock_lock lk{ splk_consumers_ };
if ( BOOST_UNLIKELY( is_closed() ) ) {
return channel_op_status::closed;
}
if ( ! is_empty_() ) {
continue;
}
if ( ! waiting_consumers_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
return channel_op_status::timeout;
}
}
}
class iterator {
private:
typedef typename std::aligned_storage< sizeof( value_type), alignof( value_type) >::type storage_type;
unbuffered_channel * chan_{ nullptr };
storage_type storage_;
void increment_( bool initial = false) {
BOOST_ASSERT( nullptr != chan_);
try {
if ( ! initial) {
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
}
::new ( static_cast< void * >( std::addressof( storage_) ) ) value_type{ chan_->value_pop() };
} catch ( fiber_error const&) {
chan_ = nullptr;
}
}
public:
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
using pointer_t = pointer;
using reference_t = reference;
iterator() = default;
explicit iterator( unbuffered_channel< T > * chan) noexcept :
chan_{ chan } {
increment_( true);
}
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++() {
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
increment_();
return * this;
}
const 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
+88
View File
@@ -0,0 +1,88 @@
#ifndef BOOST_FIBERS_WAKER_H
#define BOOST_FIBERS_WAKER_H
#include <cstddef>
#include <boost/config.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#include <boost/intrusive/slist.hpp>
namespace boost {
namespace fibers {
class context;
namespace detail {
typedef intrusive::slist_member_hook<> waker_queue_hook;
} // detail
class BOOST_FIBERS_DECL waker {
private:
context *ctx_{};
size_t epoch_{};
public:
friend class context;
waker() = default;
waker(context * ctx, const size_t epoch)
: ctx_{ ctx }
, epoch_{ epoch }
{}
bool wake() const noexcept;
};
class BOOST_FIBERS_DECL waker_with_hook : public waker {
public:
explicit waker_with_hook(waker && w)
: waker{ std::move(w) }
{}
bool is_linked() const noexcept {
return waker_queue_hook_.is_linked();
}
friend bool
operator==( waker const& lhs, waker const& rhs) noexcept {
return & lhs == & rhs;
}
public:
detail::waker_queue_hook waker_queue_hook_{};
};
namespace detail {
typedef intrusive::slist<
waker_with_hook,
intrusive::member_hook<
waker_with_hook, detail::waker_queue_hook, & waker_with_hook::waker_queue_hook_ >,
intrusive::constant_time_size< false >,
intrusive::cache_last< true >
> waker_slist_t;
}
class BOOST_FIBERS_DECL wait_queue {
private:
detail::waker_slist_t slist_{};
public:
void suspend_and_wait( detail::spinlock_lock &, context *);
bool suspend_and_wait_until( detail::spinlock_lock &,
context *,
std::chrono::steady_clock::time_point const&);
void notify_one();
void notify_all();
bool empty() const;
};
}}
#endif // BOOST_FIBERS_WAKER_H