xref: /oneTBB/test/tbb/test_mutex.cpp (revision 3ab999a0)
151c0b2f7Stbbdev /*
2b15aabb3Stbbdev     Copyright (c) 2005-2021 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
174523a761Stbbdev #include "test_mutex.h"
1851c0b2f7Stbbdev 
1951c0b2f7Stbbdev #include <tbb/spin_mutex.h>
204523a761Stbbdev #include "oneapi/tbb/mutex.h"
2151c0b2f7Stbbdev #include <tbb/spin_rw_mutex.h>
224523a761Stbbdev #include "oneapi/tbb/rw_mutex.h"
2351c0b2f7Stbbdev #include <tbb/queuing_mutex.h>
2451c0b2f7Stbbdev #include <tbb/queuing_rw_mutex.h>
25478de5b1Stbbdev #include <tbb/null_mutex.h>
26478de5b1Stbbdev #include <tbb/null_rw_mutex.h>
2751c0b2f7Stbbdev #include <tbb/parallel_for.h>
2849e08aacStbbdev #include <oneapi/tbb/detail/_utils.h>
2949e08aacStbbdev #include <oneapi/tbb/detail/_machine.h>
3051c0b2f7Stbbdev 
3151c0b2f7Stbbdev //! \file test_mutex.cpp
32*3ab999a0SPavel Kumbrasev //! \brief Test for [mutex.spin_mutex mutex.spin_rw_mutex mutex.queuing_mutex mutex.queuing_rw_mutex mutex.mutex mutex.rw_mutex mutex.speculative_spin_mutex mutex.speculative_spin_rw_mutex] specifications
3351c0b2f7Stbbdev 
3449e08aacStbbdev // TODO: Investigate why RTM doesn't work on some macOS.
3597ac8f43SAlex // TODO: Consider adding Thread Sanitizer (note that accesses inside the transaction
3697ac8f43SAlex // considered as races by Thread Sanitizer)
3797ac8f43SAlex #if __TBB_TSX_INTRINSICS_PRESENT && !__APPLE__ && !__TBB_USE_THREAD_SANITIZER
3851c0b2f7Stbbdev 
IsInsideTx()3951c0b2f7Stbbdev inline static bool IsInsideTx() {
4051c0b2f7Stbbdev     return _xtest() != 0;
4151c0b2f7Stbbdev }
4251c0b2f7Stbbdev 
have_TSX()4351c0b2f7Stbbdev bool have_TSX() {
4451c0b2f7Stbbdev     bool result = false;
4551c0b2f7Stbbdev     const int rtm_ebx_mask = 1 << 11;
4651c0b2f7Stbbdev #if _MSC_VER
4751c0b2f7Stbbdev     int info[4] = { 0,0,0,0 };
4851c0b2f7Stbbdev     const int reg_ebx = 1;
4951c0b2f7Stbbdev     __cpuidex(info, 7, 0);
5051c0b2f7Stbbdev     result = (info[reg_ebx] & rtm_ebx_mask) != 0;
5151c0b2f7Stbbdev #elif __GNUC__ || __SUNPRO_CC
5251c0b2f7Stbbdev     int32_t reg_ebx = 0;
5351c0b2f7Stbbdev     int32_t reg_eax = 7;
5451c0b2f7Stbbdev     int32_t reg_ecx = 0;
5551c0b2f7Stbbdev     __asm__ __volatile__("movl %%ebx, %%esi\n"
5651c0b2f7Stbbdev         "cpuid\n"
5751c0b2f7Stbbdev         "movl %%ebx, %0\n"
5851c0b2f7Stbbdev         "movl %%esi, %%ebx\n"
5951c0b2f7Stbbdev         : "=a"(reg_ebx) : "0" (reg_eax), "c" (reg_ecx) : "esi",
6051c0b2f7Stbbdev #if __TBB_x86_64
6151c0b2f7Stbbdev         "ebx",
6251c0b2f7Stbbdev #endif
6351c0b2f7Stbbdev         "edx"
6451c0b2f7Stbbdev     );
6551c0b2f7Stbbdev     result = (reg_ebx & rtm_ebx_mask) != 0;
6651c0b2f7Stbbdev #endif
6751c0b2f7Stbbdev     return result;
6851c0b2f7Stbbdev }
6951c0b2f7Stbbdev 
7051c0b2f7Stbbdev //! Function object for use with parallel_for.h to see if a transaction is actually attempted.
7151c0b2f7Stbbdev std::atomic<std::size_t> n_transactions_attempted;
7251c0b2f7Stbbdev template<typename C>
7351c0b2f7Stbbdev struct AddOne_CheckTransaction {
7451c0b2f7Stbbdev 
7551c0b2f7Stbbdev     AddOne_CheckTransaction& operator=(const AddOne_CheckTransaction&) = delete;
7651c0b2f7Stbbdev     AddOne_CheckTransaction(const AddOne_CheckTransaction&) = default;
7751c0b2f7Stbbdev     AddOne_CheckTransaction() = default;
7851c0b2f7Stbbdev 
7951c0b2f7Stbbdev     C& counter;
8051c0b2f7Stbbdev     /** Increments counter once for each iteration in the iteration space. */
operator ()AddOne_CheckTransaction8151c0b2f7Stbbdev     void operator()(tbb::blocked_range<size_t>& range) const {
8251c0b2f7Stbbdev         for (std::size_t i = range.begin(); i != range.end(); ++i) {
8351c0b2f7Stbbdev             bool transaction_attempted = false;
8451c0b2f7Stbbdev             {
8551c0b2f7Stbbdev                 typename C::mutex_type::scoped_lock lock(counter.mutex);
8651c0b2f7Stbbdev                 if (IsInsideTx()) transaction_attempted = true;
8751c0b2f7Stbbdev                 counter.value = counter.value + 1;
8851c0b2f7Stbbdev             }
8951c0b2f7Stbbdev             if (transaction_attempted) ++n_transactions_attempted;
9051c0b2f7Stbbdev             tbb::detail::machine_pause(static_cast<int32_t>(i));
9151c0b2f7Stbbdev         }
9251c0b2f7Stbbdev     }
AddOne_CheckTransactionAddOne_CheckTransaction9351c0b2f7Stbbdev     AddOne_CheckTransaction(C& counter_) : counter(counter_) {}
9451c0b2f7Stbbdev };
9551c0b2f7Stbbdev 
9651c0b2f7Stbbdev /* TestTransaction() checks if a speculative mutex actually uses transactions. */
9751c0b2f7Stbbdev template<typename M>
TestTransaction(const char * name)9851c0b2f7Stbbdev void TestTransaction(const char* name)
9951c0b2f7Stbbdev {
1004523a761Stbbdev     utils::Counter<M> counter;
10151c0b2f7Stbbdev     constexpr int n = 550;
10251c0b2f7Stbbdev 
10351c0b2f7Stbbdev     n_transactions_attempted = 0;
10451c0b2f7Stbbdev     for (int i = 0; i < 5 && n_transactions_attempted.load(std::memory_order_relaxed) == 0; ++i) {
10551c0b2f7Stbbdev         counter.value = 0;
1064523a761Stbbdev         tbb::parallel_for(tbb::blocked_range<std::size_t>(0, n, 2), AddOne_CheckTransaction<utils::Counter<M>>(counter));
10751c0b2f7Stbbdev         REQUIRE(counter.value == n);
10851c0b2f7Stbbdev     }
10951c0b2f7Stbbdev     REQUIRE_MESSAGE(n_transactions_attempted.load(std::memory_order_relaxed), "ERROR for " << name << ": transactions were never attempted");
11051c0b2f7Stbbdev }
11151c0b2f7Stbbdev 
11251c0b2f7Stbbdev 
11351c0b2f7Stbbdev //! \brief \ref error_guessing
11451c0b2f7Stbbdev TEST_CASE("Transaction test") {
11551c0b2f7Stbbdev     if (have_TSX()) {
11651c0b2f7Stbbdev         TestTransaction<tbb::speculative_spin_mutex>("Speculative Spin Mutex");
11751c0b2f7Stbbdev         TestTransaction<tbb::speculative_spin_rw_mutex>("Speculative Spin RW Mutex");
11851c0b2f7Stbbdev     }
11951c0b2f7Stbbdev }
12051c0b2f7Stbbdev #endif /* __TBB_TSX_TESTING_ENABLED_FOR_THIS_COMPILER */
12151c0b2f7Stbbdev 
12251c0b2f7Stbbdev //! \brief \ref error_guessing
12351c0b2f7Stbbdev TEST_CASE("test upgrade/downgrade with spin_rw_mutex") {
12451c0b2f7Stbbdev     test_rwm_upgrade_downgrade<tbb::spin_rw_mutex>();
12551c0b2f7Stbbdev }
12651c0b2f7Stbbdev 
12751c0b2f7Stbbdev //! \brief \ref error_guessing
12851c0b2f7Stbbdev TEST_CASE("test upgrade/downgrade with queueing_rw_mutex") {
12951c0b2f7Stbbdev     test_rwm_upgrade_downgrade<tbb::queuing_rw_mutex>();
13051c0b2f7Stbbdev }
13151c0b2f7Stbbdev 
13251c0b2f7Stbbdev //! \brief \ref error_guessing
133*3ab999a0SPavel Kumbrasev TEST_CASE("test upgrade/downgrade with rw_mutex") {
134*3ab999a0SPavel Kumbrasev     test_rwm_upgrade_downgrade<tbb::rw_mutex>();
135*3ab999a0SPavel Kumbrasev }
136*3ab999a0SPavel Kumbrasev 
137*3ab999a0SPavel Kumbrasev //! \brief \ref error_guessing
13851c0b2f7Stbbdev TEST_CASE("test upgrade/downgrade with speculative_spin_rw_mutex") {
13951c0b2f7Stbbdev     test_rwm_upgrade_downgrade<tbb::speculative_spin_rw_mutex>();
14051c0b2f7Stbbdev }
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev //! \brief \ref error_guessing
14351c0b2f7Stbbdev TEST_CASE("test spin_mutex with native threads") {
14451c0b2f7Stbbdev     test_with_native_threads::test<tbb::spin_mutex>();
14551c0b2f7Stbbdev }
14651c0b2f7Stbbdev 
14751c0b2f7Stbbdev //! \brief \ref error_guessing
14851c0b2f7Stbbdev TEST_CASE("test queuing_mutex with native threads") {
14951c0b2f7Stbbdev     test_with_native_threads::test<tbb::queuing_mutex>();
15051c0b2f7Stbbdev }
15151c0b2f7Stbbdev 
15251c0b2f7Stbbdev //! \brief \ref error_guessing
153*3ab999a0SPavel Kumbrasev TEST_CASE("test mutex with native threads") {
154*3ab999a0SPavel Kumbrasev     test_with_native_threads::test<tbb::mutex>();
155*3ab999a0SPavel Kumbrasev }
156*3ab999a0SPavel Kumbrasev 
157*3ab999a0SPavel Kumbrasev //! \brief \ref error_guessing
15851c0b2f7Stbbdev TEST_CASE("test spin_rw_mutex with native threads") {
15951c0b2f7Stbbdev     test_with_native_threads::test<tbb::spin_rw_mutex>();
16051c0b2f7Stbbdev     test_with_native_threads::test_rw<tbb::spin_rw_mutex>();
16151c0b2f7Stbbdev }
16251c0b2f7Stbbdev 
16351c0b2f7Stbbdev //! \brief \ref error_guessing
16451c0b2f7Stbbdev TEST_CASE("test queuing_rw_mutex with native threads") {
16551c0b2f7Stbbdev     test_with_native_threads::test<tbb::queuing_rw_mutex>();
16651c0b2f7Stbbdev     test_with_native_threads::test_rw<tbb::queuing_rw_mutex>();
16751c0b2f7Stbbdev }
1689e15720bStbbdev 
169*3ab999a0SPavel Kumbrasev //! \brief \ref error_guessing
170*3ab999a0SPavel Kumbrasev TEST_CASE("test rw_mutex with native threads") {
171*3ab999a0SPavel Kumbrasev     test_with_native_threads::test<tbb::rw_mutex>();
172*3ab999a0SPavel Kumbrasev     test_with_native_threads::test_rw<tbb::rw_mutex>();
173*3ab999a0SPavel Kumbrasev }
174*3ab999a0SPavel Kumbrasev 
1759e15720bStbbdev //! Test scoped_lock::is_writer getter
1769e15720bStbbdev //! \brief \ref error_guessing
1779e15720bStbbdev TEST_CASE("scoped_lock::is_writer") {
1789e15720bStbbdev     TestIsWriter<oneapi::tbb::spin_rw_mutex>("spin_rw_mutex");
1799e15720bStbbdev     TestIsWriter<oneapi::tbb::queuing_rw_mutex>("queuing_rw_mutex");
1809e15720bStbbdev     TestIsWriter<oneapi::tbb::speculative_spin_rw_mutex>("speculative_spin_rw_mutex");
1819e15720bStbbdev     TestIsWriter<oneapi::tbb::null_rw_mutex>("null_rw_mutex");
1829e15720bStbbdev     TestIsWriter<oneapi::tbb::rw_mutex>("rw_mutex");
1839e15720bStbbdev }
184478de5b1Stbbdev 
185478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
186478de5b1Stbbdev template <typename... Args>
187478de5b1Stbbdev concept mutexes = (... && tbb::detail::scoped_lockable<Args>);
188478de5b1Stbbdev 
189478de5b1Stbbdev template <typename... Args>
190478de5b1Stbbdev concept rw_mutexes = (... && tbb::detail::rw_scoped_lockable<Args>);
191478de5b1Stbbdev 
192478de5b1Stbbdev //! \brief \ref error_guessing
193478de5b1Stbbdev TEST_CASE("internal mutex concepts") {
194*3ab999a0SPavel Kumbrasev     static_assert(mutexes<tbb::spin_mutex, oneapi::tbb::mutex, tbb::speculative_spin_mutex, tbb::null_mutex, tbb::queuing_mutex,
195*3ab999a0SPavel Kumbrasev                           tbb::spin_rw_mutex, oneapi::tbb::rw_mutex, tbb::speculative_spin_rw_mutex, tbb::null_rw_mutex, tbb::queuing_rw_mutex>);
196*3ab999a0SPavel Kumbrasev     static_assert(rw_mutexes<tbb::spin_rw_mutex, oneapi::tbb::rw_mutex, tbb::speculative_spin_rw_mutex,
197478de5b1Stbbdev                              tbb::null_rw_mutex, tbb::queuing_rw_mutex>);
198478de5b1Stbbdev }
199478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
200