Ticket #5752: atomic_once.patch
File atomic_once.patch, 11.6 KB (added by , 10 years ago) |
---|
-
libs/thread/src/pthread/once.cpp
4 4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 5 6 6 #define __STDC_CONSTANT_MACROS 7 #include <boost/thread/detail/config.hpp> 8 #include <boost/thread/once.hpp> 7 9 #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp> 8 #include <boost/thread/once.hpp>9 10 #include <boost/assert.hpp> 11 #include <boost/static_assert.hpp> 12 #include <boost/atomic.hpp> 13 #include <boost/memory_order.hpp> 10 14 #include <pthread.h> 11 #include <stdlib.h>12 #include <memory>13 15 14 16 namespace boost 15 17 { 16 namespace detail 17 { 18 BOOST_THREAD_DECL thread_detail::uintmax_atomic_t once_global_epoch=BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C; 19 BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; 20 BOOST_THREAD_DECL pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; 18 namespace thread_detail 19 { 21 20 22 namespace 23 24 pthread_key_t epoch_tss_key;25 pthread_once_t epoch_tss_key_flag=PTHREAD_ONCE_INIT;21 enum flag_states 22 { 23 uninitialized, in_progress, initialized 24 }; 26 25 27 extern "C" 28 { 29 static void delete_epoch_tss_data(void* data) 30 { 31 free(data); 32 } 26 #if BOOST_ATOMIC_INT_LOCK_FREE == 2 27 typedef unsigned int atomic_int_type; 28 #elif BOOST_ATOMIC_SHORT_LOCK_FREE == 2 29 typedef unsigned short atomic_int_type; 30 #elif BOOST_ATOMIC_CHAR_LOCK_FREE == 2 31 typedef unsigned char atomic_int_type; 32 #elif BOOST_ATOMIC_LONG_LOCK_FREE == 2 33 typedef unsigned long atomic_int_type; 34 #elif defined(BOOST_HAS_LONG_LONG) && BOOST_ATOMIC_LLONG_LOCK_FREE == 2 35 typedef ulong_long_type atomic_int_type; 36 #else 37 // All tested integer types are not atomic, the spinlock pool will be used 38 typedef unsigned int atomic_int_type; 39 #endif 33 40 34 static void create_epoch_tss_key() 35 { 36 BOOST_VERIFY(!pthread_key_create(&epoch_tss_key,delete_epoch_tss_data)); 37 } 38 } 39 40 #if defined BOOST_THREAD_PATCH 41 const pthread_once_t pthread_once_init_value=PTHREAD_ONCE_INIT; 42 struct BOOST_THREAD_DECL delete_epoch_tss_key_on_dlclose_t 43 { 44 delete_epoch_tss_key_on_dlclose_t() 45 { 46 } 47 ~delete_epoch_tss_key_on_dlclose_t() 48 { 49 if(memcmp(&epoch_tss_key_flag, &pthread_once_init_value, sizeof(pthread_once_t))) 50 { 51 pthread_key_delete(epoch_tss_key); 52 } 53 } 54 }; 55 delete_epoch_tss_key_on_dlclose_t delete_epoch_tss_key_on_dlclose; 41 typedef boost::atomic< atomic_int_type > atomic_type 42 #if defined(__GNUC__) 43 __attribute__((may_alias)) 56 44 #endif 57 } 45 ; 46 BOOST_STATIC_ASSERT_MSG(sizeof(once_flag) >= sizeof(atomic_type), "Boost.Thread: unsupported platform"); 58 47 59 thread_detail::uintmax_atomic_t& get_once_per_thread_epoch() 48 static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; 49 static pthread_cond_t once_cv = PTHREAD_COND_INITIALIZER; 50 51 BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT 52 { 53 atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); 54 if (f.load(memory_order_acquire) != initialized) 55 { 56 while (true) 60 57 { 61 BOOST_VERIFY(!pthread_once(&epoch_tss_key_flag,create_epoch_tss_key)); 62 void* data=pthread_getspecific(epoch_tss_key); 63 if(!data) 58 atomic_int_type expected = uninitialized; 59 if (f.compare_exchange_strong(expected, in_progress, memory_order_acq_rel, memory_order_acquire)) 64 60 { 65 data=malloc(sizeof(thread_detail::uintmax_atomic_t)); 66 BOOST_VERIFY(!pthread_setspecific(epoch_tss_key,data)); 67 *static_cast<thread_detail::uintmax_atomic_t*>(data)=BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C; 61 // We have set the flag to in_progress 62 return true; 68 63 } 69 return *static_cast<thread_detail::uintmax_atomic_t*>(data); 64 else if (expected == initialized) 65 { 66 // Another thread managed to complete the initialization 67 return false; 68 } 69 else 70 { 71 // Wait until the initialization is complete 72 pthread::pthread_mutex_scoped_lock lk(&once_mutex); 73 while (f.load(memory_order_acquire) == in_progress) 74 { 75 BOOST_VERIFY(!pthread_cond_wait(&once_cv, &once_mutex)); 76 } 77 } 70 78 } 71 79 } 80 else 81 return false; 82 } 72 83 84 BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT 85 { 86 atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); 87 pthread::pthread_mutex_scoped_lock lk(&once_mutex); 88 f.store(initialized, memory_order_release); 89 BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); 73 90 } 91 92 BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT 93 { 94 atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); 95 pthread::pthread_mutex_scoped_lock lk(&once_mutex); 96 f.store(uninitialized, memory_order_release); 97 BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); 98 } 99 100 } // namespace thread_detail 101 102 } // namespace boost -
boost/thread/detail/config.hpp
131 131 #define BOOST_THREAD_DONT_PROVIDE_ONCE_CXX11 132 132 #endif 133 133 134 // For C++11 call_once interface the compiler MUST support constexpr. 135 // Otherwise once_flag would be initialized during dynamic initialization stage, which is not thread-safe. 136 #if defined(BOOST_THREAD_PROVIDES_ONCE_CXX11) && defined(BOOST_NO_CXX11_CONSTEXPR) 137 #undef BOOST_THREAD_PROVIDES_ONCE_CXX11 138 #endif 139 134 140 // THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE 135 141 #if ! defined BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE \ 136 142 && ! defined BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE -
boost/thread/pthread/once.hpp
12 12 13 13 #include <boost/thread/detail/config.hpp> 14 14 15 #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp> 16 #include <boost/thread/detail/delete.hpp> 15 #include <boost/cstdint.hpp> 17 16 #include <boost/detail/no_exceptions_support.hpp> 18 17 19 #include <boost/assert.hpp>20 18 #include <boost/config/abi_prefix.hpp> 21 19 22 #include <boost/cstdint.hpp>23 #include <pthread.h>24 #include <csignal>25 26 20 namespace boost 27 21 { 28 22 29 #define BOOST_ONCE_INITIAL_FLAG_VALUE 0 23 struct once_flag; 30 24 31 namespace thread_detail 32 { 33 //#ifdef SIG_ATOMIC_MAX 34 // typedef sig_atomic_t uintmax_atomic_t; 35 // #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C SIG_ATOMIC_MAX 36 //#else 37 typedef unsigned long uintmax_atomic_t; 38 #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_C2(value) value##ul 39 #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_C2(~0) 40 //#endif 41 } 25 namespace thread_detail 26 { 27 BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT; 28 BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT; 29 BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT; 30 } 42 31 43 32 #ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11 44 33 45 struct once_flag 46 { 47 BOOST_THREAD_NO_COPYABLE(once_flag) 48 BOOST_CONSTEXPR once_flag() BOOST_NOEXCEPT 49 : epoch(BOOST_ONCE_INITIAL_FLAG_VALUE) 50 {} 51 private: 52 volatile thread_detail::uintmax_atomic_t epoch; 53 template<typename Function> 54 friend 55 void call_once(once_flag& flag,Function f); 56 }; 34 struct once_flag 35 { 36 BOOST_THREAD_NO_COPYABLE(once_flag) 37 constexpr once_flag() BOOST_NOEXCEPT : storage(0) 38 { 39 } 57 40 41 private: 42 #if defined(__GNUC__) 43 __attribute__((may_alias)) 44 #endif 45 uintmax_t storage; 46 47 friend bool thread_detail::enter_once_region(once_flag& flag) BOOST_NOEXCEPT; 48 friend void thread_detail::commit_once_region(once_flag& flag) BOOST_NOEXCEPT; 49 friend void thread_detail::rollback_once_region(once_flag& flag) BOOST_NOEXCEPT; 50 }; 51 52 #define BOOST_ONCE_INIT once_flag() 53 58 54 #else // BOOST_THREAD_PROVIDES_ONCE_CXX11 59 55 60 struct once_flag 61 { 62 volatile thread_detail::uintmax_atomic_t epoch; 63 }; 56 struct once_flag 57 { 58 #if defined(__GNUC__) 59 __attribute__((may_alias)) 60 #endif 61 uintmax_t storage; 62 }; 64 63 65 #define BOOST_ONCE_INIT {BOOST_ONCE_INITIAL_FLAG_VALUE} 64 #define BOOST_ONCE_INIT {0} 65 66 66 #endif // BOOST_THREAD_PROVIDES_ONCE_CXX11 67 67 68 namespace detail 68 template<typename Function> 69 inline void call_once(once_flag& flag, Function f) 70 { 71 if (thread_detail::enter_once_region(flag)) 69 72 { 70 BOOST_THREAD_DECL thread_detail::uintmax_atomic_t& get_once_per_thread_epoch(); 71 BOOST_THREAD_DECL extern thread_detail::uintmax_atomic_t once_global_epoch; 72 BOOST_THREAD_DECL extern pthread_mutex_t once_epoch_mutex; 73 BOOST_THREAD_DECL extern pthread_cond_t once_epoch_cv; 74 } 75 76 // Based on Mike Burrows fast_pthread_once algorithm as described in 77 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2444.html 78 template<typename Function> 79 void call_once(once_flag& flag,Function f) 80 { 81 static thread_detail::uintmax_atomic_t const uninitialized_flag=BOOST_ONCE_INITIAL_FLAG_VALUE; 82 static thread_detail::uintmax_atomic_t const being_initialized=uninitialized_flag+1; 83 thread_detail::uintmax_atomic_t const epoch=flag.epoch; 84 thread_detail::uintmax_atomic_t& this_thread_epoch=detail::get_once_per_thread_epoch(); 85 86 if(epoch<this_thread_epoch) 73 BOOST_TRY 87 74 { 88 pthread::pthread_mutex_scoped_lock lk(&detail::once_epoch_mutex); 89 90 while(flag.epoch<=being_initialized) 91 { 92 if(flag.epoch==uninitialized_flag) 93 { 94 flag.epoch=being_initialized; 95 BOOST_TRY 96 { 97 pthread::pthread_mutex_scoped_unlock relocker(&detail::once_epoch_mutex); 98 f(); 99 } 100 BOOST_CATCH (...) 101 { 102 flag.epoch=uninitialized_flag; 103 BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv)); 104 BOOST_RETHROW 105 } 106 BOOST_CATCH_END 107 flag.epoch=--detail::once_global_epoch; 108 BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv)); 109 } 110 else 111 { 112 while(flag.epoch==being_initialized) 113 { 114 BOOST_VERIFY(!pthread_cond_wait(&detail::once_epoch_cv,&detail::once_epoch_mutex)); 115 } 116 } 117 } 118 this_thread_epoch=detail::once_global_epoch; 75 f(); 119 76 } 77 BOOST_CATCH (...) 78 { 79 thread_detail::rollback_once_region(flag); 80 BOOST_RETHROW 81 } 82 BOOST_CATCH_END 83 thread_detail::commit_once_region(flag); 120 84 } 121 85 } 122 86 87 } 88 123 89 #include <boost/config/abi_suffix.hpp> 124 90 125 91 #endif