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
25namespace tbb {
26
27#if __TBB_TSX_AVAILABLE
28namespace interface8 { namespace internal {
29 class x86_rtm_rw_mutex;
30}}
31#endif
32
33class spin_rw_mutex_v3;
34typedef spin_rw_mutex_v3 spin_rw_mutex;
35
36//! Fast, unfair, spinning reader-writer lock with backoff and writer-preference
37/** @ingroup synchronization */
38class 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
68public:
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
206protected:
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
219private:
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
231namespace tbb {
232namespace 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
243typedef interface7::internal::padded_mutex<tbb::interface8::internal::x86_rtm_rw_mutex,true> speculative_spin_rw_mutex;
244#else
245typedef interface7::internal::padded_mutex<tbb::spin_rw_mutex,true> speculative_spin_rw_mutex;
246#endif
247} // namespace interface8
248
249using 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