Index: libs/thread/src/pthread/once.cpp =================================================================== --- libs/thread/src/pthread/once.cpp (revision 82462) +++ libs/thread/src/pthread/once.cpp (working copy) @@ -4,70 +4,99 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #define __STDC_CONSTANT_MACROS +#include +#include #include -#include #include +#include +#include +#include #include -#include -#include namespace boost { - namespace detail - { - BOOST_THREAD_DECL thread_detail::uintmax_atomic_t once_global_epoch=BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C; - BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; - BOOST_THREAD_DECL pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; +namespace thread_detail +{ - namespace - { - pthread_key_t epoch_tss_key; - pthread_once_t epoch_tss_key_flag=PTHREAD_ONCE_INIT; +enum flag_states +{ + uninitialized, in_progress, initialized +}; - extern "C" - { - static void delete_epoch_tss_data(void* data) - { - free(data); - } +#if BOOST_ATOMIC_INT_LOCK_FREE == 2 +typedef unsigned int atomic_int_type; +#elif BOOST_ATOMIC_SHORT_LOCK_FREE == 2 +typedef unsigned short atomic_int_type; +#elif BOOST_ATOMIC_CHAR_LOCK_FREE == 2 +typedef unsigned char atomic_int_type; +#elif BOOST_ATOMIC_LONG_LOCK_FREE == 2 +typedef unsigned long atomic_int_type; +#elif defined(BOOST_HAS_LONG_LONG) && BOOST_ATOMIC_LLONG_LOCK_FREE == 2 +typedef ulong_long_type atomic_int_type; +#else +// All tested integer types are not atomic, the spinlock pool will be used +typedef unsigned int atomic_int_type; +#endif - static void create_epoch_tss_key() - { - BOOST_VERIFY(!pthread_key_create(&epoch_tss_key,delete_epoch_tss_data)); - } - } - -#if defined BOOST_THREAD_PATCH - const pthread_once_t pthread_once_init_value=PTHREAD_ONCE_INIT; - struct BOOST_THREAD_DECL delete_epoch_tss_key_on_dlclose_t - { - delete_epoch_tss_key_on_dlclose_t() - { - } - ~delete_epoch_tss_key_on_dlclose_t() - { - if(memcmp(&epoch_tss_key_flag, &pthread_once_init_value, sizeof(pthread_once_t))) - { - pthread_key_delete(epoch_tss_key); - } - } - }; - delete_epoch_tss_key_on_dlclose_t delete_epoch_tss_key_on_dlclose; +typedef boost::atomic< atomic_int_type > atomic_type +#if defined(__GNUC__) + __attribute__((may_alias)) #endif - } +; +BOOST_STATIC_ASSERT_MSG(sizeof(once_flag) >= sizeof(atomic_type), "Boost.Thread: unsupported platform"); - thread_detail::uintmax_atomic_t& get_once_per_thread_epoch() +static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t once_cv = PTHREAD_COND_INITIALIZER; + +BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT +{ + atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); + if (f.load(memory_order_acquire) != initialized) + { + while (true) { - BOOST_VERIFY(!pthread_once(&epoch_tss_key_flag,create_epoch_tss_key)); - void* data=pthread_getspecific(epoch_tss_key); - if(!data) + atomic_int_type expected = uninitialized; + if (f.compare_exchange_strong(expected, in_progress, memory_order_acq_rel, memory_order_acquire)) { - data=malloc(sizeof(thread_detail::uintmax_atomic_t)); - BOOST_VERIFY(!pthread_setspecific(epoch_tss_key,data)); - *static_cast(data)=BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C; + // We have set the flag to in_progress + return true; } - return *static_cast(data); + else if (expected == initialized) + { + // Another thread managed to complete the initialization + return false; + } + else + { + // Wait until the initialization is complete + pthread::pthread_mutex_scoped_lock lk(&once_mutex); + while (f.load(memory_order_acquire) == in_progress) + { + BOOST_VERIFY(!pthread_cond_wait(&once_cv, &once_mutex)); + } + } } } + else + return false; +} +BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT +{ + atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); + pthread::pthread_mutex_scoped_lock lk(&once_mutex); + f.store(initialized, memory_order_release); + BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); } + +BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT +{ + atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); + pthread::pthread_mutex_scoped_lock lk(&once_mutex); + f.store(uninitialized, memory_order_release); + BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); +} + +} // namespace thread_detail + +} // namespace boost Index: boost/thread/detail/config.hpp =================================================================== --- boost/thread/detail/config.hpp (revision 82462) +++ boost/thread/detail/config.hpp (working copy) @@ -131,6 +131,12 @@ #define BOOST_THREAD_DONT_PROVIDE_ONCE_CXX11 #endif +// For C++11 call_once interface the compiler MUST support constexpr. +// Otherwise once_flag would be initialized during dynamic initialization stage, which is not thread-safe. +#if defined(BOOST_THREAD_PROVIDES_ONCE_CXX11) && defined(BOOST_NO_CXX11_CONSTEXPR) +#undef BOOST_THREAD_PROVIDES_ONCE_CXX11 +#endif + // THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE #if ! defined BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE \ && ! defined BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE Index: boost/thread/pthread/once.hpp =================================================================== --- boost/thread/pthread/once.hpp (revision 82462) +++ boost/thread/pthread/once.hpp (working copy) @@ -12,114 +12,80 @@ #include -#include -#include +#include #include -#include #include -#include -#include -#include - namespace boost { -#define BOOST_ONCE_INITIAL_FLAG_VALUE 0 +struct once_flag; - namespace thread_detail - { -//#ifdef SIG_ATOMIC_MAX -// typedef sig_atomic_t uintmax_atomic_t; -// #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C SIG_ATOMIC_MAX -//#else - typedef unsigned long uintmax_atomic_t; - #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_C2(value) value##ul - #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_C2(~0) -//#endif - } +namespace thread_detail +{ +BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT; +BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT; +BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT; +} #ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11 - struct once_flag - { - BOOST_THREAD_NO_COPYABLE(once_flag) - BOOST_CONSTEXPR once_flag() BOOST_NOEXCEPT - : epoch(BOOST_ONCE_INITIAL_FLAG_VALUE) - {} - private: - volatile thread_detail::uintmax_atomic_t epoch; - template - friend - void call_once(once_flag& flag,Function f); - }; +struct once_flag +{ + BOOST_THREAD_NO_COPYABLE(once_flag) + constexpr once_flag() BOOST_NOEXCEPT : storage(0) + { + } +private: +#if defined(__GNUC__) + __attribute__((may_alias)) +#endif + uintmax_t storage; + + friend bool thread_detail::enter_once_region(once_flag& flag) BOOST_NOEXCEPT; + friend void thread_detail::commit_once_region(once_flag& flag) BOOST_NOEXCEPT; + friend void thread_detail::rollback_once_region(once_flag& flag) BOOST_NOEXCEPT; +}; + +#define BOOST_ONCE_INIT once_flag() + #else // BOOST_THREAD_PROVIDES_ONCE_CXX11 - struct once_flag - { - volatile thread_detail::uintmax_atomic_t epoch; - }; +struct once_flag +{ +#if defined(__GNUC__) + __attribute__((may_alias)) +#endif + uintmax_t storage; +}; -#define BOOST_ONCE_INIT {BOOST_ONCE_INITIAL_FLAG_VALUE} +#define BOOST_ONCE_INIT {0} + #endif // BOOST_THREAD_PROVIDES_ONCE_CXX11 - namespace detail +template +inline void call_once(once_flag& flag, Function f) +{ + if (thread_detail::enter_once_region(flag)) { - BOOST_THREAD_DECL thread_detail::uintmax_atomic_t& get_once_per_thread_epoch(); - BOOST_THREAD_DECL extern thread_detail::uintmax_atomic_t once_global_epoch; - BOOST_THREAD_DECL extern pthread_mutex_t once_epoch_mutex; - BOOST_THREAD_DECL extern pthread_cond_t once_epoch_cv; - } - - // Based on Mike Burrows fast_pthread_once algorithm as described in - // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2444.html - template - void call_once(once_flag& flag,Function f) - { - static thread_detail::uintmax_atomic_t const uninitialized_flag=BOOST_ONCE_INITIAL_FLAG_VALUE; - static thread_detail::uintmax_atomic_t const being_initialized=uninitialized_flag+1; - thread_detail::uintmax_atomic_t const epoch=flag.epoch; - thread_detail::uintmax_atomic_t& this_thread_epoch=detail::get_once_per_thread_epoch(); - - if(epoch #endif