1 /* 2 Copyright (c) 2005-2020 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_rw_mutex_H 18 #define __TBB_spin_rw_mutex_H 19 20 #include "tbb_stddef.h" 21 #include "tbb_machine.h" 22 #include "tbb_profiling.h" 23 #include "internal/_mutex_padding.h" 24 25 namespace tbb { 26 27 #if __TBB_TSX_AVAILABLE 28 namespace interface8 { namespace internal { 29 class x86_rtm_rw_mutex; 30 }} 31 #endif 32 33 class spin_rw_mutex_v3; 34 typedef spin_rw_mutex_v3 spin_rw_mutex; 35 36 //! Fast, unfair, spinning reader-writer lock with backoff and writer-preference 37 /** @ingroup synchronization */ 38 class spin_rw_mutex_v3 : internal::mutex_copy_deprecated_and_disabled { 39 //! @cond INTERNAL 40 41 //! Internal acquire write lock. 42 bool __TBB_EXPORTED_METHOD internal_acquire_writer(); 43 44 //! Out of line code for releasing a write lock. 45 /** This code has debug checking and instrumentation for Intel(R) Thread Checker and Intel(R) Thread Profiler. */ 46 void __TBB_EXPORTED_METHOD internal_release_writer(); 47 48 //! Internal acquire read lock. 49 void __TBB_EXPORTED_METHOD internal_acquire_reader(); 50 51 //! Internal upgrade reader to become a writer. 52 bool __TBB_EXPORTED_METHOD internal_upgrade(); 53 54 //! Out of line code for downgrading a writer to a reader. 55 /** This code has debug checking and instrumentation for Intel(R) Thread Checker and Intel(R) Thread Profiler. */ 56 void __TBB_EXPORTED_METHOD internal_downgrade(); 57 58 //! Internal release read lock. 59 void __TBB_EXPORTED_METHOD internal_release_reader(); 60 61 //! Internal try_acquire write lock. 62 bool __TBB_EXPORTED_METHOD internal_try_acquire_writer(); 63 64 //! Internal try_acquire read lock. 65 bool __TBB_EXPORTED_METHOD internal_try_acquire_reader(); 66 67 //! @endcond 68 public: 69 //! Construct unacquired mutex. spin_rw_mutex_v3()70 spin_rw_mutex_v3() : state(0) { 71 #if TBB_USE_THREADING_TOOLS 72 internal_construct(); 73 #endif 74 } 75 76 #if TBB_USE_ASSERT 77 //! Destructor asserts if the mutex is acquired, i.e. state is zero. ~spin_rw_mutex_v3()78 ~spin_rw_mutex_v3() { 79 __TBB_ASSERT( !state, "destruction of an acquired mutex"); 80 }; 81 #endif /* TBB_USE_ASSERT */ 82 83 //! The scoped locking pattern 84 /** It helps to avoid the common problem of forgetting to release lock. 85 It also nicely provides the "node" for queuing locks. */ 86 class scoped_lock : internal::no_copy { 87 #if __TBB_TSX_AVAILABLE 88 friend class tbb::interface8::internal::x86_rtm_rw_mutex; 89 #endif 90 public: 91 //! Construct lock that has not acquired a mutex. 92 /** Equivalent to zero-initialization of *this. */ scoped_lock()93 scoped_lock() : mutex(NULL), is_writer(false) {} 94 95 //! Acquire lock on given mutex. mutex(NULL)96 scoped_lock( spin_rw_mutex& m, bool write = true ) : mutex(NULL) { 97 acquire(m, write); 98 } 99 100 //! Release lock (if lock is held). ~scoped_lock()101 ~scoped_lock() { 102 if( mutex ) release(); 103 } 104 105 //! Acquire lock on given mutex. 106 void acquire( spin_rw_mutex& m, bool write = true ) { 107 __TBB_ASSERT( !mutex, "holding mutex already" ); 108 is_writer = write; 109 mutex = &m; 110 if( write ) mutex->internal_acquire_writer(); 111 else mutex->internal_acquire_reader(); 112 } 113 114 //! Upgrade reader to become a writer. 115 /** Returns whether the upgrade happened without releasing and re-acquiring the lock */ upgrade_to_writer()116 bool upgrade_to_writer() { 117 __TBB_ASSERT( mutex, "mutex is not acquired" ); 118 if (is_writer) return true; // Already a writer 119 is_writer = true; 120 return mutex->internal_upgrade(); 121 } 122 123 //! Release lock. release()124 void release() { 125 __TBB_ASSERT( mutex, "mutex is not acquired" ); 126 spin_rw_mutex *m = mutex; 127 mutex = NULL; 128 #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT 129 if( is_writer ) m->internal_release_writer(); 130 else m->internal_release_reader(); 131 #else 132 if( is_writer ) __TBB_AtomicAND( &m->state, READERS ); 133 else __TBB_FetchAndAddWrelease( &m->state, -(intptr_t)ONE_READER); 134 #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ 135 } 136 137 //! Downgrade writer to become a reader. downgrade_to_reader()138 bool downgrade_to_reader() { 139 __TBB_ASSERT( mutex, "mutex is not acquired" ); 140 if (!is_writer) return true; // Already a reader 141 #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT 142 mutex->internal_downgrade(); 143 #else 144 __TBB_FetchAndAddW( &mutex->state, ((intptr_t)ONE_READER-WRITER)); 145 #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ 146 is_writer = false; 147 return true; 148 } 149 150 //! Try acquire lock on given mutex. 151 bool try_acquire( spin_rw_mutex& m, bool write = true ) { 152 __TBB_ASSERT( !mutex, "holding mutex already" ); 153 bool result; 154 is_writer = write; 155 result = write? m.internal_try_acquire_writer() 156 : m.internal_try_acquire_reader(); 157 if( result ) 158 mutex = &m; 159 return result; 160 } 161 162 protected: 163 164 //! The pointer to the current mutex that is held, or NULL if no mutex is held. 165 spin_rw_mutex* mutex; 166 167 //! If mutex!=NULL, then is_writer is true if holding a writer lock, false if holding a reader lock. 168 /** Not defined if not holding a lock. */ 169 bool is_writer; 170 }; 171 172 // Mutex traits 173 static const bool is_rw_mutex = true; 174 static const bool is_recursive_mutex = false; 175 static const bool is_fair_mutex = false; 176 177 // ISO C++0x compatibility methods 178 179 //! Acquire writer lock lock()180 void lock() {internal_acquire_writer();} 181 182 //! Try acquiring writer lock (non-blocking) 183 /** Return true if lock acquired; false otherwise. */ try_lock()184 bool try_lock() {return internal_try_acquire_writer();} 185 186 //! Release lock unlock()187 void unlock() { 188 #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT 189 if( state&WRITER ) internal_release_writer(); 190 else internal_release_reader(); 191 #else 192 if( state&WRITER ) __TBB_AtomicAND( &state, READERS ); 193 else __TBB_FetchAndAddWrelease( &state, -(intptr_t)ONE_READER); 194 #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ 195 } 196 197 // Methods for reader locks that resemble ISO C++0x compatibility methods. 198 199 //! Acquire reader lock lock_read()200 void lock_read() {internal_acquire_reader();} 201 202 //! Try acquiring reader lock (non-blocking) 203 /** Return true if reader lock acquired; false otherwise. */ try_lock_read()204 bool try_lock_read() {return internal_try_acquire_reader();} 205 206 protected: 207 typedef intptr_t state_t; 208 static const state_t WRITER = 1; 209 static const state_t WRITER_PENDING = 2; 210 static const state_t READERS = ~(WRITER | WRITER_PENDING); 211 static const state_t ONE_READER = 4; 212 static const state_t BUSY = WRITER | READERS; 213 //! State of lock 214 /** Bit 0 = writer is holding lock 215 Bit 1 = request by a writer to acquire lock (hint to readers to wait) 216 Bit 2..N = number of readers holding lock */ 217 state_t state; 218 219 private: 220 void __TBB_EXPORTED_METHOD internal_construct(); 221 }; 222 223 __TBB_DEFINE_PROFILING_SET_NAME(spin_rw_mutex) 224 225 } // namespace tbb 226 227 #if __TBB_TSX_AVAILABLE 228 #include "internal/_x86_rtm_rw_mutex_impl.h" 229 #endif 230 231 namespace tbb { 232 namespace interface8 { 233 //! A cross-platform spin reader/writer mutex with speculative lock acquisition. 234 /** On platforms with proper HW support, this lock may speculatively execute 235 its critical sections, using HW mechanisms to detect real data races and 236 ensure atomicity of the critical sections. In particular, it uses 237 Intel(R) Transactional Synchronization Extensions (Intel(R) TSX). 238 Without such HW support, it behaves like a spin_rw_mutex. 239 It should be used for locking short critical sections where the lock is 240 contended but the data it protects are not. 241 @ingroup synchronization */ 242 #if __TBB_TSX_AVAILABLE 243 typedef interface7::internal::padded_mutex<tbb::interface8::internal::x86_rtm_rw_mutex,true> speculative_spin_rw_mutex; 244 #else 245 typedef interface7::internal::padded_mutex<tbb::spin_rw_mutex,true> speculative_spin_rw_mutex; 246 #endif 247 } // namespace interface8 248 249 using interface8::speculative_spin_rw_mutex; 250 __TBB_DEFINE_PROFILING_SET_NAME(speculative_spin_rw_mutex) 251 } // namespace tbb 252 #endif /* __TBB_spin_rw_mutex_H */ 253