diff --git a/src/corelib/compat/removed_api.cpp b/src/corelib/compat/removed_api.cpp index 0efc81d430b..5ee844409a0 100644 --- a/src/corelib/compat/removed_api.cpp +++ b/src/corelib/compat/removed_api.cpp @@ -313,6 +313,15 @@ int QMetaType::idHelper() const return registerHelper(d_ptr); } +#if QT_CONFIG(sharedmemory) +#include "qsharedmemory.h" + +void QSharedMemory::setNativeKey(const QString &key) +{ + setNativeKey(key, QNativeIpcKey::legacyDefaultTypeForOs()); +} +#endif + #include "qvariant.h" // these implementations aren't as efficient as they used to be prior to diff --git a/src/corelib/ipc/qsharedmemory.cpp b/src/corelib/ipc/qsharedmemory.cpp index a877545e6ff..0bca048e7c1 100644 --- a/src/corelib/ipc/qsharedmemory.cpp +++ b/src/corelib/ipc/qsharedmemory.cpp @@ -49,192 +49,83 @@ inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const \brief The QSharedMemory class provides access to a shared memory segment. - QSharedMemory provides access to a shared memory segment by multiple - threads and processes. It also provides a way for a single thread or - process to lock the memory for exclusive access. + QSharedMemory provides access to a \l{Shared Memory}{shared memory segment} + by multiple threads and processes. Shared memory segments are identified by a + key, represented by \l QNativeIpcKey. A key can be created in a + cross-platform manner by using platformSafeKey(). - When using this class, be aware of the following platform - differences: + One QSharedMemory object must create() the segment and this call specifies + the size of the segment. All other processes simply attach() to the segment + that must already exist. After either operation is successful, the + application may call data() to obtain a pointer to the data. - \list + To support non-atomic operations, QSharedMemory provides API to gain + exclusive access: you may lock the shared memory with lock() before reading + from or writing to the shared memory, but remember to release the lock with + unlock() after you are done. - \li Windows: QSharedMemory does not "own" the shared memory segment. - When all threads or processes that have an instance of QSharedMemory - attached to a particular shared memory segment have either destroyed - their instance of QSharedMemory or exited, the Windows kernel - releases the shared memory segment automatically. + By default, QSharedMemory automatically destroys the shared memory segment + when the last instance of QSharedMemory is \l{detach()}{detached} from the + segment, and no references to the segment remain. - \li Unix: QSharedMemory "owns" the shared memory segment. When the - last thread or process that has an instance of QSharedMemory - attached to a particular shared memory segment detaches from the - segment by destroying its instance of QSharedMemory, the destructor - releases the shared memory segment. But if that last thread or - process crashes without running the QSharedMemory destructor, the - shared memory segment survives the crash. + 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. - \li Unix: QSharedMemory can be implemented by one of two different - backends, selected at Qt build time: System V or POSIX. Qt defaults to - using the System V API if it is available, and POSIX if not. These two - backends do not interoperate, so two applications must ensure they use the - same one, even if the native key (see setNativeKey()) is the same. - - The POSIX backend can be explicitly selected using the - \c{-feature-ipc_posix} option to the Qt configure script. If it is enabled, - the \c{QT_POSIX_IPC} macro will be defined. - - \li Sandboxed applications on Apple platforms (including apps - shipped through the Apple App Store): This environment requires - the use of POSIX shared memory (instead of System V shared memory). - - Qt for iOS is built with support for POSIX shared memory out of the box. - However, Qt for \macos builds (including those from the Qt installer) default - to System V, making them unsuitable for App Store submission if QSharedMemory - is needed. See above for instructions to explicitly select the POSIX backend - when building Qt. - - In addition, in a sandboxed environment, the following caveats apply: - - \list - \li The key must be in the form \c {/}, - 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}. - - \li The key length is limited to 30 characters. - - \li On process exit, the named shared memory entries are not - cleaned up, so restarting the application and re-creating the - shared memory under the same name will fail. To work around this, - fall back to attaching to the existing shared memory entry: - - \code - - QSharedMemory shm("DEVTEAMID.app-group/shared"); - if (!shm.create(42) && shm.error() == QSharedMemory::AlreadyExists) - shm.attach(); - - \endcode - - \endlist - - \li Android: QSharedMemory is not supported. - - \endlist - - Remember to lock the shared memory with lock() before reading from - or writing to the shared memory, and remember to release the lock - with unlock() after you are done. - - QSharedMemory automatically destroys the shared memory segment when - the last instance of QSharedMemory is detached from the segment, and - no references to the segment remain. - - \warning QSharedMemory changes the key in a Qt-specific way, unless otherwise - specified. Interoperation with non-Qt applications is achieved by first creating - a default shared memory with QSharedMemory() and then setting a native key with - setNativeKey(), after ensuring they use the same low-level API (System V or - POSIX). When using native keys, shared memory is not protected against multiple - accesses on it (for example, unable to lock()) and a user-defined mechanism - should be used to achieve such protection. - - \section2 Alternative: Memory-Mapped File - - Another way to share memory between processes is by opening the same file - using \l QFile and mapping it into memory using QFile::map() (without - specifying the QFileDevice::MapPrivateOption option). Any writes to the - mapped segment will be observed by all other processes that have mapped the - same file. This solution has the major advantages of being independent of the - backend API and of being simpler to interoperate with from non-Qt - applications. And since \l{QTemporaryFile} is a \l{QFile}, applications can - use that class to achieve clean-up semantics and to create unique shared - memory segments too. - - To achieve locking of the shared memory segment, applications will need to - deploy their own mechanisms. This can be achieved by using \l - QBasicAtomicInteger or \c{std::atomic} in a pre-determined offset in the - segment itself. Higher-level locking primitives may be available on some - operating systems; for example, on Linux, \c{pthread_mutex_create()} can be - passed a flag to indicate that the mutex resides in a shared memory segment. - - A major drawback of using file-backed shared memory is that the operating - system will attempt to write the data to permanent storage, possibly causing - noticeable performance penalties. To avoid this, applications should locate a - RAM-backed filesystem, such as \c{tmpfs} on Linux (see - QStorageInfo::fileSystemType()), or pass a flag to the native file-opening - function to inform the OS to avoid committing the contents to storage. - - File-backed shared memory must be used with care if another process - participating is untrusted. The files may be truncated/shrunk and cause - applications accessing memory beyond the file's size to crash. - - \section3 Linux hints on memory-mapped files - - On modern Linux systems, while the \c{/tmp} directory is often a \c{tmpfs} - mount point, that is not a requirement. However, the \c{/dev/shm} directory - is required to be a \c{tmpfs} and exists for this very purpose. Do note that - it is world-readable and writable (like \c{/tmp} and \c{/var/tmp}), so one - must be careful of the contents revealed there. Another alternative is to use - the XDG Runtime Directory (see QStandardPaths::writableLocation() and - \l{QStandardPaths::RuntimeLocation}), which on Linux systems using systemd is - a user-specific \c{tmpfs}. - - An even more secure solution is to create a "memfd" using \c{memfd_create(2)} - and use interprocess communication to pass the file descriptor, like - \l{QDBusUnixFileDescriptor} or by letting the child process of a \l{QProcess} - inherit it. "memfds" can also be sealed against being shrunk, so they are - safe to be used when communicating with processes with a different privilege - level. - - \section3 FreeBSD hints on memory-mapped files - - FreeBSD also has \c{memfd_create(2)} and can pass file descriptors to other - processes using the same techniques as Linux. It does not have temporary - filesystems mounted by default. - - \section3 Windows hints on memory-mapped files - - On Windows, the application can request the operating system avoid committing - the file's contents to permanent storage. This request is performed by - passing the \c{FILE_ATTRIBUTE_TEMPORARY} flag in the \c{dwFlagsAndAttributes} - \c{CreateFile} Win32 function, the \c{_O_SHORT_LIVED} flag to \c{_open()} - low-level function, or by including the modifier "T" to the \c{fopen()} C - runtime function. - - There's also a flag to inform the operating system to delete the file when - the last handle to it is closed (\c{FILE_FLAG_DELETE_ON_CLOSE}, - \c{_O_TEMPORARY}, and the "D" modifier), but do note that all processes - attempting to open the file must agree on using this flag or not using it. A - mismatch will likely cause a sharing violation and failure to open the file. + \sa Inter-Process Communication, QSystemSemaphore */ /*! \overload QSharedMemory() - Constructs a shared memory object with the given \a parent. The - shared memory object's key is not set by the constructor, so the - shared memory object does not have an underlying shared memory - segment attached. The key must be set with setKey() or setNativeKey() - before create() or attach() can be used. + Constructs a shared memory object with the given \a parent. The shared memory + object's key is not set by the constructor, so the shared memory object does + not have an underlying shared memory segment attached. The key must be set + with setNativeKey() before create() or attach() can be used. - \sa setKey() + \sa setNativeKey() */ QSharedMemory::QSharedMemory(QObject *parent) - : QObject(*new QSharedMemoryPrivate, parent) + : QSharedMemory(QNativeIpcKey(), parent) { } /*! + \overload + Constructs a shared memory object with the given \a parent and with its key set to \a key. Because its key is set, its create() and attach() functions can be called. + \sa setNativeKey(), create(), attach() + */ +QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent) + : QObject(*new QSharedMemoryPrivate, parent) +{ + setNativeKey(key); +} + +/*! + \deprecated + + Constructs a shared memory object with the given \a parent and with + the legacy key set to \a key. Because its key is set, its create() and + attach() functions can be called. + + Legacy keys are deprecated. See \l{Native IPC Keys} for more information. + \sa setKey(), create(), attach() */ QSharedMemory::QSharedMemory(const QString &key, QObject *parent) : QObject(*new QSharedMemoryPrivate, parent) { + QT_WARNING_PUSH + QT_WARNING_DISABLE_DEPRECATED setKey(key); + QT_WARNING_POP } /*! @@ -248,67 +139,91 @@ QSharedMemory::QSharedMemory(const QString &key, QObject *parent) */ QSharedMemory::~QSharedMemory() { - setKey(QString()); + setNativeKey(QNativeIpcKey()); } /*! - Sets the platform independent \a key for this shared memory object. If \a key - is the same as the current key, the function returns without doing anything. + \overload - You can call key() to retrieve the platform independent key. Internally, - QSharedMemory converts this key into a platform specific key. If you instead - call nativeKey(), you will get the platform specific, converted key. - - If the shared memory object is attached to an underlying shared memory + Sets the legacy \a key for this shared memory object. If \a key is the same + as the current key, the function returns without doing anything. Otherwise, + if the shared memory object is attached to an underlying shared memory segment, it will \l {detach()} {detach} from it before setting the new key. This function does not do an attach(). + You can call key() to retrieve the legacy key. This function is mostly the + same as: + + \code + shm.setNativeKey(QSharedMemory::legacyNativeKey(key)); + \endcode + + except that it enables obtaining the legacy key using key(). + \sa key(), nativeKey(), isAttached() */ void QSharedMemory::setKey(const QString &key) { Q_D(QSharedMemory); - QString newNativeKey = - QtIpcCommon::legacyPlatformSafeKey(key, QtIpcCommon::IpcType::SharedMemory); - if (key == d->key && newNativeKey == d->nativeKey) - return; - - if (isAttached()) - detach(); - d->cleanHandle(); - d->key = key; - d->nativeKey = newNativeKey; + setNativeKey(legacyNativeKey(key)); + d->legacyKey = key; } /*! \since 4.8 + \fn void QSharedMemory::setNativeKey(const QString &key, QNativeIpcKey::Type type) + + Sets the native, platform specific, \a key for this shared memory object of + type \a type (the type parameter has been available since Qt 6.6). If \a key + is the same as the current native key, the function returns without doing + anything. Otherwise, if the shared memory object is attached to an underlying + shared memory segment, it will \l {detach()} {detach} from it before setting + the new key. This function does not do an attach(). + + This function is useful if the native key was shared from another process, + though the application must take care to ensure the key type matches what the + other process expects. See \l{Native IPC Keys} for more information. + + Portable native keys can be obtained using platformSafeKey(). + + You can call nativeKey() to retrieve the native key. + + \sa nativeKey(), nativeKeyType(), isAttached() +*/ + +/*! + \since 6.6 Sets the native, platform specific, \a key for this shared memory object. If \a key is the same as the current native key, the function returns without - doing anything. If all you want is to assign a key to a segment, you should - call setKey() instead. + doing anything. Otherwise, if the shared memory object is attached to an + underlying shared memory segment, it will \l {detach()} {detach} from it + before setting the new key. This function does not do an attach(). - You can call nativeKey() to retrieve the native key. If a native key has been - assigned, calling key() will return a null string. + This function is useful if the native key was shared from another process. + See \l{Native IPC Keys} for more information. - If the shared memory object is attached to an underlying shared memory - segment, it will \l {detach()} {detach} from it before setting the new key. - This function does not do an attach(). + Portable native keys can be obtained using platformSafeKey(). - The application will not be portable if you set a native key. + You can call nativeKey() to retrieve the native key. - \sa nativeKey(), key(), isAttached() + \sa nativeKey(), nativeKeyType(), isAttached() */ -void QSharedMemory::setNativeKey(const QString &key) +void QSharedMemory::setNativeKey(const QNativeIpcKey &key) { Q_D(QSharedMemory); - if (key == d->nativeKey && d->key.isNull()) + if (key == d->nativeKey && key.isEmpty()) return; + if (!isKeyTypeSupported(key.type())) { + d->setError(KeyError, tr("%1: unsupported key type") + .arg("QSharedMemory::setNativeKey"_L1)); + return; + } if (isAttached()) detach(); d->cleanHandle(); - d->key = QString(); + d->legacyKey = QString(); d->nativeKey = key; } @@ -352,7 +267,8 @@ bool QSharedMemoryPrivate::initKey() } /*! - Returns the key assigned with setKey() to this shared memory, or a null key + \deprecated + Returns the legacy key assigned with setKey() to this shared memory, or a null key if no key has been assigned, or if the segment is using a nativeKey(). The key is the identifier used by Qt applications to identify the shared memory segment. @@ -365,7 +281,7 @@ bool QSharedMemoryPrivate::initKey() QString QSharedMemory::key() const { Q_D(const QSharedMemory); - return d->key; + return d->legacyKey; } /*! @@ -377,10 +293,30 @@ QString QSharedMemory::key() const You can use the native key to access shared memory segments that have not been created by Qt, or to grant shared memory access to non-Qt applications. + See \l{Native IPC Keys} for more information. - \sa setKey(), setNativeKey() + \sa setNativeKey(), nativeKeyType() */ QString QSharedMemory::nativeKey() const +{ + Q_D(const QSharedMemory); + return d->nativeKey.nativeKey(); +} + +/*! + \since 6.6 + + Returns the key type for this shared memory object. The key type complements + the nativeKey() as the identifier used by the operating system to identify + the shared memory segment. + + You can use the native key to access shared memory segments that have not + been created by Qt, or to grant shared memory access to non-Qt applications. + See \l{Native IPC Keys} for more information. + + \sa nativeKey(), setNativeKey() +*/ +QNativeIpcKey QSharedMemory::nativeIpcKey() const { Q_D(const QSharedMemory); return d->nativeKey; @@ -388,7 +324,7 @@ QString QSharedMemory::nativeKey() const /*! Creates a shared memory segment of \a size bytes with the key passed to the - constructor, set with setKey() or set with setNativeKey(), then attaches to + constructor or set with setNativeKey(), then attaches to the new shared memory segment with the given access \a mode and returns \tt true. If a shared memory segment identified by the key already exists, the attach operation is not performed and \tt false is returned. When the @@ -407,14 +343,14 @@ bool QSharedMemory::create(qsizetype size, AccessMode mode) #ifndef Q_OS_WIN // Take ownership and force set initialValue because the semaphore // might have already existed from a previous crash. - d->systemSemaphore.setKey(d->key, 1, QSystemSemaphore::Create); + d->systemSemaphore.setKey(d->semaphoreNativeKey(), 1, QSystemSemaphore::Create); #endif #endif QString function = "QSharedMemory::create"_L1; #if QT_CONFIG(systemsemaphore) QSharedMemoryLocker lock(this); - if (!d->key.isNull() && !d->tryLocker(&lock, function)) + if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, function)) return false; #endif @@ -461,7 +397,7 @@ qsizetype QSharedMemory::size() const /*! Attempts to attach the process to the shared memory segment identified by the key that was passed to the constructor or to a - call to setKey() or setNativeKey(). The access \a mode is \l {QSharedMemory::} + call to setNativeKey(). The access \a mode is \l {QSharedMemory::} {ReadWrite} by default. It can also be \l {QSharedMemory::} {ReadOnly}. Returns \c true if the attach operation is successful. If false is returned, call error() to determine which error occurred. @@ -478,7 +414,7 @@ bool QSharedMemory::attach(AccessMode mode) return false; #if QT_CONFIG(systemsemaphore) QSharedMemoryLocker lock(this); - if (!d->key.isNull() && !d->tryLocker(&lock, "QSharedMemory::attach"_L1)) + if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, "QSharedMemory::attach"_L1)) return false; #endif @@ -518,7 +454,7 @@ bool QSharedMemory::detach() #if QT_CONFIG(systemsemaphore) QSharedMemoryLocker lock(this); - if (!d->key.isNull() && !d->tryLocker(&lock, "QSharedMemory::detach"_L1)) + if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, "QSharedMemory::detach"_L1)) return false; #endif @@ -526,11 +462,14 @@ bool QSharedMemory::detach() } /*! - Returns a pointer to the contents of the shared memory segment, if - one is attached. Otherwise it returns null. Remember to lock the - shared memory with lock() before reading from or writing to the - shared memory, and remember to release the lock with unlock() after - you are done. + Returns a pointer to the contents of the shared memory segment, if one is + attached. Otherwise it returns null. The value returned by this function will + not change until a \l {detach()}{detach} happens, so it is safe to store this + pointer. + + If the memory operations are not atomic, you may lock the shared memory with + lock() before reading from or writing, but remember to release the lock with + unlock() after you are done. \sa attach() */ @@ -541,11 +480,14 @@ void *QSharedMemory::data() } /*! - Returns a const pointer to the contents of the shared memory - segment, if one is attached. Otherwise it returns null. Remember to - lock the shared memory with lock() before reading from or writing to - the shared memory, and remember to release the lock with unlock() - after you are done. + Returns a const pointer to the contents of the shared memory segment, if one + is attached. Otherwise it returns null. The value returned by this function + will not change until a \l {detach()}{detach} happens, so it is safe to store + this pointer. + + If the memory operations are not atomic, you may lock the shared memory with + lock() before reading from or writing, but remember to release the lock with + unlock() after you are done. \sa attach(), create() */ @@ -703,6 +645,21 @@ void QSharedMemoryPrivate::setUnixErrorString(QLatin1StringView function) } } +bool QSharedMemory::isKeyTypeSupported(QNativeIpcKey::Type type) +{ + return QSharedMemoryPrivate::DefaultBackend::supports(type); +} + +QNativeIpcKey QSharedMemory::platformSafeKey(const QString &key, QNativeIpcKey::Type type) +{ + return { QtIpcCommon::platformSafeKey(key, IpcType::SharedMemory, type), type }; +} + +QNativeIpcKey QSharedMemory::legacyNativeKey(const QString &key, QNativeIpcKey::Type type) +{ + return { legacyPlatformSafeKey(key, IpcType::SharedMemory, type), type }; +} + #endif // QT_CONFIG(sharedmemory) QT_END_NAMESPACE diff --git a/src/corelib/ipc/qsharedmemory.h b/src/corelib/ipc/qsharedmemory.h index 653fb1fb644..ff5c918fa6a 100644 --- a/src/corelib/ipc/qsharedmemory.h +++ b/src/corelib/ipc/qsharedmemory.h @@ -4,7 +4,7 @@ #ifndef QSHAREDMEMORY_H #define QSHAREDMEMORY_H -#include +#include #ifndef QT_NO_QOBJECT # include #else @@ -12,8 +12,8 @@ # include # include #endif -QT_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE #if QT_CONFIG(sharedmemory) @@ -45,13 +45,26 @@ public: }; QSharedMemory(QObject *parent = nullptr); - QSharedMemory(const QString &key, QObject *parent = nullptr); + QSharedMemory(const QNativeIpcKey &key, QObject *parent = nullptr); ~QSharedMemory(); +#if QT_DEPRECATED_SINCE(6, 9) + QT_DEPRECATED_VERSION_X_6_9("Please refer to 'Native IPC Key' documentation") + QSharedMemory(const QString &key, QObject *parent = nullptr); + QT_DEPRECATED_VERSION_X_6_9("Please refer to 'Native IPC Key' documentation") void setKey(const QString &key); + QT_DEPRECATED_VERSION_X_6_9("Please refer to 'Native IPC Key' documentation") QString key() const; - void setNativeKey(const QString &key); +#endif + + void setNativeKey(const QNativeIpcKey &key); + void setNativeKey(const QString &key, QNativeIpcKey::Type type = QNativeIpcKey::legacyDefaultTypeForOs()) + { setNativeKey({ key, type }); } QString nativeKey() const; + QNativeIpcKey nativeIpcKey() const; +#if QT_CORE_REMOVED_SINCE(6, 5) + void setNativeKey(const QString &key); +#endif bool create(qsizetype size, AccessMode mode = ReadWrite); qsizetype size() const; @@ -72,6 +85,12 @@ public: SharedMemoryError 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(QSharedMemory) }; diff --git a/src/corelib/ipc/qsharedmemory_p.h b/src/corelib/ipc/qsharedmemory_p.h index f61065713f9..a3aed4892de 100644 --- a/src/corelib/ipc/qsharedmemory_p.h +++ b/src/corelib/ipc/qsharedmemory_p.h @@ -69,6 +69,9 @@ private: class QSharedMemoryPosix { public: + static bool supports(QNativeIpcKey::Type type) + { return type == QNativeIpcKey::Type::PosixRealtime; } + bool handle(QSharedMemoryPrivate *self); bool cleanHandle(QSharedMemoryPrivate *self); bool create(QSharedMemoryPrivate *self, qsizetype size); @@ -81,6 +84,9 @@ public: class QSharedMemorySystemV { public: + static bool supports(QNativeIpcKey::Type type) + { return quint16(type) <= 0xff; } + #if QT_CONFIG(sysv_sem) key_t handle(QSharedMemoryPrivate *self); bool cleanHandle(QSharedMemoryPrivate *self); @@ -88,6 +94,10 @@ public: bool attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode); bool detach(QSharedMemoryPrivate *self); +private: + void updateNativeKeyFile(const QNativeIpcKey &nativeKey); + + QByteArray nativeKeyFile; key_t unix_key = 0; #endif }; @@ -95,6 +105,9 @@ public: class QSharedMemoryWin32 { public: + static bool supports(QNativeIpcKey::Type type) + { return type == QNativeIpcKey::Type::Windows; } + Qt::HANDLE handle(QSharedMemoryPrivate *self); bool cleanHandle(QSharedMemoryPrivate *self); bool create(QSharedMemoryPrivate *self, qsizetype size); @@ -111,8 +124,7 @@ class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate public: void *memory = nullptr; qsizetype size = 0; - QString key; - QString nativeKey; + QNativeIpcKey nativeKey; QString errorString; #if QT_CONFIG(systemsemaphore) QSystemSemaphore systemSemaphore{ QNativeIpcKey() }; @@ -168,6 +180,8 @@ public: } QNativeIpcKey semaphoreNativeKey() const; #endif // QT_CONFIG(systemsemaphore) + + QString legacyKey; // deprecated }; QT_END_NAMESPACE diff --git a/src/corelib/ipc/qsharedmemory_posix.cpp b/src/corelib/ipc/qsharedmemory_posix.cpp index 88e3f022e06..387b7c245c0 100644 --- a/src/corelib/ipc/qsharedmemory_posix.cpp +++ b/src/corelib/ipc/qsharedmemory_posix.cpp @@ -27,8 +27,7 @@ using namespace QtIpcCommon; bool QSharedMemoryPosix::handle(QSharedMemoryPrivate *self) { // don't allow making handles on empty keys - const QString safeKey = legacyPlatformSafeKey(self->key, IpcType::SharedMemory); - if (safeKey.isEmpty()) { + if (self->nativeKey.isEmpty()) { self->setError(QSharedMemory::KeyError, QSharedMemory::tr("%1: key is empty").arg("QSharedMemory::handle"_L1)); return false; @@ -50,7 +49,7 @@ bool QSharedMemoryPosix::create(QSharedMemoryPrivate *self, qsizetype size) if (!handle(self)) return false; - const QByteArray shmName = QFile::encodeName(legacyPlatformSafeKey(self->key, IpcType::SharedMemory)); + const QByteArray shmName = QFile::encodeName(self->nativeKey.nativeKey()); int fd; #ifdef O_CLOEXEC @@ -91,7 +90,7 @@ bool QSharedMemoryPosix::create(QSharedMemoryPrivate *self, qsizetype size) bool QSharedMemoryPosix::attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode) { - const QByteArray shmName = QFile::encodeName(legacyPlatformSafeKey(self->key, IpcType::SharedMemory)); + const QByteArray shmName = QFile::encodeName(self->nativeKey.nativeKey()); const int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR); const mode_t omode = (mode == QSharedMemory::ReadOnly ? 0400 : 0600); @@ -177,7 +176,7 @@ bool QSharedMemoryPosix::detach(QSharedMemoryPrivate *self) // if there are no attachments then unlink the shared memory if (shm_nattch == 0) { - const QByteArray shmName = QFile::encodeName(legacyPlatformSafeKey(self->key, IpcType::SharedMemory)); + const QByteArray shmName = QFile::encodeName(self->nativeKey.nativeKey()); if (::shm_unlink(shmName.constData()) == -1 && errno != ENOENT) self->setUnixErrorString("QSharedMemory::detach (shm_unlink)"_L1); } diff --git a/src/corelib/ipc/qsharedmemory_systemv.cpp b/src/corelib/ipc/qsharedmemory_systemv.cpp index 98887f29fee..71a0c261015 100644 --- a/src/corelib/ipc/qsharedmemory_systemv.cpp +++ b/src/corelib/ipc/qsharedmemory_systemv.cpp @@ -26,6 +26,13 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; using namespace QtIpcCommon; +inline void QSharedMemorySystemV::updateNativeKeyFile(const QNativeIpcKey &nativeKey) +{ + Q_ASSERT(nativeKeyFile.isEmpty() ); + if (!nativeKey.nativeKey().isEmpty()) + nativeKeyFile = QFile::encodeName(nativeKey.nativeKey()); +} + /*! \internal @@ -38,7 +45,9 @@ key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self) return unix_key; // don't allow making handles on empty keys - if (self->nativeKey.isEmpty()) { + if (nativeKeyFile.isEmpty()) + updateNativeKeyFile(self->nativeKey); + if (nativeKeyFile.isEmpty()) { self->setError(QSharedMemory::KeyError, QSharedMemory::tr("%1: key is empty") .arg("QSharedMemory::handle:"_L1)); @@ -46,14 +55,14 @@ key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self) } // ftok requires that an actual file exists somewhere - if (!QFile::exists(self->nativeKey)) { + if (!QFile::exists(nativeKeyFile)) { self->setError(QSharedMemory::NotFound, QSharedMemory::tr("%1: UNIX key file doesn't exist") .arg("QSharedMemory::handle:"_L1)); return 0; } - unix_key = ftok(QFile::encodeName(self->nativeKey).constData(), 'Q'); + unix_key = ftok(nativeKeyFile, int(self->nativeKey.type())); if (-1 == unix_key) { self->setError(QSharedMemory::KeyError, QSharedMemory::tr("%1: ftok failed") @@ -66,6 +75,7 @@ key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self) bool QSharedMemorySystemV::cleanHandle(QSharedMemoryPrivate *) { unix_key = 0; + nativeKeyFile.clear(); return true; } @@ -73,7 +83,7 @@ bool QSharedMemorySystemV::create(QSharedMemoryPrivate *self, qsizetype size) { // build file if needed bool createdFile = false; - QByteArray nativeKeyFile = QFile::encodeName(self->nativeKey); + updateNativeKeyFile(self->nativeKey); int built = createUnixKeyFile(nativeKeyFile); if (built == -1) { self->setError(QSharedMemory::KeyError, @@ -161,6 +171,7 @@ bool QSharedMemorySystemV::detach(QSharedMemoryPrivate *self) // Get the number of current attachments int id = shmget(unix_key, 0, 0400); + QByteArray oldNativeKeyFile = nativeKeyFile; cleanHandle(self); struct shmid_ds shmid_ds; @@ -186,7 +197,7 @@ bool QSharedMemorySystemV::detach(QSharedMemoryPrivate *self) } // remove file - if (!unlink(QFile::encodeName(self->nativeKey))) + if (unlink(oldNativeKeyFile) < 0) return false; } return true; diff --git a/src/corelib/ipc/qsharedmemory_win.cpp b/src/corelib/ipc/qsharedmemory_win.cpp index 593c312f478..472f34f9a18 100644 --- a/src/corelib/ipc/qsharedmemory_win.cpp +++ b/src/corelib/ipc/qsharedmemory_win.cpp @@ -60,7 +60,7 @@ HANDLE QSharedMemoryWin32::handle(QSharedMemoryPrivate *self) return 0; } hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, - reinterpret_cast(self->nativeKey.utf16())); + reinterpret_cast(self->nativeKey.nativeKey().utf16())); if (!hand) { self->setWindowsErrorString(function); return 0; @@ -96,7 +96,7 @@ bool QSharedMemoryWin32::create(QSharedMemoryPrivate *self, qsizetype size) high = 0; low = DWORD(size_t(size) & 0xffffffff); hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, high, low, - reinterpret_cast(self->nativeKey.utf16())); + reinterpret_cast(self->nativeKey.nativeKey().utf16())); self->setWindowsErrorString(function); // hand is valid when it already exists unlike unix so explicitly check diff --git a/tests/auto/corelib/ipc/CMakeLists.txt b/tests/auto/corelib/ipc/CMakeLists.txt index cd6f28d553e..751d0937884 100644 --- a/tests/auto/corelib/ipc/CMakeLists.txt +++ b/tests/auto/corelib/ipc/CMakeLists.txt @@ -5,7 +5,7 @@ if(NOT ANDROID AND NOT UIKIT) if(QT_FEATURE_sharedmemory OR QT_FEATURE_systemsemaphore) add_subdirectory(qnativeipckey) endif() - if(QT_FEATURE_sharedmemory AND QT_FEATURE_private_tests) + if(QT_FEATURE_sharedmemory) add_subdirectory(qsharedmemory) endif() if(QT_FEATURE_systemsemaphore) diff --git a/tests/auto/corelib/ipc/qsharedmemory/producerconsumer/main.cpp b/tests/auto/corelib/ipc/qsharedmemory/producerconsumer/main.cpp index 6dc4fde7caa..4b30c066157 100644 --- a/tests/auto/corelib/ipc/qsharedmemory/producerconsumer/main.cpp +++ b/tests/auto/corelib/ipc/qsharedmemory/producerconsumer/main.cpp @@ -17,10 +17,9 @@ QChar get(QSharedMemory &sm, int i) return QChar::fromLatin1(((char*)sm.data())[i]); } -int readonly_segfault() +int readonly_segfault(const QNativeIpcKey &key) { - QSharedMemory sharedMemory; - sharedMemory.setKey("readonly_segfault"); + QSharedMemory sharedMemory(key); sharedMemory.create(1024, QSharedMemory::ReadOnly); sharedMemory.lock(); set(sharedMemory, 0, 'a'); @@ -28,10 +27,9 @@ int readonly_segfault() return EXIT_SUCCESS; } -int producer() +int producer(const QNativeIpcKey &key) { - QSharedMemory producer; - producer.setKey("market"); + QSharedMemory producer(key); int size = 1024; if (!producer.create(size)) { @@ -100,10 +98,9 @@ int producer() return EXIT_SUCCESS; } -int consumer() +int consumer(const QNativeIpcKey &key) { - QSharedMemory consumer; - consumer.setKey("market"); + QSharedMemory consumer(key); //qDebug("consumer starting"); int tries = 0; @@ -155,17 +152,21 @@ int main(int argc, char *argv[]) QCoreApplication app(argc, argv); QStringList arguments = app.arguments(); - if (app.arguments().size() != 2) { - qWarning("Please call the helper with the function to call as argument"); + if (app.arguments().size() != 3) { + fprintf(stderr, "Usage: %s \n" + " is one of: readonly_segfault, producer, consumer\n", + argv[0]); return EXIT_FAILURE; } QString function = arguments.at(1); + QNativeIpcKey key = QNativeIpcKey::fromString(arguments.at(2)); + if (function == QLatin1String("readonly_segfault")) - return readonly_segfault(); + return readonly_segfault(key); else if (function == QLatin1String("producer")) - return producer(); + return producer(key); else if (function == QLatin1String("consumer")) - return consumer(); + return consumer(key); else qWarning() << "Unknown function" << arguments.at(1); diff --git a/tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp b/tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp index 3dde6421522..8d86fffccbc 100644 --- a/tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp +++ b/tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp @@ -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 @@ -11,6 +12,14 @@ #include #include +#include +#include +#ifdef Q_OS_UNIX +# include +#endif + +#include "private/qtcore-config_p.h" + #define EXISTING_SHARE "existing" #define EXISTING_SIZE 1024 @@ -33,8 +42,10 @@ public Q_SLOTS: private slots: // basics void constructor(); - void key_data(); - void key(); + void nativeKey_data(); + void nativeKey(); + void legacyKey_data() { nativeKey_data(); } + void legacyKey(); void create_data(); void create(); void attach_data(); @@ -76,20 +87,25 @@ private slots: void uniqueKey(); protected: - int remove(const QString &key); + void remove(const QNativeIpcKey &key); - QString rememberKey(const QString &key) + QNativeIpcKey platformSafeKey(const QString &key) { - if (key == EXISTING_SHARE) - return key; - if (!keys.contains(key)) { - keys.append(key); - remove(key); - } - return key; + QNativeIpcKey::Type keyType = QNativeIpcKey::DefaultTypeForOs; + return QSharedMemory::platformSafeKey(key, keyType); } - QStringList keys; + QNativeIpcKey rememberKey(const QString &key) + { + QNativeIpcKey ipcKey = platformSafeKey(key); + if (!keys.contains(ipcKey)) { + keys.append(ipcKey); + remove(ipcKey); + } + return ipcKey; + } + + QList keys; QList jail; QSharedMemory *existingSharedMemory; @@ -109,10 +125,12 @@ tst_QSharedMemory::~tst_QSharedMemory() void tst_QSharedMemory::init() { - existingSharedMemory = new QSharedMemory(EXISTING_SHARE); + QNativeIpcKey key = platformSafeKey(EXISTING_SHARE); + existingSharedMemory = new QSharedMemory(key); if (!existingSharedMemory->create(EXISTING_SIZE)) { QCOMPARE(existingSharedMemory->error(), QSharedMemory::AlreadyExists); } + keys.append(key); } void tst_QSharedMemory::cleanup() @@ -121,7 +139,6 @@ void tst_QSharedMemory::cleanup() qDeleteAll(jail.begin(), jail.end()); jail.clear(); - keys.append(EXISTING_SHARE); for (int i = 0; i < keys.size(); ++i) { QSharedMemory sm(keys.at(i)); if (!sm.create(1024)) { @@ -134,65 +151,70 @@ void tst_QSharedMemory::cleanup() } } -#ifndef Q_OS_WIN -#include +#if QT_CONFIG(posix_shm) +#include +#include +#endif +#if QT_CONFIG(sysv_shm) #include -#ifndef QT_POSIX_IPC #include #include -#else -#include -#endif // QT_POSIX_IPC -#include #endif -int tst_QSharedMemory::remove(const QString &key) +void tst_QSharedMemory::remove(const QNativeIpcKey &key) { -#ifdef Q_OS_WIN - Q_UNUSED(key); - return 0; -#else - // On unix the shared memory might exists from a previously failed test + // On Unix, the shared memory might exist from a previously failed test // or segfault, remove it it does if (key.isEmpty()) - return -1; + return; - QString fileName = QtIpcCommon::legacyPlatformSafeKey(key, QtIpcCommon::IpcType::SharedMemory); + switch (key.type()) { + case QNativeIpcKey::Type::Windows: + return; -#ifndef QT_POSIX_IPC - // ftok requires that an actual file exists somewhere - if (!QFile::exists(fileName)) { - //qDebug() << "exits failed"; - return -2; + case QNativeIpcKey::Type::PosixRealtime: +#if QT_CONFIG(posix_shm) + if (shm_unlink(QFile::encodeName(key.nativeKey()).constData()) == -1) { + if (errno != ENOENT) { + perror("shm_unlink"); + return; + } + } +#endif + return; + + case QNativeIpcKey::Type::SystemV: + break; } - int unix_key = ftok(fileName.toLatin1().constData(), 'Q'); +#if QT_CONFIG(sysv_shm) + // ftok requires that an actual file exists somewhere + QString fileName = key.nativeKey(); + if (!QFile::exists(fileName)) { + //qDebug() << "exits failed"; + return; + } + + int unix_key = ftok(fileName.toLatin1().constData(), int(key.type())); if (-1 == unix_key) { - qDebug() << "ftok failed"; - return -3; + perror("ftok"); + return; } int id = shmget(unix_key, 0, 0600); if (-1 == id) { - qDebug() << "shmget failed" << strerror(errno); - return -4; + if (errno != ENOENT) + perror("shmget"); + return; } struct shmid_ds shmid_ds; if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) { - qDebug() << "shmctl failed"; - return -5; + perror("shmctl"); + return; } -#else - if (shm_unlink(QFile::encodeName(fileName).constData()) == -1) { - if (errno != ENOENT) { - qDebug() << "shm_unlink failed"; - return -5; - } - } -#endif // QT_POSIX_IPC - return QFile::remove(fileName); + QFile::remove(fileName); #endif // Q_OS_WIN } @@ -202,19 +224,25 @@ int tst_QSharedMemory::remove(const QString &key) void tst_QSharedMemory::constructor() { QSharedMemory sm; - QCOMPARE(sm.key(), QString()); QVERIFY(!sm.isAttached()); QVERIFY(!sm.data()); + QCOMPARE(sm.nativeKey(), QString()); + QCOMPARE(sm.nativeIpcKey(), QNativeIpcKey()); QCOMPARE(sm.size(), 0); QCOMPARE(sm.error(), QSharedMemory::NoError); QCOMPARE(sm.errorString(), QString()); + + QT_WARNING_PUSH + QT_WARNING_DISABLE_DEPRECATED + QCOMPARE(sm.key(), QString()); + QT_WARNING_POP } -void tst_QSharedMemory::key_data() +void tst_QSharedMemory::nativeKey_data() { QTest::addColumn("constructorKey"); QTest::addColumn("setKey"); - QTest::addColumn("setNativeKey"); + QTest::addColumn("setNativeKey"); // only used in the legacyKey test QTest::newRow("null, null, null") << QString() << QString() << QString(); QTest::newRow("one, null, null") << QString("one") << QString() << QString(); @@ -230,12 +258,43 @@ void tst_QSharedMemory::key_data() /*! Basic key testing */ -void tst_QSharedMemory::key() +void tst_QSharedMemory::nativeKey() { QFETCH(QString, constructorKey); QFETCH(QString, setKey); QFETCH(QString, setNativeKey); + QNativeIpcKey constructorIpcKey = platformSafeKey(constructorKey); + QNativeIpcKey setIpcKey = platformSafeKey(setKey); + + QSharedMemory sm(constructorIpcKey); + QCOMPARE(sm.nativeIpcKey(), constructorIpcKey); + QCOMPARE(sm.nativeKey(), constructorIpcKey.nativeKey()); + sm.setNativeKey(setIpcKey); + QCOMPARE(sm.nativeIpcKey(), setIpcKey); + QCOMPARE(sm.nativeKey(), setIpcKey.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_DISABLE_DEPRECATED +void tst_QSharedMemory::legacyKey() +{ + QFETCH(QString, constructorKey); + QFETCH(QString, setKey); + QFETCH(QString, setNativeKey); + +#ifdef Q_OS_QNX + QSKIP("The legacy native key type is incorrectly set on QNX"); +#endif QSharedMemory sm(constructorKey); QCOMPARE(sm.key(), constructorKey); QCOMPARE(sm.nativeKey().isEmpty(), constructorKey.isEmpty()); @@ -254,6 +313,7 @@ void tst_QSharedMemory::key() QCOMPARE(sm.detach(), false); } +QT_WARNING_POP void tst_QSharedMemory::create_data() { @@ -282,11 +342,12 @@ void tst_QSharedMemory::create() QFETCH(bool, canCreate); QFETCH(QSharedMemory::SharedMemoryError, error); - QSharedMemory sm(rememberKey(key)); + QNativeIpcKey nativeKey = rememberKey(key); + QSharedMemory sm(nativeKey); QCOMPARE(sm.create(size), canCreate); if (sm.error() != error) qDebug() << sm.errorString(); - QCOMPARE(sm.key(), key); + QCOMPARE(sm.nativeIpcKey(), nativeKey); if (canCreate) { QCOMPARE(sm.errorString(), QString()); QVERIFY(sm.data() != 0); @@ -303,13 +364,10 @@ void tst_QSharedMemory::attach_data() QTest::addColumn("exists"); QTest::addColumn("error"); - QTest::newRow("null key") << QString() << false << QSharedMemory::KeyError; - QTest::newRow("doesn't exists") << QString("doesntexists") << false << QSharedMemory::NotFound; + QTest::newRow("null") << QString() << false << QSharedMemory::KeyError; + QTest::newRow("doesntexists") << QString("doesntexist") << false << QSharedMemory::NotFound; - // HPUX doesn't allow for multiple attaches per process. -#ifndef Q_OS_HPUX - QTest::newRow("already exists") << QString(EXISTING_SHARE) << true << QSharedMemory::NoError; -#endif + QTest::newRow(EXISTING_SHARE) << QString(EXISTING_SHARE) << true << QSharedMemory::NoError; } /*! @@ -321,11 +379,12 @@ void tst_QSharedMemory::attach() QFETCH(bool, exists); QFETCH(QSharedMemory::SharedMemoryError, error); - QSharedMemory sm(key); + QNativeIpcKey nativeKey = platformSafeKey(key); + QSharedMemory sm(nativeKey); QCOMPARE(sm.attach(), exists); QCOMPARE(sm.isAttached(), exists); QCOMPARE(sm.error(), error); - QCOMPARE(sm.key(), key); + QCOMPARE(sm.nativeIpcKey(), nativeKey); if (exists) { QVERIFY(sm.data() != 0); QVERIFY(sm.size() != 0); @@ -352,7 +411,7 @@ void tst_QSharedMemory::lock() QVERIFY(!shm.lock()); QCOMPARE(shm.error(), QSharedMemory::LockError); - shm.setKey(rememberKey(QLatin1String("qsharedmemory"))); + shm.setNativeKey(rememberKey(QLatin1String("qsharedmemory"))); QVERIFY(!shm.lock()); QCOMPARE(shm.error(), QSharedMemory::LockError); @@ -376,12 +435,13 @@ void tst_QSharedMemory::removeWhileAttached() rememberKey("one"); // attach 1 - QSharedMemory *smOne = new QSharedMemory(QLatin1String("one")); + QNativeIpcKey keyOne = platformSafeKey("one"); + QSharedMemory *smOne = new QSharedMemory(keyOne); QVERIFY(smOne->create(1024)); QVERIFY(smOne->isAttached()); // attach 2 - QSharedMemory *smTwo = new QSharedMemory(QLatin1String("one")); + QSharedMemory *smTwo = new QSharedMemory(platformSafeKey("one")); QVERIFY(smTwo->attach()); QVERIFY(smTwo->isAttached()); @@ -389,13 +449,13 @@ void tst_QSharedMemory::removeWhileAttached() delete smOne; delete smTwo; -#ifdef QT_POSIX_IPC - // POSIX IPC doesn't guarantee that the shared memory is removed - remove("one"); -#endif + if (keyOne.type() == QNativeIpcKey::Type::PosixRealtime) { + // POSIX IPC doesn't guarantee that the shared memory is removed + remove(keyOne); + } // three shouldn't be able to attach - QSharedMemory smThree(QLatin1String("one")); + QSharedMemory smThree(keyOne); QVERIFY(!smThree.attach()); QCOMPARE(smThree.error(), QSharedMemory::NotFound); } @@ -430,11 +490,12 @@ void tst_QSharedMemory::readOnly() #elif defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) QSKIP("ASan prevents the crash this test is looking for.", SkipAll); #else - rememberKey("readonly_segfault"); + QNativeIpcKey key = rememberKey("readonly_segfault"); + // ### on windows disable the popup somehow QProcess p; - p.start(m_helperBinary, QStringList("readonly_segfault")); p.setProcessChannelMode(QProcess::ForwardedChannels); + p.start(m_helperBinary, { "readonly_segfault", key.toString() }); p.waitForFinished(); QCOMPARE(p.error(), QProcess::Crashed); #endif @@ -451,7 +512,8 @@ void tst_QSharedMemory::useTooMuchMemory() int count = 0; while (success) { QString key = QLatin1String("maxmemorytest_") + QString::number(count++); - QSharedMemory *sm = new QSharedMemory(rememberKey(key)); + QNativeIpcKey nativeKey = rememberKey(key); + QSharedMemory *sm = new QSharedMemory(nativeKey); QVERIFY(sm); jail.append(sm); int size = 32768 * 1024; @@ -465,7 +527,7 @@ void tst_QSharedMemory::useTooMuchMemory() if (!success) { QVERIFY(!sm->isAttached()); - QCOMPARE(sm->key(), key); + QCOMPARE(sm->nativeIpcKey(), nativeKey); QCOMPARE(sm->size(), 0); QVERIFY(!sm->data()); if (sm->error() != QSharedMemory::OutOfResources) @@ -496,12 +558,12 @@ void tst_QSharedMemory::attachTooMuch() QSharedMemory government(rememberKey("government")); QVERIFY(government.create(1024)); while (true) { - QSharedMemory *war = new QSharedMemory(government.key()); + QSharedMemory *war = new QSharedMemory(government.nativeIpcKey()); QVERIFY(war); jail.append(war); if (!war->attach()) { QVERIFY(!war->isAttached()); - QCOMPARE(war->key(), government.key()); + QCOMPARE(war->nativeIpcKey(), government.nativeIpcKey()); QCOMPARE(war->size(), 0); QVERIFY(!war->data()); QCOMPARE(war->error(), QSharedMemory::OutOfResources); @@ -537,8 +599,8 @@ void tst_QSharedMemory::simpleProducerConsumer() QFETCH(QSharedMemory::AccessMode, mode); rememberKey(QLatin1String("market")); - QSharedMemory producer(QLatin1String("market")); - QSharedMemory consumer(QLatin1String("market")); + QSharedMemory producer(platformSafeKey("market")); + QSharedMemory consumer(platformSafeKey("market")); int size = 512; QVERIFY(producer.create(size)); QVERIFY(consumer.attach(mode)); @@ -560,19 +622,21 @@ void tst_QSharedMemory::simpleProducerConsumer() #ifndef Q_OS_HPUX void tst_QSharedMemory::simpleDoubleProducerConsumer() { - rememberKey(QLatin1String("market")); - QSharedMemory producer(QLatin1String("market")); + QNativeIpcKey nativeKey = rememberKey(QLatin1String("market")); + QSharedMemory producer(nativeKey); int size = 512; QVERIFY(producer.create(size)); QVERIFY(producer.detach()); -#ifdef QT_POSIX_IPC - // POSIX IPC doesn't guarantee that the shared memory is removed - remove("market"); -#endif + + if (nativeKey.type() == QNativeIpcKey::Type::PosixRealtime) { + // POSIX IPC doesn't guarantee that the shared memory is removed + remove(nativeKey); + } + QVERIFY(producer.create(size)); { - QSharedMemory consumer(QLatin1String("market")); + QSharedMemory consumer(nativeKey); QVERIFY(consumer.attach()); } } @@ -580,11 +644,13 @@ void tst_QSharedMemory::simpleDoubleProducerConsumer() class Consumer : public QThread { - public: + QNativeIpcKey nativeKey; + Consumer(const QNativeIpcKey &nativeKey) : nativeKey(nativeKey) {} + void run() override { - QSharedMemory consumer(QLatin1String("market")); + QSharedMemory consumer(nativeKey); while (!consumer.attach()) { if (consumer.error() != QSharedMemory::NotFound) qDebug() << "consumer: failed to connect" << consumer.error() << consumer.errorString(); @@ -615,9 +681,8 @@ public: class Producer : public QThread { - public: - Producer() : producer(QLatin1String("market")) + Producer(const QNativeIpcKey &nativeKey) : producer(nativeKey) { int size = 1024; if (!producer.create(size)) { @@ -682,16 +747,16 @@ void tst_QSharedMemory::simpleThreadedProducerConsumer() { QFETCH(bool, producerIsThread); QFETCH(int, threads); - rememberKey(QLatin1String("market")); + QNativeIpcKey nativeKey = rememberKey(QLatin1String("market")); - Producer p; + Producer p(nativeKey); QVERIFY(p.producer.isAttached()); if (producerIsThread) p.start(); QList consumers; for (int i = 0; i < threads; ++i) { - consumers.append(new Consumer()); + consumers.append(new Consumer(nativeKey)); consumers.last()->start(); } @@ -730,17 +795,17 @@ void tst_QSharedMemory::simpleProcessProducerConsumer() QSKIP("This test is unstable: QTBUG-25655"); - rememberKey("market"); + QNativeIpcKey nativeKey = rememberKey("market"); QProcess producer; - producer.start(m_helperBinary, QStringList("producer")); + producer.start(m_helperBinary, { "producer", nativeKey.toString() }); QVERIFY2(producer.waitForStarted(), "Could not start helper binary"); QVERIFY2(producer.waitForReadyRead(), "Helper process failed to create shared memory segment: " + producer.readAllStandardError()); QList consumers; unsigned int failedProcesses = 0; - const QStringList consumerArguments = QStringList("consumer"); + QStringList consumerArguments = { "consumer", nativeKey.toString() }; for (int i = 0; i < processes; ++i) { QProcess *p = new QProcess; p->setProcessChannelMode(QProcess::ForwardedChannels); @@ -791,11 +856,11 @@ void tst_QSharedMemory::uniqueKey() QFETCH(QString, key1); QFETCH(QString, key2); - QSharedMemory sm1(key1); - QSharedMemory sm2(key2); + QSharedMemory sm1(platformSafeKey(key1)); + QSharedMemory sm2(platformSafeKey(key2)); bool setEqual = (key1 == key2); - bool keyEqual = (sm1.key() == sm2.key()); + bool keyEqual = (sm1.nativeIpcKey() == sm2.nativeIpcKey()); bool nativeEqual = (sm1.nativeKey() == sm2.nativeKey()); QCOMPARE(keyEqual, setEqual);