<commit_before>//$$CDS-header$$-2

#ifndef __CDS_LOCK_SPINLOCK_H
#define __CDS_LOCK_SPINLOCK_H

/*
    Defines spin-lock primitives
    Editions:
        2012.01.23  1.1.0 khizmax     Refactoring: use C++11 atomics
        2010.01.22  0.6.0 khizmax     Refactoring: use cds::atomic namespace
                                      Explicit memory ordering specification (atomic::memory_order_xxx)
        2006              khizmax     Created
*/

#include <cds/cxx11_atomic.h>
#include <cds/os/thread.h>
#include <cds/algo/backoff_strategy.h>
#include <cds/lock/scoped_lock.h>

#include <cds/details/noncopyable.h>

namespace cds {
    /// Synchronization primitives
    namespace lock {
        /// Spin lock.
        /**
            Simple and light-weight spin-lock critical section
            It is useful to gain access to small (short-timed) code

            Algorithm:

                TATAS (test-and-test-and-lock)
                [1984] <NAME>, <NAME>. Dynamic Decentralized Cache Schemes for MIMD Parallel Processors.

            No serialization performed - any of waiting threads may owns the spin-lock.
            This spin-lock is NOT recursive: the thread owned the lock cannot call lock() method withod deadlock.
            The method unlock() can call any thread

            DEBUG version: The spinlock stores owner thead id. Assertion is raised when:
                - double lock attempt encountered by same thread (deadlock)
                - unlock by another thread

            If spin-lock is locked the Backoff algorithm is called. Predefined backoff::LockDefault class yields current
            thread and repeats lock attempts later

            Template parameters:
                - @p Backoff    backoff strategy. Used when spin lock is locked
        */
        template <typename Backoff >
        class Spinlock
        {
        public:
            typedef        Backoff      backoff_strategy    ;        ///< back-off strategy type
        private:
            atomics::atomic<bool>    m_spin  ;       ///< Spin
#    ifdef CDS_DEBUG
            typename OS::ThreadId    m_dbgOwnerId        ;       ///< Owner thread id (only for debug mode)
#    endif

        public:
            /// Construct free (unlocked) spin-lock
            Spinlock() CDS_NOEXCEPT
#    ifdef CDS_DEBUG
                :m_dbgOwnerId( OS::c_NullThreadId )
#    endif
            {
                m_spin.store( false, atomics::memory_order_relaxed );
            }

            /// Construct spin-lock in specified state
            /**
                In debug mode: if \p bLocked = true then spin-lock is made owned by current thread
            */
            Spinlock( bool bLocked ) CDS_NOEXCEPT
#    ifdef CDS_DEBUG
                : m_dbgOwnerId( bLocked ? OS::getCurrentThreadId() : OS::c_NullThreadId )
#    endif
            {
                m_spin.store( bLocked, atomics::memory_order_relaxed );
            }

            /// 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.
            */
            Spinlock(const Spinlock<Backoff>& ) CDS_NOEXCEPT
                : m_spin( false )
#   ifdef CDS_DEBUG
                , m_dbgOwnerId( OS::c_NullThreadId )
#   endif
            {}

            /// Destructor. On debug time it checks whether spin-lock is free
            ~Spinlock()
            {
                assert( !m_spin.load( atomics::memory_order_relaxed ) );
            }

            /// Check if the spin is locked
            bool is_locked() const CDS_NOEXCEPT
            {
                return m_spin.load( atomics::memory_order_relaxed );
            }

            /// Try to lock the object
            /**
                Returns \p true if locking is succeeded
                otherwise (if the spin is already locked) returns \p false

                Debug version: deadlock can be detected
            */
            bool try_lock() CDS_NOEXCEPT
            {
                bool bCurrent = false;
                m_spin.compare_exchange_strong( bCurrent, true, atomics::memory_order_acquire, atomics::memory_order_relaxed );

                CDS_DEBUG_ONLY(
                    if ( !bCurrent ) {
                        m_dbgOwnerId = OS::getCurrentThreadId();
                    }
                )
                return !bCurrent;
            }

            /// Try to lock the object, repeat @p nTryCount times if failed
            /**
                Returns \p true if locking is succeeded
                otherwise (if the spin is already locked) returns \p false
            */
            bool try_lock( unsigned int nTryCount ) CDS_NOEXCEPT( noexept( backoff_strategy()() ) )
            {
                backoff_strategy backoff;
                while ( nTryCount-- ) {
                    if ( try_lock() )
                        return true;
                    backoff();
                }
                return false;
            }

            /// Lock the spin-lock. Waits infinitely while spin-lock is locked. Debug version: deadlock may be detected
            void lock() CDS_NOEXCEPT(noexept( backoff_strategy()() ))
            {
                backoff_strategy backoff;

                // Deadlock detected
                assert( m_dbgOwnerId != OS::getCurrentThreadId() );

                // TATAS algorithm
                while ( !try_lock() ) {
                    while ( m_spin.load( atomics::memory_order_relaxed ) ) {
                        backoff();
                    }
                }
                assert( m_dbgOwnerId == OS::getCurrentThreadId() );
            }

