1 /*
2 Copyright (c) 2005-2021 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_detail__rtm_rw_mutex_H
18 #define __TBB_detail__rtm_rw_mutex_H
19
20 #include "_assert.h"
21 #include "_utils.h"
22 #include "../spin_rw_mutex.h"
23
24 #include <atomic>
25
26 namespace tbb {
27 namespace detail {
28
29 namespace r1 {
30 struct rtm_rw_mutex_impl;
31 }
32
33 namespace d1 {
34
35 constexpr std::size_t speculation_granularity = 64;
36 #if _MSC_VER && !defined(__INTEL_COMPILER)
37 // Suppress warning: structure was padded due to alignment specifier
38 #pragma warning (push)
39 #pragma warning (disable: 4324)
40 #endif
41
42 //! Fast, unfair, spinning speculation-enabled reader-writer lock with backoff and writer-preference
43 /** @ingroup synchronization */
alignas(max_nfs_size)44 class alignas(max_nfs_size) rtm_rw_mutex : private spin_rw_mutex {
45 friend struct r1::rtm_rw_mutex_impl;
46 private:
47 enum class rtm_type {
48 rtm_not_in_mutex,
49 rtm_transacting_reader,
50 rtm_transacting_writer,
51 rtm_real_reader,
52 rtm_real_writer
53 };
54 public:
55 //! Constructors
56 rtm_rw_mutex() noexcept : write_flag(false) {
57 create_itt_sync(this, "tbb::speculative_spin_rw_mutex", "");
58 }
59
60 //! Destructor
61 ~rtm_rw_mutex() = default;
62
63 //! Represents acquisition of a mutex.
64 class scoped_lock {
65 friend struct r1::rtm_rw_mutex_impl;
66 public:
67 //! Construct lock that has not acquired a mutex.
68 /** Equivalent to zero-initialization of *this. */
69 constexpr scoped_lock() : m_mutex(nullptr), m_transaction_state(rtm_type::rtm_not_in_mutex) {}
70
71 //! Acquire lock on given mutex.
72 scoped_lock(rtm_rw_mutex& m, bool write = true) : m_mutex(nullptr), m_transaction_state(rtm_type::rtm_not_in_mutex) {
73 acquire(m, write);
74 }
75
76 //! Release lock (if lock is held).
77 ~scoped_lock() {
78 if(m_transaction_state != rtm_type::rtm_not_in_mutex) {
79 release();
80 }
81 }
82
83 //! No Copy
84 scoped_lock(const scoped_lock&) = delete;
85 scoped_lock& operator=(const scoped_lock&) = delete;
86
87 //! Acquire lock on given mutex.
88 inline void acquire(rtm_rw_mutex& m, bool write = true);
89
90 //! Try acquire lock on given mutex.
91 inline bool try_acquire(rtm_rw_mutex& m, bool write = true);
92
93 //! Release lock
94 inline void release();
95
96 //! Upgrade reader to become a writer.
97 /** Returns whether the upgrade happened without releasing and re-acquiring the lock */
98 inline bool upgrade_to_writer();
99
100 //! Downgrade writer to become a reader.
101 inline bool downgrade_to_reader();
102
103 inline bool is_writer() const;
104 private:
105 rtm_rw_mutex* m_mutex;
106 rtm_type m_transaction_state;
107 };
108
109 //! Mutex traits
110 static constexpr bool is_rw_mutex = true;
111 static constexpr bool is_recursive_mutex = false;
112 static constexpr bool is_fair_mutex = false;
113
114 private:
115 alignas(speculation_granularity) std::atomic<bool> write_flag;
116 };
117
118 #if _MSC_VER && !defined(__INTEL_COMPILER)
119 #pragma warning (pop) // 4324 warning
120 #endif
121
122 } // namespace d1
123
124 namespace r1 {
125 //! Internal acquire write lock.
126 // only_speculate == true if we're doing a try_lock, else false.
127 TBB_EXPORT void __TBB_EXPORTED_FUNC acquire_writer(d1::rtm_rw_mutex&, d1::rtm_rw_mutex::scoped_lock&, bool only_speculate = false);
128 //! Internal acquire read lock.
129 // only_speculate == true if we're doing a try_lock, else false.
130 TBB_EXPORT void __TBB_EXPORTED_FUNC acquire_reader(d1::rtm_rw_mutex&, d1::rtm_rw_mutex::scoped_lock&, bool only_speculate = false);
131 //! Internal upgrade reader to become a writer.
132 TBB_EXPORT bool __TBB_EXPORTED_FUNC upgrade(d1::rtm_rw_mutex::scoped_lock&);
133 //! Internal downgrade writer to become a reader.
134 TBB_EXPORT bool __TBB_EXPORTED_FUNC downgrade(d1::rtm_rw_mutex::scoped_lock&);
135 //! Internal try_acquire write lock.
136 TBB_EXPORT bool __TBB_EXPORTED_FUNC try_acquire_writer(d1::rtm_rw_mutex&, d1::rtm_rw_mutex::scoped_lock&);
137 //! Internal try_acquire read lock.
138 TBB_EXPORT bool __TBB_EXPORTED_FUNC try_acquire_reader(d1::rtm_rw_mutex&, d1::rtm_rw_mutex::scoped_lock&);
139 //! Internal release lock.
140 TBB_EXPORT void __TBB_EXPORTED_FUNC release(d1::rtm_rw_mutex::scoped_lock&);
141 }
142
143 namespace d1 {
144 //! Acquire lock on given mutex.
acquire(rtm_rw_mutex & m,bool write)145 void rtm_rw_mutex::scoped_lock::acquire(rtm_rw_mutex& m, bool write) {
146 __TBB_ASSERT(!m_mutex, "lock is already acquired");
147 if (write) {
148 r1::acquire_writer(m, *this);
149 } else {
150 r1::acquire_reader(m, *this);
151 }
152 }
153
154 //! Try acquire lock on given mutex.
try_acquire(rtm_rw_mutex & m,bool write)155 bool rtm_rw_mutex::scoped_lock::try_acquire(rtm_rw_mutex& m, bool write) {
156 __TBB_ASSERT(!m_mutex, "lock is already acquired");
157 if (write) {
158 return r1::try_acquire_writer(m, *this);
159 } else {
160 return r1::try_acquire_reader(m, *this);
161 }
162 }
163
164 //! Release lock
release()165 void rtm_rw_mutex::scoped_lock::release() {
166 __TBB_ASSERT(m_mutex, "lock is not acquired");
167 __TBB_ASSERT(m_transaction_state != rtm_type::rtm_not_in_mutex, "lock is not acquired");
168 return r1::release(*this);
169 }
170
171 //! Upgrade reader to become a writer.
172 /** Returns whether the upgrade happened without releasing and re-acquiring the lock */
upgrade_to_writer()173 bool rtm_rw_mutex::scoped_lock::upgrade_to_writer() {
174 __TBB_ASSERT(m_mutex, "lock is not acquired");
175 if (m_transaction_state == rtm_type::rtm_transacting_writer || m_transaction_state == rtm_type::rtm_real_writer) {
176 return true; // Already a writer
177 }
178 return r1::upgrade(*this);
179 }
180
181 //! Downgrade writer to become a reader.
downgrade_to_reader()182 bool rtm_rw_mutex::scoped_lock::downgrade_to_reader() {
183 __TBB_ASSERT(m_mutex, "lock is not acquired");
184 if (m_transaction_state == rtm_type::rtm_transacting_reader || m_transaction_state == rtm_type::rtm_real_reader) {
185 return true; // Already a reader
186 }
187 return r1::downgrade(*this);
188 }
189
is_writer()190 bool rtm_rw_mutex::scoped_lock::is_writer() const {
191 __TBB_ASSERT(m_mutex, "lock is not acquired");
192 return m_transaction_state == rtm_type::rtm_transacting_writer || m_transaction_state == rtm_type::rtm_real_writer;
193 }
194
195 #if TBB_USE_PROFILING_TOOLS
set_name(rtm_rw_mutex & obj,const char * name)196 inline void set_name(rtm_rw_mutex& obj, const char* name) {
197 itt_set_sync_name(&obj, name);
198 }
199 #if (_WIN32||_WIN64)
set_name(rtm_rw_mutex & obj,const wchar_t * name)200 inline void set_name(rtm_rw_mutex& obj, const wchar_t* name) {
201 itt_set_sync_name(&obj, name);
202 }
203 #endif // WIN
204 #else
set_name(rtm_rw_mutex &,const char *)205 inline void set_name(rtm_rw_mutex&, const char*) {}
206 #if (_WIN32||_WIN64)
set_name(rtm_rw_mutex &,const wchar_t *)207 inline void set_name(rtm_rw_mutex&, const wchar_t*) {}
208 #endif // WIN
209 #endif
210
211 } // namespace d1
212 } // namespace detail
213 } // namespace tbb
214
215 #endif // __TBB_detail__rtm_rw_mutex_H
216