| 1 | /* |
| 2 | Copyright 2005-2013 Intel Corporation. All Rights Reserved. |
| 3 | |
| 4 | This file is part of Threading Building Blocks. |
| 5 | |
| 6 | Threading Building Blocks is free software; you can redistribute it |
| 7 | and/or modify it under the terms of the GNU General Public License |
| 8 | version 2 as published by the Free Software Foundation. |
| 9 | |
| 10 | Threading Building Blocks is distributed in the hope that it will be |
| 11 | useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| 12 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | GNU General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU General Public License |
| 16 | along with Threading Building Blocks; if not, write to the Free Software |
| 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 18 | |
| 19 | As a special exception, you may use this file as part of a free software |
| 20 | library without restriction. Specifically, if other files instantiate |
| 21 | templates or use macros or inline functions from this file, or you compile |
| 22 | this file and link it with other files to produce an executable, this |
| 23 | file does not by itself cause the resulting executable to be covered by |
| 24 | the GNU General Public License. This exception does not however |
| 25 | invalidate any other reasons why the executable file might be covered by |
| 26 | the GNU General Public License. |
| 27 | */ |
| 28 | |
| 29 | #ifndef __TBB_spin_mutex_H |
| 30 | #define __TBB_spin_mutex_H |
| 31 | |
| 32 | #include <cstddef> |
| 33 | #include <new> |
| 34 | #include "aligned_space.h" |
| 35 | #include "tbb_stddef.h" |
| 36 | #include "tbb_machine.h" |
| 37 | #include "tbb_profiling.h" |
| 38 | #include "internal/_mutex_padding.h" |
| 39 | |
| 40 | namespace tbb { |
| 41 | |
| 42 | //! A lock that occupies a single byte. |
| 43 | /** A spin_mutex is a spin mutex that fits in a single byte. |
| 44 | It should be used only for locking short critical sections |
| 45 | (typically less than 20 instructions) when fairness is not an issue. |
| 46 | If zero-initialized, the mutex is considered unheld. |
| 47 | @ingroup synchronization */ |
| 48 | class spin_mutex { |
| 49 | //! 0 if lock is released, 1 if lock is acquired. |
| 50 | __TBB_atomic_flag flag; |
| 51 | |
| 52 | public: |
| 53 | //! Construct unacquired lock. |
| 54 | /** Equivalent to zero-initialization of *this. */ |
| 55 | spin_mutex() : flag(0) { |
| 56 | #if TBB_USE_THREADING_TOOLS |
| 57 | internal_construct(); |
| 58 | #endif |
| 59 | } |
| 60 | |
| 61 | //! Represents acquisition of a mutex. |
| 62 | class scoped_lock : internal::no_copy { |
| 63 | private: |
| 64 | //! Points to currently held mutex, or NULL if no lock is held. |
| 65 | spin_mutex* my_mutex; |
| 66 | |
| 67 | //! Value to store into spin_mutex::flag to unlock the mutex. |
| 68 | /** This variable is no longer used. Instead, 0 and 1 are used to |
| 69 | represent that the lock is free and acquired, respectively. |
| 70 | We keep the member variable here to ensure backward compatibility */ |
| 71 | __TBB_Flag my_unlock_value; |
| 72 | |
| 73 | //! Like acquire, but with ITT instrumentation. |
| 74 | void __TBB_EXPORTED_METHOD internal_acquire( spin_mutex& m ); |
| 75 | |
| 76 | //! Like try_acquire, but with ITT instrumentation. |
| 77 | bool __TBB_EXPORTED_METHOD internal_try_acquire( spin_mutex& m ); |
| 78 | |
| 79 | //! Like release, but with ITT instrumentation. |
| 80 | void __TBB_EXPORTED_METHOD internal_release(); |
| 81 | |
| 82 | friend class spin_mutex; |
| 83 | |
| 84 | public: |
| 85 | //! Construct without acquiring a mutex. |
| 86 | scoped_lock() : my_mutex(NULL), my_unlock_value(0) {} |
| 87 | |
| 88 | //! Construct and acquire lock on a mutex. |
| 89 | scoped_lock( spin_mutex& m ) : my_unlock_value(0) { |
| 90 | internal::suppress_unused_warning(my_unlock_value); |
| 91 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
| 92 | my_mutex=NULL; |
| 93 | internal_acquire(m); |
| 94 | #else |
| 95 | my_mutex=&m; |
| 96 | __TBB_LockByte(m.flag); |
| 97 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT*/ |
| 98 | } |
| 99 | |
| 100 | //! Acquire lock. |
| 101 | void acquire( spin_mutex& m ) { |
| 102 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
| 103 | internal_acquire(m); |
| 104 | #else |
| 105 | my_mutex = &m; |
| 106 | __TBB_LockByte(m.flag); |
| 107 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT*/ |
| 108 | } |
| 109 | |
| 110 | //! Try acquiring lock (non-blocking) |
| 111 | /** Return true if lock acquired; false otherwise. */ |
| 112 | bool try_acquire( spin_mutex& m ) { |
| 113 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
| 114 | return internal_try_acquire(m); |
| 115 | #else |
| 116 | bool result = __TBB_TryLockByte(m.flag); |
| 117 | if( result ) |
| 118 | my_mutex = &m; |
| 119 | return result; |
| 120 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT*/ |
| 121 | } |
| 122 | |
| 123 | //! Release lock |
| 124 | void release() { |
| 125 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
| 126 | internal_release(); |
| 127 | #else |
| 128 | __TBB_UnlockByte(my_mutex->flag); |
| 129 | my_mutex = NULL; |
| 130 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ |
| 131 | } |
| 132 | |
| 133 | //! Destroy lock. If holding a lock, releases the lock first. |
| 134 | ~scoped_lock() { |
| 135 | if( my_mutex ) { |
| 136 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
| 137 | internal_release(); |
| 138 | #else |
| 139 | __TBB_UnlockByte(my_mutex->flag); |
| 140 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ |
| 141 | } |
| 142 | } |
| 143 | }; |
| 144 | |
| 145 | //! Internal constructor with ITT instrumentation. |
| 146 | void __TBB_EXPORTED_METHOD internal_construct(); |
| 147 | |
| 148 | // Mutex traits |
| 149 | static const bool is_rw_mutex = false; |
| 150 | static const bool is_recursive_mutex = false; |
| 151 | static const bool is_fair_mutex = false; |
| 152 | |
| 153 | // ISO C++0x compatibility methods |
| 154 | |
| 155 | //! Acquire lock |
| 156 | void lock() { |
| 157 | #if TBB_USE_THREADING_TOOLS |
| 158 | aligned_space<scoped_lock,1> tmp; |
| 159 | new(tmp.begin()) scoped_lock(*this); |
| 160 | #else |
| 161 | __TBB_LockByte(flag); |
| 162 | #endif /* TBB_USE_THREADING_TOOLS*/ |
| 163 | } |
| 164 | |
| 165 | //! Try acquiring lock (non-blocking) |
| 166 | /** Return true if lock acquired; false otherwise. */ |
| 167 | bool try_lock() { |
| 168 | #if TBB_USE_THREADING_TOOLS |
| 169 | aligned_space<scoped_lock,1> tmp; |
| 170 | return (new(tmp.begin()) scoped_lock)->internal_try_acquire(*this); |
| 171 | #else |
| 172 | return __TBB_TryLockByte(flag); |
| 173 | #endif /* TBB_USE_THREADING_TOOLS*/ |
| 174 | } |
| 175 | |
| 176 | //! Release lock |
| 177 | void unlock() { |
| 178 | #if TBB_USE_THREADING_TOOLS |
| 179 | aligned_space<scoped_lock,1> tmp; |
| 180 | scoped_lock& s = *tmp.begin(); |
| 181 | s.my_mutex = this; |
| 182 | s.internal_release(); |
| 183 | #else |
| 184 | __TBB_store_with_release(flag, 0); |
| 185 | #endif /* TBB_USE_THREADING_TOOLS */ |
| 186 | } |
| 187 | |
| 188 | friend class scoped_lock; |
| 189 | }; // end of spin_mutex |
| 190 | |
| 191 | __TBB_DEFINE_PROFILING_SET_NAME(spin_mutex) |
| 192 | |
| 193 | } // namespace tbb |
| 194 | |
| 195 | #if ( __TBB_x86_32 || __TBB_x86_64 ) |
| 196 | #include "internal/_x86_eliding_mutex_impl.h" |
| 197 | #endif |
| 198 | |
| 199 | namespace tbb { |
| 200 | //! A cross-platform spin mutex with speculative lock acquisition. |
| 201 | /** On platforms with proper HW support, this lock may speculatively execute |
| 202 | its critical sections, using HW mechanisms to detect real data races and |
| 203 | ensure atomicity of the critical sections. In particular, it uses |
| 204 | Intel(R) Transactional Synchronization Extensions (Intel(R) TSX). |
| 205 | Without such HW support, it behaves like a spin_mutex. |
| 206 | It should be used for locking short critical sections where the lock is |
| 207 | contended but the data it protects are not. If zero-initialized, the |
| 208 | mutex is considered unheld. |
| 209 | @ingroup synchronization */ |
| 210 | |
| 211 | #if ( __TBB_x86_32 || __TBB_x86_64 ) |
| 212 | typedef interface7::internal::padded_mutex<interface7::internal::x86_eliding_mutex> speculative_spin_mutex; |
| 213 | #else |
| 214 | typedef interface7::internal::padded_mutex<spin_mutex> speculative_spin_mutex; |
| 215 | #endif |
| 216 | __TBB_DEFINE_PROFILING_SET_NAME(speculative_spin_mutex) |
| 217 | |
| 218 | } // namespace tbb |
| 219 | |
| 220 | #endif /* __TBB_spin_mutex_H */ |
| 221 | |