]>
Commit | Line | Data |
---|---|---|
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: | |
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; | |
82 | spin_wait() | |
83 | : m_count_start(), m_ul_yield_only_counts(), m_k() | |
84 | {} | |
85 | ||
86 | #ifdef BOOST_INTERPROCESS_SPIN_WAIT_DEBUG | |
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 | ||
96 | unsigned int count() const | |
97 | { return m_k; } | |
98 | ||
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 | ||
131 | void reset() | |
132 | { | |
133 | m_k = 0u; | |
134 | } | |
135 | ||
136 | private: | |
137 | ||
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 | ||
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 | |
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 |