diff --git a/src/blackmisc/lockfree.cpp b/src/blackmisc/lockfree.cpp new file mode 100644 index 000000000..d7443da77 --- /dev/null +++ b/src/blackmisc/lockfree.cpp @@ -0,0 +1,23 @@ +/* Copyright (C) 2015 +* swift project Community / Contributors +* +* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level +* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, +* including this file, may be copied, modified, propagated, or distributed except according to the terms +* contained in the LICENSE file. +*/ + +#include "lockfree.h" + +namespace BlackMisc +{ + namespace Private + { + // this is only needed for GCC <= 4.9, but we define it for all compilers to avoid warning about empty cpp file + QMutex *atomicSharedPtrMutex() + { + static QMutex mutex(QMutex::Recursive); + return &mutex; + } + } +}; diff --git a/src/blackmisc/lockfree.h b/src/blackmisc/lockfree.h index 225d72b77..fcf6b633f 100644 --- a/src/blackmisc/lockfree.h +++ b/src/blackmisc/lockfree.h @@ -14,6 +14,7 @@ #include "blackmisc/variant.h" #include +#include #include #include #include @@ -33,6 +34,45 @@ namespace BlackMisc template class LockFree; + namespace Private + { + //! \private + BLACKMISC_EXPORT QMutex *atomicSharedPtrMutex(); + + //! \private + template + std::shared_ptr atomic_load(const std::shared_ptr* ptr) + { +#if defined(Q_CC_GNU) && __GNUC__ <= 4 + QMutexLocker lock(BlackMisc::Private::atomicSharedPtrMutex()); + return *ptr; +#else + return std::atomic_load(ptr); +#endif + } + + //! \private + template + bool atomic_compare_exchange_strong(std::shared_ptr* ptr, std::shared_ptr* exp, std::shared_ptr des) + { +#if defined(Q_CC_GNU) && __GNUC__ <= 4 + std::shared_ptr tmp; + QMutexLocker lock(BlackMisc::Private::atomicSharedPtrMutex()); + if (*ptr == *exp && ! ptr->owner_before(*exp) && ! exp->owner_before(*ptr)) + { + tmp = std::move(*ptr); + *ptr = std::move(des); + return true; + } + tmp = std::move(*exp); + *exp = *ptr; + return false; +#else + return std::atomic_compare_exchange_strong(ptr, exp, des); +#endif + } + } + /*! * Return value of LockFree::read(). Allows any one thread to safely read from the lock-free object. */ @@ -103,7 +143,7 @@ namespace BlackMisc ~LockFreeUniqueWriter() Q_DECL_NOEXCEPT { if (m_ptr.use_count() == 0) { return; } // *this has been moved from - bool success = std::atomic_compare_exchange_strong(m_now, &m_old, std::shared_ptr(m_ptr)); + bool success = Private::atomic_compare_exchange_strong(m_now, &m_old, std::shared_ptr(m_ptr)); Q_ASSERT_X(success, qPrintable(name()), "UniqueWriter detected simultaneous writes"); Q_UNUSED(success); } @@ -149,13 +189,13 @@ namespace BlackMisc operator bool() { Q_ASSERT_X(m_ptr.use_count() > 0, qPrintable(name()), "SharedWriter tried to commit changes twice"); - if (std::atomic_compare_exchange_strong(m_now, &m_old, std::shared_ptr(m_ptr))) + if (Private::atomic_compare_exchange_strong(m_now, &m_old, std::shared_ptr(m_ptr))) { m_ptr.reset(); return true; } QThread::msleep(1); - m_old = std::atomic_load(m_now); + m_old = Private::atomic_load(m_now); m_ptr = std::make_shared(*m_old); return false; } @@ -222,19 +262,19 @@ namespace BlackMisc //! Return an object which can read the current value. LockFreeReader read() const { - return { std::atomic_load(&m_ptr) }; + return { Private::atomic_load(&m_ptr) }; } //! Return an object which can write a new value, as long as there are no other writes. LockFreeUniqueWriter uniqueWrite() { - return { std::atomic_load(&m_ptr), &m_ptr }; + return { Private::atomic_load(&m_ptr), &m_ptr }; }; //! Return an object which can write a new value, even if there are other writes. LockFreeSharedWriter sharedWrite() { - return { std::atomic_load(&m_ptr), &m_ptr }; + return { Private::atomic_load(&m_ptr), &m_ptr }; }; //! Pass the current value to the functor inspector, and return whatever inspector returns.