X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=cds%2Fsync%2Fspinlock.h;h=f3a915840bcfc3dfe8b5940ce72aa38a82e346be;hb=59cb651402874a22500cab3ec586565b48f76059;hp=94954db2f9dfb69a6e21b1577fa596b5ec4a7945;hpb=29ff2a580fe96e2a4cb21d1abdd089002d35e6b2;p=libcds.git diff --git a/cds/sync/spinlock.h b/cds/sync/spinlock.h index 94954db2..f3a91584 100644 --- a/cds/sync/spinlock.h +++ b/cds/sync/spinlock.h @@ -1,7 +1,7 @@ /* This file is a part of libcds - Concurrent Data Structures library - (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 Source code repo: http://github.com/khizmax/libcds/ Download: http://sourceforge.net/projects/libcds/files/ @@ -76,11 +76,12 @@ namespace cds { public: /// Construct free (unlocked) spin-lock spin_lock() CDS_NOEXCEPT - : m_spin( false ) # ifdef CDS_DEBUG - , m_dbgOwnerId( OS::c_NullThreadId ) + : m_dbgOwnerId( OS::c_NullThreadId ) # endif - {} + { + m_spin.store( false, atomics::memory_order_release ); + } /// Construct spin-lock in specified state /** @@ -91,26 +92,27 @@ namespace cds { : m_dbgOwnerId( bLocked ? cds::OS::get_current_thread_id() : cds::OS::c_NullThreadId ) # endif { - m_spin.store( bLocked, atomics::memory_order_relaxed ); + m_spin.store( bLocked, atomics::memory_order_release ); } /// Dummy copy constructor /** - In theory, spin-lock cannot be copied. However, it is not practical. - Therefore, we provide dummy copy constructor that do no copy in fact. The ctor - initializes the spin to free (unlocked) state like default ctor. + The ctor initializes the spin to free (unlocked) state like the default ctor. */ spin_lock(const spin_lock& ) CDS_NOEXCEPT : m_spin( false ) # ifdef CDS_DEBUG , m_dbgOwnerId( cds::OS::c_NullThreadId ) # endif - {} + { + CDS_TSAN_ANNOTATE_MUTEX_CREATE( this ); + } /// Destructor. On debug time it checks whether spin-lock is free ~spin_lock() { assert( !m_spin.load( atomics::memory_order_relaxed )); + CDS_TSAN_ANNOTATE_MUTEX_DESTROY( this ); } /// Check if the spin is locked @@ -128,9 +130,13 @@ namespace cds { */ bool try_lock() CDS_NOEXCEPT { - bool bCurrent = false; - - m_spin.compare_exchange_strong( bCurrent, true, atomics::memory_order_acquire, atomics::memory_order_acquire ); +# ifdef CDS_THREAD_SANITIZER_ENABLED + bool bCurrent = m_spin.exchange( true, atomics::memory_order_acq_rel ); + if ( !bCurrent ) + CDS_TSAN_ANNOTATE_MUTEX_ACQUIRED( this ); +# else + bool bCurrent = m_spin.exchange( true, atomics::memory_order_acquire ); +# endif CDS_DEBUG_ONLY( if ( !bCurrent ) { @@ -149,7 +155,7 @@ namespace cds { { backoff_strategy backoff; while ( nTryCount-- ) { - if ( try_lock() ) + if ( try_lock()) return true; backoff(); } @@ -162,14 +168,16 @@ namespace cds { backoff_strategy backoff; // Deadlock detected + CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN; assert( m_dbgOwnerId != OS::get_current_thread_id()); + CDS_TSAN_ANNOTATE_IGNORE_READS_END; // TATAS algorithm while ( !try_lock()) { - while ( m_spin.load( atomics::memory_order_relaxed )) { + while ( m_spin.load( atomics::memory_order_acquire )) backoff(); - } } + assert( m_dbgOwnerId == OS::get_current_thread_id()); } @@ -177,10 +185,10 @@ namespace cds { void unlock() CDS_NOEXCEPT { assert( m_spin.load( atomics::memory_order_relaxed )); - assert( m_dbgOwnerId == OS::get_current_thread_id()); CDS_DEBUG_ONLY( m_dbgOwnerId = OS::c_NullThreadId; ) + CDS_TSAN_ANNOTATE_MUTEX_RELEASED( this ); m_spin.store( false, atomics::memory_order_release ); } }; @@ -199,15 +207,17 @@ namespace cds { template class reentrant_spin_lock { - typedef OS::ThreadId thread_id ; ///< The type of thread id + typedef OS::ThreadId thread_id; ///< The type of thread id public: - typedef Integral integral_type ; ///< The integral type - typedef Backoff backoff_strategy ; ///< The backoff type + typedef Integral integral_type; ///< The integral type + typedef Backoff backoff_strategy; ///< The backoff type private: - atomics::atomic m_spin ; ///< spin-lock atomic - thread_id m_OwnerId ; ///< Owner thread id. If spin-lock is not locked it usually equals to \p OS::c_NullThreadId + //@cond + atomics::atomic m_spin; ///< spin-lock atomic + thread_id m_OwnerId; ///< Owner thread id. If spin-lock is not locked it usually equals to \p OS::c_NullThreadId + //@endcond private: //@cond @@ -238,7 +248,14 @@ namespace cds { bool try_acquire() CDS_NOEXCEPT { integral_type nCurrent = 0; - return m_spin.compare_exchange_weak( nCurrent, 1, atomics::memory_order_acquire, atomics::memory_order_acquire ); + bool bRet = m_spin.compare_exchange_weak( nCurrent, 1, atomics::memory_order_acquire, atomics::memory_order_acquire ); + +# ifdef CDS_THREAD_SANITIZER_ENABLED + if ( bRet ) + CDS_TSAN_ANNOTATE_MUTEX_ACQUIRED( this ); +# endif + + return bRet; } bool try_acquire( unsigned int nTryCount ) CDS_NOEXCEPT_( noexcept( backoff_strategy()())) @@ -246,7 +263,7 @@ namespace cds { backoff_strategy bkoff; while ( nTryCount-- ) { - if ( try_acquire() ) + if ( try_acquire()) return true; bkoff(); } @@ -258,7 +275,7 @@ namespace cds { // TATAS algorithm backoff_strategy bkoff; while ( !try_acquire()) { - while ( m_spin.load( atomics::memory_order_relaxed )) + while ( m_spin.load( atomics::memory_order_acquire )) bkoff(); } } @@ -269,7 +286,9 @@ namespace cds { reentrant_spin_lock() CDS_NOEXCEPT : m_spin(0) , m_OwnerId( OS::c_NullThreadId ) - {} + { + CDS_TSAN_ANNOTATE_MUTEX_CREATE( this ); + } /// Dummy copy constructor /** @@ -280,17 +299,29 @@ namespace cds { reentrant_spin_lock( const reentrant_spin_lock& ) CDS_NOEXCEPT : m_spin(0) , m_OwnerId( OS::c_NullThreadId ) - {} + { + CDS_TSAN_ANNOTATE_MUTEX_CREATE( this ); + } /// Construct object in specified state explicit reentrant_spin_lock( bool bLocked ) : m_spin(0) , m_OwnerId( OS::c_NullThreadId ) { + CDS_TSAN_ANNOTATE_MUTEX_CREATE( this ); if ( bLocked ) lock(); } + /// Dtor. Spin-lock must be unlocked + ~reentrant_spin_lock() + { + assert( m_spin.load( atomics::memory_order_acquire ) == 0 ); + assert( m_OwnerId == OS::c_NullThreadId ); + + CDS_TSAN_ANNOTATE_MUTEX_DESTROY( this ); + } + /// Checks if the spin is locked /** The spin is locked if lock count > 0 and the current thread is not an owner of the lock. @@ -301,8 +332,8 @@ namespace cds { return !( m_spin.load( atomics::memory_order_relaxed ) == 0 || is_taken( cds::OS::get_current_thread_id())); } - /// Try to lock the spin-lock (synonym for \p try_lock()) - bool try_lock() CDS_NOEXCEPT + /// Try to lock the spin-lock + bool try_lock() CDS_NOEXCEPT_( noexcept( std::declval().try_acquire())) { thread_id tid = OS::get_current_thread_id(); if ( try_taken_lock( tid )) @@ -314,7 +345,7 @@ namespace cds { return false; } - /// Try to lock the object + /// Try to lock up to \p nTryCount attempts bool try_lock( unsigned int nTryCount ) CDS_NOEXCEPT_( noexcept( std::declval().try_acquire( nTryCount ))) { thread_id tid = OS::get_current_thread_id(); @@ -337,31 +368,28 @@ namespace cds { } } - /// Unlock the spin-lock. Return \p true if the current thread is owner of spin-lock \p false otherwise - bool unlock() CDS_NOEXCEPT + /// Unlock the spin-lock + void unlock() CDS_NOEXCEPT { - if ( is_taken( OS::get_current_thread_id())) { - integral_type n = m_spin.load( atomics::memory_order_relaxed ); - if ( n > 1 ) - m_spin.store( n - 1, atomics::memory_order_relaxed ); - else { - free(); - m_spin.store( 0, atomics::memory_order_release ); - } - return true; + assert( is_taken( OS::get_current_thread_id())); + + integral_type n = m_spin.load( atomics::memory_order_relaxed ); + if ( n > 1 ) + m_spin.store( n - 1, atomics::memory_order_relaxed ); + else { + free(); + CDS_TSAN_ANNOTATE_MUTEX_RELEASED( this ); + m_spin.store( 0, atomics::memory_order_release ); } - return false; } - /// Change the owner of locked spin-lock. May be called by thread that is owner of the spin-lock - bool change_owner( OS::ThreadId newOwnerId ) CDS_NOEXCEPT + /// Change the owner of locked spin-lock. May be called by thread that owns spin-lock + void change_owner( OS::ThreadId newOwnerId ) CDS_NOEXCEPT { - if ( is_taken( OS::get_current_thread_id())) { - assert( newOwnerId != OS::c_NullThreadId ); - m_OwnerId = newOwnerId; - return true; - } - return false; + assert( is_taken( OS::get_current_thread_id())); + assert( newOwnerId != OS::c_NullThreadId ); + + m_OwnerId = newOwnerId; } };