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