Intel(R) Threading Building Blocks Doxygen Documentation version 4.2.3
Loading...
Searching...
No Matches
x86_rtm_rw_mutex.cpp
Go to the documentation of this file.
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#include "tbb/tbb_config.h"
18#if __TBB_TSX_AVAILABLE
19#include "tbb/spin_rw_mutex.h"
20#include "tbb/tbb_machine.h"
21#include "itt_notify.h"
22#include "governor.h"
23#include "tbb/atomic.h"
24
25// __TBB_RW_MUTEX_DELAY_TEST shifts the point where flags aborting speculation are
26// added to the read-set of the operation. If 1, will add the test just before
27// the transaction is ended; this technique is called lazy subscription.
28// CAUTION: due to proven issues of lazy subscription, use of __TBB_RW_MUTEX_DELAY_TEST is discouraged!
29#ifndef __TBB_RW_MUTEX_DELAY_TEST
30 #define __TBB_RW_MUTEX_DELAY_TEST 0
31#endif
32
33#if defined(_MSC_VER) && defined(_Wp64)
34 // Workaround for overzealous compiler warnings in /Wp64 mode
35 #pragma warning (disable: 4244)
36#endif
37
38namespace tbb {
39
40namespace interface8 {
41namespace internal {
42
43// abort code for mutexes that detect a conflict with another thread.
44// value is hexadecimal
45enum {
46 speculation_transaction_aborted = 0x01,
47 speculation_can_retry = 0x02,
48 speculation_memadd_conflict = 0x04,
49 speculation_buffer_overflow = 0x08,
50 speculation_breakpoint_hit = 0x10,
51 speculation_nested_abort = 0x20,
52 speculation_xabort_mask = 0xFF000000,
53 speculation_xabort_shift = 24,
54 speculation_retry = speculation_transaction_aborted
55 | speculation_can_retry
56 | speculation_memadd_conflict
57};
58
59// maximum number of times to retry
60// TODO: experiment on retry values.
61static const int retry_threshold_read = 10;
62static const int retry_threshold_write = 10;
63
65void x86_rtm_rw_mutex::internal_release(x86_rtm_rw_mutex::scoped_lock& s) {
66 switch(s.transaction_state) {
67 case RTM_transacting_writer:
68 case RTM_transacting_reader:
69 {
70 __TBB_ASSERT(__TBB_machine_is_in_transaction(), "transaction_state && not speculating");
71#if __TBB_RW_MUTEX_DELAY_TEST
72 if(s.transaction_state == RTM_transacting_reader) {
74 } else {
76 }
77#endif
79 s.my_scoped_lock.mutex = NULL;
80 }
81 break;
82 case RTM_real_reader:
83 __TBB_ASSERT(!this->w_flag, "w_flag set but read lock acquired");
84 s.my_scoped_lock.release();
85 break;
86 case RTM_real_writer:
87 __TBB_ASSERT(this->w_flag, "w_flag unset but write lock acquired");
88 this->w_flag = false;
89 s.my_scoped_lock.release();
90 break;
91 case RTM_not_in_mutex:
92 __TBB_ASSERT(false, "RTM_not_in_mutex, but in release");
93 break;
94 default:
95 __TBB_ASSERT(false, "invalid transaction_state");
96 }
97 s.transaction_state = RTM_not_in_mutex;
98}
99
101void x86_rtm_rw_mutex::internal_acquire_writer(x86_rtm_rw_mutex::scoped_lock& s, bool only_speculate)
102{
103 __TBB_ASSERT(s.transaction_state == RTM_not_in_mutex, "scoped_lock already in transaction");
105 int num_retries = 0;
106 unsigned int abort_code;
107 do {
109 if(this->state) {
110 if(only_speculate) return;
111 do {
112 backoff.pause(); // test the spin_rw_mutex (real readers or writers)
113 } while(this->state);
114 }
115 // _xbegin returns -1 on success or the abort code, so capture it
116 if(( abort_code = __TBB_machine_begin_transaction()) == ~(unsigned int)(0) )
117 {
118 // started speculation
119#if !__TBB_RW_MUTEX_DELAY_TEST
120 if(this->state) { // add spin_rw_mutex to read-set.
121 // reader or writer grabbed the lock, so abort.
123 }
124#endif
125 s.transaction_state = RTM_transacting_writer;
126 // Don not wrap the following assignment to a function,
127 // because it can abort the transaction in debug. Need mutex for release().
128 s.my_scoped_lock.mutex = this;
129 return; // successfully started speculation
130 }
131 ++num_retries;
132 } while( (abort_code & speculation_retry) != 0 && (num_retries < retry_threshold_write) );
133 }
134
135 if(only_speculate) return; // should apply a real try_lock...
136 s.my_scoped_lock.acquire(*this, true); // kill transactional writers
137 __TBB_ASSERT(!w_flag, "After acquire for write, w_flag already true");
138 w_flag = true; // kill transactional readers
139 s.transaction_state = RTM_real_writer;
140 return;
141}
142
144// only_speculate : true if we are doing a try_acquire. If true and we fail to speculate, don't
145// really acquire the lock, return and do a try_acquire on the contained spin_rw_mutex. If
146// the lock is already held by a writer, just return.
147void x86_rtm_rw_mutex::internal_acquire_reader(x86_rtm_rw_mutex::scoped_lock& s, bool only_speculate) {
148 __TBB_ASSERT(s.transaction_state == RTM_not_in_mutex, "scoped_lock already in transaction");
150 int num_retries = 0;
151 unsigned int abort_code;
152 do {
154 // if in try_acquire, and lock is held as writer, don't attempt to speculate.
155 if(w_flag) {
156 if(only_speculate) return;
157 do {
158 backoff.pause(); // test the spin_rw_mutex (real readers or writers)
159 } while(w_flag);
160 }
161 // _xbegin returns -1 on success or the abort code, so capture it
162 if((abort_code = __TBB_machine_begin_transaction()) == ~(unsigned int)(0) )
163 {
164 // started speculation
165#if !__TBB_RW_MUTEX_DELAY_TEST
166 if(w_flag) { // add w_flag to read-set.
167 __TBB_machine_transaction_conflict_abort(); // writer grabbed the lock, so abort.
168 }
169#endif
170 s.transaction_state = RTM_transacting_reader;
171 // Don not wrap the following assignment to a function,
172 // because it can abort the transaction in debug. Need mutex for release().
173 s.my_scoped_lock.mutex = this;
174 return; // successfully started speculation
175 }
176 // fallback path
177 // retry only if there is any hope of getting into a transaction soon
178 // Retry in the following cases (from Section 8.3.5 of Intel(R)
179 // Architecture Instruction Set Extensions Programming Reference):
180 // 1. abort caused by XABORT instruction (bit 0 of EAX register is set)
181 // 2. the transaction may succeed on a retry (bit 1 of EAX register is set)
182 // 3. if another logical processor conflicted with a memory address
183 // that was part of the transaction that aborted (bit 2 of EAX register is set)
184 // That is, retry if (abort_code & 0x7) is non-zero
185 ++num_retries;
186 } while( (abort_code & speculation_retry) != 0 && (num_retries < retry_threshold_read) );
187 }
188
189 if(only_speculate) return;
190 s.my_scoped_lock.acquire( *this, false );
191 s.transaction_state = RTM_real_reader;
192}
193
195
196bool x86_rtm_rw_mutex::internal_upgrade(x86_rtm_rw_mutex::scoped_lock& s)
197{
198 switch(s.transaction_state) {
199 case RTM_real_reader: {
200 s.transaction_state = RTM_real_writer;
201 bool no_release = s.my_scoped_lock.upgrade_to_writer();
202 __TBB_ASSERT(!w_flag, "After upgrade_to_writer, w_flag already true");
203 w_flag = true;
204 return no_release;
205 }
206 case RTM_transacting_reader:
207#if !__TBB_RW_MUTEX_DELAY_TEST
208 if(this->state) { // add spin_rw_mutex to read-set.
209 // Real reader or writer holds the lock; so commit the read and re-acquire for write.
210 internal_release(s);
211 internal_acquire_writer(s);
212 return false;
213 } else
214#endif
215 {
216 s.transaction_state = RTM_transacting_writer;
217 return true;
218 }
219 default:
220 __TBB_ASSERT(false, "Invalid state for upgrade");
221 return false;
222 }
223}
224
226bool x86_rtm_rw_mutex::internal_downgrade(x86_rtm_rw_mutex::scoped_lock& s) {
227 switch(s.transaction_state) {
228 case RTM_real_writer:
229 s.transaction_state = RTM_real_reader;
230 __TBB_ASSERT(w_flag, "Before downgrade_to_reader w_flag not true");
231 w_flag = false;
232 return s.my_scoped_lock.downgrade_to_reader();
233 case RTM_transacting_writer:
234#if __TBB_RW_MUTEX_DELAY_TEST
235 if(this->state) { // a reader or writer has acquired mutex for real.
237 }
238#endif
239 s.transaction_state = RTM_transacting_reader;
240 return true;
241 default:
242 __TBB_ASSERT(false, "Invalid state for downgrade");
243 return false;
244 }
245}
246
248// There may be reader(s) which acquired the spin_rw_mutex, as well as possibly
249// transactional reader(s). If this is the case, the acquire will fail, and assigning
250// w_flag will kill the transactors. So we only assign w_flag if we have successfully
251// acquired the lock.
252bool x86_rtm_rw_mutex::internal_try_acquire_writer(x86_rtm_rw_mutex::scoped_lock& s)
253{
254 internal_acquire_writer(s, /*only_speculate=*/true);
255 if(s.transaction_state == RTM_transacting_writer) {
256 return true;
257 }
258 __TBB_ASSERT(s.transaction_state == RTM_not_in_mutex, "Trying to acquire writer which is already allocated");
259 // transacting write acquire failed. try_acquire the real mutex
260 bool result = s.my_scoped_lock.try_acquire(*this, true);
261 if(result) {
262 // only shoot down readers if we're not transacting ourselves
263 __TBB_ASSERT(!w_flag, "After try_acquire_writer, w_flag already true");
264 w_flag = true;
265 s.transaction_state = RTM_real_writer;
266 }
267 return result;
268}
269
270void x86_rtm_rw_mutex::internal_construct() {
271 ITT_SYNC_CREATE(this, _T("tbb::x86_rtm_rw_mutex"), _T(""));
272}
273
274} // namespace internal
275} // namespace interface8
276} // namespace tbb
277
278#endif /* __TBB_TSX_AVAILABLE */
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:165
static void __TBB_machine_transaction_conflict_abort()
Definition: gcc_itsx.h:114
static bool __TBB_machine_is_in_transaction()
Definition: gcc_itsx.h:69
static uint32_t __TBB_machine_begin_transaction()
Definition: gcc_itsx.h:87
static void __TBB_machine_end_transaction()
Definition: gcc_itsx.h:106
void const char const char int ITT_FORMAT __itt_group_sync s
#define _T(string_literal)
Standard Windows style macro to markup the string literals.
Definition: itt_notify.h:59
#define ITT_SYNC_CREATE(obj, type, name)
Definition: itt_notify.h:115
The graph class.
Class that implements exponential backoff.
Definition: tbb_machine.h:345
void pause()
Pause for a while.
Definition: tbb_machine.h:360
static bool speculation_enabled()
Definition: governor.h:158

Copyright © 2005-2020 Intel Corporation. All Rights Reserved.

Intel, Pentium, Intel Xeon, Itanium, Intel XScale and VTune are registered trademarks or trademarks of Intel Corporation or its subsidiaries in the United States and other countries.

* Other names and brands may be claimed as the property of others.