wait.hpp 4.9 KB

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