wait.hpp 4.9 KB

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