1 | /* |
2 | Copyright (c) 2005-2019 Intel Corporation |
3 | |
4 | Licensed under the Apache License, Version 2.0 (the "License"); |
5 | you may not use this file except in compliance with the License. |
6 | You may obtain a copy of the License at |
7 | |
8 | http://www.apache.org/licenses/LICENSE-2.0 |
9 | |
10 | Unless required by applicable law or agreed to in writing, software |
11 | distributed under the License is distributed on an "AS IS" BASIS, |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | See the License for the specific language governing permissions and |
14 | limitations under the License. |
15 | */ |
16 | |
17 | #ifndef __TBB_spin_mutex_H |
18 | #define __TBB_spin_mutex_H |
19 | |
20 | #include <cstddef> |
21 | #include <new> |
22 | #include "aligned_space.h" |
23 | #include "tbb_stddef.h" |
24 | #include "tbb_machine.h" |
25 | #include "tbb_profiling.h" |
26 | #include "internal/_mutex_padding.h" |
27 | |
28 | namespace tbb { |
29 | |
30 | //! A lock that occupies a single byte. |
31 | /** A spin_mutex is a spin mutex that fits in a single byte. |
32 | It should be used only for locking short critical sections |
33 | (typically less than 20 instructions) when fairness is not an issue. |
34 | If zero-initialized, the mutex is considered unheld. |
35 | @ingroup synchronization */ |
36 | class spin_mutex : internal::mutex_copy_deprecated_and_disabled { |
37 | //! 0 if lock is released, 1 if lock is acquired. |
38 | __TBB_atomic_flag flag; |
39 | |
40 | public: |
41 | //! Construct unacquired lock. |
42 | /** Equivalent to zero-initialization of *this. */ |
43 | spin_mutex() : flag(0) { |
44 | #if TBB_USE_THREADING_TOOLS |
45 | internal_construct(); |
46 | #endif |
47 | } |
48 | |
49 | //! Represents acquisition of a mutex. |
50 | class scoped_lock : internal::no_copy { |
51 | private: |
52 | //! Points to currently held mutex, or NULL if no lock is held. |
53 | spin_mutex* my_mutex; |
54 | |
55 | //! Value to store into spin_mutex::flag to unlock the mutex. |
56 | /** This variable is no longer used. Instead, 0 and 1 are used to |
57 | represent that the lock is free and acquired, respectively. |
58 | We keep the member variable here to ensure backward compatibility */ |
59 | __TBB_Flag my_unlock_value; |
60 | |
61 | //! Like acquire, but with ITT instrumentation. |
62 | void __TBB_EXPORTED_METHOD internal_acquire( spin_mutex& m ); |
63 | |
64 | //! Like try_acquire, but with ITT instrumentation. |
65 | bool __TBB_EXPORTED_METHOD internal_try_acquire( spin_mutex& m ); |
66 | |
67 | //! Like release, but with ITT instrumentation. |
68 | void __TBB_EXPORTED_METHOD internal_release(); |
69 | |
70 | friend class spin_mutex; |
71 | |
72 | public: |
73 | //! Construct without acquiring a mutex. |
74 | scoped_lock() : my_mutex(NULL), my_unlock_value(0) {} |
75 | |
76 | //! Construct and acquire lock on a mutex. |
77 | scoped_lock( spin_mutex& m ) : my_unlock_value(0) { |
78 | internal::suppress_unused_warning(my_unlock_value); |
79 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
80 | my_mutex=NULL; |
81 | internal_acquire(m); |
82 | #else |
83 | my_mutex=&m; |
84 | __TBB_LockByte(m.flag); |
85 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT*/ |
86 | } |
87 | |
88 | //! Acquire lock. |
89 | void acquire( spin_mutex& m ) { |
90 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
91 | internal_acquire(m); |
92 | #else |
93 | my_mutex = &m; |
94 | __TBB_LockByte(m.flag); |
95 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT*/ |
96 | } |
97 | |
98 | //! Try acquiring lock (non-blocking) |
99 | /** Return true if lock acquired; false otherwise. */ |
100 | bool try_acquire( spin_mutex& m ) { |
101 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
102 | return internal_try_acquire(m); |
103 | #else |
104 | bool result = __TBB_TryLockByte(m.flag); |
105 | if( result ) |
106 | my_mutex = &m; |
107 | return result; |
108 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT*/ |
109 | } |
110 | |
111 | //! Release lock |
112 | void release() { |
113 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
114 | internal_release(); |
115 | #else |
116 | __TBB_UnlockByte(my_mutex->flag); |
117 | my_mutex = NULL; |
118 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ |
119 | } |
120 | |
121 | //! Destroy lock. If holding a lock, releases the lock first. |
122 | ~scoped_lock() { |
123 | if( my_mutex ) { |
124 | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT |
125 | internal_release(); |
126 | #else |
127 | __TBB_UnlockByte(my_mutex->flag); |
128 | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ |
129 | } |
130 | } |
131 | }; |
132 | |
133 | //! Internal constructor with ITT instrumentation. |
134 | void __TBB_EXPORTED_METHOD internal_construct(); |
135 | |
136 | // Mutex traits |
137 | static const bool is_rw_mutex = false; |
138 | static const bool is_recursive_mutex = false; |
139 | static const bool is_fair_mutex = false; |
140 | |
141 | // ISO C++0x compatibility methods |
142 | |
143 | //! Acquire lock |
144 | void lock() { |
145 | #if TBB_USE_THREADING_TOOLS |
146 | aligned_space<scoped_lock> tmp; |
147 | new(tmp.begin()) scoped_lock(*this); |
148 | #else |
149 | __TBB_LockByte(flag); |
150 | #endif /* TBB_USE_THREADING_TOOLS*/ |
151 | } |
152 | |
153 | //! Try acquiring lock (non-blocking) |
154 | /** Return true if lock acquired; false otherwise. */ |
155 | bool try_lock() { |
156 | #if TBB_USE_THREADING_TOOLS |
157 | aligned_space<scoped_lock> tmp; |
158 | return (new(tmp.begin()) scoped_lock)->internal_try_acquire(*this); |
159 | #else |
160 | return __TBB_TryLockByte(flag); |
161 | #endif /* TBB_USE_THREADING_TOOLS*/ |
162 | } |
163 | |
164 | //! Release lock |
165 | void unlock() { |
166 | #if TBB_USE_THREADING_TOOLS |
167 | aligned_space<scoped_lock> tmp; |
168 | scoped_lock& s = *tmp.begin(); |
169 | s.my_mutex = this; |
170 | s.internal_release(); |
171 | #else |
172 | __TBB_UnlockByte(flag); |
173 | #endif /* TBB_USE_THREADING_TOOLS */ |
174 | } |
175 | |
176 | friend class scoped_lock; |
177 | }; // end of spin_mutex |
178 | |
179 | __TBB_DEFINE_PROFILING_SET_NAME(spin_mutex) |
180 | |
181 | } // namespace tbb |
182 | |
183 | #if ( __TBB_x86_32 || __TBB_x86_64 ) |
184 | #include "internal/_x86_eliding_mutex_impl.h" |
185 | #endif |
186 | |
187 | namespace tbb { |
188 | //! A cross-platform spin mutex with speculative lock acquisition. |
189 | /** On platforms with proper HW support, this lock may speculatively execute |
190 | its critical sections, using HW mechanisms to detect real data races and |
191 | ensure atomicity of the critical sections. In particular, it uses |
192 | Intel(R) Transactional Synchronization Extensions (Intel(R) TSX). |
193 | Without such HW support, it behaves like a spin_mutex. |
194 | It should be used for locking short critical sections where the lock is |
195 | contended but the data it protects are not. If zero-initialized, the |
196 | mutex is considered unheld. |
197 | @ingroup synchronization */ |
198 | |
199 | #if ( __TBB_x86_32 || __TBB_x86_64 ) |
200 | typedef interface7::internal::padded_mutex<interface7::internal::x86_eliding_mutex,false> speculative_spin_mutex; |
201 | #else |
202 | typedef interface7::internal::padded_mutex<spin_mutex,false> speculative_spin_mutex; |
203 | #endif |
204 | __TBB_DEFINE_PROFILING_SET_NAME(speculative_spin_mutex) |
205 | |
206 | } // namespace tbb |
207 | |
208 | #endif /* __TBB_spin_mutex_H */ |
209 | |