xref: /oneTBB/test/tbb/test_mutex.cpp (revision adf44fbf)
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