diff options
Diffstat (limited to 'share/qtcreator/qml/qmlpuppet/container/sharedmemory_unix.cpp')
-rw-r--r-- | share/qtcreator/qml/qmlpuppet/container/sharedmemory_unix.cpp | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/share/qtcreator/qml/qmlpuppet/container/sharedmemory_unix.cpp b/share/qtcreator/qml/qmlpuppet/container/sharedmemory_unix.cpp new file mode 100644 index 0000000000..d356dd31ef --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/container/sharedmemory_unix.cpp @@ -0,0 +1,500 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "sharedmemory.h" +#include <qdir.h> +#include <qcryptographichash.h> + +#include "qplatformdefs.h" + +#include <errno.h> + +#include <sys/mman.h> +#include <sys/stat.h> /* For mode constants */ +#include <fcntl.h> /* For O_* constants */ + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef Q_OS_OSX +#include <sys/posix_shm.h> +#endif +#include <fcntl.h> +#include <unistd.h> + +#include <private/qcore_unix_p.h> + +#ifdef Q_OS_OSX +#define NAME_MAX PSHMNAMLEN +#endif + +#ifndef QStringLiteral +#define QStringLiteral(str) QString::fromUtf8(str) +#endif + +#include <QRegExp> + +namespace QmlDesigner { + +class SharedMemoryLocker +{ +public: + SharedMemoryLocker(SharedMemory *sharedMemory) : m_sharedMemory(sharedMemory) + { + Q_ASSERT(m_sharedMemory); + } + + ~SharedMemoryLocker() + { + if (m_sharedMemory) + m_sharedMemory->unlock(); + } + + bool lock() + { + if (m_sharedMemory && m_sharedMemory->lock()) + return true; + m_sharedMemory = 0; + return false; + } + + bool tryLocker(const QString function) { + if (!lock()) { + m_sharedMemory->m_errorString = QStringLiteral("%1: unable to lock").arg(function); + m_sharedMemory->m_error = QSharedMemory::LockError; + return false; + } + return true; + } + +private: + SharedMemory *m_sharedMemory; +}; + +static QByteArray makePlatformSafeKey(const QString &key) +{ + if (key.isEmpty()) + return QByteArray(); + QByteArray data(QCryptographicHash::hash(key.toLatin1(), QCryptographicHash::Sha1).toBase64()); + + data = data.replace('+', '-'); + data = data.replace('/', '_'); + + data.truncate(31); // OS X is only supporting 31 byte long names + + return data; +} + + +SharedMemory::SharedMemory() + : m_memory(0), + m_size(0), + m_error(QSharedMemory::NoError), + m_systemSemaphore(QString()), + m_lockedByMe(false), + m_fileHandle(-1), + m_createdByMe(false) +{ +} + +SharedMemory::SharedMemory(const QString &key) + : m_memory(0), + m_size(0), + m_error(QSharedMemory::NoError), + m_systemSemaphore(QString()), + m_lockedByMe(false), + m_fileHandle(-1), + m_createdByMe(false) +{ + setKey(key); +} + +SharedMemory::~SharedMemory() +{ + if (m_memory) { + munmap(m_memory, m_size); + } + + if (m_fileHandle != -1) { + close(m_fileHandle); + if (m_createdByMe) + shm_unlink(m_nativeKey); + } + + setKey(QString()); +} + +void SharedMemory::setKey(const QString &key) +{ + if (key == m_key && makePlatformSafeKey(key) == m_nativeKey) + return; + + if (isAttached()) + detach(); + cleanHandleInternal(); + m_key = key; + m_nativeKey = makePlatformSafeKey(key); +} + +QString SharedMemory::key() const +{ + return m_key; +} + +bool SharedMemory::create(int size, QSharedMemory::AccessMode mode) +{ + if (!initKeyInternal()) + return false; + + + m_systemSemaphore.setKey(m_key, 1, QSystemSemaphore::Create); + + + QString function = QLatin1String("SharedMemory::create"); + + SharedMemoryLocker lock(this); + if (!m_key.isNull() && !lock.tryLocker(function)) + return false; + + if (size <= 0) { + m_error = QSharedMemory::InvalidSize; + m_errorString = QStringLiteral("%1: create size is less then 0").arg(function); + return false; + } + + return createInternal(mode ,size); +} + +int SharedMemory::size() const +{ + return m_size; +} + +bool SharedMemory::attach(QSharedMemory::AccessMode mode) +{ + if (isAttached() || !initKeyInternal()) + return false; + + SharedMemoryLocker lock(this); + if (!m_key.isNull() && !lock.tryLocker(QStringLiteral("SharedMemory::attach"))) + return false; + + if (isAttached() || !handle()) + return false; + + return attachInternal(mode); +} + + +bool SharedMemory::isAttached() const +{ + return (0 != m_memory); +} + +bool SharedMemory::detach() +{ + if (!isAttached()) + return false; + + SharedMemoryLocker lock(this); + if (!m_key.isNull() && !lock.tryLocker(QStringLiteral("SharedMemory::detach"))) + return false; + + return detachInternal(); +} + +void *SharedMemory::data() +{ + return m_memory; +} + +const void* SharedMemory::constData() const +{ + return m_memory; +} + +const void *SharedMemory::data() const +{ + return m_memory; +} + +bool SharedMemory::lock() +{ + if (m_lockedByMe) { + qWarning("SharedMemory::lock: already locked"); + return true; + } + if (m_systemSemaphore.acquire()) { + m_lockedByMe = true; + return true; + } + QString function = QStringLiteral("SharedMemory::lock"); + m_errorString = QStringLiteral("%1: unable to lock").arg(function); + m_error = QSharedMemory::LockError; + return false; +} + +bool SharedMemory::unlock() +{ + if (!m_lockedByMe) + return false; + m_lockedByMe = false; + if (m_systemSemaphore.release()) + return true; + QString function = QStringLiteral("SharedMemory::unlock"); + m_errorString = QStringLiteral("%1: unable to unlock").arg(function); + m_error = QSharedMemory::LockError; + return false; +} + +QSharedMemory::SharedMemoryError SharedMemory::error() const +{ + return m_error; +} + +QString SharedMemory::errorString() const +{ + return m_errorString; +} + +void SharedMemory::setErrorString(const QString &function) +{ + // EINVAL is handled in functions so they can give better error strings + switch (errno) { + case EACCES: + m_errorString = QStringLiteral("%1: permission denied").arg(function); + m_error = QSharedMemory::PermissionDenied; + break; + case EEXIST: + m_errorString = QStringLiteral("%1: already exists").arg(function); + m_error = QSharedMemory::AlreadyExists; + break; + case ENOENT: + m_errorString = QStringLiteral("%1: doesn't exist").arg(function); + m_error = QSharedMemory::NotFound; + break; + case EMFILE: + case ENOMEM: + case ENOSPC: + m_errorString = QStringLiteral("%1: out of resources").arg(function); + m_error = QSharedMemory::OutOfResources; + break; + default: + m_errorString = QStringLiteral("%1: unknown error %2").arg(function).arg(strerror(errno)); + m_error = QSharedMemory::UnknownError; + } +} + +bool SharedMemory::initKeyInternal() +{ + if (!cleanHandleInternal()) + return false; + + m_systemSemaphore.setKey(QString(), 1); + m_systemSemaphore.setKey(m_key, 1); + if (m_systemSemaphore.error() != QSystemSemaphore::NoError) { + m_errorString = QStringLiteral("SharedMemoryPrivate::initKey: unable to set key on lock"); + switch (m_systemSemaphore.error()) { + case QSystemSemaphore::PermissionDenied: + m_error = QSharedMemory::PermissionDenied; + break; + case QSystemSemaphore::KeyError: + m_error = QSharedMemory::KeyError; + break; + case QSystemSemaphore::AlreadyExists: + m_error = QSharedMemory::AlreadyExists; + break; + case QSystemSemaphore::NotFound: + m_error = QSharedMemory::NotFound; + break; + case QSystemSemaphore::OutOfResources: + m_error = QSharedMemory::OutOfResources; + break; + case QSystemSemaphore::UnknownError: + default: + m_error = QSharedMemory::UnknownError; + break; + } + + return false; + } + + m_errorString = QString(); + m_error = QSharedMemory::NoError; + return true; +} + +int SharedMemory::handle() +{ + return m_fileHandle; +} + +bool SharedMemory::cleanHandleInternal() +{ + m_fileHandle = -1; + return true; +} + +bool SharedMemory::createInternal(QSharedMemory::AccessMode mode, int size) +{ + detachInternal(); + +#ifdef Q_OS_OSX + if (m_fileHandle > -1 && m_createdByMe) { + close(m_fileHandle); + shm_unlink(m_nativeKey.constData()); + m_fileHandle = -1; + } +#endif + + if (m_fileHandle == -1) { + int permission = mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR; + m_fileHandle = shm_open(m_nativeKey.constData(), O_CREAT | permission, 0666); + + if (m_fileHandle == -1) { + switch (errno) { + case EINVAL: + m_errorString = QStringLiteral("QSharedMemory::create: key is not invalid"); + m_error = QSharedMemory::KeyError; + break; + case EMFILE: + m_errorString = QStringLiteral("QSharedMemory::create: maximum file limit reached"); + m_error = QSharedMemory::UnknownError; + break; + case ENAMETOOLONG: + m_errorString = QStringLiteral("QSharedMemory::create: key is to long"); + m_error = QSharedMemory::KeyError; + break; + default: + setErrorString(QStringLiteral("SharedMemory::create")); + } + return false; + } + + m_createdByMe = true; + } + + struct stat statBuffer; + fstat(m_fileHandle, &statBuffer); + int fileSize = statBuffer.st_size; + + if (fileSize < size) { + int returnValue = ftruncate(m_fileHandle, size); + if (returnValue == -1) { + switch (errno) { + case EFBIG: + m_errorString = QStringLiteral("QSharedMemory::create: size is to large"); + m_error = QSharedMemory::InvalidSize; + break; + default: + setErrorString(QStringLiteral("SharedMemory::create")); + } + + close(m_fileHandle); + shm_unlink(m_nativeKey.constData()); + m_fileHandle = -1; + m_size = 0; + + return false; + } + } + + int protection = mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_WRITE; + m_memory = mmap(0, size, protection, MAP_SHARED, m_fileHandle, 0); + + if (m_memory == MAP_FAILED) { + close(m_fileHandle); + shm_unlink(m_nativeKey.constData()); + m_memory = 0; + m_fileHandle = -1; + m_size = 0; + + return false; + } + + m_size = size; + + return true; +} + +bool SharedMemory::attachInternal(QSharedMemory::AccessMode mode) +{ + if (m_fileHandle == -1) { + int permission = mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR; + m_fileHandle = shm_open(m_nativeKey.constData(), permission, 0666); + if (m_fileHandle == -1) { + switch (errno) { + case EINVAL: + m_errorString = QStringLiteral("QSharedMemory::attach: key is invalid"); + m_error = QSharedMemory::KeyError; + break; + case EMFILE: + m_errorString = QStringLiteral("QSharedMemory::attach: maximum file limit reached"); + m_error = QSharedMemory::UnknownError; + break; + case ENAMETOOLONG: + m_errorString = QStringLiteral("QSharedMemory::attach: key is to long"); + m_error = QSharedMemory::KeyError; + break; + default: + setErrorString(QStringLiteral("SharedMemory::attach")); + } + return false; + } + } + + struct stat statBuffer; + fstat(m_fileHandle, &statBuffer); + int size = statBuffer.st_size; + + int protection = mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_WRITE; + m_memory = mmap(0, size, protection, MAP_SHARED, m_fileHandle, 0); + + if (m_memory == MAP_FAILED) { + m_memory = 0; + + return false; + } + + m_size = size; + + return true; +} + +bool SharedMemory::detachInternal() +{ + if (m_memory) { + munmap(m_memory, m_size); + m_memory = 0; + m_size = 0; + } + + return false; +} + +} // namespace QmlDesigner |