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