1*49e08aacStbbdev /* 2*49e08aacStbbdev Copyright (c) 2005-2020 Intel Corporation 3*49e08aacStbbdev 4*49e08aacStbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*49e08aacStbbdev you may not use this file except in compliance with the License. 6*49e08aacStbbdev You may obtain a copy of the License at 7*49e08aacStbbdev 8*49e08aacStbbdev http://www.apache.org/licenses/LICENSE-2.0 9*49e08aacStbbdev 10*49e08aacStbbdev Unless required by applicable law or agreed to in writing, software 11*49e08aacStbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*49e08aacStbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*49e08aacStbbdev See the License for the specific language governing permissions and 14*49e08aacStbbdev limitations under the License. 15*49e08aacStbbdev */ 16*49e08aacStbbdev 17*49e08aacStbbdev #ifndef __TBB_queuing_mutex_H 18*49e08aacStbbdev #define __TBB_queuing_mutex_H 19*49e08aacStbbdev 20*49e08aacStbbdev #include "detail/_namespace_injection.h" 21*49e08aacStbbdev #include "detail/_assert.h" 22*49e08aacStbbdev #include "detail/_utils.h" 23*49e08aacStbbdev 24*49e08aacStbbdev #include "profiling.h" 25*49e08aacStbbdev 26*49e08aacStbbdev #include <atomic> 27*49e08aacStbbdev 28*49e08aacStbbdev namespace tbb { 29*49e08aacStbbdev namespace detail { 30*49e08aacStbbdev namespace d1 { 31*49e08aacStbbdev 32*49e08aacStbbdev //! Queuing mutex with local-only spinning. 33*49e08aacStbbdev /** @ingroup synchronization */ 34*49e08aacStbbdev class queuing_mutex { 35*49e08aacStbbdev public: 36*49e08aacStbbdev //! Construct unacquired mutex. 37*49e08aacStbbdev queuing_mutex() noexcept { 38*49e08aacStbbdev create_itt_sync(this, "tbb::queuing_mutex", ""); 39*49e08aacStbbdev }; 40*49e08aacStbbdev 41*49e08aacStbbdev queuing_mutex(const queuing_mutex&) = delete; 42*49e08aacStbbdev queuing_mutex& operator=(const queuing_mutex&) = delete; 43*49e08aacStbbdev 44*49e08aacStbbdev //! The scoped locking pattern 45*49e08aacStbbdev /** It helps to avoid the common problem of forgetting to release lock. 46*49e08aacStbbdev It also nicely provides the "node" for queuing locks. */ 47*49e08aacStbbdev class scoped_lock { 48*49e08aacStbbdev //! Reset fields to mean "no lock held". 49*49e08aacStbbdev void reset() { 50*49e08aacStbbdev m_mutex = nullptr; 51*49e08aacStbbdev } 52*49e08aacStbbdev 53*49e08aacStbbdev public: 54*49e08aacStbbdev //! Construct lock that has not acquired a mutex. 55*49e08aacStbbdev /** Equivalent to zero-initialization of *this. */ 56*49e08aacStbbdev scoped_lock() = default; 57*49e08aacStbbdev 58*49e08aacStbbdev //! Acquire lock on given mutex. 59*49e08aacStbbdev scoped_lock(queuing_mutex& m) { 60*49e08aacStbbdev acquire(m); 61*49e08aacStbbdev } 62*49e08aacStbbdev 63*49e08aacStbbdev //! Release lock (if lock is held). 64*49e08aacStbbdev ~scoped_lock() { 65*49e08aacStbbdev if (m_mutex) release(); 66*49e08aacStbbdev } 67*49e08aacStbbdev 68*49e08aacStbbdev //! No Copy 69*49e08aacStbbdev scoped_lock( const scoped_lock& ) = delete; 70*49e08aacStbbdev scoped_lock& operator=( const scoped_lock& ) = delete; 71*49e08aacStbbdev 72*49e08aacStbbdev //! Acquire lock on given mutex. 73*49e08aacStbbdev void acquire( queuing_mutex& m ) { 74*49e08aacStbbdev __TBB_ASSERT(!m_mutex, "scoped_lock is already holding a mutex"); 75*49e08aacStbbdev 76*49e08aacStbbdev // Must set all fields before the exchange, because once the 77*49e08aacStbbdev // exchange executes, *this becomes accessible to other threads. 78*49e08aacStbbdev m_mutex = &m; 79*49e08aacStbbdev m_next.store(nullptr, std::memory_order_relaxed); 80*49e08aacStbbdev m_going.store(0U, std::memory_order_relaxed); 81*49e08aacStbbdev 82*49e08aacStbbdev // x86 compare exchange operation always has a strong fence 83*49e08aacStbbdev // "sending" the fields initialized above to other processors. 84*49e08aacStbbdev scoped_lock* pred = m.q_tail.exchange(this); 85*49e08aacStbbdev if (pred) { 86*49e08aacStbbdev call_itt_notify(prepare, &m); 87*49e08aacStbbdev __TBB_ASSERT(pred->m_next.load(std::memory_order_relaxed) == nullptr, "the predecessor has another successor!"); 88*49e08aacStbbdev 89*49e08aacStbbdev pred->m_next.store(this, std::memory_order_relaxed); 90*49e08aacStbbdev spin_wait_while_eq(m_going, 0U); 91*49e08aacStbbdev } 92*49e08aacStbbdev call_itt_notify(acquired, &m); 93*49e08aacStbbdev 94*49e08aacStbbdev // Force acquire so that user's critical section receives correct values 95*49e08aacStbbdev // from processor that was previously in the user's critical section. 96*49e08aacStbbdev atomic_fence(std::memory_order_acquire); 97*49e08aacStbbdev } 98*49e08aacStbbdev 99*49e08aacStbbdev //! Acquire lock on given mutex if free (i.e. non-blocking) 100*49e08aacStbbdev bool try_acquire( queuing_mutex& m ) { 101*49e08aacStbbdev __TBB_ASSERT(!m_mutex, "scoped_lock is already holding a mutex"); 102*49e08aacStbbdev 103*49e08aacStbbdev // Must set all fields before the compare_exchange_strong, because once the 104*49e08aacStbbdev // compare_exchange_strong executes, *this becomes accessible to other threads. 105*49e08aacStbbdev m_next.store(nullptr, std::memory_order_relaxed); 106*49e08aacStbbdev m_going.store(0U, std::memory_order_relaxed); 107*49e08aacStbbdev 108*49e08aacStbbdev scoped_lock* expected = nullptr; 109*49e08aacStbbdev // The compare_exchange_strong must have release semantics, because we are 110*49e08aacStbbdev // "sending" the fields initialized above to other processors. 111*49e08aacStbbdev // x86 compare exchange operation always has a strong fence 112*49e08aacStbbdev if (!m.q_tail.compare_exchange_strong(expected, this)) 113*49e08aacStbbdev return false; 114*49e08aacStbbdev 115*49e08aacStbbdev m_mutex = &m; 116*49e08aacStbbdev 117*49e08aacStbbdev // Force acquire so that user's critical section receives correct values 118*49e08aacStbbdev // from processor that was previously in the user's critical section. 119*49e08aacStbbdev atomic_fence(std::memory_order_acquire); 120*49e08aacStbbdev call_itt_notify(acquired, &m); 121*49e08aacStbbdev return true; 122*49e08aacStbbdev } 123*49e08aacStbbdev 124*49e08aacStbbdev //! Release lock. 125*49e08aacStbbdev void release() 126*49e08aacStbbdev { 127*49e08aacStbbdev __TBB_ASSERT(this->m_mutex, "no lock acquired"); 128*49e08aacStbbdev 129*49e08aacStbbdev call_itt_notify(releasing, this->m_mutex); 130*49e08aacStbbdev 131*49e08aacStbbdev if (m_next.load(std::memory_order_relaxed) == nullptr) { 132*49e08aacStbbdev scoped_lock* expected = this; 133*49e08aacStbbdev if (m_mutex->q_tail.compare_exchange_strong(expected, nullptr)) { 134*49e08aacStbbdev // this was the only item in the queue, and the queue is now empty. 135*49e08aacStbbdev reset(); 136*49e08aacStbbdev return; 137*49e08aacStbbdev } 138*49e08aacStbbdev // Someone in the queue 139*49e08aacStbbdev spin_wait_while_eq(m_next, nullptr); 140*49e08aacStbbdev } 141*49e08aacStbbdev m_next.load(std::memory_order_relaxed)->m_going.store(1U, std::memory_order_release); 142*49e08aacStbbdev 143*49e08aacStbbdev reset(); 144*49e08aacStbbdev } 145*49e08aacStbbdev 146*49e08aacStbbdev private: 147*49e08aacStbbdev //! The pointer to the mutex owned, or NULL if not holding a mutex. 148*49e08aacStbbdev queuing_mutex* m_mutex{nullptr}; 149*49e08aacStbbdev 150*49e08aacStbbdev //! The pointer to the next competitor for a mutex 151*49e08aacStbbdev std::atomic<scoped_lock*> m_next{nullptr}; 152*49e08aacStbbdev 153*49e08aacStbbdev //! The local spin-wait variable 154*49e08aacStbbdev /** Inverted (0 - blocked, 1 - acquired the mutex) for the sake of 155*49e08aacStbbdev zero-initialization. Defining it as an entire word instead of 156*49e08aacStbbdev a byte seems to help performance slightly. */ 157*49e08aacStbbdev std::atomic<uintptr_t> m_going{0U}; 158*49e08aacStbbdev }; 159*49e08aacStbbdev 160*49e08aacStbbdev // Mutex traits 161*49e08aacStbbdev static constexpr bool is_rw_mutex = false; 162*49e08aacStbbdev static constexpr bool is_recursive_mutex = false; 163*49e08aacStbbdev static constexpr bool is_fair_mutex = true; 164*49e08aacStbbdev 165*49e08aacStbbdev private: 166*49e08aacStbbdev //! The last competitor requesting the lock 167*49e08aacStbbdev std::atomic<scoped_lock*> q_tail{nullptr}; 168*49e08aacStbbdev 169*49e08aacStbbdev }; 170*49e08aacStbbdev 171*49e08aacStbbdev #if TBB_USE_PROFILING_TOOLS 172*49e08aacStbbdev inline void set_name(queuing_mutex& obj, const char* name) { 173*49e08aacStbbdev itt_set_sync_name(&obj, name); 174*49e08aacStbbdev } 175*49e08aacStbbdev #if (_WIN32||_WIN64) && !__MINGW32__ 176*49e08aacStbbdev inline void set_name(queuing_mutex& obj, const wchar_t* name) { 177*49e08aacStbbdev itt_set_sync_name(&obj, name); 178*49e08aacStbbdev } 179*49e08aacStbbdev #endif //WIN 180*49e08aacStbbdev #else 181*49e08aacStbbdev inline void set_name(queuing_mutex&, const char*) {} 182*49e08aacStbbdev #if (_WIN32||_WIN64) && !__MINGW32__ 183*49e08aacStbbdev inline void set_name(queuing_mutex&, const wchar_t*) {} 184*49e08aacStbbdev #endif //WIN 185*49e08aacStbbdev #endif 186*49e08aacStbbdev } // namespace d1 187*49e08aacStbbdev } // namespace detail 188*49e08aacStbbdev 189*49e08aacStbbdev inline namespace v1 { 190*49e08aacStbbdev using detail::d1::queuing_mutex; 191*49e08aacStbbdev } // namespace v1 192*49e08aacStbbdev namespace profiling { 193*49e08aacStbbdev using detail::d1::set_name; 194*49e08aacStbbdev } 195*49e08aacStbbdev } // namespace tbb 196*49e08aacStbbdev 197*49e08aacStbbdev #endif /* __TBB_queuing_mutex_H */ 198