diff --git a/include/boost/fiber/algo/work_stealing.hpp b/include/boost/fiber/algo/work_stealing.hpp index 66cadd12..4dfee8a0 100644 --- a/include/boost/fiber/algo/work_stealing.hpp +++ b/include/boost/fiber/algo/work_stealing.hpp @@ -38,9 +38,9 @@ class work_stealing : public algorithm { std::size_t idx_; std::size_t max_idx_; #ifdef BOOST_FIBERS_USE_SPMC_QUEUE - alignas(cache_alignment) detail::context_spmc_queue rqueue_{}; + BOOST_FIBER_ALIGNAS(cache_alignment, detail::context_spmc_queue) rqueue_{}; #else - alignas(cache_alignment) detail::context_spinlock_queue rqueue_{}; + BOOST_FIBER_ALIGNAS(cache_alignment, detail::context_spinlock_queue) rqueue_{}; #endif std::mutex mtx_{}; std::condition_variable cnd_{}; diff --git a/include/boost/fiber/buffered_channel.hpp b/include/boost/fiber/buffered_channel.hpp index 2cd49451..c3bedf61 100644 --- a/include/boost/fiber/buffered_channel.hpp +++ b/include/boost/fiber/buffered_channel.hpp @@ -42,25 +42,25 @@ class buffered_channel { typedef typename std::aligned_storage< sizeof( T), alignof( T) >::type storage_type; typedef context::wait_queue_t wait_queue_type; - struct alignas(cache_alignment) slot { + struct BOOST_FIBER_ALIGNAS(cache_alignment, slot { std::atomic< std::size_t > cycle{ 0 }; storage_type storage{}; slot() = default; - }; + }); // procuder cacheline - alignas(cache_alignment) std::atomic< std::size_t > producer_idx_{ 0 }; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic< std::size_t >) producer_idx_{ 0 }; // consumer cacheline - alignas(cache_alignment) std::atomic< std::size_t > consumer_idx_{ 0 }; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic< std::size_t >) consumer_idx_{ 0 }; // shared write cacheline - alignas(cache_alignment) std::atomic_bool closed_{ false }; - alignas(cache_alignment) mutable detail::spinlock splk_producers_{}; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic_bool) closed_{ false }; + BOOST_FIBER_ALIGNAS(cache_alignment, mutable detail::spinlock) splk_producers_{}; wait_queue_type waiting_producers_{}; - alignas(cache_alignment) mutable detail::spinlock splk_consumers_{}; + BOOST_FIBER_ALIGNAS(cache_alignment, mutable detail::spinlock) splk_consumers_{}; wait_queue_type waiting_consumers_{}; // shared read cacheline - alignas(cache_alignment) slot * slots_{ nullptr }; + BOOST_FIBER_ALIGNAS(cache_alignment, slot *) slots_{ nullptr }; std::size_t capacity_; char pad_[cacheline_length]; diff --git a/include/boost/fiber/context.hpp b/include/boost/fiber/context.hpp index 773528e3..5f65021d 100644 --- a/include/boost/fiber/context.hpp +++ b/include/boost/fiber/context.hpp @@ -175,21 +175,21 @@ class BOOST_FIBERS_DECL context { 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 }; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic< std::size_t >) use_count_{ 0 }; #else - alignas(cache_alignment) std::size_t use_count_{ 0 }; + BOOST_FIBER_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_{}; + BOOST_FIBER_ALIGNAS(cache_alignment, detail::remote_ready_hook) remote_ready_hook_{}; std::atomic< context * > remote_nxt_{ nullptr }; #endif - alignas(cache_alignment) detail::spinlock splk_{}; + BOOST_FIBER_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 }; + BOOST_FIBER_ALIGNAS(cache_alignment, scheduler) * scheduler_{ nullptr }; fss_data_t fss_data_{}; detail::sleep_hook sleep_hook_{}; detail::ready_hook ready_hook_{}; diff --git a/include/boost/fiber/detail/config.hpp b/include/boost/fiber/detail/config.hpp index f65d4891..00181632 100644 --- a/include/boost/fiber/detail/config.hpp +++ b/include/boost/fiber/detail/config.hpp @@ -59,7 +59,48 @@ // 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 }; +# define CACHE_ALIGNMENT 64 +# define CACHELINE_LENGTH 64 + +#if defined(BOOST_NO_CXX11_ALIGNAS) +# define cache_alignment CACHE_ALIGNMENT +# define cacheline_length CACHELINE_LENGTH +# define BOOST_FIBER_ALIGNAS_BEGIN(alignment) +# define BOOST_FIBER_ALIGNAS_END(alignment) __attribute__((aligned (alignment))) +#else + static constexpr std::size_t cache_alignment{ CACHE_ALIGNMENT }; + static constexpr std::size_t cacheline_length{ CACHELINE_LENGTH }; +# define BOOST_FIBER_ALIGNAS_BEGIN(alignment) alignas(alignment) +# define BOOST_FIBER_ALIGNAS_END(alignment) +#endif +#define BOOST_FIBER_ALIGNAS(alignment, subject) BOOST_FIBER_ALIGNAS_BEGIN(alignment) subject BOOST_FIBER_ALIGNAS_END(alignment) + +#if defined(BOOST_NO_CXX11_THREAD_LOCAL) +# include +namespace boost { +namespace fibers { + template + class initialized_thread_specific_ptr { + public: + initialized_thread_specific_ptr() { + tss_.reset(new T); + } + + T& operator*() { + return *tss_; + } + + private: + boost::thread_specific_ptr tss_; + }; +}} +# define BOOST_FIBER_DECLARE_THREAD_LOCAL(type, name) boost::fibers::initialized_thread_specific_ptr name +# define BOOST_FIBER_DEFINE_THREAD_LOCAL(type, name) boost::fibers::initialized_thread_specific_ptr name +# define BOOST_FIBER_USE_THREAD_LOCAL(val) (*val) +#else +# define BOOST_FIBER_DECLARE_THREAD_LOCAL(type, name) thread_local type name +# define BOOST_FIBER_DEFINE_THREAD_LOCAL(type, name) thread_local type name +# define BOOST_FIBER_USE_THREAD_LOCAL(val) val +#endif #endif // BOOST_FIBERS_DETAIL_CONFIG_H diff --git a/include/boost/fiber/detail/context_spinlock_queue.hpp b/include/boost/fiber/detail/context_spinlock_queue.hpp index e0ebdabd..7ba7cbf5 100644 --- a/include/boost/fiber/detail/context_spinlock_queue.hpp +++ b/include/boost/fiber/detail/context_spinlock_queue.hpp @@ -30,7 +30,7 @@ class context_spinlock_queue { private: typedef context * slot_type; - alignas(cache_alignment) mutable spinlock splk_{}; + BOOST_FIBER_ALIGNAS(cache_alignment, mutable spinlock) splk_{}; std::size_t pidx_{ 0 }; std::size_t cidx_{ 0 }; std::size_t capacity_; diff --git a/include/boost/fiber/detail/context_spmc_queue.hpp b/include/boost/fiber/detail/context_spmc_queue.hpp index 27256233..ec202dea 100644 --- a/include/boost/fiber/detail/context_spmc_queue.hpp +++ b/include/boost/fiber/detail/context_spmc_queue.hpp @@ -92,9 +92,9 @@ class context_spmc_queue { } }; - 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_; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic< std::size_t >) top_{ 0 }; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic< std::size_t >) bottom_{ 0 }; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic< array * >) array_; std::vector< array * > old_arrays_{}; char padding_[cacheline_length]; diff --git a/include/boost/fiber/detail/spinlock_ttas.hpp b/include/boost/fiber/detail/spinlock_ttas.hpp index 3710b531..d7cef278 100644 --- a/include/boost/fiber/detail/spinlock_ttas.hpp +++ b/include/boost/fiber/detail/spinlock_ttas.hpp @@ -10,6 +10,7 @@ #include #include #include +#define __GLIBCXX_USE_NANOSLEEP #include #include @@ -91,9 +92,9 @@ class spinlock_ttas { // 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 BOOST_FIBER_DEFINE_THREAD_LOCAL(std::minstd_rand, generator); const std::size_t z = - std::uniform_int_distribution< std::size_t >{ 0, static_cast< std::size_t >( 1) << collisions }( generator); + std::uniform_int_distribution< std::size_t >{ 0, static_cast< std::size_t >( 1) << collisions }(BOOST_FIBER_USE_THREAD_LOCAL(generator)); ++collisions; for ( std::size_t i = 0; i < z; ++i) { // -> reduces the power consumed by the CPU diff --git a/include/boost/fiber/detail/spinlock_ttas_adaptive.hpp b/include/boost/fiber/detail/spinlock_ttas_adaptive.hpp index ca8c4c8a..348b953b 100644 --- a/include/boost/fiber/detail/spinlock_ttas_adaptive.hpp +++ b/include/boost/fiber/detail/spinlock_ttas_adaptive.hpp @@ -90,9 +90,9 @@ class spinlock_ttas_adaptive { // 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 BOOST_FIBER_DEFINE_THREAD_LOCAL(std::minstd_rand, generator); const std::size_t z = - std::uniform_int_distribution< std::size_t >{ 0, static_cast< std::size_t >( 1) << collisions }( generator); + std::uniform_int_distribution< std::size_t >{ 0, static_cast< std::size_t >( 1) << collisions }(BOOST_FIBER_USE_THREAD_LOCAL(generator)); ++collisions; for ( std::size_t i = 0; i < z; ++i) { // -> reduces the power consumed by the CPU diff --git a/include/boost/fiber/detail/spinlock_ttas_adaptive_futex.hpp b/include/boost/fiber/detail/spinlock_ttas_adaptive_futex.hpp index 6973a3f5..5a8be78d 100644 --- a/include/boost/fiber/detail/spinlock_ttas_adaptive_futex.hpp +++ b/include/boost/fiber/detail/spinlock_ttas_adaptive_futex.hpp @@ -70,9 +70,9 @@ class spinlock_ttas_adaptive_futex { // 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 BOOST_FIBER_DEFINE_THREAD_LOCAL(std::minstd_rand, generator); const std::int32_t z = std::uniform_int_distribution< std::int32_t >{ - 0, static_cast< std::int32_t >( 1) << collisions }( generator); + 0, static_cast< std::int32_t >( 1) << collisions }(BOOST_FIBER_USE_THREAD_LOCAL(generator)); ++collisions; for ( std::int32_t i = 0; i < z; ++i) { // -> reduces the power consumed by the CPU diff --git a/include/boost/fiber/detail/spinlock_ttas_futex.hpp b/include/boost/fiber/detail/spinlock_ttas_futex.hpp index 18a5178e..738fb3df 100644 --- a/include/boost/fiber/detail/spinlock_ttas_futex.hpp +++ b/include/boost/fiber/detail/spinlock_ttas_futex.hpp @@ -66,9 +66,9 @@ class spinlock_ttas_futex { // 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 BOOST_FIBER_DEFINE_THREAD_LOCAL(std::minstd_rand, generator); const std::int32_t z = std::uniform_int_distribution< std::int32_t >{ - 0, static_cast< std::int32_t >( 1) << collisions }( generator); + 0, static_cast< std::int32_t >( 1) << collisions }(BOOST_FIBER_USE_THREAD_LOCAL(generator)); ++collisions; for ( std::int32_t i = 0; i < z; ++i) { // -> reduces the power consumed by the CPU diff --git a/include/boost/fiber/scheduler.hpp b/include/boost/fiber/scheduler.hpp index 4a5f0dab..f150ebad 100644 --- a/include/boost/fiber/scheduler.hpp +++ b/include/boost/fiber/scheduler.hpp @@ -90,7 +90,7 @@ class BOOST_FIBERS_DECL scheduler { detail::spinlock remote_ready_splk_{}; remote_ready_queue_type remote_ready_queue_{}; #endif - alignas(cache_alignment) std::unique_ptr< algo::algorithm > algo_; + BOOST_FIBER_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_{}; diff --git a/include/boost/fiber/unbuffered_channel.hpp b/include/boost/fiber/unbuffered_channel.hpp index 38a2d611..1dbafcb1 100644 --- a/include/boost/fiber/unbuffered_channel.hpp +++ b/include/boost/fiber/unbuffered_channel.hpp @@ -38,7 +38,7 @@ class unbuffered_channel { private: typedef context::wait_queue_t wait_queue_type; - struct alignas(cache_alignment) slot { + struct BOOST_FIBER_ALIGNAS_BEGIN(cache_alignment) slot { value_type value; context * ctx; @@ -51,15 +51,15 @@ class unbuffered_channel { value{ std::move( value_) }, ctx{ ctx_ } { } - }; + } BOOST_FIBER_ALIGNAS_END(cache_alignment); // shared cacheline - alignas(cache_alignment) std::atomic< slot * > slot_{ nullptr }; + BOOST_FIBER_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_{}; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic_bool) closed_{ false }; + BOOST_FIBER_ALIGNAS(cache_alignment, mutable detail::spinlock) splk_producers_{}; wait_queue_type waiting_producers_{}; - alignas( cache_alignment) mutable detail::spinlock splk_consumers_{}; + BOOST_FIBER_ALIGNAS(cache_alignment, mutable detail::spinlock) splk_consumers_{}; wait_queue_type waiting_consumers_{}; char pad_[cacheline_length]; diff --git a/performance/thread/buffered_channel.hpp b/performance/thread/buffered_channel.hpp index 7f021dfc..933d9a0d 100644 --- a/performance/thread/buffered_channel.hpp +++ b/performance/thread/buffered_channel.hpp @@ -40,24 +40,24 @@ class buffered_channel { private: typedef typename std::aligned_storage< sizeof( T), alignof( T) >::type storage_type; - struct alignas(cache_alignment) slot { + struct BOOST_FIBER_ALIGNAS(cache_alignment, slot { std::atomic< std::size_t > cycle{ 0 }; storage_type storage{}; slot() = default; - }; + }); - // procuder cacheline - alignas(cache_alignment) std::atomic< std::size_t > producer_idx_{ 0 }; + // producer cacheline + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic< std::size_t >) producer_idx_{ 0 }; // consumer cacheline - alignas(cache_alignment) std::atomic< std::size_t > consumer_idx_{ 0 }; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic< std::size_t >) consumer_idx_{ 0 }; // shared write cacheline - alignas(cache_alignment) std::atomic_bool closed_{ false }; + BOOST_FIBER_ALIGNAS(cache_alignment, std::atomic_bool) closed_{ false }; mutable std::mutex mtx_{}; std::condition_variable not_full_cnd_{}; std::condition_variable not_empty_cnd_{}; // shared read cacheline - alignas(cache_alignment) slot * slots_{ nullptr }; + BOOST_FIBER_ALIGNAS(cache_alignment, slot *) slots_{ nullptr }; std::size_t capacity_; char pad_[cacheline_length]; std::size_t waiting_consumer_{ 0 }; diff --git a/src/algo/work_stealing.cpp b/src/algo/work_stealing.cpp index b6b9f928..deabf357 100644 --- a/src/algo/work_stealing.cpp +++ b/src/algo/work_stealing.cpp @@ -52,10 +52,10 @@ work_stealing::pick_next() noexcept { context::active()->attach( ctx); } } else { - static thread_local std::minstd_rand generator; + static BOOST_FIBER_DEFINE_THREAD_LOCAL(std::minstd_rand, generator); std::size_t idx = 0; do { - idx = std::uniform_int_distribution< std::size_t >{ 0, max_idx_ }( generator); + idx = std::uniform_int_distribution< std::size_t >{ 0, max_idx_ }(BOOST_FIBER_USE_THREAD_LOCAL(generator)); } while ( idx == idx_); ctx = schedulers_[idx]->steal(); if ( nullptr != ctx) { diff --git a/src/context.cpp b/src/context.cpp index 0729b39a..bb79d781 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -51,11 +51,11 @@ static intrusive_ptr< context > make_dispatcher_context( scheduler * sched) { // schwarz counter struct context_initializer { - static thread_local context * active_; - static thread_local std::size_t counter_; + static BOOST_FIBER_DECLARE_THREAD_LOCAL(context*, active_); + static BOOST_FIBER_DECLARE_THREAD_LOCAL(std::size_t, counter_); context_initializer() { - if ( 0 == counter_++) { + if ( 0 == BOOST_FIBER_USE_THREAD_LOCAL(counter_)++) { # if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) // allocate memory for main context and scheduler constexpr std::size_t size = sizeof( context) + sizeof( scheduler); @@ -72,7 +72,7 @@ struct context_initializer { // create and attach dispatcher context to scheduler sched->attach_dispatcher_context( make_dispatcher_context( sched) ); // make main context to active context - active_ = main_ctx; + BOOST_FIBER_USE_THREAD_LOCAL(active_) = main_ctx; # else constexpr std::size_t alignment = 64; // alignof( capture_t); constexpr std::size_t ctx_size = sizeof( context); @@ -104,14 +104,14 @@ struct context_initializer { // create and attach dispatcher context to scheduler sched->attach_dispatcher_context( make_dispatcher_context( sched) ); // make main context to active context - active_ = main_ctx; + BOOST_FIBER_USE_THREAD_LOCAL(active_) = main_ctx; # endif } } ~context_initializer() { - if ( 0 == --counter_) { - context * main_ctx = active_; + if ( 0 == --BOOST_FIBER_USE_THREAD_LOCAL(counter_)) { + context * main_ctx = BOOST_FIBER_USE_THREAD_LOCAL(active_); BOOST_ASSERT( main_ctx->is_context( type::main_context) ); scheduler * sched = main_ctx->get_scheduler(); sched->~scheduler(); @@ -128,23 +128,23 @@ struct context_initializer { }; // zero-initialization -thread_local context * context_initializer::active_; -thread_local std::size_t context_initializer::counter_; +BOOST_FIBER_DEFINE_THREAD_LOCAL(context *, context_initializer::active_); +BOOST_FIBER_DEFINE_THREAD_LOCAL(std::size_t, context_initializer::counter_); context * context::active() noexcept { #if (BOOST_EXECUTION_CONTEXT==1) // initialized the first time control passes; per thread - thread_local static boost::context::detail::activation_record_initializer rec_initializer; + static BOOST_FIBER_DEFINE_THREAD_LOCAL(boost::context::detail::activation_record_initializer, rec_initializer_); #endif // initialized the first time control passes; per thread - thread_local static context_initializer ctx_initializer; - return context_initializer::active_; + static BOOST_FIBER_DEFINE_THREAD_LOCAL(context_initializer, ctx_initializer); + return BOOST_FIBER_USE_THREAD_LOCAL(context_initializer::active_); } void context::reset_active() noexcept { - context_initializer::active_ = nullptr; + BOOST_FIBER_USE_THREAD_LOCAL(context_initializer::active_) = nullptr; } #if (BOOST_EXECUTION_CONTEXT==1) @@ -271,7 +271,7 @@ context::resume() noexcept { context * prev = this; // context_initializer::active_ will point to `this` // prev will point to previous active context - std::swap( context_initializer::active_, prev); + std::swap( BOOST_FIBER_USE_THREAD_LOCAL(context_initializer::active_), prev); #if (BOOST_EXECUTION_CONTEXT==1) detail::data_t d{}; #else @@ -285,7 +285,7 @@ context::resume( detail::spinlock_lock & lk) noexcept { context * prev = this; // context_initializer::active_ will point to `this` // prev will point to previous active context - std::swap( context_initializer::active_, prev); + std::swap( BOOST_FIBER_USE_THREAD_LOCAL(context_initializer::active_), prev); #if (BOOST_EXECUTION_CONTEXT==1) detail::data_t d{ & lk }; #else @@ -299,7 +299,7 @@ context::resume( context * ready_ctx) noexcept { context * prev = this; // context_initializer::active_ will point to `this` // prev will point to previous active context - std::swap( context_initializer::active_, prev); + std::swap( BOOST_FIBER_USE_THREAD_LOCAL(context_initializer::active_), prev); #if (BOOST_EXECUTION_CONTEXT==1) detail::data_t d{ ready_ctx }; #else @@ -373,7 +373,7 @@ context::suspend_with_cc() noexcept { context * prev = this; // context_initializer::active_ will point to `this` // prev will point to previous active context - std::swap( context_initializer::active_, prev); + std::swap( BOOST_FIBER_USE_THREAD_LOCAL(context_initializer::active_), prev); detail::data_t d{ prev }; // context switch return c_.resume( & d);