/* * Copyright Andrey Semashev 2007 - 2015. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ /*! * \file atomic_queue.hpp * \author Andrey Semashev * \date 14.07.2009 * * \brief This header is the Boost.Log library implementation, see the library documentation * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. */ #ifndef BOOST_LOG_ATOMIC_QUEUE_HPP_INCLUDED_ #define BOOST_LOG_ATOMIC_QUEUE_HPP_INCLUDED_ #include #include #include #include #include // Detect if atomic CAS is supported #if defined(__GNUC__) #if (__SIZEOF_POINTER__ == 1 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1)) ||\ (__SIZEOF_POINTER__ == 2 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2)) ||\ (__SIZEOF_POINTER__ == 4 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) ||\ (__SIZEOF_POINTER__ == 8 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) ||\ (__SIZEOF_POINTER__ == 16 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)) #define BOOST_LOG_CAS_PTR(expected, new_value, pointer)\ __sync_bool_compare_and_swap(pointer, expected, new_value) #endif // __SIZEOF_POINTER__ == N && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_N) #endif // defined(__GNUC__) // For sake of older GCC versions on Windows, this section should go after GCC #if !defined(BOOST_LOG_CAS_PTR) && defined(BOOST_WINDOWS) #include #define BOOST_LOG_CAS_PTR(expected, new_value, pointer)\ (BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER((void**)(pointer), (void*)(new_value), (void*)(expected)) == (expected)) #endif // !defined(BOOST_LOG_CAS_PTR) && defined(BOOST_WINDOWS) #if !defined(BOOST_LOG_CAS_PTR) #include #include #endif // !defined(BOOST_LOG_CAS_PTR) #if defined(__GNUC__) #define BOOST_LOG_ALIGN(x) __attribute__((aligned(x))) #elif defined(_MSC_VER) #define BOOST_LOG_ALIGN(x) __declspec(align(x)) #else #define BOOST_LOG_ALIGN(x) #endif #define BOOST_LOG_CPU_CACHE_LINE_SIZE 64 namespace boost { BOOST_LOG_OPEN_NAMESPACE namespace aux { /*! * A simple atomic queue class. Allows to put and eject elements from different threads concurrently. */ template< typename T, typename AllocatorT = std::allocator< T > > class BOOST_LOG_ALIGN(BOOST_LOG_CPU_CACHE_LINE_SIZE) atomic_queue { public: typedef typename AllocatorT::BOOST_NESTED_TEMPLATE rebind< T >::other allocator; typedef typename allocator::value_type value_type; typedef typename allocator::reference reference; typedef typename allocator::pointer pointer; typedef typename allocator::const_reference const_reference; typedef typename allocator::const_pointer const_pointer; typedef typename allocator::difference_type difference_type; struct node; friend struct node; struct node { node* m_pPrev; node* m_pNext; value_type m_Value; node() : m_pPrev(NULL), m_pNext(NULL) { } explicit node(value_type const& val) : m_pPrev(NULL), m_pNext(NULL), m_Value(val) { } static void* operator new(std::size_t size) { BOOST_ASSERT(size == sizeof(node)); return atomic_queue::g_Allocator.allocate(1); } static void operator delete(void* p) { atomic_queue::g_Allocator.deallocate(reinterpret_cast< node* >(p), 1); } }; private: typedef typename AllocatorT::BOOST_NESTED_TEMPLATE rebind< node >::other internal_allocator; struct implementation { node* m_pLast; #if !defined(BOOST_LOG_CAS_PTR) spin_mutex m_Mutex; #endif // !defined(BOOST_LOG_CAS_PTR) }; private: implementation m_Impl; // Padding to avoid false aliasing unsigned char padding [ ((sizeof(implementation) + BOOST_LOG_CPU_CACHE_LINE_SIZE - 1) / BOOST_LOG_CPU_CACHE_LINE_SIZE) * BOOST_LOG_CPU_CACHE_LINE_SIZE - sizeof(implementation) ]; static internal_allocator g_Allocator; public: //! Constructor. Creates an empty queue. atomic_queue() { m_Impl.m_pLast = NULL; } //! Destructor. Destroys all contained elements, if any. ~atomic_queue() { node* p = m_Impl.m_pLast; while (p != NULL) { node* prev = p->m_pPrev; delete p; p = prev; } } //! Puts an element to the queue. void push(value_type const& val) { #if !defined(BOOST_LOG_CAS_PTR) node* p = new node(val); lock_guard< spin_mutex > _(m_Impl.m_Mutex); p->m_pPrev = m_Impl.m_pLast; m_Impl.m_pLast = p; #else // !defined(BOOST_LOG_CAS_PTR) node* p = new node(val); node* last; do { last = static_cast< node* volatile& >(m_Impl.m_pLast); p->m_pPrev = last; } while (!BOOST_LOG_CAS_PTR(last, p, &m_Impl.m_pLast)); #endif // !defined(BOOST_LOG_CAS_PTR) } //! Attempts to put an element to the queue. bool try_push(value_type const& val) { #if !defined(BOOST_LOG_CAS_PTR) unique_lock< spin_mutex > lock(m_Impl.m_Mutex, try_to_lock); if (lock) { node* p = new node(val); p->m_pPrev = m_Impl.m_pLast; m_Impl.m_pLast = p; return true; } else return false; #else // !defined(BOOST_LOG_CAS_PTR) /* node* p = new node(val); node* last; last = static_cast< node* volatile& >(m_Impl.m_pLast); p->m_pPrev = last; bool done = BOOST_LOG_CAS_PTR(last, p, &m_Impl.m_pLast); if (!done) delete p; return done; */ // No point for the backoff scheme since we'll most likely lose more // on memory (de)allocation than on one or several loops of trial to CAS. push(val); return true; #endif // !defined(BOOST_LOG_CAS_PTR) } //! Removes all elements from the queue and returns a reference to the first element. std::pair< node*, node* > eject_nodes() { node* p; #if !defined(BOOST_LOG_CAS_PTR) { lock_guard< spin_mutex > _(m_Impl.m_Mutex); p = m_Impl.m_pLast; m_Impl.m_pLast = NULL; } #else // !defined(BOOST_LOG_CAS_PTR) do { p = static_cast< node* volatile& >(m_Impl.m_pLast); } while (!BOOST_LOG_CAS_PTR(p, static_cast< node* >(NULL), &m_Impl.m_pLast)); #endif // !defined(BOOST_LOG_CAS_PTR) // Reconstruct forward references std::pair< node*, node* > res(static_cast< node* >(NULL), p); if (p) { for (node* prev = p->m_pPrev; prev != NULL; prev = p->m_pPrev) { prev->m_pNext = p; p = prev; } res.first = p; } return res; } }; template< typename T, typename AllocatorT > typename atomic_queue< T, AllocatorT >::internal_allocator atomic_queue< T, AllocatorT >::g_Allocator; } // namespace aux BOOST_LOG_CLOSE_NAMESPACE // namespace log } // namespace boost #include #endif // BOOST_LOG_ATOMIC_QUEUE_HPP_INCLUDED_