mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-06 07:27:50 +00:00
Added thirdparty: boost library
This commit is contained in:
+162
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 21–28,
|
||||
// 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,88 @@
|
||||
#ifndef BOOST_FIBERS_WAKER_H
|
||||
#define BOOST_FIBERS_WAKER_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/spinlock.hpp>
|
||||
#include <boost/intrusive/slist.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
|
||||
class context;
|
||||
|
||||
namespace detail {
|
||||
|
||||
typedef intrusive::slist_member_hook<> waker_queue_hook;
|
||||
|
||||
} // detail
|
||||
|
||||
|
||||
class BOOST_FIBERS_DECL waker {
|
||||
private:
|
||||
context *ctx_{};
|
||||
size_t epoch_{};
|
||||
|
||||
public:
|
||||
friend class context;
|
||||
|
||||
waker() = default;
|
||||
|
||||
waker(context * ctx, const size_t epoch)
|
||||
: ctx_{ ctx }
|
||||
, epoch_{ epoch }
|
||||
{}
|
||||
|
||||
bool wake() const noexcept;
|
||||
};
|
||||
|
||||
|
||||
class BOOST_FIBERS_DECL waker_with_hook : public waker {
|
||||
public:
|
||||
explicit waker_with_hook(waker && w)
|
||||
: waker{ std::move(w) }
|
||||
{}
|
||||
|
||||
bool is_linked() const noexcept {
|
||||
return waker_queue_hook_.is_linked();
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator==( waker const& lhs, waker const& rhs) noexcept {
|
||||
return & lhs == & rhs;
|
||||
}
|
||||
|
||||
public:
|
||||
detail::waker_queue_hook waker_queue_hook_{};
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
typedef intrusive::slist<
|
||||
waker_with_hook,
|
||||
intrusive::member_hook<
|
||||
waker_with_hook, detail::waker_queue_hook, & waker_with_hook::waker_queue_hook_ >,
|
||||
intrusive::constant_time_size< false >,
|
||||
intrusive::cache_last< true >
|
||||
> waker_slist_t;
|
||||
}
|
||||
|
||||
class BOOST_FIBERS_DECL wait_queue {
|
||||
private:
|
||||
detail::waker_slist_t slist_{};
|
||||
|
||||
public:
|
||||
void suspend_and_wait( detail::spinlock_lock &, context *);
|
||||
bool suspend_and_wait_until( detail::spinlock_lock &,
|
||||
context *,
|
||||
std::chrono::steady_clock::time_point const&);
|
||||
void notify_one();
|
||||
void notify_all();
|
||||
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // BOOST_FIBERS_WAKER_H
|
||||
Reference in New Issue
Block a user