From 96995db4af6e1f5e9fe313e4c71a41fd939fedf8 Mon Sep 17 00:00:00 2001 From: Tobias Koenig Date: Mon, 22 Dec 2014 11:24:41 +0100 Subject: Add POSIX IPC support to QSystemSemaphore and QSharedMemory This patch is a forward-port from 4.8 branch (d869e1ad4b0007757e97046609de2097cd9e9c5d). Change-Id: I6ae36a5417d1176fbecf775668f6033b1cb22a94 Reviewed-by: Konstantin Ritt Reviewed-by: Thiago Macieira --- src/corelib/kernel/kernel.pri | 6 +- src/corelib/kernel/qsharedmemory.cpp | 2 + src/corelib/kernel/qsharedmemory_p.h | 4 + src/corelib/kernel/qsharedmemory_posix.cpp | 210 ++++++++++++++++++++ src/corelib/kernel/qsharedmemory_systemv.cpp | 254 ++++++++++++++++++++++++ src/corelib/kernel/qsharedmemory_unix.cpp | 199 +------------------ src/corelib/kernel/qsystemsemaphore.cpp | 2 +- src/corelib/kernel/qsystemsemaphore_p.h | 11 +- src/corelib/kernel/qsystemsemaphore_posix.cpp | 182 +++++++++++++++++ src/corelib/kernel/qsystemsemaphore_systemv.cpp | 198 ++++++++++++++++++ src/corelib/kernel/qsystemsemaphore_unix.cpp | 150 +------------- 11 files changed, 882 insertions(+), 336 deletions(-) create mode 100644 src/corelib/kernel/qsharedmemory_posix.cpp create mode 100644 src/corelib/kernel/qsharedmemory_systemv.cpp create mode 100644 src/corelib/kernel/qsystemsemaphore_posix.cpp create mode 100644 src/corelib/kernel/qsystemsemaphore_systemv.cpp (limited to 'src/corelib') diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 0ece4a395c..87367a9ee7 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -149,7 +149,11 @@ unix|integrity { contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) !android { - SOURCES += kernel/qsharedmemory_unix.cpp \ + SOURCES += kernel/qsharedmemory_posix.cpp \ + kernel/qsharedmemory_systemv.cpp \ + kernel/qsharedmemory_unix.cpp \ + kernel/qsystemsemaphore_posix.cpp \ + kernel/qsystemsemaphore_systemv.cpp \ kernel/qsystemsemaphore_unix.cpp } else { SOURCES += kernel/qsharedmemory_android.cpp \ diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp index 22d1148677..c5452fe8ab 100644 --- a/src/corelib/kernel/qsharedmemory.cpp +++ b/src/corelib/kernel/qsharedmemory.cpp @@ -69,6 +69,8 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, result.append(QLatin1String(hex)); #ifdef Q_OS_WIN return result; +#elif defined(QT_POSIX_IPC) + return QLatin1Char('/') + result; #else return QDir::tempPath() + QLatin1Char('/') + result; #endif diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h index 0e2f40bf8c..f1e29b9741 100644 --- a/src/corelib/kernel/qsharedmemory_p.h +++ b/src/corelib/kernel/qsharedmemory_p.h @@ -122,6 +122,8 @@ public: const QString &prefix = QLatin1String("qipc_sharedmemory_")); #ifdef Q_OS_WIN Qt::HANDLE handle(); +#elif defined(QT_POSIX_IPC) + int handle(); #else key_t handle(); #endif @@ -147,6 +149,8 @@ public: private: #ifdef Q_OS_WIN Qt::HANDLE hand; +#elif defined(QT_POSIX_IPC) + int hand; #else key_t unix_key; #endif diff --git a/src/corelib/kernel/qsharedmemory_posix.cpp b/src/corelib/kernel/qsharedmemory_posix.cpp new file mode 100644 index 0000000000..de45172295 --- /dev/null +++ b/src/corelib/kernel/qsharedmemory_posix.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Konstantin Ritt +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#include "qsharedmemory.h" +#include "qsharedmemory_p.h" +#include "qsystemsemaphore.h" +#include + +#include + +#ifdef QT_POSIX_IPC + +#ifndef QT_NO_SHAREDMEMORY +#include +#include +#include +#include +#include +#include + +#include "private/qcore_unix_p.h" + +QT_BEGIN_NAMESPACE + +int QSharedMemoryPrivate::handle() +{ + // don't allow making handles on empty keys + const QString safeKey = makePlatformSafeKey(key); + if (safeKey.isEmpty()) { + errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle")); + error = QSharedMemory::KeyError; + return 0; + } + + return 1; +} + +bool QSharedMemoryPrivate::cleanHandle() +{ + qt_safe_close(hand); + hand = -1; + + return true; +} + +bool QSharedMemoryPrivate::create(int size) +{ + if (!handle()) + return false; + + const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key)); + + int fd; + EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0600)); + if (fd == -1) { + const int errorNumber = errno; + const QString function = QLatin1String("QSharedMemory::create"); + switch (errorNumber) { + case ENAMETOOLONG: + case EINVAL: + errorString = QSharedMemory::tr("%1: bad name").arg(function); + error = QSharedMemory::KeyError; + break; + default: + setErrorString(function); + } + return false; + } + + // the size may only be set once + int ret; + EINTR_LOOP(ret, QT_FTRUNCATE(fd, size)); + if (ret == -1) { + setErrorString(QLatin1String("QSharedMemory::create (ftruncate)")); + qt_safe_close(fd); + return false; + } + + qt_safe_close(fd); + + return true; +} + +bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode) +{ + const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key)); + + const int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR); + const mode_t omode = (mode == QSharedMemory::ReadOnly ? 0400 : 0600); + + EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag, omode)); + if (hand == -1) { + const int errorNumber = errno; + const QString function = QLatin1String("QSharedMemory::attach (shm_open)"); + switch (errorNumber) { + case ENAMETOOLONG: + case EINVAL: + errorString = QSharedMemory::tr("%1: bad name").arg(function); + error = QSharedMemory::KeyError; + break; + default: + setErrorString(function); + } + hand = -1; + return false; + } + + // grab the size + QT_STATBUF st; + if (QT_FSTAT(hand, &st) == -1) { + setErrorString(QLatin1String("QSharedMemory::attach (fstat)")); + cleanHandle(); + return false; + } + size = st.st_size; + + // grab the memory + const int mprot = (mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE); + memory = QT_MMAP(0, size, mprot, MAP_SHARED, hand, 0); + if (memory == MAP_FAILED || !memory) { + setErrorString(QLatin1String("QSharedMemory::attach (mmap)")); + cleanHandle(); + memory = 0; + size = 0; + return false; + } + + return true; +} + +bool QSharedMemoryPrivate::detach() +{ + // detach from the memory segment + if (::munmap(memory, size) == -1) { + setErrorString(QLatin1String("QSharedMemory::detach (munmap)")); + return false; + } + memory = 0; + size = 0; + +#ifdef Q_OS_QNX + // On QNX the st_nlink field of struct stat contains the number of + // active shm_open() connections to the shared memory file, so we + // can use it to automatically clean up the file once the last + // user has detached from it. + + // get the number of current attachments + int shm_nattch = 0; + QT_STATBUF st; + if (QT_FSTAT(hand, &st) == 0) { + // subtract 2 from linkcount: one for our own open and one for the dir entry + shm_nattch = st.st_nlink - 2; + } + + cleanHandle(); + + // if there are no attachments then unlink the shared memory + if (shm_nattch == 0) { + const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key)); + if (::shm_unlink(shmName.constData()) == -1 && errno != ENOENT) + setErrorString(QLatin1String("QSharedMemory::detach (shm_unlink)")); + } +#else + // On non-QNX systems (tested Linux and Haiku), the st_nlink field is always 1, + // so we'll simply leak the shared memory files. + cleanHandle(); +#endif + + return true; +} + +QT_END_NAMESPACE + +#endif // QT_NO_SHAREDMEMORY + +#endif // QT_POSIX_IPC diff --git a/src/corelib/kernel/qsharedmemory_systemv.cpp b/src/corelib/kernel/qsharedmemory_systemv.cpp new file mode 100644 index 0000000000..b3fe300a43 --- /dev/null +++ b/src/corelib/kernel/qsharedmemory_systemv.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#include "qsharedmemory.h" +#include "qsharedmemory_p.h" +#include "qsystemsemaphore.h" +#include +#include + +#include + +#ifndef QT_POSIX_IPC + +#ifndef QT_NO_SHAREDMEMORY +#include +#include +#include +#include +#include +#include +#endif //QT_NO_SHAREDMEMORY + +#include "private/qcore_unix_p.h" + +#ifndef QT_NO_SHAREDMEMORY +QT_BEGIN_NAMESPACE + +/*! + \internal + + If not already made create the handle used for accessing the shared memory. +*/ +key_t QSharedMemoryPrivate::handle() +{ + // already made + if (unix_key) + return unix_key; + + // don't allow making handles on empty keys + if (nativeKey.isEmpty()) { + errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle:")); + error = QSharedMemory::KeyError; + return 0; + } + + // ftok requires that an actual file exists somewhere + if (!QFile::exists(nativeKey)) { + errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle:")); + error = QSharedMemory::NotFound; + return 0; + } + + unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q'); + if (-1 == unix_key) { + errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle:")); + error = QSharedMemory::KeyError; + unix_key = 0; + } + return unix_key; +} + +#endif // QT_NO_SHAREDMEMORY + +#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE)) +/*! + \internal + Creates the unix file if needed. + returns \c true if the unix file was created. + + -1 error + 0 already existed + 1 created + */ +int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName) +{ + int fd = qt_safe_open(QFile::encodeName(fileName).constData(), + O_EXCL | O_CREAT | O_RDWR, 0640); + if (-1 == fd) { + if (errno == EEXIST) + return 0; + return -1; + } else { + close(fd); + } + return 1; +} +#endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE + +#ifndef QT_NO_SHAREDMEMORY + +bool QSharedMemoryPrivate::cleanHandle() +{ + unix_key = 0; + return true; +} + +bool QSharedMemoryPrivate::create(int size) +{ + // build file if needed + bool createdFile = false; + int built = createUnixKeyFile(nativeKey); + if (built == -1) { + errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::handle:")); + error = QSharedMemory::KeyError; + return false; + } + if (built == 1) { + createdFile = true; + } + + // get handle + if (!handle()) { + if (createdFile) + QFile::remove(nativeKey); + return false; + } + + // create + if (-1 == shmget(unix_key, size, 0600 | IPC_CREAT | IPC_EXCL)) { + QString function = QLatin1String("QSharedMemory::create"); + switch (errno) { + case EINVAL: + errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg(QLatin1String("QSharedMemory::handle")); + error = QSharedMemory::InvalidSize; + break; + default: + setErrorString(function); + } + if (createdFile && error != QSharedMemory::AlreadyExists) + QFile::remove(nativeKey); + return false; + } + + return true; +} + +bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode) +{ + // grab the shared memory segment id + int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0400 : 0600)); + if (-1 == id) { + setErrorString(QLatin1String("QSharedMemory::attach (shmget)")); + return false; + } + + // grab the memory + memory = shmat(id, 0, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0)); + if ((void*) - 1 == memory) { + memory = 0; + setErrorString(QLatin1String("QSharedMemory::attach (shmat)")); + return false; + } + + // grab the size + shmid_ds shmid_ds; + if (!shmctl(id, IPC_STAT, &shmid_ds)) { + size = (int)shmid_ds.shm_segsz; + } else { + setErrorString(QLatin1String("QSharedMemory::attach (shmctl)")); + return false; + } + + return true; +} + +bool QSharedMemoryPrivate::detach() +{ + // detach from the memory segment + if (-1 == shmdt(memory)) { + QString function = QLatin1String("QSharedMemory::detach"); + switch (errno) { + case EINVAL: + errorString = QSharedMemory::tr("%1: not attached").arg(function); + error = QSharedMemory::NotFound; + break; + default: + setErrorString(function); + } + return false; + } + memory = 0; + size = 0; + + // Get the number of current attachments + int id = shmget(unix_key, 0, 0400); + cleanHandle(); + + struct shmid_ds shmid_ds; + if (0 != shmctl(id, IPC_STAT, &shmid_ds)) { + switch (errno) { + case EINVAL: + return true; + default: + return false; + } + } + // If there are no attachments then remove it. + if (shmid_ds.shm_nattch == 0) { + // mark for removal + if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) { + setErrorString(QLatin1String("QSharedMemory::remove")); + switch (errno) { + case EINVAL: + return true; + default: + return false; + } + } + + // remove file + if (!QFile::remove(nativeKey)) + return false; + } + return true; +} + + +QT_END_NAMESPACE + +#endif // QT_NO_SHAREDMEMORY + +#endif // QT_POSIX_IPC diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp index 8d616af4fe..5ba770c212 100644 --- a/src/corelib/kernel/qsharedmemory_unix.cpp +++ b/src/corelib/kernel/qsharedmemory_unix.cpp @@ -36,7 +36,6 @@ #include "qsharedmemory.h" #include "qsharedmemory_p.h" #include "qsystemsemaphore.h" -#include #include #include @@ -44,7 +43,11 @@ #ifndef QT_NO_SHAREDMEMORY #include #include +#ifndef QT_POSIX_IPC #include +#else +#include +#endif #include #include #include @@ -60,7 +63,11 @@ QSharedMemoryPrivate::QSharedMemoryPrivate() #ifndef QT_NO_SYSTEMSEMAPHORE systemSemaphore(QString()), lockedByMe(false), #endif +#ifndef QT_POSIX_IPC unix_key(0) +#else + hand(-1) +#endif { } @@ -95,196 +102,6 @@ void QSharedMemoryPrivate::setErrorString(const QString &function) } } -/*! - \internal - - If not already made create the handle used for accessing the shared memory. -*/ -key_t QSharedMemoryPrivate::handle() -{ - // already made - if (unix_key) - return unix_key; - - // don't allow making handles on empty keys - if (nativeKey.isEmpty()) { - errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle:")); - error = QSharedMemory::KeyError; - return 0; - } - - // ftok requires that an actual file exists somewhere - if (!QFile::exists(nativeKey)) { - errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle:")); - error = QSharedMemory::NotFound; - return 0; - } - - unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q'); - if (-1 == unix_key) { - errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle:")); - error = QSharedMemory::KeyError; - unix_key = 0; - } - return unix_key; -} - -#endif // QT_NO_SHAREDMEMORY - -#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE)) -/*! - \internal - Creates the unix file if needed. - returns \c true if the unix file was created. - - -1 error - 0 already existed - 1 created - */ -int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName) -{ - int fd = qt_safe_open(QFile::encodeName(fileName).constData(), - O_EXCL | O_CREAT | O_RDWR, 0640); - if (-1 == fd) { - if (errno == EEXIST) - return 0; - return -1; - } else { - close(fd); - } - return 1; -} -#endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE - -#ifndef QT_NO_SHAREDMEMORY - -bool QSharedMemoryPrivate::cleanHandle() -{ - unix_key = 0; - return true; -} - -bool QSharedMemoryPrivate::create(int size) -{ - // build file if needed - bool createdFile = false; - int built = createUnixKeyFile(nativeKey); - if (built == -1) { - errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::handle:")); - error = QSharedMemory::KeyError; - return false; - } - if (built == 1) { - createdFile = true; - } - - // get handle - if (!handle()) { - if (createdFile) - QFile::remove(nativeKey); - return false; - } - - // create - if (-1 == shmget(unix_key, size, 0600 | IPC_CREAT | IPC_EXCL)) { - QString function = QLatin1String("QSharedMemory::create"); - switch (errno) { - case EINVAL: - errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg(QLatin1String("QSharedMemory::handle")); - error = QSharedMemory::InvalidSize; - break; - default: - setErrorString(function); - } - if (createdFile && error != QSharedMemory::AlreadyExists) - QFile::remove(nativeKey); - return false; - } - - return true; -} - -bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode) -{ - // grab the shared memory segment id - int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0400 : 0600)); - if (-1 == id) { - setErrorString(QLatin1String("QSharedMemory::attach (shmget)")); - return false; - } - - // grab the memory - memory = shmat(id, 0, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0)); - if ((void*) - 1 == memory) { - memory = 0; - setErrorString(QLatin1String("QSharedMemory::attach (shmat)")); - return false; - } - - // grab the size - shmid_ds shmid_ds; - if (!shmctl(id, IPC_STAT, &shmid_ds)) { - size = (int)shmid_ds.shm_segsz; - } else { - setErrorString(QLatin1String("QSharedMemory::attach (shmctl)")); - return false; - } - - return true; -} - -bool QSharedMemoryPrivate::detach() -{ - // detach from the memory segment - if (-1 == shmdt(memory)) { - QString function = QLatin1String("QSharedMemory::detach"); - switch (errno) { - case EINVAL: - errorString = QSharedMemory::tr("%1: not attached").arg(function); - error = QSharedMemory::NotFound; - break; - default: - setErrorString(function); - } - return false; - } - memory = 0; - size = 0; - - // Get the number of current attachments - int id = shmget(unix_key, 0, 0400); - cleanHandle(); - - struct shmid_ds shmid_ds; - if (0 != shmctl(id, IPC_STAT, &shmid_ds)) { - switch (errno) { - case EINVAL: - return true; - default: - return false; - } - } - // If there are no attachments then remove it. - if (shmid_ds.shm_nattch == 0) { - // mark for removal - if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) { - setErrorString(QLatin1String("QSharedMemory::remove")); - switch (errno) { - case EINVAL: - return true; - default: - return false; - } - } - - // remove file - if (!QFile::remove(nativeKey)) - return false; - } - return true; -} - - QT_END_NAMESPACE #endif // QT_NO_SHAREDMEMORY diff --git a/src/corelib/kernel/qsystemsemaphore.cpp b/src/corelib/kernel/qsystemsemaphore.cpp index 27955885a8..bf6b0ef4cf 100644 --- a/src/corelib/kernel/qsystemsemaphore.cpp +++ b/src/corelib/kernel/qsystemsemaphore.cpp @@ -222,7 +222,7 @@ void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode m if (key == d->key && mode == Open) return; d->clearError(); -#if !defined(Q_OS_WIN) +#if !defined(Q_OS_WIN) && !defined(QT_POSIX_IPC) // optimization to not destroy/create the file & semaphore if (key == d->key && mode == Create && d->createdSemaphore && d->createdFile) { d->initialValue = initialValue; diff --git a/src/corelib/kernel/qsystemsemaphore_p.h b/src/corelib/kernel/qsystemsemaphore_p.h index d71ab4bc36..762c54480d 100644 --- a/src/corelib/kernel/qsystemsemaphore_p.h +++ b/src/corelib/kernel/qsystemsemaphore_p.h @@ -53,6 +53,9 @@ #ifndef Q_OS_WINCE # include #endif +#ifdef QT_POSIX_IPC +# include +#endif QT_BEGIN_NAMESPACE @@ -75,6 +78,9 @@ public: #ifdef Q_OS_WIN Qt::HANDLE handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); void setErrorString(const QString &function); +#elif defined(QT_POSIX_IPC) + bool handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); + void setErrorString(const QString &function); #else key_t handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); void setErrorString(const QString &function); @@ -88,11 +94,14 @@ public: #ifdef Q_OS_WIN Qt::HANDLE semaphore; Qt::HANDLE semaphoreLock; +#elif defined(QT_POSIX_IPC) + sem_t *semaphore; + bool createdSemaphore; #else + key_t unix_key; int semaphore; bool createdFile; bool createdSemaphore; - key_t unix_key; #endif QString errorString; QSystemSemaphore::SystemSemaphoreError error; diff --git a/src/corelib/kernel/qsystemsemaphore_posix.cpp b/src/corelib/kernel/qsystemsemaphore_posix.cpp new file mode 100644 index 0000000000..1ee643a6ab --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore_posix.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Konstantin Ritt +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsystemsemaphore.h" +#include "qsystemsemaphore_p.h" + +#include +#include +#include + +#ifdef QT_POSIX_IPC + +#ifndef QT_NO_SYSTEMSEMAPHORE + +#include +#include +#include +#include +#include + +#include "private/qcore_unix_p.h" + +// OpenBSD 4.2 doesn't define EIDRM, see BUGS section: +// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2 +#if defined(Q_OS_OPENBSD) && !defined(EIDRM) +#define EIDRM EINVAL +#endif + +QT_BEGIN_NAMESPACE + +bool QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) +{ + if (semaphore != SEM_FAILED) + return true; // we already have a semaphore + + if (fileName.isEmpty()) { + errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle")); + error = QSystemSemaphore::KeyError; + return false; + } + + const QByteArray semName = QFile::encodeName(fileName); + + // Always try with O_EXCL so we know whether we created the semaphore. + int oflag = O_CREAT | O_EXCL; + for (int tryNum = 0, maxTries = 1; tryNum < maxTries; ++tryNum) { + do { + semaphore = ::sem_open(semName.constData(), oflag, 0600, initialValue); + } while (semaphore == SEM_FAILED && errno == EINTR); + if (semaphore == SEM_FAILED && errno == EEXIST) { + if (mode == QSystemSemaphore::Create) { + if (::sem_unlink(semName.constData()) == -1 && errno != ENOENT) { + setErrorString(QLatin1String("QSystemSemaphore::handle (sem_unlink)")); + return false; + } + // Race condition: the semaphore might be recreated before + // we call sem_open again, so we'll retry several times. + maxTries = 3; + } else { + // Race condition: if it no longer exists at the next sem_open + // call, we won't realize we created it, so we'll leak it later. + oflag &= ~O_EXCL; + maxTries = 2; + } + } else { + break; + } + } + + if (semaphore == SEM_FAILED) { + setErrorString(QLatin1String("QSystemSemaphore::handle")); + return false; + } + + createdSemaphore = (oflag & O_EXCL) != 0; + + return true; +} + +void QSystemSemaphorePrivate::cleanHandle() +{ + if (semaphore != SEM_FAILED) { + if (::sem_close(semaphore) == -1) { + setErrorString(QLatin1String("QSystemSemaphore::cleanHandle (sem_close)")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::cleanHandle sem_close failed."); +#endif + } + semaphore = SEM_FAILED; + } + + if (createdSemaphore) { + if (::sem_unlink(QFile::encodeName(fileName).constData()) == -1 && errno != ENOENT) { + setErrorString(QLatin1String("QSystemSemaphore::cleanHandle (sem_unlink)")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::cleanHandle sem_unlink failed."); +#endif + } + createdSemaphore = false; + } +} + +bool QSystemSemaphorePrivate::modifySemaphore(int count) +{ + if (!handle()) + return false; + + if (count > 0) { + int cnt = count; + do { + if (::sem_post(semaphore) == -1) { + setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_post)")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::modify sem_post failed") << count << errno; +#endif + // rollback changes to preserve the SysV semaphore behavior + for ( ; cnt < count; ++cnt) { + int res; + EINTR_LOOP(res, ::sem_wait(semaphore)); + } + return false; + } + --cnt; + } while (cnt > 0); + } else { + int res; + EINTR_LOOP(res, ::sem_wait(semaphore)); + if (res == -1) { + // If the semaphore was removed be nice and create it and then modifySemaphore again + if (errno == EINVAL || errno == EIDRM) { + semaphore = SEM_FAILED; + return modifySemaphore(count); + } + setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_wait)")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::modify sem_wait failed") << count << errno; +#endif + return false; + } + } + + clearError(); + return true; +} + +QT_END_NAMESPACE + +#endif // QT_NO_SYSTEMSEMAPHORE + +#endif // QT_POSIX_IPC diff --git a/src/corelib/kernel/qsystemsemaphore_systemv.cpp b/src/corelib/kernel/qsystemsemaphore_systemv.cpp new file mode 100644 index 0000000000..f212f5e49f --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore_systemv.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsystemsemaphore.h" +#include "qsystemsemaphore_p.h" + +#include +#include +#include + +#ifndef QT_POSIX_IPC + +#ifndef QT_NO_SYSTEMSEMAPHORE + +#include +#include +#include +#include +#include + +#include "private/qcore_unix_p.h" + +// OpenBSD 4.2 doesn't define EIDRM, see BUGS section: +// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2 +#if defined(Q_OS_OPENBSD) && !defined(EIDRM) +#define EIDRM EINVAL +#endif + +QT_BEGIN_NAMESPACE + +/*! + \internal + + Setup unix_key + */ +key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) +{ + if (key.isEmpty()){ + errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + error = QSystemSemaphore::KeyError; + return -1; + } + + // ftok requires that an actual file exists somewhere + if (-1 != unix_key) + return unix_key; + + // Create the file needed for ftok + int built = QSharedMemoryPrivate::createUnixKeyFile(fileName); + if (-1 == built) { + errorString = QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + error = QSystemSemaphore::KeyError; + return -1; + } + createdFile = (1 == built); + + // Get the unix key for the created file + unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q'); + if (-1 == unix_key) { + errorString = QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + error = QSystemSemaphore::KeyError; + return -1; + } + + // Get semaphore + semaphore = semget(unix_key, 1, 0600 | IPC_CREAT | IPC_EXCL); + if (-1 == semaphore) { + if (errno == EEXIST) + semaphore = semget(unix_key, 1, 0600 | IPC_CREAT); + if (-1 == semaphore) { + setErrorString(QLatin1String("QSystemSemaphore::handle")); + cleanHandle(); + return -1; + } + } else { + createdSemaphore = true; + // Force cleanup of file, it is possible that it can be left over from a crash + createdFile = true; + } + + if (mode == QSystemSemaphore::Create) { + createdSemaphore = true; + createdFile = true; + } + + // Created semaphore so initialize its value. + if (createdSemaphore && initialValue >= 0) { + qt_semun init_op; + init_op.val = initialValue; + if (-1 == semctl(semaphore, 0, SETVAL, init_op)) { + setErrorString(QLatin1String("QSystemSemaphore::handle")); + cleanHandle(); + return -1; + } + } + + return unix_key; +} + +/*! + \internal + + Cleanup the unix_key + */ +void QSystemSemaphorePrivate::cleanHandle() +{ + unix_key = -1; + + // remove the file if we made it + if (createdFile) { + QFile::remove(fileName); + createdFile = false; + } + + if (createdSemaphore) { + if (-1 != semaphore) { + if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) { + setErrorString(QLatin1String("QSystemSemaphore::cleanHandle")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::cleanHandle semctl failed."); +#endif + } + semaphore = -1; + } + createdSemaphore = false; + } +} + +/*! + \internal + */ +bool QSystemSemaphorePrivate::modifySemaphore(int count) +{ + if (-1 == handle()) + return false; + + struct sembuf operation; + operation.sem_num = 0; + operation.sem_op = count; + operation.sem_flg = SEM_UNDO; + + int res; + EINTR_LOOP(res, semop(semaphore, &operation, 1)); + if (-1 == res) { + // If the semaphore was removed be nice and create it and then modifySemaphore again + if (errno == EINVAL || errno == EIDRM) { + semaphore = -1; + cleanHandle(); + handle(); + return modifySemaphore(count); + } + setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::modify failed") << count << semctl(semaphore, 0, GETVAL) << errno << EIDRM << EINVAL; +#endif + return false; + } + + clearError(); + return true; +} + + +QT_END_NAMESPACE + +#endif // QT_NO_SYSTEMSEMAPHORE + +#endif // QT_POSIX_IPC diff --git a/src/corelib/kernel/qsystemsemaphore_unix.cpp b/src/corelib/kernel/qsystemsemaphore_unix.cpp index 3d0cc76804..e8b3f14375 100644 --- a/src/corelib/kernel/qsystemsemaphore_unix.cpp +++ b/src/corelib/kernel/qsystemsemaphore_unix.cpp @@ -35,30 +35,29 @@ #include "qsystemsemaphore_p.h" #include -#include #include #ifndef QT_NO_SYSTEMSEMAPHORE #include #include +#ifndef QT_POSIX_IPC #include +#endif #include #include #include "private/qcore_unix_p.h" -// OpenBSD 4.2 doesn't define EIDRM, see BUGS section: -// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2 -#if defined(Q_OS_OPENBSD) && !defined(EIDRM) -#define EIDRM EINVAL -#endif - QT_BEGIN_NAMESPACE QSystemSemaphorePrivate::QSystemSemaphorePrivate() : - semaphore(-1), createdFile(false), - createdSemaphore(false), unix_key(-1), error(QSystemSemaphore::NoError) +#ifndef QT_POSIX_IPC + unix_key(-1), semaphore(-1), createdFile(false), +#else + semaphore(SEM_FAILED), +#endif // QT_POSIX_IPC + createdSemaphore(false), error(QSystemSemaphore::NoError) { } @@ -93,139 +92,6 @@ void QSystemSemaphorePrivate::setErrorString(const QString &function) } } -/*! - \internal - - Setup unix_key - */ -key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) -{ - if (key.isEmpty()){ - errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); - error = QSystemSemaphore::KeyError; - return -1; - } - - // ftok requires that an actual file exists somewhere - if (-1 != unix_key) - return unix_key; - - // Create the file needed for ftok - int built = QSharedMemoryPrivate::createUnixKeyFile(fileName); - if (-1 == built) { - errorString = QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); - error = QSystemSemaphore::KeyError; - return -1; - } - createdFile = (1 == built); - - // Get the unix key for the created file - unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q'); - if (-1 == unix_key) { - errorString = QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); - error = QSystemSemaphore::KeyError; - return -1; - } - - // Get semaphore - semaphore = semget(unix_key, 1, 0600 | IPC_CREAT | IPC_EXCL); - if (-1 == semaphore) { - if (errno == EEXIST) - semaphore = semget(unix_key, 1, 0600 | IPC_CREAT); - if (-1 == semaphore) { - setErrorString(QLatin1String("QSystemSemaphore::handle")); - cleanHandle(); - return -1; - } - } else { - createdSemaphore = true; - // Force cleanup of file, it is possible that it can be left over from a crash - createdFile = true; - } - - if (mode == QSystemSemaphore::Create) { - createdSemaphore = true; - createdFile = true; - } - - // Created semaphore so initialize its value. - if (createdSemaphore && initialValue >= 0) { - qt_semun init_op; - init_op.val = initialValue; - if (-1 == semctl(semaphore, 0, SETVAL, init_op)) { - setErrorString(QLatin1String("QSystemSemaphore::handle")); - cleanHandle(); - return -1; - } - } - - return unix_key; -} - -/*! - \internal - - Cleanup the unix_key - */ -void QSystemSemaphorePrivate::cleanHandle() -{ - unix_key = -1; - - // remove the file if we made it - if (createdFile) { - QFile::remove(fileName); - createdFile = false; - } - - if (createdSemaphore) { - if (-1 != semaphore) { - if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) { - setErrorString(QLatin1String("QSystemSemaphore::cleanHandle")); -#if defined QSYSTEMSEMAPHORE_DEBUG - qDebug() << QLatin1String("QSystemSemaphore::cleanHandle semctl failed."); -#endif - } - semaphore = -1; - } - createdSemaphore = false; - } -} - -/*! - \internal - */ -bool QSystemSemaphorePrivate::modifySemaphore(int count) -{ - if (-1 == handle()) - return false; - - struct sembuf operation; - operation.sem_num = 0; - operation.sem_op = count; - operation.sem_flg = SEM_UNDO; - - int res; - EINTR_LOOP(res, semop(semaphore, &operation, 1)); - if (-1 == res) { - // If the semaphore was removed be nice and create it and then modifySemaphore again - if (errno == EINVAL || errno == EIDRM) { - semaphore = -1; - cleanHandle(); - handle(); - return modifySemaphore(count); - } - setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore")); -#if defined QSYSTEMSEMAPHORE_DEBUG - qDebug() << QLatin1String("QSystemSemaphore::modify failed") << count << semctl(semaphore, 0, GETVAL) << errno << EIDRM << EINVAL; -#endif - return false; - } - - clearError(); - return true; -} - - QT_END_NAMESPACE #endif // QT_NO_SYSTEMSEMAPHORE -- cgit v1.2.3