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 WrapRt::WrapRt
) )
qt_internal_extend_target(Core CONDITION UNIX qt_internal_extend_target(Core CONDITION QT_FEATURE_posix_shm
SOURCES SOURCES
ipc/qsharedmemory_posix.cpp ipc/qsharedmemory_posix.cpp
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_sysv_shm
SOURCES
ipc/qsharedmemory_systemv.cpp ipc/qsharedmemory_systemv.cpp
) )
qt_internal_extend_target(Core CONDITION QT_FEATURE_posix_sem qt_internal_extend_target(Core CONDITION QT_FEATURE_posix_sem

View File

@ -23,6 +23,30 @@ QT_BEGIN_NAMESPACE
using namespace QtIpcCommon; using namespace QtIpcCommon;
using namespace Qt::StringLiterals; 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) #if QT_CONFIG(systemsemaphore)
inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const
{ {
@ -103,7 +127,7 @@ QSharedMemory::QSharedMemory(QObject *parent)
\sa setNativeKey(), create(), attach() \sa setNativeKey(), create(), attach()
*/ */
QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent) QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent) : QObject(*new QSharedMemoryPrivate(key.type()), parent)
{ {
setNativeKey(key); setNativeKey(key);
} }
@ -120,12 +144,9 @@ QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
\sa setKey(), create(), attach() \sa setKey(), create(), attach()
*/ */
QSharedMemory::QSharedMemory(const QString &key, QObject *parent) QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent) : QSharedMemory(legacyNativeKey(key), parent)
{ {
QT_WARNING_PUSH d_func()->legacyKey = key;
QT_WARNING_DISABLE_DEPRECATED
setKey(key);
QT_WARNING_POP
} }
/*! /*!
@ -139,7 +160,10 @@ QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
*/ */
QSharedMemory::~QSharedMemory() QSharedMemory::~QSharedMemory()
{ {
setNativeKey(QNativeIpcKey()); Q_D(QSharedMemory);
if (isAttached())
detach();
d->cleanHandle();
} }
/*! /*!
@ -224,7 +248,15 @@ void QSharedMemory::setNativeKey(const QNativeIpcKey &key)
detach(); detach();
d->cleanHandle(); d->cleanHandle();
d->legacyKey = QString(); d->legacyKey = QString();
if (key.type() == d->nativeKey.type()) {
// we can reuse the backend
d->nativeKey = key; d->nativeKey = key;
} else {
// we must recreate the backend
d->destructBackend();
d->nativeKey = key;
d->constructBackend();
}
} }
bool QSharedMemoryPrivate::initKey(SemaphoreAccessMode mode) bool QSharedMemoryPrivate::initKey(SemaphoreAccessMode mode)
@ -642,7 +674,13 @@ void QSharedMemoryPrivate::setUnixErrorString(QLatin1StringView function)
bool QSharedMemory::isKeyTypeSupported(QNativeIpcKey::Type type) 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) QNativeIpcKey QSharedMemory::platformSafeKey(const QString &key, QNativeIpcKey::Type type)

View File

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

View File

@ -24,6 +24,15 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
using namespace QtIpcCommon; 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) bool QSharedMemoryPosix::handle(QSharedMemoryPrivate *self)
{ {
// don't allow making handles on empty keys // don't allow making handles on empty keys

View File

@ -20,12 +20,29 @@
#include <unistd.h> #include <unistd.h>
#include "private/qcore_unix_p.h" #include "private/qcore_unix_p.h"
#if defined(Q_OS_DARWIN)
#include "private/qcore_mac_p.h"
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
using namespace QtIpcCommon; 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) inline void QSharedMemorySystemV::updateNativeKeyFile(const QNativeIpcKey &nativeKey)
{ {
Q_ASSERT(nativeKeyFile.isEmpty() ); Q_ASSERT(nativeKeyFile.isEmpty() );

View File

@ -85,33 +85,6 @@ static QNativeIpcKey::Type stringToType(QStringView typeString)
return QNativeIpcKey::Type{}; 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 \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_shm);
return QT_CONFIG(sysv_sem); 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 template <auto Member1, auto... Members> class IpcStorageVariant
{ {

View File

@ -19,6 +19,7 @@
#endif #endif
#include "private/qtcore-config_p.h" #include "private/qtcore-config_p.h"
#include "../ipctestcommon.h"
#define EXISTING_SIZE 1024 #define EXISTING_SIZE 1024
@ -31,15 +32,11 @@ class tst_QSharedMemory : public QObject
{ {
Q_OBJECT Q_OBJECT
public:
tst_QSharedMemory();
virtual ~tst_QSharedMemory();
public Q_SLOTS: public Q_SLOTS:
void initTestCase();
void init(); void init();
void cleanup(); void cleanup();
private slots: private slots:
// basics // basics
void constructor(); void constructor();
@ -51,6 +48,8 @@ private slots:
void create(); void create();
void attach_data(); void attach_data();
void attach(); void attach();
void changeKeyType_data() { attach_data(); }
void changeKeyType();
void lock(); void lock();
// custom edge cases // custom edge cases
@ -92,7 +91,7 @@ protected:
QNativeIpcKey platformSafeKey(const QString &key) QNativeIpcKey platformSafeKey(const QString &key)
{ {
QNativeIpcKey::Type keyType = QNativeIpcKey::DefaultTypeForOs; QFETCH_GLOBAL(QNativeIpcKey::Type, keyType);
return QSharedMemory::platformSafeKey(mangleKey(key), keyType); return QSharedMemory::platformSafeKey(mangleKey(key), keyType);
} }
@ -108,21 +107,16 @@ protected:
QList<QNativeIpcKey> keys; QList<QNativeIpcKey> keys;
QList<QSharedMemory*> jail; QList<QSharedMemory*> jail;
QSharedMemory *existingSharedMemory; QSharedMemory *existingSharedMemory = nullptr;
int seq = 0; int seq = 0;
private: private:
const QString m_helperBinary; const QString m_helperBinary = "./producerconsumer_helper";
}; };
tst_QSharedMemory::tst_QSharedMemory() void tst_QSharedMemory::initTestCase()
: existingSharedMemory(0)
, m_helperBinary("./producerconsumer_helper")
{
}
tst_QSharedMemory::~tst_QSharedMemory()
{ {
IpcTestCommon::addGlobalTestRows<QSharedMemory>();
} }
void tst_QSharedMemory::init() void tst_QSharedMemory::init()
@ -285,6 +279,25 @@ void tst_QSharedMemory::nativeKey()
QCOMPARE(sm.size(), 0); QCOMPARE(sm.size(), 0);
QCOMPARE(sm.detach(), false); 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 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() void tst_QSharedMemory::lock()
{ {
QSharedMemory shm; QSharedMemory shm;