IPC: add support for multiple backends to QSharedMemory

Simultaneously.

Change-Id: If4c23ea3719947d790d4fffd17152760989b9bc6
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2022-10-13 10:52:34 -07:00
parent 32a06e9830
commit 0740ab56d7
8 changed files with 168 additions and 70 deletions

View File

@ -1135,9 +1135,12 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_clock_gettime AND UNIX
WrapRt::WrapRt
)
qt_internal_extend_target(Core CONDITION UNIX
qt_internal_extend_target(Core CONDITION QT_FEATURE_posix_shm
SOURCES
ipc/qsharedmemory_posix.cpp
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_sysv_shm
SOURCES
ipc/qsharedmemory_systemv.cpp
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_posix_sem

View File

@ -23,6 +23,30 @@ QT_BEGIN_NAMESPACE
using namespace QtIpcCommon;
using namespace Qt::StringLiterals;
#if __cplusplus >= 202002L
using std::construct_at;
#else
template <typename T> static void construct_at(T *ptr)
{
new (ptr) T;
}
#endif
QSharedMemoryPrivate::~QSharedMemoryPrivate()
{
destructBackend();
}
inline void QSharedMemoryPrivate::constructBackend()
{
visit([](auto p) { construct_at(p); });
}
inline void QSharedMemoryPrivate::destructBackend()
{
visit([](auto p) { std::destroy_at(p); });
}
#if QT_CONFIG(systemsemaphore)
inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const
{
@ -103,7 +127,7 @@ QSharedMemory::QSharedMemory(QObject *parent)
\sa setNativeKey(), create(), attach()
*/
QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent)
: QObject(*new QSharedMemoryPrivate(key.type()), parent)
{
setNativeKey(key);
}
@ -120,12 +144,9 @@ QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
\sa setKey(), create(), attach()
*/
QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent)
: QSharedMemory(legacyNativeKey(key), parent)
{
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
setKey(key);
QT_WARNING_POP
d_func()->legacyKey = key;
}
/*!
@ -139,7 +160,10 @@ QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
*/
QSharedMemory::~QSharedMemory()
{
setNativeKey(QNativeIpcKey());
Q_D(QSharedMemory);
if (isAttached())
detach();
d->cleanHandle();
}
/*!
@ -224,7 +248,15 @@ void QSharedMemory::setNativeKey(const QNativeIpcKey &key)
detach();
d->cleanHandle();
d->legacyKey = QString();
d->nativeKey = key;
if (key.type() == d->nativeKey.type()) {
// we can reuse the backend
d->nativeKey = key;
} else {
// we must recreate the backend
d->destructBackend();
d->nativeKey = key;
d->constructBackend();
}
}
bool QSharedMemoryPrivate::initKey(SemaphoreAccessMode mode)
@ -642,7 +674,13 @@ void QSharedMemoryPrivate::setUnixErrorString(QLatin1StringView function)
bool QSharedMemory::isKeyTypeSupported(QNativeIpcKey::Type type)
{
return QSharedMemoryPrivate::DefaultBackend::supports(type);
if (!isIpcSupported(IpcType::SharedMemory, type))
return false;
using Variant = decltype(QSharedMemoryPrivate::backend);
return Variant::staticVisit(type, [](auto ptr) {
using Impl = std::decay_t<decltype(*ptr)>;
return Impl::runtimeSupportCheck();
});
}
QNativeIpcKey QSharedMemory::platformSafeKey(const QString &key, QNativeIpcKey::Type type)

View File

@ -21,6 +21,7 @@
#if QT_CONFIG(sharedmemory)
#include "qsystemsemaphore.h"
#include "qtipccommon_p.h"
#include "private/qobject_p.h"
#if QT_CONFIG(posix_shm)
@ -69,8 +70,10 @@ private:
class QSharedMemoryPosix
{
public:
static constexpr bool Enabled = QT_CONFIG(posix_shm);
static bool supports(QNativeIpcKey::Type type)
{ return type == QNativeIpcKey::Type::PosixRealtime; }
static bool runtimeSupportCheck();
bool handle(QSharedMemoryPrivate *self);
bool cleanHandle(QSharedMemoryPrivate *self);
@ -84,8 +87,10 @@ public:
class QSharedMemorySystemV
{
public:
static constexpr bool Enabled = QT_CONFIG(sysv_shm);
static bool supports(QNativeIpcKey::Type type)
{ return quint16(type) <= 0xff; }
static bool runtimeSupportCheck();
#if QT_CONFIG(sysv_sem)
key_t handle(QSharedMemoryPrivate *self);
@ -105,6 +110,12 @@ private:
class QSharedMemoryWin32
{
public:
#ifdef Q_OS_WIN32
static constexpr bool Enabled = true;
#else
static constexpr bool Enabled = false;
#endif
static bool runtimeSupportCheck() { return Enabled; }
static bool supports(QNativeIpcKey::Type type)
{ return type == QNativeIpcKey::Type::Windows; }
@ -122,6 +133,10 @@ class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate
Q_DECLARE_PUBLIC(QSharedMemory)
public:
QSharedMemoryPrivate(QNativeIpcKey::Type type) : nativeKey(type)
{ constructBackend(); }
~QSharedMemoryPrivate();
void *memory = nullptr;
qsizetype size = 0;
QNativeIpcKey nativeKey;
@ -135,36 +150,43 @@ public:
#endif
QSharedMemory::SharedMemoryError error = QSharedMemory::NoError;
#if defined(Q_OS_WIN)
using DefaultBackend = QSharedMemoryWin32;
#elif defined(QT_POSIX_IPC)
using DefaultBackend = QSharedMemoryPosix;
#else
using DefaultBackend = QSharedMemorySystemV;
#endif
DefaultBackend backend;
union Backend {
Backend() {}
~Backend() {}
QSharedMemoryPosix posix;
QSharedMemorySystemV sysv;
QSharedMemoryWin32 win32;
};
QtIpcCommon::IpcStorageVariant<&Backend::posix, &Backend::sysv, &Backend::win32> backend;
void constructBackend();
void destructBackend();
bool initKey(SemaphoreAccessMode mode);
template <typename Lambda> auto visit(const Lambda &lambda)
{
return backend.visit(nativeKey.type(), lambda);
}
bool handle()
{
return backend.handle(this);
return visit([=](auto p) { return !!p->handle(this); });
}
bool cleanHandle()
{
return backend.cleanHandle(this);
return visit([=](auto p) { return p->cleanHandle(this); });
}
bool create(qsizetype size)
{
return backend.create(this, size);
return visit([=](auto p) { return p->create(this, size); });
}
bool attach(QSharedMemory::AccessMode mode)
{
return backend.attach(this, mode);
return visit([=](auto p) { return p->attach(this, mode); });
}
bool detach()
{
return backend.detach(this);
return visit([=](auto p) { return p->detach(this); });
}
inline void setError(QSharedMemory::SharedMemoryError e, const QString &message)

View File

@ -24,6 +24,15 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
using namespace QtIpcCommon;
bool QSharedMemoryPosix::runtimeSupportCheck()
{
static const bool result = []() {
shm_open("", 0, 0); // this WILL fail
return errno != ENOSYS;
}();
return result;
}
bool QSharedMemoryPosix::handle(QSharedMemoryPrivate *self)
{
// don't allow making handles on empty keys

View File

@ -20,12 +20,29 @@
#include <unistd.h>
#include "private/qcore_unix_p.h"
#if defined(Q_OS_DARWIN)
#include "private/qcore_mac_p.h"
#endif
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
using namespace QtIpcCommon;
bool QSharedMemorySystemV::runtimeSupportCheck()
{
#if defined(Q_OS_DARWIN)
if (qt_apple_isSandboxed())
return false;
#endif
static const bool result = []() {
shmget(IPC_PRIVATE, ~size_t(0), 0); // this will fail
return errno != ENOSYS;
}();
return result;
}
inline void QSharedMemorySystemV::updateNativeKeyFile(const QNativeIpcKey &nativeKey)
{
Q_ASSERT(nativeKeyFile.isEmpty() );

View File

@ -85,33 +85,6 @@ static QNativeIpcKey::Type stringToType(QStringView typeString)
return QNativeIpcKey::Type{};
}
#if defined(Q_OS_DARWIN) || defined(QT_BUILD_INTERNAL)
/*!
\internal
Returns whether this IPC type \a ipcType and key type \a keyType is
supported on the current machine, with a runtime check.
*/
bool QtIpcCommon::isIpcSupportedAtRuntime(IpcType ipcType, QNativeIpcKey::Type keyType)
{
switch (keyType) {
case QNativeIpcKey::Type::SystemV:
break;
case QNativeIpcKey::Type::PosixRealtime:
case QNativeIpcKey::Type::Windows:
return isIpcSupported(ipcType, keyType);
}
# if defined(Q_OS_DARWIN)
// System V IPC is not supported on Apple platforms if the application is
// currently running in a sandbox.
return !qt_apple_isSandboxed();
# endif
return isIpcSupported(ipcType, keyType);
}
#endif
/*!
\internal

View File

@ -57,11 +57,6 @@ static constexpr bool isIpcSupported(IpcType ipcType, QNativeIpcKey::Type type)
return QT_CONFIG(sysv_shm);
return QT_CONFIG(sysv_sem);
}
#if defined(Q_OS_DARWIN) || defined(QT_BUILD_INTERNAL)
bool isIpcSupportedAtRuntime(IpcType type, QNativeIpcKey::Type);
#else
static constexpr auto isIpcSupportedAtRuntime = isIpcSupported;
#endif
template <auto Member1, auto... Members> class IpcStorageVariant
{

View File

@ -19,6 +19,7 @@
#endif
#include "private/qtcore-config_p.h"
#include "../ipctestcommon.h"
#define EXISTING_SIZE 1024
@ -31,15 +32,11 @@ class tst_QSharedMemory : public QObject
{
Q_OBJECT
public:
tst_QSharedMemory();
virtual ~tst_QSharedMemory();
public Q_SLOTS:
void initTestCase();
void init();
void cleanup();
private slots:
// basics
void constructor();
@ -51,6 +48,8 @@ private slots:
void create();
void attach_data();
void attach();
void changeKeyType_data() { attach_data(); }
void changeKeyType();
void lock();
// custom edge cases
@ -92,7 +91,7 @@ protected:
QNativeIpcKey platformSafeKey(const QString &key)
{
QNativeIpcKey::Type keyType = QNativeIpcKey::DefaultTypeForOs;
QFETCH_GLOBAL(QNativeIpcKey::Type, keyType);
return QSharedMemory::platformSafeKey(mangleKey(key), keyType);
}
@ -108,21 +107,16 @@ protected:
QList<QNativeIpcKey> keys;
QList<QSharedMemory*> jail;
QSharedMemory *existingSharedMemory;
QSharedMemory *existingSharedMemory = nullptr;
int seq = 0;
private:
const QString m_helperBinary;
const QString m_helperBinary = "./producerconsumer_helper";
};
tst_QSharedMemory::tst_QSharedMemory()
: existingSharedMemory(0)
, m_helperBinary("./producerconsumer_helper")
{
}
tst_QSharedMemory::~tst_QSharedMemory()
void tst_QSharedMemory::initTestCase()
{
IpcTestCommon::addGlobalTestRows<QSharedMemory>();
}
void tst_QSharedMemory::init()
@ -285,6 +279,25 @@ void tst_QSharedMemory::nativeKey()
QCOMPARE(sm.size(), 0);
QCOMPARE(sm.detach(), false);
// change the key type
QNativeIpcKey::Type nextKeyType = IpcTestCommon::nextKeyType(setIpcKey.type());
if (nextKeyType != setIpcKey.type()) {
QNativeIpcKey setIpcKey2 = QSharedMemory::platformSafeKey(setKey, nextKeyType);
sm.setNativeKey(setIpcKey2);
QCOMPARE(sm.nativeIpcKey(), setIpcKey2);
QCOMPARE(sm.nativeKey(), setIpcKey2.nativeKey());
QCOMPARE(sm.isAttached(), false);
QCOMPARE(sm.error(), QSharedMemory::NoError);
QCOMPARE(sm.errorString(), QString());
QVERIFY(!sm.data());
QCOMPARE(sm.size(), 0);
QCOMPARE(sm.detach(), false);
}
}
QT_WARNING_PUSH
@ -408,6 +421,34 @@ void tst_QSharedMemory::attach()
}
}
void tst_QSharedMemory::changeKeyType()
{
QFETCH(QString, key);
QFETCH(bool, exists);
QFETCH(QSharedMemory::SharedMemoryError, error);
QNativeIpcKey nativeKey = platformSafeKey(key);
QNativeIpcKey::Type nextKeyType = IpcTestCommon::nextKeyType(nativeKey.type());
if (nextKeyType == nativeKey.type())
QSKIP("System only supports one key type");
// qDebug() << "Changing from" << nativeKey.type() << "to" << nextKeyType;
QSharedMemory sm(nativeKey);
QCOMPARE(sm.attach(), exists);
QCOMPARE(sm.error(), error);
QNativeIpcKey nextKey =
QSharedMemory::platformSafeKey(mangleKey(key), nextKeyType);
sm.setNativeKey(nextKey);
QCOMPARE(sm.isAttached(), false);
QVERIFY(!sm.attach());
if (exists)
QCOMPARE(sm.error(), QSharedMemory::NotFound);
else
QCOMPARE(sm.error(), error);
}
void tst_QSharedMemory::lock()
{
QSharedMemory shm;