IPC: add native key support to QSystemSemaphore

And deprecate the non-native key support API. Qt 7 may not even store
the old, non-native Qt.

Documentation in a new commit, when the dust settles.

Change-Id: I12a088d1ae424825abd3fffd171d2b549eeed040
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Thiago Macieira 2022-10-11 17:37:59 -07:00
parent 510e0914c0
commit 3ae052d3bb
12 changed files with 261 additions and 131 deletions

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QSystemSemaphore sem("market", 3, QSystemSemaphore::Create);
QSystemSemaphore sem(QSystemSemaphore::platformSafeKey("market"), 3, QSystemSemaphore::Create);
// resources available == 3
sem.acquire(); // resources available == 2
sem.acquire(); // resources available == 1
@ -13,7 +13,7 @@ sem.release(2); // resources available == 3
//! [1]
QSystemSemaphore sem("market", 5, QSystemSemaphore::Create);
QSystemSemaphore sem(QSystemSemaphore::platformSafeKey("market"), 5, QSystemSemaphore::Create);
for (int i = 0; i < 5; ++i) // acquire all 5 resources
sem.acquire();
sem.release(5); // release the 5 resources

View File

@ -12,11 +12,35 @@
# include <qt_windows.h>
#endif
#ifndef MAX_PATH
# define MAX_PATH PATH_MAX
#endif
QT_BEGIN_NAMESPACE
#if QT_CONFIG(sharedmemory)
using namespace QtIpcCommon;
using namespace Qt::StringLiterals;
#if QT_CONFIG(sharedmemory)
#if QT_CONFIG(systemsemaphore)
inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const
{
if (isIpcSupported(IpcType::SharedMemory, QNativeIpcKey::Type::Windows)
&& nativeKey.type() == QNativeIpcKey::Type::Windows) {
// native keys are plain kernel object names, limited to MAX_PATH
auto suffix = "_sem"_L1;
QString semkey = nativeKey.nativeKey();
semkey.truncate(MAX_PATH - suffix.size() - 1);
semkey += suffix;
return { semkey, QNativeIpcKey::Type::Windows };
}
// System V and POSIX keys appear to operate in different namespaces, so we
// can just use the same native key
return nativeKey;
}
#endif
/*!
\class QSharedMemory
@ -293,8 +317,8 @@ bool QSharedMemoryPrivate::initKey()
if (!cleanHandle())
return false;
#if QT_CONFIG(systemsemaphore)
systemSemaphore.setKey(QString(), 1);
systemSemaphore.setKey(key, 1);
systemSemaphore.setNativeKey(QNativeIpcKey(), 1);
systemSemaphore.setNativeKey(semaphoreNativeKey(), 1);
if (systemSemaphore.error() != QSystemSemaphore::NoError) {
QString function = "QSharedMemoryPrivate::initKey"_L1;
errorString = QSharedMemory::tr("%1: unable to set key on lock").arg(function);

View File

@ -115,7 +115,7 @@ public:
QString nativeKey;
QString errorString;
#if QT_CONFIG(systemsemaphore)
QSystemSemaphore systemSemaphore{QString()};
QSystemSemaphore systemSemaphore{ QNativeIpcKey() };
bool lockedByMe = false;
#endif
QSharedMemory::SharedMemoryError error = QSharedMemory::NoError;
@ -166,6 +166,7 @@ public:
}
return true;
}
QNativeIpcKey semaphoreNativeKey() const;
#endif // QT_CONFIG(systemsemaphore)
};

View File

@ -7,6 +7,9 @@
QT_BEGIN_NAMESPACE
using namespace QtIpcCommon;
using namespace Qt::StringLiterals;
#if QT_CONFIG(systemsemaphore)
/*!
@ -16,12 +19,10 @@ QT_BEGIN_NAMESPACE
\brief The QSystemSemaphore class provides a general counting system semaphore.
A semaphore is a generalization of a mutex. While a mutex can be
locked only once, a semaphore can be acquired multiple times.
Typically, a semaphore is used to protect a certain number of
identical resources.
A system semaphore is a generalization of \l QSemaphore. Typically, a
semaphore is used to protect a certain number of identical resources.
Like its lighter counterpart QSemaphore, a QSystemSemaphore can be
Like its lighter counterpart, a QSystemSemaphore can be
accessed from multiple \l {QThread} {threads}. Unlike QSemaphore, a
QSystemSemaphore can also be accessed from multiple \l {QProcess}
{processes}. This means QSystemSemaphore is a much heavier class, so
@ -38,66 +39,43 @@ QT_BEGIN_NAMESPACE
process. The function can also be called with a parameter n > 1,
which releases n resources.
A system semaphore is created with a string key that other processes
can use to use the same semaphore.
System semaphores are identified by a key, represented by \l QNativeIpcKey. A
key can be created in a cross-platform manner by using platformSafeKey(). A
system semaphore is created by the QSystemSemaphore constructor when passed
an access mode parameter of AccessMode::Create. Once it is created, other
processes may attach to the same semaphore using the same key and an access
mode parameter of AccessMode::Open.
Example: Create a system semaphore
\snippet code/src_corelib_kernel_qsystemsemaphore.cpp 0
A typical application of system semaphores is for controlling access
to a circular buffer shared by a producer process and a consumer
processes.
For details on the key types, platform-specific limitations, and
interoperability with older or non-Qt applications, see the \l{Native IPC
Key} documentation. That includes important information for sandboxed
applications on Apple platforms, including all apps obtained via the Apple
App Store.
\section1 Platform-Specific Behavior
When using this class, be aware of the following platform
differences:
\b{Windows:} QSystemSemaphore does not own its underlying system
semaphore. Windows owns it. This means that when all instances of
QSystemSemaphore for a particular key have been destroyed, either by
having their destructors called, or because one or more processes
crash, Windows removes the underlying system semaphore.
\b{Unix:}
\list
\li QSystemSemaphore owns the underlying system semaphore
in Unix systems. This means that the last process having an instance of
QSystemSemaphore for a particular key must remove the underlying
system semaphore in its destructor. If the last process crashes
without running the QSystemSemaphore destructor, Unix does not
automatically remove the underlying system semaphore, and the
semaphore survives the crash. A subsequent process that constructs a
QSystemSemaphore with the same key will then be given the existing
system semaphore. In that case, if the QSystemSemaphore constructor
has specified its \l {QSystemSemaphore::AccessMode} {access mode} as
\l {QSystemSemaphore::} {Open}, its initial resource count will not
be reset to the one provided but remain set to the value it received
in the crashed process. To protect against this, the first process
to create a semaphore for a particular key (usually a server), must
pass its \l {QSystemSemaphore::AccessMode} {access mode} as \l
{QSystemSemaphore::} {Create}, which will force Unix to reset the
resource count in the underlying system semaphore.
\li When a process using QSystemSemaphore terminates for
any reason, Unix automatically reverses the effect of all acquire
operations that were not released. Thus if the process acquires a
resource and then exits without releasing it, Unix will release that
resource.
\endlist
\b{Apple platforms:} Sandboxed applications (including apps
shipped through the Apple App Store) require the key to
be in the form \c {<application group identifier>/<custom identifier>},
as documented \l {https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24}
{here} and \l {https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups}
{here}, and the key length is limited to 30 characters.
\sa QSharedMemory, QSemaphore
\sa Inter-Process Communication, QSharedMemory, QSemaphore
*/
/*!
\deprecated
Requests a system semaphore identified by the legacy key \a key. This
constructor does the same as:
\code
QSystemSemaphore(QSystemSemaphore::legacyNativeKey(key), initialValue, mode)
\endcode
except that it stores the legacy native key to retrieve using key().
*/
QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode)
: QSystemSemaphore(legacyNativeKey(key), initialValue, mode)
{
d->legacyKey = key;
}
/*!
Requests a system semaphore for the specified \a key. The parameters
\a initialValue and \a mode are used according to the following
@ -134,10 +112,10 @@ QT_BEGIN_NAMESPACE
\sa acquire(), key()
*/
QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode)
QSystemSemaphore::QSystemSemaphore(const QNativeIpcKey &key, int initialValue, AccessMode mode)
: d(new QSystemSemaphorePrivate)
{
setKey(key, initialValue, mode);
setNativeKey(key, initialValue, mode);
}
/*!
@ -192,16 +170,25 @@ QSystemSemaphore::~QSystemSemaphore()
create a new semaphore with the new \a key. The \a initialValue and
\a mode parameters are as defined for the constructor.
\sa QSystemSemaphore(), key()
This function is useful if the native key was shared from another process.
See \l{Native IPC Keys} for more information.
\sa QSystemSemaphore(), nativeKey()
*/
void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
void QSystemSemaphore::setNativeKey(const QNativeIpcKey &key, int initialValue, AccessMode mode)
{
if (key == d->key && mode == Open)
if (key == d->nativeKey && mode == Open)
return;
if (!isKeyTypeSupported(key.type())) {
d->setError(KeyError, tr("%1: unsupported key type")
.arg("QSystemSemaphore::setNativeKey"_L1));
return;
}
d->clearError();
#if !defined(Q_OS_WIN) && !defined(QT_POSIX_IPC)
// optimization to not destroy/create the file & semaphore
if (key == d->key && mode == Create && d->backend.createdSemaphore && d->backend.createdFile) {
if (key == d->nativeKey && mode == Create && d->backend.createdSemaphore && d->backend.createdFile) {
d->initialValue = initialValue;
d->backend.unix_key = -1;
d->handle(mode);
@ -209,22 +196,55 @@ void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode m
}
#endif
d->cleanHandle();
d->key = key;
d->nativeKey = key;
d->initialValue = initialValue;
// cache the file name so it doesn't have to be generated all the time.
d->fileName = d->makeKeyFileName();
d->handle(mode);
d->legacyKey.clear();
}
/*!
Returns the key assigned to this system semaphore. The key is the
name by which the semaphore can be accessed from other processes.
You can use the native key to access system semaphores that have not been
created by Qt, or to grant access to non-Qt applications. See \l{Native IPC
Keys} for more information.
\sa setNativeKey()
*/
QNativeIpcKey QSystemSemaphore::nativeIpcKey() const
{
return d->nativeKey;
}
/*!
\deprecated
This function works the same as the constructor. It reconstructs
this QSystemSemaphore object. If the new \a key is different from
the old key, calling this function is like calling the destructor of
the semaphore with the old key, then calling the constructor to
create a new semaphore with the new \a key. The \a initialValue and
\a mode parameters are as defined for the constructor.
\sa QSystemSemaphore(), key()
*/
void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
{
setNativeKey(legacyNativeKey(key), initialValue, mode);
d->legacyKey = key;
}
/*!
\deprecated
Returns the legacy key assigned to this system semaphore. The key is the
name by which the semaphore can be accessed from other processes.
\sa setKey()
*/
QString QSystemSemaphore::key() const
{
return d->key;
return d->legacyKey;
}
/*!
@ -360,6 +380,21 @@ void QSystemSemaphorePrivate::setUnixErrorString(QLatin1StringView function)
}
}
bool QSystemSemaphore::isKeyTypeSupported(QNativeIpcKey::Type type)
{
return QSystemSemaphorePrivate::DefaultBackend::supports(type);
}
QNativeIpcKey QSystemSemaphore::platformSafeKey(const QString &key, QNativeIpcKey::Type type)
{
return { QtIpcCommon::platformSafeKey(key, IpcType::SystemSemaphore, type), type };
}
QNativeIpcKey QSystemSemaphore::legacyNativeKey(const QString &key, QNativeIpcKey::Type type)
{
return { legacyPlatformSafeKey(key, IpcType::SystemSemaphore, type), type };
}
#endif // QT_CONFIG(systemsemaphore)
QT_END_NAMESPACE

View File

@ -5,12 +5,12 @@
#define QSYSTEMSEMAPHORE_H
#include <QtCore/qcoreapplication.h>
#include <QtCore/qtipccommon.h>
#include <QtCore/qstring.h>
#include <QtCore/qscopedpointer.h>
QT_BEGIN_NAMESPACE
#if QT_CONFIG(systemsemaphore)
class QSystemSemaphorePrivate;
@ -38,11 +38,23 @@ public:
UnknownError
};
QSystemSemaphore(const QString &key, int initialValue = 0, AccessMode mode = Open);
QSystemSemaphore(const QNativeIpcKey &key, int initialValue = 0, AccessMode = Open);
~QSystemSemaphore();
void setNativeKey(const QNativeIpcKey &key, int initialValue = 0, AccessMode = Open);
void setNativeKey(const QString &key, int initialValue = 0, AccessMode mode = Open,
QNativeIpcKey::Type type = QNativeIpcKey::legacyDefaultTypeForOs())
{ setNativeKey({ key, type }, initialValue, mode); }
QNativeIpcKey nativeIpcKey() const;
#if QT_DEPRECATED_SINCE(6, 9)
QT_DEPRECATED_VERSION_X_6_9("Please refer to 'Native IPC Key' documentation")
QSystemSemaphore(const QString &key, int initialValue = 0, AccessMode mode = Open);
QT_DEPRECATED_VERSION_X_6_9("Please refer to 'Native IPC Key' documentation")
void setKey(const QString &key, int initialValue = 0, AccessMode mode = Open);
QT_DEPRECATED_VERSION_X_6_9("Please refer to 'Native IPC Key' documentation")
QString key() const;
#endif
bool acquire();
bool release(int n = 1);
@ -50,6 +62,12 @@ public:
SystemSemaphoreError error() const;
QString errorString() const;
static bool isKeyTypeSupported(QNativeIpcKey::Type type) Q_DECL_CONST_FUNCTION;
static QNativeIpcKey platformSafeKey(const QString &key,
QNativeIpcKey::Type type = QNativeIpcKey::DefaultTypeForOs);
static QNativeIpcKey legacyNativeKey(const QString &key,
QNativeIpcKey::Type type = QNativeIpcKey::legacyDefaultTypeForOs());
private:
Q_DISABLE_COPY(QSystemSemaphore)
QScopedPointer<QSystemSemaphorePrivate> d;

View File

@ -42,6 +42,9 @@ class QSystemSemaphorePrivate;
struct QSystemSemaphorePosix
{
static bool supports(QNativeIpcKey::Type type)
{ return type == QNativeIpcKey::Type::PosixRealtime; }
bool handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode);
void cleanHandle(QSystemSemaphorePrivate *self);
bool modifySemaphore(QSystemSemaphorePrivate *self, int count);
@ -52,11 +55,15 @@ struct QSystemSemaphorePosix
struct QSystemSemaphoreSystemV
{
static bool supports(QNativeIpcKey::Type type)
{ return quint16(type) <= 0xff; }
#if QT_CONFIG(sysv_sem)
key_t handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode);
void cleanHandle(QSystemSemaphorePrivate *self);
bool modifySemaphore(QSystemSemaphorePrivate *self, int count);
QByteArray nativeKeyFile;
key_t unix_key = -1;
int semaphore = -1;
bool createdFile = false;
@ -66,6 +73,9 @@ struct QSystemSemaphoreSystemV
struct QSystemSemaphoreWin32
{
static bool supports(QNativeIpcKey::Type type)
{ return type == QNativeIpcKey::Type::Windows; }
//#ifdef Q_OS_WIN32 but there's nothing Windows-specific in the header
Qt::HANDLE handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode);
void cleanHandle(QSystemSemaphorePrivate *self);
@ -77,11 +87,6 @@ struct QSystemSemaphoreWin32
class QSystemSemaphorePrivate
{
public:
QString makeKeyFileName()
{
return QtIpcCommon::legacyPlatformSafeKey(key, QtIpcCommon::IpcType::SystemSemaphore);
}
void setWindowsErrorString(QLatin1StringView function); // Windows only
void setUnixErrorString(QLatin1StringView function);
inline void setError(QSystemSemaphore::SystemSemaphoreError e, const QString &message)
@ -89,8 +94,7 @@ public:
inline void clearError()
{ setError(QSystemSemaphore::NoError, QString()); }
QString key;
QString fileName;
QNativeIpcKey nativeKey;
QString errorString;
int initialValue;
QSystemSemaphore::SystemSemaphoreError error = QSystemSemaphore::NoError;
@ -116,6 +120,8 @@ public:
{
return backend.modifySemaphore(this, count);
}
QString legacyKey; // deprecated
};
QT_END_NAMESPACE

View File

@ -34,15 +34,14 @@ bool QSystemSemaphorePosix::handle(QSystemSemaphorePrivate *self, QSystemSemapho
if (semaphore != SEM_FAILED)
return true; // we already have a semaphore
if (self->fileName.isEmpty()) {
const QByteArray semName = QFile::encodeName(self->nativeKey.nativeKey());
if (semName.isEmpty()) {
self->setError(QSystemSemaphore::KeyError,
QSystemSemaphore::tr("%1: key is empty")
.arg("QSystemSemaphore::handle"_L1));
return false;
}
const QByteArray semName = QFile::encodeName(self->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) {
@ -92,8 +91,8 @@ void QSystemSemaphorePosix::cleanHandle(QSystemSemaphorePrivate *self)
}
if (createdSemaphore) {
if (::sem_unlink(QFile::encodeName(self->fileName).constData()) == -1
&& errno != ENOENT) {
const QByteArray semName = QFile::encodeName(self->nativeKey.nativeKey());
if (::sem_unlink(semName) == -1 && errno != ENOENT) {
self->setUnixErrorString("QSystemSemaphore::cleanHandle (sem_unlink)"_L1);
#if defined QSYSTEMSEMAPHORE_DEBUG
qDebug("QSystemSemaphorePosix::cleanHandle sem_unlink failed.");

View File

@ -52,19 +52,19 @@ key_t QSystemSemaphoreSystemV::handle(QSystemSemaphorePrivate *self, QSystemSema
}
#endif
if (self->key.isEmpty()) {
nativeKeyFile = QFile::encodeName(self->nativeKey.nativeKey());
if (nativeKeyFile.isEmpty()) {
self->setError(QSystemSemaphore::KeyError,
QSystemSemaphore::tr("%1: key is empty")
.arg("QSystemSemaphore::handle:"_L1));
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 = QtIpcCommon::createUnixKeyFile(QFile::encodeName(self->fileName));
// ftok requires that an actual file exists somewhere
int built = QtIpcCommon::createUnixKeyFile(nativeKeyFile);
if (-1 == built) {
self->setError(QSystemSemaphore::KeyError,
QSystemSemaphore::tr("%1: unable to make key")
@ -75,7 +75,7 @@ key_t QSystemSemaphoreSystemV::handle(QSystemSemaphorePrivate *self, QSystemSema
createdFile = (1 == built);
// Get the unix key for the created file
unix_key = ftok(QFile::encodeName(self->fileName).constData(), 'Q');
unix_key = ftok(nativeKeyFile, int(self->nativeKey.type()));
if (-1 == unix_key) {
self->setError(QSystemSemaphore::KeyError,
QSystemSemaphore::tr("%1: ftok failed")
@ -129,7 +129,7 @@ void QSystemSemaphoreSystemV::cleanHandle(QSystemSemaphorePrivate *self)
// remove the file if we made it
if (createdFile) {
QFile::remove(self->fileName);
unlink(nativeKeyFile.constData());
createdFile = false;
}

View File

@ -42,13 +42,13 @@ void QSystemSemaphorePrivate::setWindowsErrorString(QLatin1StringView function)
HANDLE QSystemSemaphoreWin32::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode)
{
// don't allow making handles on empty keys
if (self->key.isEmpty())
if (self->nativeKey.isEmpty())
return 0;
// Create it if it doesn't already exists.
if (semaphore == 0) {
semaphore = CreateSemaphore(0, self->initialValue, MAXLONG,
reinterpret_cast<const wchar_t*>(self->fileName.utf16()));
reinterpret_cast<const wchar_t*>(self->nativeKey.nativeKey().utf16()));
if (semaphore == NULL)
self->setWindowsErrorString("QSystemSemaphore::handle"_L1);
}

View File

@ -478,7 +478,8 @@ struct RunnerLocker
{
runner.release();
}
QSystemSemaphore runner{QStringLiteral("androidtestrunner"), 1, QSystemSemaphore::Open};
QSystemSemaphore runner{ QSystemSemaphore::platformSafeKey(u"androidtestrunner"_s),
1, QSystemSemaphore::Open };
};
int main(int argc, char *argv[])

View File

@ -6,9 +6,9 @@
#include <QStringList>
#include <QSystemSemaphore>
int acquire(int count = 1)
int acquire(const QNativeIpcKey &key, int count = 1)
{
QSystemSemaphore sem("store");
QSystemSemaphore sem(key);
for (int i = 0; i < count; ++i) {
if (!sem.acquire()) {
@ -20,9 +20,9 @@ int acquire(int count = 1)
return EXIT_SUCCESS;
}
int release()
int release(const QNativeIpcKey &key)
{
QSystemSemaphore sem("store");
QSystemSemaphore sem(key);
if (!sem.release()) {
qWarning() << "Could not release" << sem.key();
return EXIT_FAILURE;
@ -31,9 +31,9 @@ int release()
return EXIT_SUCCESS;
}
int acquirerelease()
int acquirerelease(const QNativeIpcKey &key)
{
QSystemSemaphore sem("store");
QSystemSemaphore sem(key);
if (!sem.acquire()) {
qWarning() << "Could not acquire" << sem.key();
return EXIT_FAILURE;
@ -52,11 +52,15 @@ int main(int argc, char *argv[])
QStringList arguments = app.arguments();
// binary name is not used here
arguments.takeFirst();
if (arguments.size() < 1) {
qWarning("Please call the helper with the function to call as argument");
if (arguments.size() < 2) {
fprintf(stderr,
"Usage: %s <acquire|release|acquirerelease> <key> [other args...]\n",
argv[0]);
return EXIT_FAILURE;
}
QString function = arguments.takeFirst();
QNativeIpcKey key = QNativeIpcKey::fromString(arguments.takeFirst());
if (function == QLatin1String("acquire")) {
int count = 1;
bool ok = true;
@ -64,11 +68,11 @@ int main(int argc, char *argv[])
count = arguments.takeFirst().toInt(&ok);
if (!ok)
count = 1;
return acquire(count);
return acquire(key, count);
} else if (function == QLatin1String("release")) {
return release();
return release(key);
} else if (function == QLatin1String("acquirerelease")) {
return acquirerelease();
return acquirerelease(key);
} else {
qWarning() << "Unknown function" << function;
}

View File

@ -1,4 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2022 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
@ -20,13 +21,21 @@ class tst_QSystemSemaphore : public QObject
public:
tst_QSystemSemaphore();
QNativeIpcKey platformSafeKey(const QString &key)
{
QNativeIpcKey::Type keyType = QNativeIpcKey::DefaultTypeForOs;
return QSystemSemaphore::platformSafeKey(key, keyType);
}
public Q_SLOTS:
void init();
void cleanup();
private slots:
void key_data();
void key();
void nativeKey_data();
void nativeKey();
void legacyKey_data() { nativeKey_data(); }
void legacyKey();
void basicacquire();
void complexacquire();
@ -37,9 +46,7 @@ private slots:
void processes_data();
void processes();
#if !defined(Q_OS_WIN) && !defined(QT_POSIX_IPC)
void undo();
#endif
void initialValue();
private:
@ -55,7 +62,8 @@ tst_QSystemSemaphore::tst_QSystemSemaphore()
void tst_QSystemSemaphore::init()
{
existingLock = new QSystemSemaphore(EXISTING_SHARE, 1, QSystemSemaphore::Create);
QNativeIpcKey key = platformSafeKey(EXISTING_SHARE);
existingLock = new QSystemSemaphore(key, 1, QSystemSemaphore::Create);
}
void tst_QSystemSemaphore::cleanup()
@ -63,7 +71,7 @@ void tst_QSystemSemaphore::cleanup()
delete existingLock;
}
void tst_QSystemSemaphore::key_data()
void tst_QSystemSemaphore::nativeKey_data()
{
QTest::addColumn<QString>("constructorKey");
QTest::addColumn<QString>("setKey");
@ -76,7 +84,27 @@ void tst_QSystemSemaphore::key_data()
/*!
Basic key testing
*/
void tst_QSystemSemaphore::key()
void tst_QSystemSemaphore::nativeKey()
{
QFETCH(QString, constructorKey);
QFETCH(QString, setKey);
QNativeIpcKey constructorIpcKey = platformSafeKey(constructorKey);
QNativeIpcKey setIpcKey = platformSafeKey(setKey);
QSystemSemaphore sem(constructorIpcKey);
QCOMPARE(sem.nativeIpcKey(), constructorIpcKey);
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
QCOMPARE(sem.errorString(), QString());
sem.setNativeKey(setIpcKey);
QCOMPARE(sem.nativeIpcKey(), setIpcKey);
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
QCOMPARE(sem.errorString(), QString());
}
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
void tst_QSystemSemaphore::legacyKey()
{
QFETCH(QString, constructorKey);
QFETCH(QString, setKey);
@ -91,10 +119,12 @@ void tst_QSystemSemaphore::key()
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
QCOMPARE(sem.errorString(), QString());
}
QT_WARNING_POP
void tst_QSystemSemaphore::basicacquire()
{
QSystemSemaphore sem("QSystemSemaphore_basicacquire", 1, QSystemSemaphore::Create);
QNativeIpcKey key = platformSafeKey("QSystemSemaphore_basicacquire");
QSystemSemaphore sem(key, 1, QSystemSemaphore::Create);
QVERIFY(sem.acquire());
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
QVERIFY(sem.release());
@ -104,7 +134,8 @@ void tst_QSystemSemaphore::basicacquire()
void tst_QSystemSemaphore::complexacquire()
{
QSystemSemaphore sem("QSystemSemaphore_complexacquire", 2, QSystemSemaphore::Create);
QNativeIpcKey key = platformSafeKey("QSystemSemaphore_complexacquire");
QSystemSemaphore sem(key, 2, QSystemSemaphore::Create);
QVERIFY(sem.acquire());
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
QVERIFY(sem.release());
@ -126,7 +157,8 @@ void tst_QSystemSemaphore::complexacquire()
void tst_QSystemSemaphore::release()
{
QSystemSemaphore sem("QSystemSemaphore_release", 0, QSystemSemaphore::Create);
QNativeIpcKey key = platformSafeKey("QSystemSemaphore_release");
QSystemSemaphore sem(key, 0, QSystemSemaphore::Create);
QVERIFY(sem.release());
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
QVERIFY(sem.release());
@ -147,7 +179,8 @@ void tst_QSystemSemaphore::basicProcesses()
#if !QT_CONFIG(process)
QSKIP("No qprocess support", SkipAll);
#else
QSystemSemaphore sem("store", 0, QSystemSemaphore::Create);
QNativeIpcKey key = platformSafeKey("store");
QSystemSemaphore sem(key, 0, QSystemSemaphore::Create);
QProcess acquire;
acquire.setProcessChannelMode(QProcess::ForwardedChannels);
@ -155,12 +188,12 @@ void tst_QSystemSemaphore::basicProcesses()
QProcess release;
release.setProcessChannelMode(QProcess::ForwardedChannels);
acquire.start(m_helperBinary, QStringList("acquire"));
acquire.start(m_helperBinary, { "acquire", key.toString() });
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
acquire.waitForFinished(HELPERWAITTIME);
QCOMPARE(acquire.state(), QProcess::Running);
acquire.kill();
release.start(m_helperBinary, QStringList("release"));
release.start(m_helperBinary, { "release", key.toString() });
QVERIFY2(release.waitForStarted(), "Could not start helper binary");
acquire.waitForFinished(HELPERWAITTIME);
release.waitForFinished(HELPERWAITTIME);
@ -183,7 +216,8 @@ void tst_QSystemSemaphore::processes()
#if !QT_CONFIG(process)
QSKIP("No qprocess support", SkipAll);
#else
QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
QNativeIpcKey key = platformSafeKey("store");
QSystemSemaphore sem(key, 1, QSystemSemaphore::Create);
QFETCH(int, processes);
QList<QString> scripts(processes, "acquirerelease");
@ -193,7 +227,7 @@ void tst_QSystemSemaphore::processes()
QProcess *p = new QProcess;
p->setProcessChannelMode(QProcess::ForwardedChannels);
consumers.append(p);
p->start(m_helperBinary, QStringList(scripts.at(i)));
p->start(m_helperBinary, { scripts.at(i), key.toString() });
}
while (!consumers.isEmpty()) {
@ -205,16 +239,24 @@ void tst_QSystemSemaphore::processes()
#endif
}
// This test only checks a system v unix behavior.
#if !defined(Q_OS_WIN) && !defined(QT_POSIX_IPC)
void tst_QSystemSemaphore::undo()
{
#if !QT_CONFIG(process)
QSKIP("No qprocess support", SkipAll);
#else
QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
QNativeIpcKey key = platformSafeKey("store");
switch (key.type()) {
case QNativeIpcKey::Type::PosixRealtime:
case QNativeIpcKey::Type::Windows:
QSKIP("This test only checks a System V behavior.");
QStringList acquireArguments = QStringList("acquire");
case QNativeIpcKey::Type::SystemV:
break;
}
QSystemSemaphore sem(key, 1, QSystemSemaphore::Create);
QStringList acquireArguments = { "acquire", key.toString() };
QProcess acquire;
acquire.setProcessChannelMode(QProcess::ForwardedChannels);
acquire.start(m_helperBinary, acquireArguments);
@ -230,17 +272,17 @@ void tst_QSystemSemaphore::undo()
QVERIFY(acquire.state()== QProcess::NotRunning);
#endif
}
#endif
void tst_QSystemSemaphore::initialValue()
{
#if !QT_CONFIG(process)
QSKIP("No qprocess support", SkipAll);
#else
QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
QNativeIpcKey key = platformSafeKey("store");
QSystemSemaphore sem(key, 1, QSystemSemaphore::Create);
QStringList acquireArguments = QStringList("acquire");
QStringList releaseArguments = QStringList("release");
QStringList acquireArguments = { "acquire", key.toString() };
QStringList releaseArguments = { "release", key.toString() };
QProcess acquire;
acquire.setProcessChannelMode(QProcess::ForwardedChannels);