• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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