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