1 ////////////////////////////////////////////////////////////////////////////// 2 // 3 // (C) Copyright Peter Dimov 2008. 4 // (C) Copyright Ion Gaztanaga 2013-2013. Distributed under the Boost 5 // Software License, Version 1.0. (See accompanying file 6 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 // 8 // See http://www.boost.org/libs/interprocess for documentation. 9 // 10 ////////////////////////////////////////////////////////////////////////////// 11 12 //Parts of this file come from boost/smart_ptr/detail/yield_k.hpp 13 //Many thanks to Peter Dimov. 14 15 #ifndef BOOST_INTERPROCESS_SYNC_WAIT_HPP_INCLUDED 16 #define BOOST_INTERPROCESS_SYNC_WAIT_HPP_INCLUDED 17 18 #ifndef BOOST_CONFIG_HPP 19 # include <boost/config.hpp> 20 #endif 21 # 22 #if defined(BOOST_HAS_PRAGMA_ONCE) 23 # pragma once 24 #endif 25 26 #include <boost/interprocess/detail/config_begin.hpp> 27 #include <boost/interprocess/detail/workaround.hpp> 28 #include <boost/interprocess/detail/os_thread_functions.hpp> 29 30 //#define BOOST_INTERPROCESS_SPIN_WAIT_DEBUG 31 #ifdef BOOST_INTERPROCESS_SPIN_WAIT_DEBUG 32 #include <iostream> 33 #endif 34 35 // BOOST_INTERPROCESS_SMT_PAUSE 36 37 #if defined(_MSC_VER) && ( defined(_M_IX86) || defined(_M_X64) ) 38 39 extern "C" void _mm_pause(); 40 #pragma intrinsic( _mm_pause ) 41 42 #define BOOST_INTERPROCESS_SMT_PAUSE _mm_pause(); 43 44 #elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) && !defined(_CRAYC) 45 46 #define BOOST_INTERPROCESS_SMT_PAUSE __asm__ __volatile__( "rep; nop" : : : "memory" ); 47 48 #endif 49 50 namespace boost{ 51 namespace interprocess{ 52 namespace ipcdetail { 53 54 template<int Dummy = 0> 55 class num_core_holder 56 { 57 public: get()58 static unsigned int get() 59 { 60 if(!num_cores){ 61 return ipcdetail::get_num_cores(); 62 } 63 else{ 64 return num_cores; 65 } 66 } 67 68 private: 69 static unsigned int num_cores; 70 }; 71 72 template<int Dummy> 73 unsigned int num_core_holder<Dummy>::num_cores = ipcdetail::get_num_cores(); 74 75 } //namespace ipcdetail { 76 77 class spin_wait 78 { 79 public: 80 81 static const unsigned int nop_pause_limit = 32u; spin_wait()82 spin_wait() 83 : m_count_start(), m_ul_yield_only_counts(), m_k() 84 {} 85 86 #ifdef BOOST_INTERPROCESS_SPIN_WAIT_DEBUG ~spin_wait()87 ~spin_wait() 88 { 89 if(m_k){ 90 std::cout << "final m_k: " << m_k 91 << " system tick(us): " << ipcdetail::get_system_tick_us() << std::endl; 92 } 93 } 94 #endif 95 count() const96 unsigned int count() const 97 { return m_k; } 98 yield()99 void yield() 100 { 101 //Lazy initialization of limits 102 if( !m_k){ 103 this->init_limits(); 104 } 105 //Nop tries 106 if( m_k < (nop_pause_limit >> 2) ){ 107 108 } 109 //Pause tries if the processor supports it 110 #if defined(BOOST_INTERPROCESS_SMT_PAUSE) 111 else if( m_k < nop_pause_limit ){ 112 BOOST_INTERPROCESS_SMT_PAUSE 113 } 114 #endif 115 //Yield/Sleep strategy 116 else{ 117 //Lazy initialization of tick information 118 if(m_k == nop_pause_limit){ 119 this->init_tick_info(); 120 } 121 else if( this->yield_or_sleep() ){ 122 ipcdetail::thread_yield(); 123 } 124 else{ 125 ipcdetail::thread_sleep_tick(); 126 } 127 } 128 ++m_k; 129 } 130 reset()131 void reset() 132 { 133 m_k = 0u; 134 } 135 136 private: 137 init_limits()138 void init_limits() 139 { 140 unsigned int num_cores = ipcdetail::num_core_holder<0>::get(); 141 m_k = num_cores > 1u ? 0u : nop_pause_limit; 142 } 143 init_tick_info()144 void init_tick_info() 145 { 146 m_ul_yield_only_counts = ipcdetail::get_system_tick_in_highres_counts(); 147 m_count_start = ipcdetail::get_current_system_highres_count(); 148 } 149 150 //Returns true if yield must be called, false is sleep must be called yield_or_sleep()151 bool yield_or_sleep() 152 { 153 if(!m_ul_yield_only_counts){ //If yield-only limit was reached then yield one in every two tries 154 return (m_k & 1u) != 0; 155 } 156 else{ //Try to see if we've reched yield-only time limit 157 const ipcdetail::OS_highres_count_t now = ipcdetail::get_current_system_highres_count(); 158 const ipcdetail::OS_highres_count_t elapsed = ipcdetail::system_highres_count_subtract(now, m_count_start); 159 if(!ipcdetail::system_highres_count_less_ul(elapsed, m_ul_yield_only_counts)){ 160 #ifdef BOOST_INTERPROCESS_SPIN_WAIT_DEBUG 161 std::cout << "elapsed!\n" 162 << " m_ul_yield_only_counts: " << m_ul_yield_only_counts 163 << " system tick(us): " << ipcdetail::get_system_tick_us() << '\n' 164 << " m_k: " << m_k << " elapsed counts: "; 165 ipcdetail::ostream_highres_count(std::cout, elapsed) << std::endl; 166 #endif 167 //Yield-only time reached, now it's time to sleep 168 m_ul_yield_only_counts = 0ul; 169 return false; 170 } 171 } 172 return true; //Otherwise yield 173 } 174 175 ipcdetail::OS_highres_count_t m_count_start; 176 unsigned long m_ul_yield_only_counts; 177 unsigned int m_k; 178 }; 179 180 } // namespace interprocess 181 } // namespace boost 182 183 #include <boost/interprocess/detail/config_end.hpp> 184 185 #endif // #ifndef BOOST_INTERPROCESS_SYNC_WAIT_HPP_INCLUDED 186