Chris@16
|
1 /*
|
Chris@101
|
2 * Copyright Andrey Semashev 2007 - 2015.
|
Chris@16
|
3 * Distributed under the Boost Software License, Version 1.0.
|
Chris@16
|
4 * (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
5 * http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
6 */
|
Chris@16
|
7 /*!
|
Chris@16
|
8 * \file spin_mutex.hpp
|
Chris@16
|
9 * \author Andrey Semashev
|
Chris@16
|
10 * \date 01.08.2010
|
Chris@16
|
11 *
|
Chris@16
|
12 * \brief This header is the Boost.Log library implementation, see the library documentation
|
Chris@16
|
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
|
Chris@16
|
14 */
|
Chris@16
|
15
|
Chris@16
|
16 #ifndef BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_
|
Chris@16
|
17 #define BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_
|
Chris@16
|
18
|
Chris@16
|
19 #include <boost/log/detail/config.hpp>
|
Chris@16
|
20
|
Chris@16
|
21 #ifdef BOOST_HAS_PRAGMA_ONCE
|
Chris@16
|
22 #pragma once
|
Chris@16
|
23 #endif
|
Chris@16
|
24
|
Chris@16
|
25 #ifndef BOOST_LOG_NO_THREADS
|
Chris@16
|
26
|
Chris@16
|
27 #include <boost/throw_exception.hpp>
|
Chris@16
|
28 #include <boost/thread/exceptions.hpp>
|
Chris@16
|
29
|
Chris@16
|
30 #if defined(BOOST_THREAD_POSIX) // This one can be defined by users, so it should go first
|
Chris@16
|
31 #define BOOST_LOG_SPIN_MUTEX_USE_PTHREAD
|
Chris@16
|
32 #elif defined(BOOST_WINDOWS)
|
Chris@16
|
33 #define BOOST_LOG_SPIN_MUTEX_USE_WINAPI
|
Chris@16
|
34 #elif defined(BOOST_HAS_PTHREADS)
|
Chris@16
|
35 #define BOOST_LOG_SPIN_MUTEX_USE_PTHREAD
|
Chris@16
|
36 #endif
|
Chris@16
|
37
|
Chris@16
|
38 #if defined(BOOST_LOG_SPIN_MUTEX_USE_WINAPI)
|
Chris@16
|
39
|
Chris@16
|
40 #include <boost/detail/interlocked.hpp>
|
Chris@16
|
41
|
Chris@16
|
42 #if defined(BOOST_USE_WINDOWS_H)
|
Chris@16
|
43
|
Chris@16
|
44 #ifndef _WIN32_WINNT
|
Chris@16
|
45 #define _WIN32_WINNT 0x0500
|
Chris@16
|
46 #endif
|
Chris@16
|
47
|
Chris@16
|
48 #include <windows.h>
|
Chris@16
|
49
|
Chris@16
|
50 #else // defined(BOOST_USE_WINDOWS_H)
|
Chris@16
|
51
|
Chris@16
|
52 namespace boost {
|
Chris@16
|
53
|
Chris@16
|
54 BOOST_LOG_OPEN_NAMESPACE
|
Chris@16
|
55
|
Chris@16
|
56 namespace aux {
|
Chris@16
|
57
|
Chris@16
|
58 extern "C" {
|
Chris@16
|
59
|
Chris@16
|
60 __declspec(dllimport) int __stdcall SwitchToThread();
|
Chris@16
|
61
|
Chris@16
|
62 } // extern "C"
|
Chris@16
|
63
|
Chris@16
|
64 } // namespace aux
|
Chris@16
|
65
|
Chris@16
|
66 BOOST_LOG_CLOSE_NAMESPACE // namespace log
|
Chris@16
|
67
|
Chris@16
|
68 } // namespace boost
|
Chris@16
|
69
|
Chris@16
|
70 #endif // BOOST_USE_WINDOWS_H
|
Chris@16
|
71
|
Chris@16
|
72 #if defined(__INTEL_COMPILER) || defined(_MSC_VER)
|
Chris@16
|
73 # if defined(_M_IX86)
|
Chris@16
|
74 # define BOOST_LOG_PAUSE_OP __asm { pause }
|
Chris@16
|
75 # elif defined(_M_AMD64)
|
Chris@16
|
76 extern "C" void _mm_pause(void);
|
Chris@16
|
77 #pragma intrinsic(_mm_pause)
|
Chris@16
|
78 # define BOOST_LOG_PAUSE_OP _mm_pause()
|
Chris@16
|
79 # endif
|
Chris@16
|
80 # if defined(__INTEL_COMPILER)
|
Chris@16
|
81 # define BOOST_LOG_COMPILER_BARRIER __memory_barrier()
|
Chris@16
|
82 # else
|
Chris@16
|
83 extern "C" void _ReadWriteBarrier(void);
|
Chris@16
|
84 #pragma intrinsic(_ReadWriteBarrier)
|
Chris@16
|
85 # define BOOST_LOG_COMPILER_BARRIER _ReadWriteBarrier()
|
Chris@16
|
86 # endif
|
Chris@16
|
87 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
|
Chris@16
|
88 # define BOOST_LOG_PAUSE_OP __asm__ __volatile__("pause;")
|
Chris@16
|
89 # define BOOST_LOG_COMPILER_BARRIER __asm__ __volatile__("" : : : "memory")
|
Chris@16
|
90 #endif
|
Chris@16
|
91
|
Chris@16
|
92 #include <boost/log/detail/header.hpp>
|
Chris@16
|
93
|
Chris@16
|
94 namespace boost {
|
Chris@16
|
95
|
Chris@16
|
96 BOOST_LOG_OPEN_NAMESPACE
|
Chris@16
|
97
|
Chris@16
|
98 namespace aux {
|
Chris@16
|
99
|
Chris@16
|
100 //! A simple spinning mutex
|
Chris@16
|
101 class spin_mutex
|
Chris@16
|
102 {
|
Chris@16
|
103 private:
|
Chris@16
|
104 enum state
|
Chris@16
|
105 {
|
Chris@16
|
106 initial_pause = 2,
|
Chris@16
|
107 max_pause = 16
|
Chris@16
|
108 };
|
Chris@16
|
109
|
Chris@16
|
110 long m_State;
|
Chris@16
|
111
|
Chris@16
|
112 public:
|
Chris@16
|
113 spin_mutex() : m_State(0) {}
|
Chris@16
|
114
|
Chris@16
|
115 bool try_lock()
|
Chris@16
|
116 {
|
Chris@16
|
117 return (BOOST_INTERLOCKED_COMPARE_EXCHANGE(&m_State, 1L, 0L) == 0L);
|
Chris@16
|
118 }
|
Chris@16
|
119
|
Chris@16
|
120 void lock()
|
Chris@16
|
121 {
|
Chris@16
|
122 #if defined(BOOST_LOG_PAUSE_OP)
|
Chris@101
|
123 unsigned int pause_count = initial_pause;
|
Chris@16
|
124 #endif
|
Chris@16
|
125 while (!try_lock())
|
Chris@16
|
126 {
|
Chris@16
|
127 #if defined(BOOST_LOG_PAUSE_OP)
|
Chris@16
|
128 if (pause_count < max_pause)
|
Chris@16
|
129 {
|
Chris@101
|
130 for (unsigned int i = 0; i < pause_count; ++i)
|
Chris@16
|
131 {
|
Chris@16
|
132 BOOST_LOG_PAUSE_OP;
|
Chris@16
|
133 }
|
Chris@16
|
134 pause_count += pause_count;
|
Chris@16
|
135 }
|
Chris@16
|
136 else
|
Chris@16
|
137 {
|
Chris@16
|
138 // Restart spinning after waking up this thread
|
Chris@16
|
139 pause_count = initial_pause;
|
Chris@16
|
140 SwitchToThread();
|
Chris@16
|
141 }
|
Chris@16
|
142 #else
|
Chris@16
|
143 SwitchToThread();
|
Chris@16
|
144 #endif
|
Chris@16
|
145 }
|
Chris@16
|
146 }
|
Chris@16
|
147
|
Chris@16
|
148 void unlock()
|
Chris@16
|
149 {
|
Chris@16
|
150 #if (defined(_M_IX86) || defined(_M_AMD64)) && defined(BOOST_LOG_COMPILER_BARRIER)
|
Chris@16
|
151 BOOST_LOG_COMPILER_BARRIER;
|
Chris@16
|
152 m_State = 0L;
|
Chris@16
|
153 BOOST_LOG_COMPILER_BARRIER;
|
Chris@16
|
154 #else
|
Chris@16
|
155 BOOST_INTERLOCKED_EXCHANGE(&m_State, 0L);
|
Chris@16
|
156 #endif
|
Chris@16
|
157 }
|
Chris@16
|
158
|
Chris@16
|
159 // Non-copyable
|
Chris@16
|
160 BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&))
|
Chris@16
|
161 BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&))
|
Chris@16
|
162 };
|
Chris@16
|
163
|
Chris@16
|
164 #undef BOOST_LOG_PAUSE_OP
|
Chris@16
|
165 #undef BOOST_LOG_COMPILER_BARRIER
|
Chris@16
|
166
|
Chris@16
|
167 } // namespace aux
|
Chris@16
|
168
|
Chris@16
|
169 BOOST_LOG_CLOSE_NAMESPACE // namespace log
|
Chris@16
|
170
|
Chris@16
|
171 } // namespace boost
|
Chris@16
|
172
|
Chris@16
|
173 #include <boost/log/detail/footer.hpp>
|
Chris@16
|
174
|
Chris@16
|
175 #elif defined(BOOST_LOG_SPIN_MUTEX_USE_PTHREAD)
|
Chris@16
|
176
|
Chris@16
|
177 #include <pthread.h>
|
Chris@16
|
178 #include <boost/assert.hpp>
|
Chris@16
|
179 #include <boost/log/detail/header.hpp>
|
Chris@16
|
180
|
Chris@16
|
181 namespace boost {
|
Chris@16
|
182
|
Chris@16
|
183 BOOST_LOG_OPEN_NAMESPACE
|
Chris@16
|
184
|
Chris@16
|
185 namespace aux {
|
Chris@16
|
186
|
Chris@16
|
187 #if defined(_POSIX_SPIN_LOCKS) && _POSIX_SPIN_LOCKS > 0
|
Chris@16
|
188
|
Chris@16
|
189 //! A simple spinning mutex
|
Chris@16
|
190 class spin_mutex
|
Chris@16
|
191 {
|
Chris@16
|
192 private:
|
Chris@16
|
193 pthread_spinlock_t m_State;
|
Chris@16
|
194
|
Chris@16
|
195 public:
|
Chris@16
|
196 spin_mutex()
|
Chris@16
|
197 {
|
Chris@16
|
198 const int err = pthread_spin_init(&m_State, PTHREAD_PROCESS_PRIVATE);
|
Chris@16
|
199 if (err != 0)
|
Chris@16
|
200 throw_exception< thread_resource_error >(err, "failed to initialize a spin mutex", "spin_mutex::spin_mutex()", __FILE__, __LINE__);
|
Chris@16
|
201 }
|
Chris@16
|
202
|
Chris@16
|
203 ~spin_mutex()
|
Chris@16
|
204 {
|
Chris@16
|
205 BOOST_VERIFY(pthread_spin_destroy(&m_State) == 0);
|
Chris@16
|
206 }
|
Chris@16
|
207
|
Chris@16
|
208 bool try_lock()
|
Chris@16
|
209 {
|
Chris@16
|
210 const int err = pthread_spin_trylock(&m_State);
|
Chris@16
|
211 if (err == 0)
|
Chris@16
|
212 return true;
|
Chris@16
|
213 if (err != EBUSY)
|
Chris@16
|
214 throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::try_lock()", __FILE__, __LINE__);
|
Chris@16
|
215 return false;
|
Chris@16
|
216 }
|
Chris@16
|
217
|
Chris@16
|
218 void lock()
|
Chris@16
|
219 {
|
Chris@16
|
220 const int err = pthread_spin_lock(&m_State);
|
Chris@16
|
221 if (err != 0)
|
Chris@16
|
222 throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::lock()", __FILE__, __LINE__);
|
Chris@16
|
223 }
|
Chris@16
|
224
|
Chris@16
|
225 void unlock()
|
Chris@16
|
226 {
|
Chris@16
|
227 BOOST_VERIFY(pthread_spin_unlock(&m_State) == 0);
|
Chris@16
|
228 }
|
Chris@16
|
229
|
Chris@16
|
230 // Non-copyable
|
Chris@16
|
231 BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&))
|
Chris@16
|
232 BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&))
|
Chris@16
|
233
|
Chris@16
|
234 private:
|
Chris@16
|
235 template< typename ExceptionT >
|
Chris@16
|
236 static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_exception(int err, const char* descr, const char* func, const char* file, int line)
|
Chris@16
|
237 {
|
Chris@16
|
238 #if !defined(BOOST_EXCEPTION_DISABLE)
|
Chris@16
|
239 boost::exception_detail::throw_exception_(ExceptionT(err, descr), func, file, line);
|
Chris@16
|
240 #else
|
Chris@16
|
241 boost::throw_exception(ExceptionT(err, descr));
|
Chris@16
|
242 #endif
|
Chris@16
|
243 }
|
Chris@16
|
244 };
|
Chris@16
|
245
|
Chris@16
|
246 #else // defined(_POSIX_SPIN_LOCKS)
|
Chris@16
|
247
|
Chris@16
|
248 //! Backup implementation in case if pthreads don't support spin locks
|
Chris@16
|
249 class spin_mutex
|
Chris@16
|
250 {
|
Chris@16
|
251 private:
|
Chris@16
|
252 pthread_mutex_t m_State;
|
Chris@16
|
253
|
Chris@16
|
254 public:
|
Chris@16
|
255 spin_mutex()
|
Chris@16
|
256 {
|
Chris@16
|
257 const int err = pthread_mutex_init(&m_State, NULL);
|
Chris@16
|
258 if (err != 0)
|
Chris@16
|
259 throw_exception< thread_resource_error >(err, "failed to initialize a spin mutex", "spin_mutex::spin_mutex()", __FILE__, __LINE__);
|
Chris@16
|
260 }
|
Chris@16
|
261
|
Chris@16
|
262 ~spin_mutex()
|
Chris@16
|
263 {
|
Chris@16
|
264 BOOST_VERIFY(pthread_mutex_destroy(&m_State) == 0);
|
Chris@16
|
265 }
|
Chris@16
|
266
|
Chris@16
|
267 bool try_lock()
|
Chris@16
|
268 {
|
Chris@16
|
269 const int err = pthread_mutex_trylock(&m_State);
|
Chris@16
|
270 if (err == 0)
|
Chris@16
|
271 return true;
|
Chris@16
|
272 if (err != EBUSY)
|
Chris@16
|
273 throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::try_lock()", __FILE__, __LINE__);
|
Chris@16
|
274 return false;
|
Chris@16
|
275 }
|
Chris@16
|
276
|
Chris@16
|
277 void lock()
|
Chris@16
|
278 {
|
Chris@16
|
279 const int err = pthread_mutex_lock(&m_State);
|
Chris@16
|
280 if (err != 0)
|
Chris@16
|
281 throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::lock()", __FILE__, __LINE__);
|
Chris@16
|
282 }
|
Chris@16
|
283
|
Chris@16
|
284 void unlock()
|
Chris@16
|
285 {
|
Chris@16
|
286 BOOST_VERIFY(pthread_mutex_unlock(&m_State) == 0);
|
Chris@16
|
287 }
|
Chris@16
|
288
|
Chris@16
|
289 // Non-copyable
|
Chris@16
|
290 BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&))
|
Chris@16
|
291 BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&))
|
Chris@16
|
292
|
Chris@16
|
293 private:
|
Chris@16
|
294 template< typename ExceptionT >
|
Chris@16
|
295 static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_exception(int err, const char* descr, const char* func, const char* file, int line)
|
Chris@16
|
296 {
|
Chris@16
|
297 #if !defined(BOOST_EXCEPTION_DISABLE)
|
Chris@16
|
298 boost::exception_detail::throw_exception_(ExceptionT(err, descr), func, file, line);
|
Chris@16
|
299 #else
|
Chris@16
|
300 boost::throw_exception(ExceptionT(err, descr));
|
Chris@16
|
301 #endif
|
Chris@16
|
302 }
|
Chris@16
|
303 };
|
Chris@16
|
304
|
Chris@16
|
305 #endif // defined(_POSIX_SPIN_LOCKS)
|
Chris@16
|
306
|
Chris@16
|
307 } // namespace aux
|
Chris@16
|
308
|
Chris@16
|
309 BOOST_LOG_CLOSE_NAMESPACE // namespace log
|
Chris@16
|
310
|
Chris@16
|
311 } // namespace boost
|
Chris@16
|
312
|
Chris@16
|
313 #include <boost/log/detail/footer.hpp>
|
Chris@16
|
314
|
Chris@16
|
315 #endif
|
Chris@16
|
316
|
Chris@16
|
317 #endif // BOOST_LOG_NO_THREADS
|
Chris@16
|
318
|
Chris@16
|
319 #endif // BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_
|