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