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