IPC: add native key support to QSharedMemory
And deprecate the non-native key support API. Qt 7 may not even store the old, non-native key. Documentation in a new commit, when the dust settles. Drive-by updates to the tst_QSharedMemory::attach row names, to fix grammar and remove spaces and apostrophe. Change-Id: I12a088d1ae424825abd3fffd171d3025c77f94d2 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
3ae052d3bb
commit
2c286561bb
@ -313,6 +313,15 @@ int QMetaType::idHelper() const
|
|||||||
return registerHelper(d_ptr);
|
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"
|
#include "qvariant.h"
|
||||||
|
|
||||||
// these implementations aren't as efficient as they used to be prior to
|
// these implementations aren't as efficient as they used to be prior to
|
||||||
|
@ -49,192 +49,83 @@ inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const
|
|||||||
|
|
||||||
\brief The QSharedMemory class provides access to a shared memory segment.
|
\brief The QSharedMemory class provides access to a shared memory segment.
|
||||||
|
|
||||||
QSharedMemory provides access to a shared memory segment by multiple
|
QSharedMemory provides access to a \l{Shared Memory}{shared memory segment}
|
||||||
threads and processes. It also provides a way for a single thread or
|
by multiple threads and processes. Shared memory segments are identified by a
|
||||||
process to lock the memory for exclusive access.
|
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
|
One QSharedMemory object must create() the segment and this call specifies
|
||||||
differences:
|
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.
|
By default, QSharedMemory automatically destroys the shared memory segment
|
||||||
When all threads or processes that have an instance of QSharedMemory
|
when the last instance of QSharedMemory is \l{detach()}{detached} from the
|
||||||
attached to a particular shared memory segment have either destroyed
|
segment, and no references to the segment remain.
|
||||||
their instance of QSharedMemory or exited, the Windows kernel
|
|
||||||
releases the shared memory segment automatically.
|
|
||||||
|
|
||||||
\li Unix: QSharedMemory "owns" the shared memory segment. When the
|
For details on the key types, platform-specific limitations, and
|
||||||
last thread or process that has an instance of QSharedMemory
|
interoperability with older or non-Qt applications, see the \l{Native IPC
|
||||||
attached to a particular shared memory segment detaches from the
|
Key} documentation. That includes important information for sandboxed
|
||||||
segment by destroying its instance of QSharedMemory, the destructor
|
applications on Apple platforms, including all apps obtained via the Apple
|
||||||
releases the shared memory segment. But if that last thread or
|
App Store.
|
||||||
process crashes without running the QSharedMemory destructor, the
|
|
||||||
shared memory segment survives the crash.
|
|
||||||
|
|
||||||
\li Unix: QSharedMemory can be implemented by one of two different
|
\sa Inter-Process Communication, QSystemSemaphore
|
||||||
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 {<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}.
|
|
||||||
|
|
||||||
\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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\overload QSharedMemory()
|
\overload QSharedMemory()
|
||||||
|
|
||||||
Constructs a shared memory object with the given \a parent. The
|
Constructs a shared memory object with the given \a parent. The shared memory
|
||||||
shared memory object's key is not set by the constructor, so the
|
object's key is not set by the constructor, so the shared memory object does
|
||||||
shared memory object does not have an underlying shared memory
|
not have an underlying shared memory segment attached. The key must be set
|
||||||
segment attached. The key must be set with setKey() or setNativeKey()
|
with setNativeKey() before create() or attach() can be used.
|
||||||
before create() or attach() can be used.
|
|
||||||
|
|
||||||
\sa setKey()
|
\sa setNativeKey()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
QSharedMemory::QSharedMemory(QObject *parent)
|
QSharedMemory::QSharedMemory(QObject *parent)
|
||||||
: QObject(*new QSharedMemoryPrivate, parent)
|
: QSharedMemory(QNativeIpcKey(), parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
\overload
|
||||||
|
|
||||||
Constructs a shared memory object with the given \a parent and with
|
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
|
its key set to \a key. Because its key is set, its create() and
|
||||||
attach() functions can be called.
|
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()
|
\sa setKey(), create(), attach()
|
||||||
*/
|
*/
|
||||||
QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
|
QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
|
||||||
: QObject(*new QSharedMemoryPrivate, parent)
|
: QObject(*new QSharedMemoryPrivate, parent)
|
||||||
{
|
{
|
||||||
|
QT_WARNING_PUSH
|
||||||
|
QT_WARNING_DISABLE_DEPRECATED
|
||||||
setKey(key);
|
setKey(key);
|
||||||
|
QT_WARNING_POP
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -248,67 +139,91 @@ QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
|
|||||||
*/
|
*/
|
||||||
QSharedMemory::~QSharedMemory()
|
QSharedMemory::~QSharedMemory()
|
||||||
{
|
{
|
||||||
setKey(QString());
|
setNativeKey(QNativeIpcKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the platform independent \a key for this shared memory object. If \a key
|
\overload
|
||||||
is the same as the current key, the function returns without doing anything.
|
|
||||||
|
|
||||||
You can call key() to retrieve the platform independent key. Internally,
|
Sets the legacy \a key for this shared memory object. If \a key is the same
|
||||||
QSharedMemory converts this key into a platform specific key. If you instead
|
as the current key, the function returns without doing anything. Otherwise,
|
||||||
call nativeKey(), you will get the platform specific, converted key.
|
if the shared memory object is attached to an underlying shared memory
|
||||||
|
|
||||||
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.
|
segment, it will \l {detach()} {detach} from it before setting the new key.
|
||||||
This function does not do an attach().
|
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()
|
\sa key(), nativeKey(), isAttached()
|
||||||
*/
|
*/
|
||||||
void QSharedMemory::setKey(const QString &key)
|
void QSharedMemory::setKey(const QString &key)
|
||||||
{
|
{
|
||||||
Q_D(QSharedMemory);
|
Q_D(QSharedMemory);
|
||||||
QString newNativeKey =
|
setNativeKey(legacyNativeKey(key));
|
||||||
QtIpcCommon::legacyPlatformSafeKey(key, QtIpcCommon::IpcType::SharedMemory);
|
d->legacyKey = key;
|
||||||
if (key == d->key && newNativeKey == d->nativeKey)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (isAttached())
|
|
||||||
detach();
|
|
||||||
d->cleanHandle();
|
|
||||||
d->key = key;
|
|
||||||
d->nativeKey = newNativeKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\since 4.8
|
\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
|
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
|
\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
|
doing anything. Otherwise, if the shared memory object is attached to an
|
||||||
call setKey() instead.
|
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
|
This function is useful if the native key was shared from another process.
|
||||||
assigned, calling key() will return a null string.
|
See \l{Native IPC Keys} for more information.
|
||||||
|
|
||||||
If the shared memory object is attached to an underlying shared memory
|
Portable native keys can be obtained using platformSafeKey().
|
||||||
segment, it will \l {detach()} {detach} from it before setting the new key.
|
|
||||||
This function does not do an attach().
|
|
||||||
|
|
||||||
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);
|
Q_D(QSharedMemory);
|
||||||
if (key == d->nativeKey && d->key.isNull())
|
if (key == d->nativeKey && key.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
if (!isKeyTypeSupported(key.type())) {
|
||||||
|
d->setError(KeyError, tr("%1: unsupported key type")
|
||||||
|
.arg("QSharedMemory::setNativeKey"_L1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAttached())
|
if (isAttached())
|
||||||
detach();
|
detach();
|
||||||
d->cleanHandle();
|
d->cleanHandle();
|
||||||
d->key = QString();
|
d->legacyKey = QString();
|
||||||
d->nativeKey = key;
|
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
|
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
|
key is the identifier used by Qt applications to identify the shared memory
|
||||||
segment.
|
segment.
|
||||||
@ -365,7 +281,7 @@ bool QSharedMemoryPrivate::initKey()
|
|||||||
QString QSharedMemory::key() const
|
QString QSharedMemory::key() const
|
||||||
{
|
{
|
||||||
Q_D(const QSharedMemory);
|
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
|
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.
|
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
|
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);
|
Q_D(const QSharedMemory);
|
||||||
return d->nativeKey;
|
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
|
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
|
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,
|
\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
|
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
|
#ifndef Q_OS_WIN
|
||||||
// Take ownership and force set initialValue because the semaphore
|
// Take ownership and force set initialValue because the semaphore
|
||||||
// might have already existed from a previous crash.
|
// 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
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QString function = "QSharedMemory::create"_L1;
|
QString function = "QSharedMemory::create"_L1;
|
||||||
#if QT_CONFIG(systemsemaphore)
|
#if QT_CONFIG(systemsemaphore)
|
||||||
QSharedMemoryLocker lock(this);
|
QSharedMemoryLocker lock(this);
|
||||||
if (!d->key.isNull() && !d->tryLocker(&lock, function))
|
if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, function))
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -461,7 +397,7 @@ qsizetype QSharedMemory::size() const
|
|||||||
/*!
|
/*!
|
||||||
Attempts to attach the process to the shared memory segment
|
Attempts to attach the process to the shared memory segment
|
||||||
identified by the key that was passed to the constructor or to a
|
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::}
|
{ReadWrite} by default. It can also be \l {QSharedMemory::}
|
||||||
{ReadOnly}. Returns \c true if the attach operation is successful. If
|
{ReadOnly}. Returns \c true if the attach operation is successful. If
|
||||||
false is returned, call error() to determine which error occurred.
|
false is returned, call error() to determine which error occurred.
|
||||||
@ -478,7 +414,7 @@ bool QSharedMemory::attach(AccessMode mode)
|
|||||||
return false;
|
return false;
|
||||||
#if QT_CONFIG(systemsemaphore)
|
#if QT_CONFIG(systemsemaphore)
|
||||||
QSharedMemoryLocker lock(this);
|
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;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -518,7 +454,7 @@ bool QSharedMemory::detach()
|
|||||||
|
|
||||||
#if QT_CONFIG(systemsemaphore)
|
#if QT_CONFIG(systemsemaphore)
|
||||||
QSharedMemoryLocker lock(this);
|
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;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -526,11 +462,14 @@ bool QSharedMemory::detach()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a pointer to the contents of the shared memory segment, if
|
Returns a pointer to the contents of the shared memory segment, if one is
|
||||||
one is attached. Otherwise it returns null. Remember to lock the
|
attached. Otherwise it returns null. The value returned by this function will
|
||||||
shared memory with lock() before reading from or writing to the
|
not change until a \l {detach()}{detach} happens, so it is safe to store this
|
||||||
shared memory, and remember to release the lock with unlock() after
|
pointer.
|
||||||
you are done.
|
|
||||||
|
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()
|
\sa attach()
|
||||||
*/
|
*/
|
||||||
@ -541,11 +480,14 @@ void *QSharedMemory::data()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a const pointer to the contents of the shared memory
|
Returns a const pointer to the contents of the shared memory segment, if one
|
||||||
segment, if one is attached. Otherwise it returns null. Remember to
|
is attached. Otherwise it returns null. The value returned by this function
|
||||||
lock the shared memory with lock() before reading from or writing to
|
will not change until a \l {detach()}{detach} happens, so it is safe to store
|
||||||
the shared memory, and remember to release the lock with unlock()
|
this pointer.
|
||||||
after you are done.
|
|
||||||
|
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()
|
\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)
|
#endif // QT_CONFIG(sharedmemory)
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#ifndef QSHAREDMEMORY_H
|
#ifndef QSHAREDMEMORY_H
|
||||||
#define QSHAREDMEMORY_H
|
#define QSHAREDMEMORY_H
|
||||||
|
|
||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qtipccommon.h>
|
||||||
#ifndef QT_NO_QOBJECT
|
#ifndef QT_NO_QOBJECT
|
||||||
# include <QtCore/qobject.h>
|
# include <QtCore/qobject.h>
|
||||||
#else
|
#else
|
||||||
@ -12,8 +12,8 @@
|
|||||||
# include <QtCore/qscopedpointer.h>
|
# include <QtCore/qscopedpointer.h>
|
||||||
# include <QtCore/qstring.h>
|
# include <QtCore/qstring.h>
|
||||||
#endif
|
#endif
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
#if QT_CONFIG(sharedmemory)
|
#if QT_CONFIG(sharedmemory)
|
||||||
|
|
||||||
@ -45,13 +45,26 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
QSharedMemory(QObject *parent = nullptr);
|
QSharedMemory(QObject *parent = nullptr);
|
||||||
QSharedMemory(const QString &key, QObject *parent = nullptr);
|
QSharedMemory(const QNativeIpcKey &key, QObject *parent = nullptr);
|
||||||
~QSharedMemory();
|
~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);
|
void setKey(const QString &key);
|
||||||
|
QT_DEPRECATED_VERSION_X_6_9("Please refer to 'Native IPC Key' documentation")
|
||||||
QString key() const;
|
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;
|
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);
|
bool create(qsizetype size, AccessMode mode = ReadWrite);
|
||||||
qsizetype size() const;
|
qsizetype size() const;
|
||||||
@ -72,6 +85,12 @@ public:
|
|||||||
SharedMemoryError error() const;
|
SharedMemoryError error() const;
|
||||||
QString errorString() 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:
|
private:
|
||||||
Q_DISABLE_COPY(QSharedMemory)
|
Q_DISABLE_COPY(QSharedMemory)
|
||||||
};
|
};
|
||||||
|
@ -69,6 +69,9 @@ private:
|
|||||||
class QSharedMemoryPosix
|
class QSharedMemoryPosix
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static bool supports(QNativeIpcKey::Type type)
|
||||||
|
{ return type == QNativeIpcKey::Type::PosixRealtime; }
|
||||||
|
|
||||||
bool handle(QSharedMemoryPrivate *self);
|
bool handle(QSharedMemoryPrivate *self);
|
||||||
bool cleanHandle(QSharedMemoryPrivate *self);
|
bool cleanHandle(QSharedMemoryPrivate *self);
|
||||||
bool create(QSharedMemoryPrivate *self, qsizetype size);
|
bool create(QSharedMemoryPrivate *self, qsizetype size);
|
||||||
@ -81,6 +84,9 @@ public:
|
|||||||
class QSharedMemorySystemV
|
class QSharedMemorySystemV
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static bool supports(QNativeIpcKey::Type type)
|
||||||
|
{ return quint16(type) <= 0xff; }
|
||||||
|
|
||||||
#if QT_CONFIG(sysv_sem)
|
#if QT_CONFIG(sysv_sem)
|
||||||
key_t handle(QSharedMemoryPrivate *self);
|
key_t handle(QSharedMemoryPrivate *self);
|
||||||
bool cleanHandle(QSharedMemoryPrivate *self);
|
bool cleanHandle(QSharedMemoryPrivate *self);
|
||||||
@ -88,6 +94,10 @@ public:
|
|||||||
bool attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode);
|
bool attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode);
|
||||||
bool detach(QSharedMemoryPrivate *self);
|
bool detach(QSharedMemoryPrivate *self);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateNativeKeyFile(const QNativeIpcKey &nativeKey);
|
||||||
|
|
||||||
|
QByteArray nativeKeyFile;
|
||||||
key_t unix_key = 0;
|
key_t unix_key = 0;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@ -95,6 +105,9 @@ public:
|
|||||||
class QSharedMemoryWin32
|
class QSharedMemoryWin32
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static bool supports(QNativeIpcKey::Type type)
|
||||||
|
{ return type == QNativeIpcKey::Type::Windows; }
|
||||||
|
|
||||||
Qt::HANDLE handle(QSharedMemoryPrivate *self);
|
Qt::HANDLE handle(QSharedMemoryPrivate *self);
|
||||||
bool cleanHandle(QSharedMemoryPrivate *self);
|
bool cleanHandle(QSharedMemoryPrivate *self);
|
||||||
bool create(QSharedMemoryPrivate *self, qsizetype size);
|
bool create(QSharedMemoryPrivate *self, qsizetype size);
|
||||||
@ -111,8 +124,7 @@ class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate
|
|||||||
public:
|
public:
|
||||||
void *memory = nullptr;
|
void *memory = nullptr;
|
||||||
qsizetype size = 0;
|
qsizetype size = 0;
|
||||||
QString key;
|
QNativeIpcKey nativeKey;
|
||||||
QString nativeKey;
|
|
||||||
QString errorString;
|
QString errorString;
|
||||||
#if QT_CONFIG(systemsemaphore)
|
#if QT_CONFIG(systemsemaphore)
|
||||||
QSystemSemaphore systemSemaphore{ QNativeIpcKey() };
|
QSystemSemaphore systemSemaphore{ QNativeIpcKey() };
|
||||||
@ -168,6 +180,8 @@ public:
|
|||||||
}
|
}
|
||||||
QNativeIpcKey semaphoreNativeKey() const;
|
QNativeIpcKey semaphoreNativeKey() const;
|
||||||
#endif // QT_CONFIG(systemsemaphore)
|
#endif // QT_CONFIG(systemsemaphore)
|
||||||
|
|
||||||
|
QString legacyKey; // deprecated
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -27,8 +27,7 @@ using namespace QtIpcCommon;
|
|||||||
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
|
||||||
const QString safeKey = legacyPlatformSafeKey(self->key, IpcType::SharedMemory);
|
if (self->nativeKey.isEmpty()) {
|
||||||
if (safeKey.isEmpty()) {
|
|
||||||
self->setError(QSharedMemory::KeyError,
|
self->setError(QSharedMemory::KeyError,
|
||||||
QSharedMemory::tr("%1: key is empty").arg("QSharedMemory::handle"_L1));
|
QSharedMemory::tr("%1: key is empty").arg("QSharedMemory::handle"_L1));
|
||||||
return false;
|
return false;
|
||||||
@ -50,7 +49,7 @@ bool QSharedMemoryPosix::create(QSharedMemoryPrivate *self, qsizetype size)
|
|||||||
if (!handle(self))
|
if (!handle(self))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const QByteArray shmName = QFile::encodeName(legacyPlatformSafeKey(self->key, IpcType::SharedMemory));
|
const QByteArray shmName = QFile::encodeName(self->nativeKey.nativeKey());
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
#ifdef O_CLOEXEC
|
#ifdef O_CLOEXEC
|
||||||
@ -91,7 +90,7 @@ bool QSharedMemoryPosix::create(QSharedMemoryPrivate *self, qsizetype size)
|
|||||||
|
|
||||||
bool QSharedMemoryPosix::attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode)
|
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 int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR);
|
||||||
const mode_t omode = (mode == QSharedMemory::ReadOnly ? 0400 : 0600);
|
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 there are no attachments then unlink the shared memory
|
||||||
if (shm_nattch == 0) {
|
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)
|
if (::shm_unlink(shmName.constData()) == -1 && errno != ENOENT)
|
||||||
self->setUnixErrorString("QSharedMemory::detach (shm_unlink)"_L1);
|
self->setUnixErrorString("QSharedMemory::detach (shm_unlink)"_L1);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,13 @@ QT_BEGIN_NAMESPACE
|
|||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
using namespace QtIpcCommon;
|
using namespace QtIpcCommon;
|
||||||
|
|
||||||
|
inline void QSharedMemorySystemV::updateNativeKeyFile(const QNativeIpcKey &nativeKey)
|
||||||
|
{
|
||||||
|
Q_ASSERT(nativeKeyFile.isEmpty() );
|
||||||
|
if (!nativeKey.nativeKey().isEmpty())
|
||||||
|
nativeKeyFile = QFile::encodeName(nativeKey.nativeKey());
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
|
|
||||||
@ -38,7 +45,9 @@ key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self)
|
|||||||
return unix_key;
|
return unix_key;
|
||||||
|
|
||||||
// don't allow making handles on empty keys
|
// 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,
|
self->setError(QSharedMemory::KeyError,
|
||||||
QSharedMemory::tr("%1: key is empty")
|
QSharedMemory::tr("%1: key is empty")
|
||||||
.arg("QSharedMemory::handle:"_L1));
|
.arg("QSharedMemory::handle:"_L1));
|
||||||
@ -46,14 +55,14 @@ key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ftok requires that an actual file exists somewhere
|
// ftok requires that an actual file exists somewhere
|
||||||
if (!QFile::exists(self->nativeKey)) {
|
if (!QFile::exists(nativeKeyFile)) {
|
||||||
self->setError(QSharedMemory::NotFound,
|
self->setError(QSharedMemory::NotFound,
|
||||||
QSharedMemory::tr("%1: UNIX key file doesn't exist")
|
QSharedMemory::tr("%1: UNIX key file doesn't exist")
|
||||||
.arg("QSharedMemory::handle:"_L1));
|
.arg("QSharedMemory::handle:"_L1));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unix_key = ftok(QFile::encodeName(self->nativeKey).constData(), 'Q');
|
unix_key = ftok(nativeKeyFile, int(self->nativeKey.type()));
|
||||||
if (-1 == unix_key) {
|
if (-1 == unix_key) {
|
||||||
self->setError(QSharedMemory::KeyError,
|
self->setError(QSharedMemory::KeyError,
|
||||||
QSharedMemory::tr("%1: ftok failed")
|
QSharedMemory::tr("%1: ftok failed")
|
||||||
@ -66,6 +75,7 @@ key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self)
|
|||||||
bool QSharedMemorySystemV::cleanHandle(QSharedMemoryPrivate *)
|
bool QSharedMemorySystemV::cleanHandle(QSharedMemoryPrivate *)
|
||||||
{
|
{
|
||||||
unix_key = 0;
|
unix_key = 0;
|
||||||
|
nativeKeyFile.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +83,7 @@ bool QSharedMemorySystemV::create(QSharedMemoryPrivate *self, qsizetype size)
|
|||||||
{
|
{
|
||||||
// build file if needed
|
// build file if needed
|
||||||
bool createdFile = false;
|
bool createdFile = false;
|
||||||
QByteArray nativeKeyFile = QFile::encodeName(self->nativeKey);
|
updateNativeKeyFile(self->nativeKey);
|
||||||
int built = createUnixKeyFile(nativeKeyFile);
|
int built = createUnixKeyFile(nativeKeyFile);
|
||||||
if (built == -1) {
|
if (built == -1) {
|
||||||
self->setError(QSharedMemory::KeyError,
|
self->setError(QSharedMemory::KeyError,
|
||||||
@ -161,6 +171,7 @@ bool QSharedMemorySystemV::detach(QSharedMemoryPrivate *self)
|
|||||||
|
|
||||||
// Get the number of current attachments
|
// Get the number of current attachments
|
||||||
int id = shmget(unix_key, 0, 0400);
|
int id = shmget(unix_key, 0, 0400);
|
||||||
|
QByteArray oldNativeKeyFile = nativeKeyFile;
|
||||||
cleanHandle(self);
|
cleanHandle(self);
|
||||||
|
|
||||||
struct shmid_ds shmid_ds;
|
struct shmid_ds shmid_ds;
|
||||||
@ -186,7 +197,7 @@ bool QSharedMemorySystemV::detach(QSharedMemoryPrivate *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove file
|
// remove file
|
||||||
if (!unlink(QFile::encodeName(self->nativeKey)))
|
if (unlink(oldNativeKeyFile) < 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -60,7 +60,7 @@ HANDLE QSharedMemoryWin32::handle(QSharedMemoryPrivate *self)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false,
|
hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false,
|
||||||
reinterpret_cast<const wchar_t *>(self->nativeKey.utf16()));
|
reinterpret_cast<const wchar_t *>(self->nativeKey.nativeKey().utf16()));
|
||||||
if (!hand) {
|
if (!hand) {
|
||||||
self->setWindowsErrorString(function);
|
self->setWindowsErrorString(function);
|
||||||
return 0;
|
return 0;
|
||||||
@ -96,7 +96,7 @@ bool QSharedMemoryWin32::create(QSharedMemoryPrivate *self, qsizetype size)
|
|||||||
high = 0;
|
high = 0;
|
||||||
low = DWORD(size_t(size) & 0xffffffff);
|
low = DWORD(size_t(size) & 0xffffffff);
|
||||||
hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, high, low,
|
hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, high, low,
|
||||||
reinterpret_cast<const wchar_t *>(self->nativeKey.utf16()));
|
reinterpret_cast<const wchar_t *>(self->nativeKey.nativeKey().utf16()));
|
||||||
self->setWindowsErrorString(function);
|
self->setWindowsErrorString(function);
|
||||||
|
|
||||||
// hand is valid when it already exists unlike unix so explicitly check
|
// hand is valid when it already exists unlike unix so explicitly check
|
||||||
|
@ -5,7 +5,7 @@ if(NOT ANDROID AND NOT UIKIT)
|
|||||||
if(QT_FEATURE_sharedmemory OR QT_FEATURE_systemsemaphore)
|
if(QT_FEATURE_sharedmemory OR QT_FEATURE_systemsemaphore)
|
||||||
add_subdirectory(qnativeipckey)
|
add_subdirectory(qnativeipckey)
|
||||||
endif()
|
endif()
|
||||||
if(QT_FEATURE_sharedmemory AND QT_FEATURE_private_tests)
|
if(QT_FEATURE_sharedmemory)
|
||||||
add_subdirectory(qsharedmemory)
|
add_subdirectory(qsharedmemory)
|
||||||
endif()
|
endif()
|
||||||
if(QT_FEATURE_systemsemaphore)
|
if(QT_FEATURE_systemsemaphore)
|
||||||
|
@ -17,10 +17,9 @@ QChar get(QSharedMemory &sm, int i)
|
|||||||
return QChar::fromLatin1(((char*)sm.data())[i]);
|
return QChar::fromLatin1(((char*)sm.data())[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int readonly_segfault()
|
int readonly_segfault(const QNativeIpcKey &key)
|
||||||
{
|
{
|
||||||
QSharedMemory sharedMemory;
|
QSharedMemory sharedMemory(key);
|
||||||
sharedMemory.setKey("readonly_segfault");
|
|
||||||
sharedMemory.create(1024, QSharedMemory::ReadOnly);
|
sharedMemory.create(1024, QSharedMemory::ReadOnly);
|
||||||
sharedMemory.lock();
|
sharedMemory.lock();
|
||||||
set(sharedMemory, 0, 'a');
|
set(sharedMemory, 0, 'a');
|
||||||
@ -28,10 +27,9 @@ int readonly_segfault()
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int producer()
|
int producer(const QNativeIpcKey &key)
|
||||||
{
|
{
|
||||||
QSharedMemory producer;
|
QSharedMemory producer(key);
|
||||||
producer.setKey("market");
|
|
||||||
|
|
||||||
int size = 1024;
|
int size = 1024;
|
||||||
if (!producer.create(size)) {
|
if (!producer.create(size)) {
|
||||||
@ -100,10 +98,9 @@ int producer()
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int consumer()
|
int consumer(const QNativeIpcKey &key)
|
||||||
{
|
{
|
||||||
QSharedMemory consumer;
|
QSharedMemory consumer(key);
|
||||||
consumer.setKey("market");
|
|
||||||
|
|
||||||
//qDebug("consumer starting");
|
//qDebug("consumer starting");
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
@ -155,17 +152,21 @@ int main(int argc, char *argv[])
|
|||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
QStringList arguments = app.arguments();
|
QStringList arguments = app.arguments();
|
||||||
if (app.arguments().size() != 2) {
|
if (app.arguments().size() != 3) {
|
||||||
qWarning("Please call the helper with the function to call as argument");
|
fprintf(stderr, "Usage: %s <mode> <key>\n"
|
||||||
|
"<mode> is one of: readonly_segfault, producer, consumer\n",
|
||||||
|
argv[0]);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
QString function = arguments.at(1);
|
QString function = arguments.at(1);
|
||||||
|
QNativeIpcKey key = QNativeIpcKey::fromString(arguments.at(2));
|
||||||
|
|
||||||
if (function == QLatin1String("readonly_segfault"))
|
if (function == QLatin1String("readonly_segfault"))
|
||||||
return readonly_segfault();
|
return readonly_segfault(key);
|
||||||
else if (function == QLatin1String("producer"))
|
else if (function == QLatin1String("producer"))
|
||||||
return producer();
|
return producer(key);
|
||||||
else if (function == QLatin1String("consumer"))
|
else if (function == QLatin1String("consumer"))
|
||||||
return consumer();
|
return consumer(key);
|
||||||
else
|
else
|
||||||
qWarning() << "Unknown function" << arguments.at(1);
|
qWarning() << "Unknown function" << arguments.at(1);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
// 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
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@ -11,6 +12,14 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "private/qtcore-config_p.h"
|
||||||
|
|
||||||
#define EXISTING_SHARE "existing"
|
#define EXISTING_SHARE "existing"
|
||||||
#define EXISTING_SIZE 1024
|
#define EXISTING_SIZE 1024
|
||||||
|
|
||||||
@ -33,8 +42,10 @@ public Q_SLOTS:
|
|||||||
private slots:
|
private slots:
|
||||||
// basics
|
// basics
|
||||||
void constructor();
|
void constructor();
|
||||||
void key_data();
|
void nativeKey_data();
|
||||||
void key();
|
void nativeKey();
|
||||||
|
void legacyKey_data() { nativeKey_data(); }
|
||||||
|
void legacyKey();
|
||||||
void create_data();
|
void create_data();
|
||||||
void create();
|
void create();
|
||||||
void attach_data();
|
void attach_data();
|
||||||
@ -76,20 +87,25 @@ private slots:
|
|||||||
void uniqueKey();
|
void uniqueKey();
|
||||||
|
|
||||||
protected:
|
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)
|
QNativeIpcKey::Type keyType = QNativeIpcKey::DefaultTypeForOs;
|
||||||
return key;
|
return QSharedMemory::platformSafeKey(key, keyType);
|
||||||
if (!keys.contains(key)) {
|
|
||||||
keys.append(key);
|
|
||||||
remove(key);
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList keys;
|
QNativeIpcKey rememberKey(const QString &key)
|
||||||
|
{
|
||||||
|
QNativeIpcKey ipcKey = platformSafeKey(key);
|
||||||
|
if (!keys.contains(ipcKey)) {
|
||||||
|
keys.append(ipcKey);
|
||||||
|
remove(ipcKey);
|
||||||
|
}
|
||||||
|
return ipcKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QNativeIpcKey> keys;
|
||||||
QList<QSharedMemory*> jail;
|
QList<QSharedMemory*> jail;
|
||||||
QSharedMemory *existingSharedMemory;
|
QSharedMemory *existingSharedMemory;
|
||||||
|
|
||||||
@ -109,10 +125,12 @@ tst_QSharedMemory::~tst_QSharedMemory()
|
|||||||
|
|
||||||
void tst_QSharedMemory::init()
|
void tst_QSharedMemory::init()
|
||||||
{
|
{
|
||||||
existingSharedMemory = new QSharedMemory(EXISTING_SHARE);
|
QNativeIpcKey key = platformSafeKey(EXISTING_SHARE);
|
||||||
|
existingSharedMemory = new QSharedMemory(key);
|
||||||
if (!existingSharedMemory->create(EXISTING_SIZE)) {
|
if (!existingSharedMemory->create(EXISTING_SIZE)) {
|
||||||
QCOMPARE(existingSharedMemory->error(), QSharedMemory::AlreadyExists);
|
QCOMPARE(existingSharedMemory->error(), QSharedMemory::AlreadyExists);
|
||||||
}
|
}
|
||||||
|
keys.append(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QSharedMemory::cleanup()
|
void tst_QSharedMemory::cleanup()
|
||||||
@ -121,7 +139,6 @@ void tst_QSharedMemory::cleanup()
|
|||||||
qDeleteAll(jail.begin(), jail.end());
|
qDeleteAll(jail.begin(), jail.end());
|
||||||
jail.clear();
|
jail.clear();
|
||||||
|
|
||||||
keys.append(EXISTING_SHARE);
|
|
||||||
for (int i = 0; i < keys.size(); ++i) {
|
for (int i = 0; i < keys.size(); ++i) {
|
||||||
QSharedMemory sm(keys.at(i));
|
QSharedMemory sm(keys.at(i));
|
||||||
if (!sm.create(1024)) {
|
if (!sm.create(1024)) {
|
||||||
@ -134,65 +151,70 @@ void tst_QSharedMemory::cleanup()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#if QT_CONFIG(posix_shm)
|
||||||
#include <private/qtipccommon_p.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
#if QT_CONFIG(sysv_shm)
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#ifndef QT_POSIX_IPC
|
|
||||||
#include <sys/ipc.h>
|
#include <sys/ipc.h>
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
#else
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#endif // QT_POSIX_IPC
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int tst_QSharedMemory::remove(const QString &key)
|
void tst_QSharedMemory::remove(const QNativeIpcKey &key)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
// On Unix, the shared memory might exist from a previously failed test
|
||||||
Q_UNUSED(key);
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
// On unix the shared memory might exists from a previously failed test
|
|
||||||
// or segfault, remove it it does
|
// or segfault, remove it it does
|
||||||
if (key.isEmpty())
|
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
|
case QNativeIpcKey::Type::PosixRealtime:
|
||||||
// ftok requires that an actual file exists somewhere
|
#if QT_CONFIG(posix_shm)
|
||||||
if (!QFile::exists(fileName)) {
|
if (shm_unlink(QFile::encodeName(key.nativeKey()).constData()) == -1) {
|
||||||
//qDebug() << "exits failed";
|
if (errno != ENOENT) {
|
||||||
return -2;
|
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) {
|
if (-1 == unix_key) {
|
||||||
qDebug() << "ftok failed";
|
perror("ftok");
|
||||||
return -3;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = shmget(unix_key, 0, 0600);
|
int id = shmget(unix_key, 0, 0600);
|
||||||
if (-1 == id) {
|
if (-1 == id) {
|
||||||
qDebug() << "shmget failed" << strerror(errno);
|
if (errno != ENOENT)
|
||||||
return -4;
|
perror("shmget");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct shmid_ds shmid_ds;
|
struct shmid_ds shmid_ds;
|
||||||
if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
|
if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
|
||||||
qDebug() << "shmctl failed";
|
perror("shmctl");
|
||||||
return -5;
|
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
|
#endif // Q_OS_WIN
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,19 +224,25 @@ int tst_QSharedMemory::remove(const QString &key)
|
|||||||
void tst_QSharedMemory::constructor()
|
void tst_QSharedMemory::constructor()
|
||||||
{
|
{
|
||||||
QSharedMemory sm;
|
QSharedMemory sm;
|
||||||
QCOMPARE(sm.key(), QString());
|
|
||||||
QVERIFY(!sm.isAttached());
|
QVERIFY(!sm.isAttached());
|
||||||
QVERIFY(!sm.data());
|
QVERIFY(!sm.data());
|
||||||
|
QCOMPARE(sm.nativeKey(), QString());
|
||||||
|
QCOMPARE(sm.nativeIpcKey(), QNativeIpcKey());
|
||||||
QCOMPARE(sm.size(), 0);
|
QCOMPARE(sm.size(), 0);
|
||||||
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
||||||
QCOMPARE(sm.errorString(), QString());
|
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<QString>("constructorKey");
|
QTest::addColumn<QString>("constructorKey");
|
||||||
QTest::addColumn<QString>("setKey");
|
QTest::addColumn<QString>("setKey");
|
||||||
QTest::addColumn<QString>("setNativeKey");
|
QTest::addColumn<QString>("setNativeKey"); // only used in the legacyKey test
|
||||||
|
|
||||||
QTest::newRow("null, null, null") << QString() << QString() << QString();
|
QTest::newRow("null, null, null") << QString() << QString() << QString();
|
||||||
QTest::newRow("one, null, null") << QString("one") << QString() << QString();
|
QTest::newRow("one, null, null") << QString("one") << QString() << QString();
|
||||||
@ -230,12 +258,43 @@ void tst_QSharedMemory::key_data()
|
|||||||
/*!
|
/*!
|
||||||
Basic key testing
|
Basic key testing
|
||||||
*/
|
*/
|
||||||
void tst_QSharedMemory::key()
|
void tst_QSharedMemory::nativeKey()
|
||||||
{
|
{
|
||||||
QFETCH(QString, constructorKey);
|
QFETCH(QString, constructorKey);
|
||||||
QFETCH(QString, setKey);
|
QFETCH(QString, setKey);
|
||||||
QFETCH(QString, setNativeKey);
|
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);
|
QSharedMemory sm(constructorKey);
|
||||||
QCOMPARE(sm.key(), constructorKey);
|
QCOMPARE(sm.key(), constructorKey);
|
||||||
QCOMPARE(sm.nativeKey().isEmpty(), constructorKey.isEmpty());
|
QCOMPARE(sm.nativeKey().isEmpty(), constructorKey.isEmpty());
|
||||||
@ -254,6 +313,7 @@ void tst_QSharedMemory::key()
|
|||||||
|
|
||||||
QCOMPARE(sm.detach(), false);
|
QCOMPARE(sm.detach(), false);
|
||||||
}
|
}
|
||||||
|
QT_WARNING_POP
|
||||||
|
|
||||||
void tst_QSharedMemory::create_data()
|
void tst_QSharedMemory::create_data()
|
||||||
{
|
{
|
||||||
@ -282,11 +342,12 @@ void tst_QSharedMemory::create()
|
|||||||
QFETCH(bool, canCreate);
|
QFETCH(bool, canCreate);
|
||||||
QFETCH(QSharedMemory::SharedMemoryError, error);
|
QFETCH(QSharedMemory::SharedMemoryError, error);
|
||||||
|
|
||||||
QSharedMemory sm(rememberKey(key));
|
QNativeIpcKey nativeKey = rememberKey(key);
|
||||||
|
QSharedMemory sm(nativeKey);
|
||||||
QCOMPARE(sm.create(size), canCreate);
|
QCOMPARE(sm.create(size), canCreate);
|
||||||
if (sm.error() != error)
|
if (sm.error() != error)
|
||||||
qDebug() << sm.errorString();
|
qDebug() << sm.errorString();
|
||||||
QCOMPARE(sm.key(), key);
|
QCOMPARE(sm.nativeIpcKey(), nativeKey);
|
||||||
if (canCreate) {
|
if (canCreate) {
|
||||||
QCOMPARE(sm.errorString(), QString());
|
QCOMPARE(sm.errorString(), QString());
|
||||||
QVERIFY(sm.data() != 0);
|
QVERIFY(sm.data() != 0);
|
||||||
@ -303,13 +364,10 @@ void tst_QSharedMemory::attach_data()
|
|||||||
QTest::addColumn<bool>("exists");
|
QTest::addColumn<bool>("exists");
|
||||||
QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
|
QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
|
||||||
|
|
||||||
QTest::newRow("null key") << QString() << false << QSharedMemory::KeyError;
|
QTest::newRow("null") << QString() << false << QSharedMemory::KeyError;
|
||||||
QTest::newRow("doesn't exists") << QString("doesntexists") << false << QSharedMemory::NotFound;
|
QTest::newRow("doesntexists") << QString("doesntexist") << false << QSharedMemory::NotFound;
|
||||||
|
|
||||||
// HPUX doesn't allow for multiple attaches per process.
|
QTest::newRow(EXISTING_SHARE) << QString(EXISTING_SHARE) << true << QSharedMemory::NoError;
|
||||||
#ifndef Q_OS_HPUX
|
|
||||||
QTest::newRow("already exists") << QString(EXISTING_SHARE) << true << QSharedMemory::NoError;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -321,11 +379,12 @@ void tst_QSharedMemory::attach()
|
|||||||
QFETCH(bool, exists);
|
QFETCH(bool, exists);
|
||||||
QFETCH(QSharedMemory::SharedMemoryError, error);
|
QFETCH(QSharedMemory::SharedMemoryError, error);
|
||||||
|
|
||||||
QSharedMemory sm(key);
|
QNativeIpcKey nativeKey = platformSafeKey(key);
|
||||||
|
QSharedMemory sm(nativeKey);
|
||||||
QCOMPARE(sm.attach(), exists);
|
QCOMPARE(sm.attach(), exists);
|
||||||
QCOMPARE(sm.isAttached(), exists);
|
QCOMPARE(sm.isAttached(), exists);
|
||||||
QCOMPARE(sm.error(), error);
|
QCOMPARE(sm.error(), error);
|
||||||
QCOMPARE(sm.key(), key);
|
QCOMPARE(sm.nativeIpcKey(), nativeKey);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
QVERIFY(sm.data() != 0);
|
QVERIFY(sm.data() != 0);
|
||||||
QVERIFY(sm.size() != 0);
|
QVERIFY(sm.size() != 0);
|
||||||
@ -352,7 +411,7 @@ void tst_QSharedMemory::lock()
|
|||||||
QVERIFY(!shm.lock());
|
QVERIFY(!shm.lock());
|
||||||
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
||||||
|
|
||||||
shm.setKey(rememberKey(QLatin1String("qsharedmemory")));
|
shm.setNativeKey(rememberKey(QLatin1String("qsharedmemory")));
|
||||||
|
|
||||||
QVERIFY(!shm.lock());
|
QVERIFY(!shm.lock());
|
||||||
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
||||||
@ -376,12 +435,13 @@ void tst_QSharedMemory::removeWhileAttached()
|
|||||||
rememberKey("one");
|
rememberKey("one");
|
||||||
|
|
||||||
// attach 1
|
// attach 1
|
||||||
QSharedMemory *smOne = new QSharedMemory(QLatin1String("one"));
|
QNativeIpcKey keyOne = platformSafeKey("one");
|
||||||
|
QSharedMemory *smOne = new QSharedMemory(keyOne);
|
||||||
QVERIFY(smOne->create(1024));
|
QVERIFY(smOne->create(1024));
|
||||||
QVERIFY(smOne->isAttached());
|
QVERIFY(smOne->isAttached());
|
||||||
|
|
||||||
// attach 2
|
// attach 2
|
||||||
QSharedMemory *smTwo = new QSharedMemory(QLatin1String("one"));
|
QSharedMemory *smTwo = new QSharedMemory(platformSafeKey("one"));
|
||||||
QVERIFY(smTwo->attach());
|
QVERIFY(smTwo->attach());
|
||||||
QVERIFY(smTwo->isAttached());
|
QVERIFY(smTwo->isAttached());
|
||||||
|
|
||||||
@ -389,13 +449,13 @@ void tst_QSharedMemory::removeWhileAttached()
|
|||||||
delete smOne;
|
delete smOne;
|
||||||
delete smTwo;
|
delete smTwo;
|
||||||
|
|
||||||
#ifdef QT_POSIX_IPC
|
if (keyOne.type() == QNativeIpcKey::Type::PosixRealtime) {
|
||||||
// POSIX IPC doesn't guarantee that the shared memory is removed
|
// POSIX IPC doesn't guarantee that the shared memory is removed
|
||||||
remove("one");
|
remove(keyOne);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
// three shouldn't be able to attach
|
// three shouldn't be able to attach
|
||||||
QSharedMemory smThree(QLatin1String("one"));
|
QSharedMemory smThree(keyOne);
|
||||||
QVERIFY(!smThree.attach());
|
QVERIFY(!smThree.attach());
|
||||||
QCOMPARE(smThree.error(), QSharedMemory::NotFound);
|
QCOMPARE(smThree.error(), QSharedMemory::NotFound);
|
||||||
}
|
}
|
||||||
@ -430,11 +490,12 @@ void tst_QSharedMemory::readOnly()
|
|||||||
#elif defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
|
#elif defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
|
||||||
QSKIP("ASan prevents the crash this test is looking for.", SkipAll);
|
QSKIP("ASan prevents the crash this test is looking for.", SkipAll);
|
||||||
#else
|
#else
|
||||||
rememberKey("readonly_segfault");
|
QNativeIpcKey key = rememberKey("readonly_segfault");
|
||||||
|
|
||||||
// ### on windows disable the popup somehow
|
// ### on windows disable the popup somehow
|
||||||
QProcess p;
|
QProcess p;
|
||||||
p.start(m_helperBinary, QStringList("readonly_segfault"));
|
|
||||||
p.setProcessChannelMode(QProcess::ForwardedChannels);
|
p.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
|
p.start(m_helperBinary, { "readonly_segfault", key.toString() });
|
||||||
p.waitForFinished();
|
p.waitForFinished();
|
||||||
QCOMPARE(p.error(), QProcess::Crashed);
|
QCOMPARE(p.error(), QProcess::Crashed);
|
||||||
#endif
|
#endif
|
||||||
@ -451,7 +512,8 @@ void tst_QSharedMemory::useTooMuchMemory()
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
while (success) {
|
while (success) {
|
||||||
QString key = QLatin1String("maxmemorytest_") + QString::number(count++);
|
QString key = QLatin1String("maxmemorytest_") + QString::number(count++);
|
||||||
QSharedMemory *sm = new QSharedMemory(rememberKey(key));
|
QNativeIpcKey nativeKey = rememberKey(key);
|
||||||
|
QSharedMemory *sm = new QSharedMemory(nativeKey);
|
||||||
QVERIFY(sm);
|
QVERIFY(sm);
|
||||||
jail.append(sm);
|
jail.append(sm);
|
||||||
int size = 32768 * 1024;
|
int size = 32768 * 1024;
|
||||||
@ -465,7 +527,7 @@ void tst_QSharedMemory::useTooMuchMemory()
|
|||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
QVERIFY(!sm->isAttached());
|
QVERIFY(!sm->isAttached());
|
||||||
QCOMPARE(sm->key(), key);
|
QCOMPARE(sm->nativeIpcKey(), nativeKey);
|
||||||
QCOMPARE(sm->size(), 0);
|
QCOMPARE(sm->size(), 0);
|
||||||
QVERIFY(!sm->data());
|
QVERIFY(!sm->data());
|
||||||
if (sm->error() != QSharedMemory::OutOfResources)
|
if (sm->error() != QSharedMemory::OutOfResources)
|
||||||
@ -496,12 +558,12 @@ void tst_QSharedMemory::attachTooMuch()
|
|||||||
QSharedMemory government(rememberKey("government"));
|
QSharedMemory government(rememberKey("government"));
|
||||||
QVERIFY(government.create(1024));
|
QVERIFY(government.create(1024));
|
||||||
while (true) {
|
while (true) {
|
||||||
QSharedMemory *war = new QSharedMemory(government.key());
|
QSharedMemory *war = new QSharedMemory(government.nativeIpcKey());
|
||||||
QVERIFY(war);
|
QVERIFY(war);
|
||||||
jail.append(war);
|
jail.append(war);
|
||||||
if (!war->attach()) {
|
if (!war->attach()) {
|
||||||
QVERIFY(!war->isAttached());
|
QVERIFY(!war->isAttached());
|
||||||
QCOMPARE(war->key(), government.key());
|
QCOMPARE(war->nativeIpcKey(), government.nativeIpcKey());
|
||||||
QCOMPARE(war->size(), 0);
|
QCOMPARE(war->size(), 0);
|
||||||
QVERIFY(!war->data());
|
QVERIFY(!war->data());
|
||||||
QCOMPARE(war->error(), QSharedMemory::OutOfResources);
|
QCOMPARE(war->error(), QSharedMemory::OutOfResources);
|
||||||
@ -537,8 +599,8 @@ void tst_QSharedMemory::simpleProducerConsumer()
|
|||||||
QFETCH(QSharedMemory::AccessMode, mode);
|
QFETCH(QSharedMemory::AccessMode, mode);
|
||||||
|
|
||||||
rememberKey(QLatin1String("market"));
|
rememberKey(QLatin1String("market"));
|
||||||
QSharedMemory producer(QLatin1String("market"));
|
QSharedMemory producer(platformSafeKey("market"));
|
||||||
QSharedMemory consumer(QLatin1String("market"));
|
QSharedMemory consumer(platformSafeKey("market"));
|
||||||
int size = 512;
|
int size = 512;
|
||||||
QVERIFY(producer.create(size));
|
QVERIFY(producer.create(size));
|
||||||
QVERIFY(consumer.attach(mode));
|
QVERIFY(consumer.attach(mode));
|
||||||
@ -560,19 +622,21 @@ void tst_QSharedMemory::simpleProducerConsumer()
|
|||||||
#ifndef Q_OS_HPUX
|
#ifndef Q_OS_HPUX
|
||||||
void tst_QSharedMemory::simpleDoubleProducerConsumer()
|
void tst_QSharedMemory::simpleDoubleProducerConsumer()
|
||||||
{
|
{
|
||||||
rememberKey(QLatin1String("market"));
|
QNativeIpcKey nativeKey = rememberKey(QLatin1String("market"));
|
||||||
QSharedMemory producer(QLatin1String("market"));
|
QSharedMemory producer(nativeKey);
|
||||||
int size = 512;
|
int size = 512;
|
||||||
QVERIFY(producer.create(size));
|
QVERIFY(producer.create(size));
|
||||||
QVERIFY(producer.detach());
|
QVERIFY(producer.detach());
|
||||||
#ifdef QT_POSIX_IPC
|
|
||||||
// POSIX IPC doesn't guarantee that the shared memory is removed
|
if (nativeKey.type() == QNativeIpcKey::Type::PosixRealtime) {
|
||||||
remove("market");
|
// POSIX IPC doesn't guarantee that the shared memory is removed
|
||||||
#endif
|
remove(nativeKey);
|
||||||
|
}
|
||||||
|
|
||||||
QVERIFY(producer.create(size));
|
QVERIFY(producer.create(size));
|
||||||
|
|
||||||
{
|
{
|
||||||
QSharedMemory consumer(QLatin1String("market"));
|
QSharedMemory consumer(nativeKey);
|
||||||
QVERIFY(consumer.attach());
|
QVERIFY(consumer.attach());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -580,11 +644,13 @@ void tst_QSharedMemory::simpleDoubleProducerConsumer()
|
|||||||
|
|
||||||
class Consumer : public QThread
|
class Consumer : public QThread
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
QNativeIpcKey nativeKey;
|
||||||
|
Consumer(const QNativeIpcKey &nativeKey) : nativeKey(nativeKey) {}
|
||||||
|
|
||||||
void run() override
|
void run() override
|
||||||
{
|
{
|
||||||
QSharedMemory consumer(QLatin1String("market"));
|
QSharedMemory consumer(nativeKey);
|
||||||
while (!consumer.attach()) {
|
while (!consumer.attach()) {
|
||||||
if (consumer.error() != QSharedMemory::NotFound)
|
if (consumer.error() != QSharedMemory::NotFound)
|
||||||
qDebug() << "consumer: failed to connect" << consumer.error() << consumer.errorString();
|
qDebug() << "consumer: failed to connect" << consumer.error() << consumer.errorString();
|
||||||
@ -615,9 +681,8 @@ public:
|
|||||||
|
|
||||||
class Producer : public QThread
|
class Producer : public QThread
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Producer() : producer(QLatin1String("market"))
|
Producer(const QNativeIpcKey &nativeKey) : producer(nativeKey)
|
||||||
{
|
{
|
||||||
int size = 1024;
|
int size = 1024;
|
||||||
if (!producer.create(size)) {
|
if (!producer.create(size)) {
|
||||||
@ -682,16 +747,16 @@ void tst_QSharedMemory::simpleThreadedProducerConsumer()
|
|||||||
{
|
{
|
||||||
QFETCH(bool, producerIsThread);
|
QFETCH(bool, producerIsThread);
|
||||||
QFETCH(int, threads);
|
QFETCH(int, threads);
|
||||||
rememberKey(QLatin1String("market"));
|
QNativeIpcKey nativeKey = rememberKey(QLatin1String("market"));
|
||||||
|
|
||||||
Producer p;
|
Producer p(nativeKey);
|
||||||
QVERIFY(p.producer.isAttached());
|
QVERIFY(p.producer.isAttached());
|
||||||
if (producerIsThread)
|
if (producerIsThread)
|
||||||
p.start();
|
p.start();
|
||||||
|
|
||||||
QList<Consumer*> consumers;
|
QList<Consumer*> consumers;
|
||||||
for (int i = 0; i < threads; ++i) {
|
for (int i = 0; i < threads; ++i) {
|
||||||
consumers.append(new Consumer());
|
consumers.append(new Consumer(nativeKey));
|
||||||
consumers.last()->start();
|
consumers.last()->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -730,17 +795,17 @@ void tst_QSharedMemory::simpleProcessProducerConsumer()
|
|||||||
|
|
||||||
QSKIP("This test is unstable: QTBUG-25655");
|
QSKIP("This test is unstable: QTBUG-25655");
|
||||||
|
|
||||||
rememberKey("market");
|
QNativeIpcKey nativeKey = rememberKey("market");
|
||||||
|
|
||||||
QProcess producer;
|
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.waitForStarted(), "Could not start helper binary");
|
||||||
QVERIFY2(producer.waitForReadyRead(), "Helper process failed to create shared memory segment: " +
|
QVERIFY2(producer.waitForReadyRead(), "Helper process failed to create shared memory segment: " +
|
||||||
producer.readAllStandardError());
|
producer.readAllStandardError());
|
||||||
|
|
||||||
QList<QProcess*> consumers;
|
QList<QProcess*> consumers;
|
||||||
unsigned int failedProcesses = 0;
|
unsigned int failedProcesses = 0;
|
||||||
const QStringList consumerArguments = QStringList("consumer");
|
QStringList consumerArguments = { "consumer", nativeKey.toString() };
|
||||||
for (int i = 0; i < processes; ++i) {
|
for (int i = 0; i < processes; ++i) {
|
||||||
QProcess *p = new QProcess;
|
QProcess *p = new QProcess;
|
||||||
p->setProcessChannelMode(QProcess::ForwardedChannels);
|
p->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
@ -791,11 +856,11 @@ void tst_QSharedMemory::uniqueKey()
|
|||||||
QFETCH(QString, key1);
|
QFETCH(QString, key1);
|
||||||
QFETCH(QString, key2);
|
QFETCH(QString, key2);
|
||||||
|
|
||||||
QSharedMemory sm1(key1);
|
QSharedMemory sm1(platformSafeKey(key1));
|
||||||
QSharedMemory sm2(key2);
|
QSharedMemory sm2(platformSafeKey(key2));
|
||||||
|
|
||||||
bool setEqual = (key1 == key2);
|
bool setEqual = (key1 == key2);
|
||||||
bool keyEqual = (sm1.key() == sm2.key());
|
bool keyEqual = (sm1.nativeIpcKey() == sm2.nativeIpcKey());
|
||||||
bool nativeEqual = (sm1.nativeKey() == sm2.nativeKey());
|
bool nativeEqual = (sm1.nativeKey() == sm2.nativeKey());
|
||||||
|
|
||||||
QCOMPARE(keyEqual, setEqual);
|
QCOMPARE(keyEqual, setEqual);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user