diff --git a/doc/changelog.qbk b/doc/changelog.qbk index dc00fdf..a69cd4d 100644 --- a/doc/changelog.qbk +++ b/doc/changelog.qbk @@ -15,6 +15,7 @@ * On Windows, the configuration macro `BOOST_LOG_USE_WINNT6_API` is no longer used by the library. The target Windows version should be configured by defining macro `BOOST_USE_WINAPI_VERSION` to the desired WinAPI version (e.g. 0x0501 for Windows XP). This macro is used in multiple different Boost libraries besides Boost.Log. * The default target Windows version is now Vista on compilers with recent enough Windows SDK and XP on others. The default is no longer chosen by Boost.Log but by the common WinAPI submodule used by different Boost libraries. +* Changed internal mutex type in the thread-safe queue implementation that is used in asynchronous sink frontends with the `unbounded_fifo_queue` policy. The updated queue has improved performance in case of heavy thread contention (hundreds and thousands of logging threads) on POSIX-compatible systems. [*Bug fixes:] diff --git a/include/boost/log/detail/adaptive_mutex.hpp b/include/boost/log/detail/adaptive_mutex.hpp index 866c4af..c15e89b 100644 --- a/include/boost/log/detail/adaptive_mutex.hpp +++ b/include/boost/log/detail/adaptive_mutex.hpp @@ -5,7 +5,7 @@ * http://www.boost.org/LICENSE_1_0.txt) */ /*! - * \file spin_mutex.hpp + * \file adaptive_mutex.hpp * \author Andrey Semashev * \date 01.08.2010 * @@ -13,8 +13,8 @@ * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. */ -#ifndef BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_ -#define BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_ +#ifndef BOOST_LOG_DETAIL_ADAPTIVE_MUTEX_HPP_INCLUDED_ +#define BOOST_LOG_DETAIL_ADAPTIVE_MUTEX_HPP_INCLUDED_ #include @@ -28,14 +28,14 @@ #include #if defined(BOOST_THREAD_POSIX) // This one can be defined by users, so it should go first -#define BOOST_LOG_SPIN_MUTEX_USE_PTHREAD +#define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD #elif defined(BOOST_WINDOWS) -#define BOOST_LOG_SPIN_MUTEX_USE_WINAPI +#define BOOST_LOG_ADAPTIVE_MUTEX_USE_WINAPI #elif defined(BOOST_HAS_PTHREADS) -#define BOOST_LOG_SPIN_MUTEX_USE_PTHREAD +#define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD #endif -#if defined(BOOST_LOG_SPIN_MUTEX_USE_WINAPI) +#if defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_WINAPI) #include @@ -97,8 +97,8 @@ BOOST_LOG_OPEN_NAMESPACE namespace aux { -//! A simple spinning mutex -class spin_mutex +//! A mutex that performs spinning or thread yielding in case of contention +class adaptive_mutex { private: enum state @@ -110,7 +110,7 @@ private: long m_State; public: - spin_mutex() : m_State(0) {} + adaptive_mutex() : m_State(0) {} bool try_lock() { @@ -157,8 +157,8 @@ public: } // Non-copyable - BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&)) - BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&)) + BOOST_DELETED_FUNCTION(adaptive_mutex(adaptive_mutex const&)) + BOOST_DELETED_FUNCTION(adaptive_mutex& operator= (adaptive_mutex const&)) }; #undef BOOST_LOG_PAUSE_OP @@ -172,94 +172,46 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #include -#elif defined(BOOST_LOG_SPIN_MUTEX_USE_PTHREAD) +#elif defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD) #include #include #include +#if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) +#define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP +#endif + namespace boost { BOOST_LOG_OPEN_NAMESPACE namespace aux { -#if defined(_POSIX_SPIN_LOCKS) && _POSIX_SPIN_LOCKS > 0 - -//! A simple spinning mutex -class spin_mutex -{ -private: - pthread_spinlock_t m_State; - -public: - spin_mutex() - { - const int err = pthread_spin_init(&m_State, PTHREAD_PROCESS_PRIVATE); - if (err != 0) - throw_exception< thread_resource_error >(err, "failed to initialize a spin mutex", "spin_mutex::spin_mutex()", __FILE__, __LINE__); - } - - ~spin_mutex() - { - BOOST_VERIFY(pthread_spin_destroy(&m_State) == 0); - } - - bool try_lock() - { - const int err = pthread_spin_trylock(&m_State); - if (err == 0) - return true; - if (err != EBUSY) - throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::try_lock()", __FILE__, __LINE__); - return false; - } - - void lock() - { - const int err = pthread_spin_lock(&m_State); - if (err != 0) - throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::lock()", __FILE__, __LINE__); - } - - void unlock() - { - BOOST_VERIFY(pthread_spin_unlock(&m_State) == 0); - } - - // Non-copyable - BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&)) - BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&)) - -private: - template< typename ExceptionT > - static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_exception(int err, const char* descr, const char* func, const char* file, int line) - { -#if !defined(BOOST_EXCEPTION_DISABLE) - boost::exception_detail::throw_exception_(ExceptionT(err, descr), func, file, line); -#else - boost::throw_exception(ExceptionT(err, descr)); -#endif - } -}; - -#else // defined(_POSIX_SPIN_LOCKS) - -//! Backup implementation in case if pthreads don't support spin locks -class spin_mutex +//! A mutex that performs spinning or thread yielding in case of contention +class adaptive_mutex { private: pthread_mutex_t m_State; public: - spin_mutex() + adaptive_mutex() { +#if defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP) + pthread_mutexattr_t attrs; + pthread_mutexattr_init(&attrs); + pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ADAPTIVE_NP); + + const int err = pthread_mutex_init(&m_State, &attrs); + pthread_mutexattr_destroy(&attrs); +#else const int err = pthread_mutex_init(&m_State, NULL); - if (err != 0) - throw_exception< thread_resource_error >(err, "failed to initialize a spin mutex", "spin_mutex::spin_mutex()", __FILE__, __LINE__); +#endif + if (BOOST_UNLIKELY(err != 0)) + throw_exception< thread_resource_error >(err, "Failed to initialize an adaptive mutex", "adaptive_mutex::adaptive_mutex()", __FILE__, __LINE__); } - ~spin_mutex() + ~adaptive_mutex() { BOOST_VERIFY(pthread_mutex_destroy(&m_State) == 0); } @@ -269,16 +221,16 @@ public: const int err = pthread_mutex_trylock(&m_State); if (err == 0) return true; - if (err != EBUSY) - throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::try_lock()", __FILE__, __LINE__); + if (BOOST_UNLIKELY(err != EBUSY)) + throw_exception< lock_error >(err, "Failed to lock an adaptive mutex", "adaptive_mutex::try_lock()", __FILE__, __LINE__); return false; } void lock() { const int err = pthread_mutex_lock(&m_State); - if (err != 0) - throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::lock()", __FILE__, __LINE__); + if (BOOST_UNLIKELY(err != 0)) + throw_exception< lock_error >(err, "Failed to lock an adaptive mutex", "adaptive_mutex::lock()", __FILE__, __LINE__); } void unlock() @@ -287,8 +239,8 @@ public: } // Non-copyable - BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&)) - BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&)) + BOOST_DELETED_FUNCTION(adaptive_mutex(adaptive_mutex const&)) + BOOST_DELETED_FUNCTION(adaptive_mutex& operator= (adaptive_mutex const&)) private: template< typename ExceptionT > @@ -302,8 +254,6 @@ private: } }; -#endif // defined(_POSIX_SPIN_LOCKS) - } // namespace aux BOOST_LOG_CLOSE_NAMESPACE // namespace log @@ -316,4 +266,4 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #endif // BOOST_LOG_NO_THREADS -#endif // BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_ +#endif // BOOST_LOG_DETAIL_ADAPTIVE_MUTEX_HPP_INCLUDED_ diff --git a/src/threadsafe_queue.cpp b/src/threadsafe_queue.cpp index 4354d06..f7049e6 100644 --- a/src/threadsafe_queue.cpp +++ b/src/threadsafe_queue.cpp @@ -46,7 +46,7 @@ class threadsafe_queue_impl_generic : { private: //! Mutex type to be used - typedef spin_mutex mutex_type; + typedef adaptive_mutex mutex_type; /*! * A structure that contains a pointer to the node and the associated mutex. @@ -116,10 +116,6 @@ public: } private: - // Copying and assignment are closed - threadsafe_queue_impl_generic(threadsafe_queue_impl_generic const&); - threadsafe_queue_impl_generic& operator= (threadsafe_queue_impl_generic const&); - BOOST_FORCEINLINE static void set_next(node_base* p, node_base* next) { p->next.data[0] = next; @@ -128,6 +124,10 @@ private: { return static_cast< node_base* >(p->next.data[0]); } + + // Copying and assignment are closed + BOOST_DELETED_FUNCTION(threadsafe_queue_impl_generic(threadsafe_queue_impl_generic const&)) + BOOST_DELETED_FUNCTION(threadsafe_queue_impl_generic& operator= (threadsafe_queue_impl_generic const&)) }; BOOST_LOG_API threadsafe_queue_impl* threadsafe_queue_impl::create(node_base* first_node) @@ -138,7 +138,7 @@ BOOST_LOG_API threadsafe_queue_impl* threadsafe_queue_impl::create(node_base* fi BOOST_LOG_API void* threadsafe_queue_impl::operator new (std::size_t size) { void* p = alignment::aligned_alloc(BOOST_LOG_CPU_CACHE_LINE_SIZE, size); - if (!p) + if (BOOST_UNLIKELY(!p)) BOOST_THROW_EXCEPTION(std::bad_alloc()); return p; }