Home ⌂Doc Index ◂Up ▴
Intel(R) Threading Building Blocks Doxygen Documentation  version 4.2.3
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 
38 namespace tbb {
39 
40 namespace interface8 {
41 namespace internal {
42 
43 // abort code for mutexes that detect a conflict with another thread.
44 // value is hexadecimal
45 enum {
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.
61 static const int retry_threshold_read = 10;
62 static const int retry_threshold_write = 10;
63 
65 void 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 
101 void 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.
147 void 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 
196 bool 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 
226 bool 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.
252 bool 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 
270 void 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 */
void __TBB_EXPORTED_FUNC __TBB_machine_end_transaction()
void __TBB_EXPORTED_FUNC __TBB_machine_transaction_conflict_abort()
__int8 __TBB_EXPORTED_FUNC __TBB_machine_is_in_transaction()
void pause()
Pause for a while.
Definition: tbb_machine.h:360
unsigned __int32 __TBB_EXPORTED_FUNC __TBB_machine_begin_transaction()
#define _T(string_literal)
Standard Windows style macro to markup the string literals.
Definition: itt_notify.h:59
Class that implements exponential backoff.
Definition: tbb_machine.h:345
void const char const char int ITT_FORMAT __itt_group_sync s
#define ITT_SYNC_CREATE(obj, type, name)
Definition: itt_notify.h:115
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:165
The graph class.
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.