wait.hpp 5.6 KB

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