            /// Unlock the spin-lock. Debug version: deadlock may be detected
            void unlock() CDS_NOEXCEPT<commit_msg>spinlock.h bugfix<commit_after>//$$CDS-header$$-2

#ifndef __CDS_LOCK_SPINLOCK_H
#define __CDS_LOCK_SPINLOCK_H

/*
    Defines spin-lock primitives
    Editions:
        2012.01.23  1.1.0 khizmax     Refactoring: use C++11 atomics
        2010.01.22  0.6.0 khizmax     Refactoring: use cds::atomic namespace
                                      Explicit memory ordering specification (atomic::memory_order_xxx)
        2006              khizmax     Created
*/

#include <cds/cxx11_atomic.h>
#include <cds/os/thread.h>
#include <cds/algo/backoff_strategy.h>
#include <cds/lock/scoped_lock.h>

#include <cds/details/noncopyable.h>

namespace cds {
    /// Synchronization primitives
    namespace lock {
        /// Spin lock.
        /**
            Simple and light-weight spin-lock critical section
            It is useful to gain access to small (short-timed) code

            Algorithm:

                TATAS (test-and-test-and-lock)
                [1984] <NAME>, <NAME>. Dynamic Decentralized Cache Schemes for MIMD Parallel Processors.

            No serialization performed - any of waiting threads may owns the spin-lock.
            This spin-lock is NOT recursive: the thread owned the lock cannot call lock() method withod deadlock.
            The method unlock() can call any thread

            DEBUG version: The spinlock stores owner thead id. Assertion is raised when:
                - double lock attempt encountered by same thread (deadlock)
                - unlock by another thread

            If spin-lock is locked the Backoff algorithm is called. Predefined backoff::LockDefault class yields current
            thread and repeats lock attempts later

            Template parameters:
                - @p Backoff    backoff strategy. Used when spin lock is locked
        */
        template <typename Backoff >
        class Spinlock
        {
        public:
            typedef        Backoff      backoff_strategy    ;        ///< back-off strategy type
        private:
            atomics::atomic<bool>    m_spin  ;       ///< Spin
#    ifdef CDS_DEBUG
            typename OS::ThreadId    m_dbgOwnerId        ;       ///< Owner thread id (only for debug mode)
#    endif

        public:
            /// Construct free (unlocked) spin-lock
            Spinlock() CDS_NOEXCEPT
#    ifdef CDS_DEBUG
                :m_dbgOwnerId( OS::c_NullThreadId )
#    endif
            {
                m_spin.store( false, atomics::memory_order_relaxed );
            }

            /// Construct spin-lock in specified state
            /**
                In debug mode: if \p bLocked = true then spin-lock is made owned by current thread
            */
            Spinlock( bool bLocked ) CDS_NOEXCEPT
#    ifdef CDS_DEBUG
                : m_dbgOwnerId( bLocked ? OS::getCurrentThreadId() : OS::c_NullThreadId )
#    endif
            {
                m_spin.store( bLocked, atomics::memory_order_relaxed );
            }

            /// 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.
            */
            Spinlock(const Spinlock<Backoff>& ) CDS_NOEXCEPT
                : m_spin( false )
#   ifdef CDS_DEBUG
                , m_dbgOwnerId( OS::c_NullThreadId )
#   endif
            {}

            /// Destructor. On debug time it checks whether spin-lock is free
            ~Spinlock()
            {
                assert( !m_spin.load( atomics::memory_order_relaxed ) );
            }

            /// Check if the spin is locked
            bool is_locked() const CDS_NOEXCEPT
            {
                return m_spin.load( atomics::memory_order_relaxed );
            }

            /// Try to lock the object
            /**
                Returns \p true if locking is succeeded
                otherwise (if the spin is already locked) returns \p false

                Debug version: deadlock can be detected
            */
            bool try_lock() CDS_NOEXCEPT
            {
                bool bCurrent = false;
                m_spin.compare_exchange_strong( bCurrent, true, atomics::memory_order_acquire, atomics::memory_order_relaxed );

                CDS_DEBUG_ONLY(
                    if ( !bCurrent ) {
                        m_dbgOwnerId = OS::getCurrentThreadId();
                    }
                )
                return !bCurrent;
            }

            /// Try to lock the object, repeat @p nTryCount times if failed
            /**
                Returns \p true if locking is succeeded
                otherwise (if the spin is already locked) returns \p false
            */
            bool try_lock( unsigned int nTryCount ) CDS_NOEXCEPT( noexcept( backoff_strategy()() ) )
            {
                backoff_strategy backoff;
                while ( nTryCount-- ) {
                    if ( try_lock() )
                        return true;
                    backoff();
                }
                return false;
            }

            /// Lock the spin-lock. Waits infinitely while spin-lock is locked. Debug version: deadlock may be detected
            void lock() CDS_NOEXCEPT(noexcept( backoff_strategy()() ))
            {
                backoff_strategy backoff;

                // Deadlock detected
                assert( m_dbgOwnerId != OS::getCurrentThreadId() );

                // TATAS algorithm
                while ( !try_lock() ) {
                    while ( m_spin.load( atomics::memory_order_relaxed ) ) {
                        backoff();
                    }
                }
                assert( m_dbgOwnerId == OS::getCurrentThreadId() );
            }

            /// Unlock the spin-lock. Debug version: deadlock may be detected
            void unlock() CDS_NOEXCEPT
{'fraction_non_alphanumeric': 0.07437531443904075, 'fraction_numerical': 0.0058695287606909275, 'mean_word_length': 1.4505855763303883, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7277081', 'n_tokens_mistral': 3246, 'n_tokens_neox': 3001, 'n_words': 932}