/* Copyright 2009 David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef KDEVPLATFORM_SPINLOCK_H #define KDEVPLATFORM_SPINLOCK_H #include #include #include #include #include namespace KDevelop { inline uint timevalToMilliSeconds(timeval v) { return v.tv_sec * 1000 + v.tv_usec / 1000; } struct SpinLockData { SpinLockData() : needWake(false) { lock = 0; } QAtomicInt lock; //The variable that manages the lock volatile bool needWake; QMutex waitingMutex; QWaitCondition waitingCondition; }; ///This spin-lock uses a QWaitCondition together with a QMutex as fallback in case the lock is already taken. ///That means that this spin-lock is only more efficient than a QMutex when there is a low amount of lock-contention. ///When there is a high amount of lock-contention, QMutex should be more efficient. ///@note This implementation does not guarantee an optimal behavior as QMutex does: There is no guarantee /// that a waiting thread is really woken up in exactly the moment the lock becomes available. /// In _most_ cases it should work, but in worst-case the thread sleeps @p mSleep milliseconds. ///@param mSleep maximum number of milliseconds to sleep when waiting for the lock ///@param Timeout the timeount value template class SpinLock { public: SpinLock(SpinLockData& data) : m_data(data), m_locked(true) { if(m_data.lock.testAndSetOrdered(0, 1)) { //Success at the first try }else if(mTimeout) { //Spin with timeout //Start spinning timeval startTime; gettimeofday(&startTime, 0); while(!m_data.lock.testAndSetOrdered(0, 1)) { timeval currentTime; gettimeofday(¤tTime, 0); timeval waited; timersub(¤tTime, &startTime, &waited); if(mTimeout && timevalToMilliSeconds(waited) > mTimeout) { m_locked = false; break; } //We need to wait now QMutexLocker lock(&m_data.waitingMutex); m_data.needWake = true; m_data.waitingCondition.wait(&m_data.waitingMutex, mSleep); } }else{ //No timeout while(!m_data.lock.testAndSetOrdered(0, 1)) { //We need to wait now QMutexLocker lock(&m_data.waitingMutex); m_data.needWake = true; m_data.waitingCondition.wait(&m_data.waitingMutex, mSleep); } } } ~SpinLock() { if(m_locked) { bool needWake = m_data.needWake; m_data.needWake = false; m_data.lock = 0; if(needWake) m_data.waitingCondition.wakeOne(); } } //Returns whether the lock succeeded bool locked() const { return m_locked; } private: SpinLockData& m_data; bool m_locked; }; } #endif // KDEVPLATFORM_SPINLOCK_H