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 #define TBB_PREVIEW_MUTEXES 1 18 #include "test_mutex.h" 19 20 #include <tbb/spin_mutex.h> 21 #include "oneapi/tbb/mutex.h" 22 #include <tbb/spin_rw_mutex.h> 23 #include "oneapi/tbb/rw_mutex.h" 24 #include <tbb/queuing_mutex.h> 25 #include <tbb/queuing_rw_mutex.h> 26 #include <tbb/null_mutex.h> 27 #include <tbb/null_rw_mutex.h> 28 #include <tbb/parallel_for.h> 29 #include <oneapi/tbb/detail/_utils.h> 30 #include <oneapi/tbb/detail/_machine.h> 31 32 //! \file test_mutex.cpp 33 //! \brief Test for [mutex.spin_mutex mutex.spin_rw_mutex mutex.queuing_mutex mutex.queuing_rw_mutexmutex.speculative_spin_mutex mutex.speculative_spin_rw_mutex] specifications 34 35 // TODO: Investigate why RTM doesn't work on some macOS. 36 // TODO: Consider adding Thread Sanitizer (note that accesses inside the transaction 37 // considered as races by Thread Sanitizer) 38 #if __TBB_TSX_INTRINSICS_PRESENT && !__APPLE__ && !__TBB_USE_THREAD_SANITIZER 39 40 inline static bool IsInsideTx() { 41 return _xtest() != 0; 42 } 43 44 bool have_TSX() { 45 bool result = false; 46 const int rtm_ebx_mask = 1 << 11; 47 #if _MSC_VER 48 int info[4] = { 0,0,0,0 }; 49 const int reg_ebx = 1; 50 __cpuidex(info, 7, 0); 51 result = (info[reg_ebx] & rtm_ebx_mask) != 0; 52 #elif __GNUC__ || __SUNPRO_CC 53 int32_t reg_ebx = 0; 54 int32_t reg_eax = 7; 55 int32_t reg_ecx = 0; 56 __asm__ __volatile__("movl %%ebx, %%esi\n" 57 "cpuid\n" 58 "movl %%ebx, %0\n" 59 "movl %%esi, %%ebx\n" 60 : "=a"(reg_ebx) : "0" (reg_eax), "c" (reg_ecx) : "esi", 61 #if __TBB_x86_64 62 "ebx", 63 #endif 64 "edx" 65 ); 66 result = (reg_ebx & rtm_ebx_mask) != 0; 67 #endif 68 return result; 69 } 70 71 //! Function object for use with parallel_for.h to see if a transaction is actually attempted. 72 std::atomic<std::size_t> n_transactions_attempted; 73 template<typename C> 74 struct AddOne_CheckTransaction { 75 76 AddOne_CheckTransaction& operator=(const AddOne_CheckTransaction&) = delete; 77 AddOne_CheckTransaction(const AddOne_CheckTransaction&) = default; 78 AddOne_CheckTransaction() = default; 79 80 C& counter; 81 /** Increments counter once for each iteration in the iteration space. */ 82 void operator()(tbb::blocked_range<size_t>& range) const { 83 for (std::size_t i = range.begin(); i != range.end(); ++i) { 84 bool transaction_attempted = false; 85 { 86 typename C::mutex_type::scoped_lock lock(counter.mutex); 87 if (IsInsideTx()) transaction_attempted = true; 88 counter.value = counter.value + 1; 89 } 90 if (transaction_attempted) ++n_transactions_attempted; 91 tbb::detail::machine_pause(static_cast<int32_t>(i)); 92 } 93 } 94 AddOne_CheckTransaction(C& counter_) : counter(counter_) {} 95 }; 96 97 /* TestTransaction() checks if a speculative mutex actually uses transactions. */ 98 template<typename M> 99 void TestTransaction(const char* name) 100 { 101 utils::Counter<M> counter; 102 constexpr int n = 550; 103 104 n_transactions_attempted = 0; 105 for (int i = 0; i < 5 && n_transactions_attempted.load(std::memory_order_relaxed) == 0; ++i) { 106 counter.value = 0; 107 tbb::parallel_for(tbb::blocked_range<std::size_t>(0, n, 2), AddOne_CheckTransaction<utils::Counter<M>>(counter)); 108 REQUIRE(counter.value == n); 109 } 110 REQUIRE_MESSAGE(n_transactions_attempted.load(std::memory_order_relaxed), "ERROR for " << name << ": transactions were never attempted"); 111 } 112 113 114 //! \brief \ref error_guessing 115 TEST_CASE("Transaction test") { 116 if (have_TSX()) { 117 TestTransaction<tbb::speculative_spin_mutex>("Speculative Spin Mutex"); 118 TestTransaction<tbb::speculative_spin_rw_mutex>("Speculative Spin RW Mutex"); 119 } 120 } 121 #endif /* __TBB_TSX_TESTING_ENABLED_FOR_THIS_COMPILER */ 122 123 //! \brief \ref error_guessing 124 TEST_CASE("test upgrade/downgrade with spin_rw_mutex") { 125 test_rwm_upgrade_downgrade<tbb::spin_rw_mutex>(); 126 } 127 128 //! \brief \ref error_guessing 129 TEST_CASE("test upgrade/downgrade with queueing_rw_mutex") { 130 test_rwm_upgrade_downgrade<tbb::queuing_rw_mutex>(); 131 } 132 133 //! \brief \ref error_guessing 134 TEST_CASE("test upgrade/downgrade with speculative_spin_rw_mutex") { 135 test_rwm_upgrade_downgrade<tbb::speculative_spin_rw_mutex>(); 136 } 137 138 //! \brief \ref error_guessing 139 TEST_CASE("test spin_mutex with native threads") { 140 test_with_native_threads::test<tbb::spin_mutex>(); 141 } 142 143 //! \brief \ref error_guessing 144 TEST_CASE("test queuing_mutex with native threads") { 145 test_with_native_threads::test<tbb::queuing_mutex>(); 146 } 147 148 //! \brief \ref error_guessing 149 TEST_CASE("test spin_rw_mutex with native threads") { 150 test_with_native_threads::test<tbb::spin_rw_mutex>(); 151 test_with_native_threads::test_rw<tbb::spin_rw_mutex>(); 152 } 153 154 //! \brief \ref error_guessing 155 TEST_CASE("test queuing_rw_mutex with native threads") { 156 test_with_native_threads::test<tbb::queuing_rw_mutex>(); 157 test_with_native_threads::test_rw<tbb::queuing_rw_mutex>(); 158 } 159 160 //! Test scoped_lock::is_writer getter 161 //! \brief \ref error_guessing 162 TEST_CASE("scoped_lock::is_writer") { 163 TestIsWriter<oneapi::tbb::spin_rw_mutex>("spin_rw_mutex"); 164 TestIsWriter<oneapi::tbb::queuing_rw_mutex>("queuing_rw_mutex"); 165 TestIsWriter<oneapi::tbb::speculative_spin_rw_mutex>("speculative_spin_rw_mutex"); 166 TestIsWriter<oneapi::tbb::null_rw_mutex>("null_rw_mutex"); 167 TestIsWriter<oneapi::tbb::rw_mutex>("rw_mutex"); 168 } 169 170 #if __TBB_CPP20_CONCEPTS_PRESENT 171 template <typename... Args> 172 concept mutexes = (... && tbb::detail::scoped_lockable<Args>); 173 174 template <typename... Args> 175 concept rw_mutexes = (... && tbb::detail::rw_scoped_lockable<Args>); 176 177 //! \brief \ref error_guessing 178 TEST_CASE("internal mutex concepts") { 179 static_assert(mutexes<tbb::spin_mutex, tbb::speculative_spin_mutex, tbb::null_mutex, tbb::queuing_mutex, 180 tbb::spin_rw_mutex, tbb::speculative_spin_rw_mutex, tbb::null_rw_mutex, tbb::queuing_rw_mutex>); 181 static_assert(rw_mutexes<tbb::spin_rw_mutex, tbb::speculative_spin_rw_mutex, 182 tbb::null_rw_mutex, tbb::queuing_rw_mutex>); 183 } 184 #endif // __TBB_CPP20_CONCEPTS_PRESENT 185