_x86_rtm_rw_mutex_impl.h 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. Copyright (c) 2005-2020 Intel Corporation
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. #ifndef __TBB__x86_rtm_rw_mutex_impl_H
  14. #define __TBB__x86_rtm_rw_mutex_impl_H
  15. #ifndef __TBB_spin_rw_mutex_H
  16. #error Do not #include this internal file directly; use public TBB headers instead.
  17. #endif
  18. #if __TBB_TSX_AVAILABLE
  19. #include "../tbb_stddef.h"
  20. #include "../tbb_machine.h"
  21. #include "../tbb_profiling.h"
  22. #include "../spin_rw_mutex.h"
  23. namespace tbb {
  24. namespace interface8 {
  25. namespace internal {
  26. enum RTM_type {
  27. RTM_not_in_mutex,
  28. RTM_transacting_reader,
  29. RTM_transacting_writer,
  30. RTM_real_reader,
  31. RTM_real_writer
  32. };
  33. static const unsigned long speculation_granularity = 64;
  34. //! Fast, unfair, spinning speculation-enabled reader-writer lock with backoff and
  35. // writer-preference
  36. /** @ingroup synchronization */
  37. class x86_rtm_rw_mutex: private spin_rw_mutex {
  38. #if __TBB_USE_X86_RTM_RW_MUTEX || __TBB_GCC_VERSION < 40000
  39. // bug in gcc 3.x.x causes syntax error in spite of the friend declaration below.
  40. // Make the scoped_lock public in that case.
  41. public:
  42. #else
  43. private:
  44. #endif
  45. friend class interface7::internal::padded_mutex<x86_rtm_rw_mutex,true>;
  46. class scoped_lock; // should be private
  47. friend class scoped_lock;
  48. private:
  49. //! @cond INTERNAL
  50. //! Internal construct unacquired mutex.
  51. void __TBB_EXPORTED_METHOD internal_construct();
  52. //! Internal acquire write lock.
  53. // only_speculate == true if we're doing a try_lock, else false.
  54. void __TBB_EXPORTED_METHOD internal_acquire_writer(x86_rtm_rw_mutex::scoped_lock&, bool only_speculate=false);
  55. //! Internal acquire read lock.
  56. // only_speculate == true if we're doing a try_lock, else false.
  57. void __TBB_EXPORTED_METHOD internal_acquire_reader(x86_rtm_rw_mutex::scoped_lock&, bool only_speculate=false);
  58. //! Internal upgrade reader to become a writer.
  59. bool __TBB_EXPORTED_METHOD internal_upgrade( x86_rtm_rw_mutex::scoped_lock& );
  60. //! Out of line code for downgrading a writer to a reader.
  61. bool __TBB_EXPORTED_METHOD internal_downgrade( x86_rtm_rw_mutex::scoped_lock& );
  62. //! Internal try_acquire write lock.
  63. bool __TBB_EXPORTED_METHOD internal_try_acquire_writer( x86_rtm_rw_mutex::scoped_lock& );
  64. //! Internal release lock.
  65. void __TBB_EXPORTED_METHOD internal_release( x86_rtm_rw_mutex::scoped_lock& );
  66. static x86_rtm_rw_mutex* internal_get_mutex( const spin_rw_mutex::scoped_lock& lock )
  67. {
  68. return static_cast<x86_rtm_rw_mutex*>( lock.mutex );
  69. }
  70. static void internal_set_mutex( spin_rw_mutex::scoped_lock& lock, spin_rw_mutex* mtx )
  71. {
  72. lock.mutex = mtx;
  73. }
  74. //! @endcond
  75. public:
  76. //! Construct unacquired mutex.
  77. x86_rtm_rw_mutex() {
  78. w_flag = false;
  79. #if TBB_USE_THREADING_TOOLS
  80. internal_construct();
  81. #endif
  82. }
  83. #if TBB_USE_ASSERT
  84. //! Empty destructor.
  85. ~x86_rtm_rw_mutex() {}
  86. #endif /* TBB_USE_ASSERT */
  87. // Mutex traits
  88. static const bool is_rw_mutex = true;
  89. static const bool is_recursive_mutex = false;
  90. static const bool is_fair_mutex = false;
  91. #if __TBB_USE_X86_RTM_RW_MUTEX || __TBB_GCC_VERSION < 40000
  92. #else
  93. // by default we will not provide the scoped_lock interface. The user
  94. // should use the padded version of the mutex. scoped_lock is used in
  95. // padded_mutex template.
  96. private:
  97. #endif
  98. //! The scoped locking pattern
  99. /** It helps to avoid the common problem of forgetting to release lock.
  100. It also nicely provides the "node" for queuing locks. */
  101. // Speculation-enabled scoped lock for spin_rw_mutex
  102. // The idea is to be able to reuse the acquire/release methods of spin_rw_mutex
  103. // and its scoped lock wherever possible. The only way to use a speculative lock is to use
  104. // a scoped_lock. (because transaction_state must be local)
  105. class scoped_lock : tbb::internal::no_copy {
  106. friend class x86_rtm_rw_mutex;
  107. spin_rw_mutex::scoped_lock my_scoped_lock;
  108. RTM_type transaction_state;
  109. public:
  110. //! Construct lock that has not acquired a mutex.
  111. /** Equivalent to zero-initialization of *this. */
  112. scoped_lock() : my_scoped_lock(), transaction_state(RTM_not_in_mutex) {
  113. }
  114. //! Acquire lock on given mutex.
  115. scoped_lock( x86_rtm_rw_mutex& m, bool write = true ) : my_scoped_lock(),
  116. transaction_state(RTM_not_in_mutex) {
  117. acquire(m, write);
  118. }
  119. //! Release lock (if lock is held).
  120. ~scoped_lock() {
  121. if(transaction_state != RTM_not_in_mutex) release();
  122. }
  123. //! Acquire lock on given mutex.
  124. void acquire( x86_rtm_rw_mutex& m, bool write = true ) {
  125. if( write ) m.internal_acquire_writer(*this);
  126. else m.internal_acquire_reader(*this);
  127. }
  128. //! Release lock
  129. void release() {
  130. x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
  131. __TBB_ASSERT( mutex, "lock is not acquired" );
  132. __TBB_ASSERT( transaction_state!=RTM_not_in_mutex, "lock is not acquired" );
  133. return mutex->internal_release(*this);
  134. }
  135. //! Upgrade reader to become a writer.
  136. /** Returns whether the upgrade happened without releasing and re-acquiring the lock */
  137. bool upgrade_to_writer() {
  138. x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
  139. __TBB_ASSERT( mutex, "lock is not acquired" );
  140. if (transaction_state == RTM_transacting_writer || transaction_state == RTM_real_writer)
  141. return true; // Already a writer
  142. return mutex->internal_upgrade(*this);
  143. }
  144. //! Downgrade writer to become a reader.
  145. /** Returns whether the downgrade happened without releasing and re-acquiring the lock */
  146. bool downgrade_to_reader() {
  147. x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
  148. __TBB_ASSERT( mutex, "lock is not acquired" );
  149. if (transaction_state == RTM_transacting_reader || transaction_state == RTM_real_reader)
  150. return true; // Already a reader
  151. return mutex->internal_downgrade(*this);
  152. }
  153. //! Attempt to acquire mutex.
  154. /** returns true if successful. */
  155. bool try_acquire( x86_rtm_rw_mutex& m, bool write = true ) {
  156. #if TBB_USE_ASSERT
  157. x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
  158. __TBB_ASSERT( !mutex, "lock is already acquired" );
  159. #endif
  160. // have to assign m to our mutex.
  161. // cannot set the mutex, because try_acquire in spin_rw_mutex depends on it being NULL.
  162. if(write) return m.internal_try_acquire_writer(*this);
  163. // speculatively acquire the lock. If this fails, do try_acquire on the spin_rw_mutex.
  164. m.internal_acquire_reader(*this, /*only_speculate=*/true);
  165. if(transaction_state == RTM_transacting_reader) return true;
  166. if( my_scoped_lock.try_acquire(m, false)) {
  167. transaction_state = RTM_real_reader;
  168. return true;
  169. }
  170. return false;
  171. }
  172. }; // class x86_rtm_rw_mutex::scoped_lock
  173. // ISO C++0x compatibility methods not provided because we cannot maintain
  174. // state about whether a thread is in a transaction.
  175. private:
  176. char pad[speculation_granularity-sizeof(spin_rw_mutex)]; // padding
  177. // If true, writer holds the spin_rw_mutex.
  178. tbb::atomic<bool> w_flag; // want this on a separate cache line
  179. }; // x86_rtm_rw_mutex
  180. } // namespace internal
  181. } // namespace interface8
  182. } // namespace tbb
  183. #endif /* __TBB_TSX_AVAILABLE */
  184. #endif /* __TBB__x86_rtm_rw_mutex_impl_H */