/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtSystems module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qsystemreadwritelock_p.h" #include #include ///#define QSYSTEMREADWRITELOCK_DEBUG 1 #ifdef QSYSTEMREADWRITELOCK_DEBUG #include #endif QT_BEGIN_NAMESPACE class QSystemReadWriteLockPrivate { public: QSystemReadWriteLockPrivate(const QString &key, QSystemReadWriteLock::AccessMode mode); ~QSystemReadWriteLockPrivate(); int &accessCount(); unsigned int &waitingReaders(); unsigned int &waitingWriters(); QSystemReadWriteLock::SystemReadWriteLockError convertError(QSystemSemaphore::SystemSemaphoreError error); QSystemReadWriteLock::SystemReadWriteLockError convertError(QSharedMemory::SharedMemoryError error); QString m_key; QSharedMemory m_counts; QSystemSemaphore m_readerWait; QSystemSemaphore m_writerWait; bool m_isInitialized; QSystemReadWriteLock::SystemReadWriteLockError m_error; QString m_errorString; }; QSystemReadWriteLockPrivate::QSystemReadWriteLockPrivate(const QString &key, QSystemReadWriteLock::AccessMode mode) :m_key(key), m_counts(key + QLatin1String("_counts")), m_readerWait(key + QLatin1String("_readerWait"), 0, (mode==QSystemReadWriteLock::Create)?QSystemSemaphore::Create:QSystemSemaphore::Open), m_writerWait(key + QLatin1String("_writerWait"), 0, (mode==QSystemReadWriteLock::Create)?QSystemSemaphore::Create:QSystemSemaphore::Open), m_isInitialized(false), m_error(QSystemReadWriteLock::NoError), m_errorString(QString()) { bool isAttached = false; int retries=5; for (int i=0; i < retries; ++i){ if (m_counts.attach()) { isAttached = true; break; } else { if (m_counts.error() == QSharedMemory::NotFound) { if (m_counts.create(3 * sizeof(int))) { isAttached = true; break; } } } } if (!isAttached) { m_error = convertError(m_counts.error()); m_errorString = QObject::tr("QSystemReadWriteLockPrivate::QSystemReadWriteLockPrivate: " "Unable to create/attach to shared memory"); return; } else { if (mode == QSystemReadWriteLock::Create) { if(!m_counts.lock()) { m_error = convertError(m_counts.error()); m_errorString = QObject::tr("QSystemReadWriteLockPrivate::QSystemReadWriteLockPrivate: " "Unable to initialize shared memory"); return; } int * data = (int *)m_counts.data(); ::memset(data, 0, 3 * sizeof(int)); m_counts.unlock(); } m_isInitialized = true; } } QSystemReadWriteLockPrivate::~QSystemReadWriteLockPrivate() { } QSystemReadWriteLock::SystemReadWriteLockError QSystemReadWriteLockPrivate::convertError(QSystemSemaphore::SystemSemaphoreError error) { switch(error) { case QSystemSemaphore::NoError: return QSystemReadWriteLock::NoError; case QSystemSemaphore::PermissionDenied: return QSystemReadWriteLock::PermissionDenied; case QSystemSemaphore::KeyError: return QSystemReadWriteLock::KeyError; case QSystemSemaphore::NotFound: return QSystemReadWriteLock::NotFound; case QSystemSemaphore::OutOfResources: return QSystemReadWriteLock::OutOfResources; case QSystemSemaphore::AlreadyExists: case QSystemSemaphore::UnknownError: default: return QSystemReadWriteLock::UnknownError; } } QSystemReadWriteLock::SystemReadWriteLockError QSystemReadWriteLockPrivate::convertError(QSharedMemory::SharedMemoryError error) { switch(error){ case QSharedMemory::NoError: return QSystemReadWriteLock::NoError; case QSharedMemory::PermissionDenied: return QSystemReadWriteLock::PermissionDenied; case QSharedMemory::KeyError: return QSystemReadWriteLock::KeyError; case QSharedMemory::NotFound: return QSystemReadWriteLock::NotFound; case QSharedMemory::LockError: return QSystemReadWriteLock::LockError; case QSharedMemory::UnknownError: default: return QSystemReadWriteLock::UnknownError; } } QSystemReadWriteLock::QSystemReadWriteLock(const QString &key, AccessMode mode) :d(new QSystemReadWriteLockPrivate(key,mode)) { } QSystemReadWriteLock::~QSystemReadWriteLock() { Q_ASSERT(d); delete d; d = 0; } bool QSystemReadWriteLock::lockForRead() { #if defined QSYSTEMREADWRITELOCK_DEBUG qDebug() << "QSystemReadWriteLock::lockForRead() \naccessCount =" << d->accessCount() << "\twaitingWriters=" << d->waitingWriters() << "\twaitingReaders=" << d->waitingReaders(); #endif if (!d->m_isInitialized) { d->m_error = QSystemReadWriteLock::FailedToInitialize; d->m_errorString = QObject::tr("QSystemReadWriteLock::lockForRead(): cannot peform operation, lock initialization had not been successful"); return false; } if(!d->m_counts.lock()) { d->m_error = d->convertError(d->m_counts.error()); d->m_errorString = QObject::tr("QSystemReadWriteLock::lockForRead(): cannot perform operation, locking of shared memory was unsuccessful"); return false; } while(d->accessCount() < 0 || d->waitingWriters() > 0 ) { ++d->waitingReaders(); d->m_counts.unlock(); d->m_readerWait.acquire(); d->m_counts.lock(); --d->waitingReaders(); } ++d->accessCount(); Q_ASSERT_X(d->accessCount() > 0, "QSystemReadWriteLock::lockForRead()", "Overflow in lock counter"); #if defined QSYSTEMREADWRITELOCK_DEBUG qDebug() << "QSystemReadWriteLock::lockForRead() \naccessCount =" << d->accessCount() << "\twaitingWriters=" << d->waitingWriters() << "\twaitingReaders=" << d->waitingReaders(); #endif d->m_error = QSystemReadWriteLock::NoError; d->m_errorString.clear(); d->m_counts.unlock(); return true; } bool QSystemReadWriteLock::lockForWrite() { #if defined QSYSTEMREADWRITELOCK_DEBUG qDebug() << "QSystemReadWriteLock::lockForWrite() \naccessCount =" << d->accessCount() << "\twaitingWriters=" << d->waitingWriters() << "\twaitingReaders=" << d->waitingReaders(); #endif if (!d->m_isInitialized) { d->m_error = QSystemReadWriteLock::FailedToInitialize; d->m_errorString = QObject::tr("QSystemReadWriteLock::lockForWrite(): cannot peform operation, lock initialization had not been successful"); return false; } if(!d->m_counts.lock()) { d->m_error = d->convertError(d->m_counts.error()); d->m_errorString = QObject::tr("QSystemReadWriteLock::lockForwrite(): cannot perform operation, locking of shared memory was unsuccessful"); return false; } #if defined QSYSTEMREADWRITELOCK_DEBUG qDebug() << "QSystemReadWriteLock::lockForWrite() \naccessCount =" << d->accessCount() << "\twaitingWriters=" << d->waitingWriters() << "\twaitingReaders=" << d->waitingReaders(); #endif while(d->accessCount() != 0) { ++d->waitingWriters(); d->m_counts.unlock(); d->m_writerWait.acquire(); d->m_counts.lock(); --d->waitingWriters(); } --d->accessCount(); #if defined QSYSTEMREADWRITELOCK_DEBUG qDebug() << "QSystemReadWriteLock::lockForWrite() \naccessCount =" << d->accessCount() << "\twaitingWriters=" << d->waitingWriters() << "\twaitingReaders=" << d->waitingReaders(); #endif Q_ASSERT_X(d->accessCount() < 0, "QSystemReadWriteLock::lockForWrite()", "Overflow in lock counter"); d->m_error = QSystemReadWriteLock::NoError; d->m_errorString.clear(); d->m_counts.unlock(); return true; } void QSystemReadWriteLock::unlock() { if (!d->m_isInitialized) { d->m_error = QSystemReadWriteLock::FailedToInitialize; d->m_errorString = QObject::tr("QSystemReadWriteLock::unlock(): cannot peform operation, lock initialization had not been successful"); return; } if(!d->m_counts.lock()) { d->m_error = d->convertError(d->m_counts.error()); d->m_errorString = QObject::tr("QSystemReadWriteLock::lockForwrite(): cannot perform operation, locking of shared memory was unsuccessful"); return; } Q_ASSERT_X(d->accessCount() != 0, "QSystemReadWriteLock::unlock()", "Cannot unlock an unlocked lock"); bool unlocked = false; if (d->accessCount() > 0) { unlocked = --d->accessCount() == 0; } else if (d->accessCount() < 0 && ++d->accessCount() == 0) { unlocked = true; } if (unlocked) { if (d->waitingWriters() > 0 ) { d->m_writerWait.release(); } else if (d->waitingReaders()) { d->m_readerWait.release(d->waitingReaders()); } } #if defined QSYSTEMREADWRITELOCK_DEBUG qDebug() << "QSystemReadWriteLock::unlock() \naccessCount =" << d->accessCount() << "\twaitingWriters=" << d->waitingWriters() << "\twaitingReaders=" << d->waitingReaders(); #endif d->m_error = QSystemReadWriteLock::NoError; d->m_errorString.clear(); d->m_counts.unlock(); return; } int& QSystemReadWriteLockPrivate::accessCount() { return *((int *)m_counts.data()); } unsigned int &QSystemReadWriteLockPrivate::waitingReaders() { return *(((unsigned int*)m_counts.data())+1); } unsigned int &QSystemReadWriteLockPrivate::waitingWriters() { return *(((unsigned int*)m_counts.data())+2); } QSystemReadWriteLock::SystemReadWriteLockError QSystemReadWriteLock::error() const { return d->m_error; } QString QSystemReadWriteLock::errorString() const { return d->m_errorString; } QString QSystemReadWriteLock::key() const { return d->m_key; } QT_END_NAMESPACE