From 0740ab56d7c3b540677e33e28924d47bce87c1cf Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 13 Oct 2022 10:52:34 -0700 Subject: [PATCH] IPC: add support for multiple backends to QSharedMemory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simultaneously. Change-Id: If4c23ea3719947d790d4fffd17152760989b9bc6 Reviewed-by: Tor Arne Vestbø Reviewed-by: Fabian Kosmale --- src/corelib/CMakeLists.txt | 5 +- src/corelib/ipc/qsharedmemory.cpp | 56 ++++++++++++--- src/corelib/ipc/qsharedmemory_p.h | 48 +++++++++---- src/corelib/ipc/qsharedmemory_posix.cpp | 9 +++ src/corelib/ipc/qsharedmemory_systemv.cpp | 17 +++++ src/corelib/ipc/qtipccommon.cpp | 27 ------- src/corelib/ipc/qtipccommon_p.h | 5 -- .../ipc/qsharedmemory/tst_qsharedmemory.cpp | 71 +++++++++++++++---- 8 files changed, 168 insertions(+), 70 deletions(-) diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 60bf9381717..1f6e01541fa 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -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 diff --git a/src/corelib/ipc/qsharedmemory.cpp b/src/corelib/ipc/qsharedmemory.cpp index 87437cd8be3..5dac2f775ff 100644 --- a/src/corelib/ipc/qsharedmemory.cpp +++ b/src/corelib/ipc/qsharedmemory.cpp @@ -23,6 +23,30 @@ QT_BEGIN_NAMESPACE using namespace QtIpcCommon; using namespace Qt::StringLiterals; +#if __cplusplus >= 202002L +using std::construct_at; +#else +template 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; + return Impl::runtimeSupportCheck(); + }); } QNativeIpcKey QSharedMemory::platformSafeKey(const QString &key, QNativeIpcKey::Type type) diff --git a/src/corelib/ipc/qsharedmemory_p.h b/src/corelib/ipc/qsharedmemory_p.h index c52bc9e0a2c..8ff76f3f00d 100644 --- a/src/corelib/ipc/qsharedmemory_p.h +++ b/src/corelib/ipc/qsharedmemory_p.h @@ -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 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) diff --git a/src/corelib/ipc/qsharedmemory_posix.cpp b/src/corelib/ipc/qsharedmemory_posix.cpp index 387b7c245c0..c2c0bff07bf 100644 --- a/src/corelib/ipc/qsharedmemory_posix.cpp +++ b/src/corelib/ipc/qsharedmemory_posix.cpp @@ -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 diff --git a/src/corelib/ipc/qsharedmemory_systemv.cpp b/src/corelib/ipc/qsharedmemory_systemv.cpp index 71a0c261015..385ffdd5975 100644 --- a/src/corelib/ipc/qsharedmemory_systemv.cpp +++ b/src/corelib/ipc/qsharedmemory_systemv.cpp @@ -20,12 +20,29 @@ #include #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() ); diff --git a/src/corelib/ipc/qtipccommon.cpp b/src/corelib/ipc/qtipccommon.cpp index 34c13e7a5cb..050c79c6290 100644 --- a/src/corelib/ipc/qtipccommon.cpp +++ b/src/corelib/ipc/qtipccommon.cpp @@ -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 diff --git a/src/corelib/ipc/qtipccommon_p.h b/src/corelib/ipc/qtipccommon_p.h index 72b624c78f8..bdcdaabd53f 100644 --- a/src/corelib/ipc/qtipccommon_p.h +++ b/src/corelib/ipc/qtipccommon_p.h @@ -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 class IpcStorageVariant { diff --git a/tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp b/tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp index 44f5cee82d0..4c871e02699 100644 --- a/tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp +++ b/tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp @@ -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 keys; QList 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(); } 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;