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__x86_rtm_rw_mutex_impl_H
18#define __TBB__x86_rtm_rw_mutex_impl_H
19
20#ifndef __TBB_spin_rw_mutex_H
21#error Do not #include this internal file directly; use public TBB headers instead.
22#endif
23
24#if __TBB_TSX_AVAILABLE
25
26#include "../tbb_stddef.h"
27#include "../tbb_machine.h"
28#include "../tbb_profiling.h"
29#include "../spin_rw_mutex.h"
30
31namespace tbb {
32namespace interface8 {
33namespace internal {
34
35enum RTM_type {
36 RTM_not_in_mutex,
37 RTM_transacting_reader,
38 RTM_transacting_writer,
39 RTM_real_reader,
40 RTM_real_writer
41};
42
43static const unsigned long speculation_granularity = 64;
44
45//! Fast, unfair, spinning speculation-enabled reader-writer lock with backoff and
46// writer-preference
47/** @ingroup synchronization */
48class x86_rtm_rw_mutex: private spin_rw_mutex {
49#if __TBB_USE_X86_RTM_RW_MUTEX || __TBB_GCC_VERSION < 40000
50// bug in gcc 3.x.x causes syntax error in spite of the friend declaration below.
51// Make the scoped_lock public in that case.
52public:
53#else
54private:
55#endif
56 friend class interface7::internal::padded_mutex<x86_rtm_rw_mutex,true>;
57 class scoped_lock; // should be private
58 friend class scoped_lock;
59private:
60 //! @cond INTERNAL
61
62 //! Internal construct unacquired mutex.
63 void __TBB_EXPORTED_METHOD internal_construct();
64
65 //! Internal acquire write lock.
66 // only_speculate == true if we're doing a try_lock, else false.
67 void __TBB_EXPORTED_METHOD internal_acquire_writer(x86_rtm_rw_mutex::scoped_lock&, bool only_speculate=false);
68
69 //! Internal acquire read lock.
70 // only_speculate == true if we're doing a try_lock, else false.
71 void __TBB_EXPORTED_METHOD internal_acquire_reader(x86_rtm_rw_mutex::scoped_lock&, bool only_speculate=false);
72
73 //! Internal upgrade reader to become a writer.
74 bool __TBB_EXPORTED_METHOD internal_upgrade( x86_rtm_rw_mutex::scoped_lock& );
75
76 //! Out of line code for downgrading a writer to a reader.
77 bool __TBB_EXPORTED_METHOD internal_downgrade( x86_rtm_rw_mutex::scoped_lock& );
78
79 //! Internal try_acquire write lock.
80 bool __TBB_EXPORTED_METHOD internal_try_acquire_writer( x86_rtm_rw_mutex::scoped_lock& );
81
82 //! Internal release lock.
83 void __TBB_EXPORTED_METHOD internal_release( x86_rtm_rw_mutex::scoped_lock& );
84
85 static x86_rtm_rw_mutex* internal_get_mutex( const spin_rw_mutex::scoped_lock& lock )
86 {
87 return static_cast<x86_rtm_rw_mutex*>( lock.mutex );
88 }
89 static void internal_set_mutex( spin_rw_mutex::scoped_lock& lock, spin_rw_mutex* mtx )
90 {
91 lock.mutex = mtx;
92 }
93 //! @endcond
94public:
95 //! Construct unacquired mutex.
96 x86_rtm_rw_mutex() {
97 w_flag = false;
98#if TBB_USE_THREADING_TOOLS
99 internal_construct();
100#endif
101 }
102
103#if TBB_USE_ASSERT
104 //! Empty destructor.
105 ~x86_rtm_rw_mutex() {}
106#endif /* TBB_USE_ASSERT */
107
108 // Mutex traits
109 static const bool is_rw_mutex = true;
110 static const bool is_recursive_mutex = false;
111 static const bool is_fair_mutex = false;
112
113#if __TBB_USE_X86_RTM_RW_MUTEX || __TBB_GCC_VERSION < 40000
114#else
115 // by default we will not provide the scoped_lock interface. The user
116 // should use the padded version of the mutex. scoped_lock is used in
117 // padded_mutex template.
118private:
119#endif
120 //! The scoped locking pattern
121 /** It helps to avoid the common problem of forgetting to release lock.
122 It also nicely provides the "node" for queuing locks. */
123 // Speculation-enabled scoped lock for spin_rw_mutex
124 // The idea is to be able to reuse the acquire/release methods of spin_rw_mutex
125 // and its scoped lock wherever possible. The only way to use a speculative lock is to use
126 // a scoped_lock. (because transaction_state must be local)
127
128 class scoped_lock : tbb::internal::no_copy {
129 friend class x86_rtm_rw_mutex;
130 spin_rw_mutex::scoped_lock my_scoped_lock;
131
132 RTM_type transaction_state;
133
134 public:
135 //! Construct lock that has not acquired a mutex.
136 /** Equivalent to zero-initialization of *this. */
137 scoped_lock() : my_scoped_lock(), transaction_state(RTM_not_in_mutex) {
138 }
139
140 //! Acquire lock on given mutex.
141 scoped_lock( x86_rtm_rw_mutex& m, bool write = true ) : my_scoped_lock(),
142 transaction_state(RTM_not_in_mutex) {
143 acquire(m, write);
144 }
145
146 //! Release lock (if lock is held).
147 ~scoped_lock() {
148 if(transaction_state != RTM_not_in_mutex) release();
149 }
150
151 //! Acquire lock on given mutex.
152 void acquire( x86_rtm_rw_mutex& m, bool write = true ) {
153 if( write ) m.internal_acquire_writer(*this);
154 else m.internal_acquire_reader(*this);
155 }
156
157 //! Release lock
158 void release() {
159 x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
160 __TBB_ASSERT( mutex, "lock is not acquired" );
161 __TBB_ASSERT( transaction_state!=RTM_not_in_mutex, "lock is not acquired" );
162 return mutex->internal_release(*this);
163 }
164
165 //! Upgrade reader to become a writer.
166 /** Returns whether the upgrade happened without releasing and re-acquiring the lock */
167 bool upgrade_to_writer() {
168 x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
169 __TBB_ASSERT( mutex, "lock is not acquired" );
170 if (transaction_state == RTM_transacting_writer || transaction_state == RTM_real_writer)
171 return true; // Already a writer
172 return mutex->internal_upgrade(*this);
173 }
174
175 //! Downgrade writer to become a reader.
176 /** Returns whether the downgrade happened without releasing and re-acquiring the lock */
177 bool downgrade_to_reader() {
178 x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
179 __TBB_ASSERT( mutex, "lock is not acquired" );
180 if (transaction_state == RTM_transacting_reader || transaction_state == RTM_real_reader)
181 return true; // Already a reader
182 return mutex->internal_downgrade(*this);
183 }
184
185 //! Attempt to acquire mutex.
186 /** returns true if successful. */
187 bool try_acquire( x86_rtm_rw_mutex& m, bool write = true ) {
188#if TBB_USE_ASSERT
189 x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
190 __TBB_ASSERT( !mutex, "lock is already acquired" );
191#endif
192 // have to assign m to our mutex.
193 // cannot set the mutex, because try_acquire in spin_rw_mutex depends on it being NULL.
194 if(write) return m.internal_try_acquire_writer(*this);
195 // speculatively acquire the lock. If this fails, do try_acquire on the spin_rw_mutex.
196 m.internal_acquire_reader(*this, /*only_speculate=*/true);
197 if(transaction_state == RTM_transacting_reader) return true;
198 if( my_scoped_lock.try_acquire(m, false)) {
199 transaction_state = RTM_real_reader;
200 return true;
201 }
202 return false;
203 }
204
205 }; // class x86_rtm_rw_mutex::scoped_lock
206
207 // ISO C++0x compatibility methods not provided because we cannot maintain
208 // state about whether a thread is in a transaction.
209
210private:
211 char pad[speculation_granularity-sizeof(spin_rw_mutex)]; // padding
212
213 // If true, writer holds the spin_rw_mutex.
214 tbb::atomic<bool> w_flag; // want this on a separate cache line
215
216}; // x86_rtm_rw_mutex
217
218} // namespace internal
219} // namespace interface8
220} // namespace tbb
221
222#endif /* __TBB_TSX_AVAILABLE */
223#endif /* __TBB__x86_rtm_rw_mutex_impl_H */
224