| 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_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. | 
| 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. | 
| 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. */ | 
| 93 |         scoped_lock() : mutex(NULL), is_writer(false) {} | 
| 94 |  | 
| 95 |         //! Acquire lock on given mutex. | 
| 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). | 
| 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 */ | 
| 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. | 
| 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. | 
| 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 | 
| 180 |     void lock() {internal_acquire_writer();} | 
| 181 |  | 
| 182 |     //! Try acquiring writer lock (non-blocking) | 
| 183 |     /** Return true if lock acquired; false otherwise. */ | 
| 184 |     bool try_lock() {return internal_try_acquire_writer();} | 
| 185 |  | 
| 186 |     //! Release lock | 
| 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 | 
| 200 |     void lock_read() {internal_acquire_reader();} | 
| 201 |  | 
| 202 |     //! Try acquiring reader lock (non-blocking) | 
| 203 |     /** Return true if reader lock acquired; false otherwise. */ | 
| 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 |  |