IPC: Long live QNativeIpcKey key
Common to both QSharedMemory and QSystemSemaphore, this will hold the native key and will replace the concept of non-native key in those classes. Change-Id: Id8d5e3999fe94b03acc1fffd171c03197aea6016 Reviewed-by: Nicholas Bennett <nicholas.bennett@qt.io>
This commit is contained in:
parent
a467362aa8
commit
7a37083817
@ -110,7 +110,7 @@ qt_internal_add_module(Core
|
|||||||
global/qxptype_traits.h
|
global/qxptype_traits.h
|
||||||
ipc/qsharedmemory.cpp ipc/qsharedmemory.h ipc/qsharedmemory_p.h
|
ipc/qsharedmemory.cpp ipc/qsharedmemory.h ipc/qsharedmemory_p.h
|
||||||
ipc/qsystemsemaphore.cpp ipc/qsystemsemaphore.h ipc/qsystemsemaphore_p.h
|
ipc/qsystemsemaphore.cpp ipc/qsystemsemaphore.h ipc/qsystemsemaphore_p.h
|
||||||
ipc/qtipccommon.cpp ipc/qtipccommon_p.h
|
ipc/qtipccommon.cpp ipc/qtipccommon.h ipc/qtipccommon_p.h
|
||||||
io/qabstractfileengine.cpp io/qabstractfileengine_p.h
|
io/qabstractfileengine.cpp io/qabstractfileengine_p.h
|
||||||
io/qbuffer.cpp io/qbuffer.h
|
io/qbuffer.cpp io/qbuffer.h
|
||||||
io/qdataurl.cpp io/qdataurl_p.h
|
io/qdataurl.cpp io/qdataurl_p.h
|
||||||
|
488
src/corelib/doc/src/ipc.qdoc
Normal file
488
src/corelib/doc/src/ipc.qdoc
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
// Copyright (C) 2022 Intel Corporation.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\page ipc.html
|
||||||
|
\title Inter-Process Communication
|
||||||
|
\ingroup groups
|
||||||
|
\ingroup frameworks-technologies
|
||||||
|
\keyword ipc
|
||||||
|
|
||||||
|
\brief An overview of Qt's inter-process communication functionality
|
||||||
|
|
||||||
|
Qt supports many ways of communicating with other processes running in the
|
||||||
|
same system or in different systems. There are basically three types of
|
||||||
|
inter-process communication mechanisms:
|
||||||
|
|
||||||
|
\list 1
|
||||||
|
\li Synchronization primitives
|
||||||
|
\li Exchanging of arbitrary byte-level data
|
||||||
|
\li Passing structured messages
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
\section1 Synchronization primitives
|
||||||
|
|
||||||
|
Qt only provides one class for explicit inter-process synchronization:
|
||||||
|
\l{QSystemSemaphore}. A QSystemSemaphore is like a \l{QSemaphore} that is
|
||||||
|
accessible by multiple processes in the same system. It is globally
|
||||||
|
identified by a "key", which in Qt is represented by the \l{QNativeIpcKey}
|
||||||
|
class. Additionally, depending on the OS, Qt may support multiple different
|
||||||
|
backends for sharing memory; see the \l{Native IPC Keys} documentation for
|
||||||
|
more information and limitations.
|
||||||
|
|
||||||
|
It is possible to use regular thread-synchronization primitives such as
|
||||||
|
mutexes, wait conditions, and read-write locks, located in memory that is
|
||||||
|
shared between processes. Qt does not provide any class to support this,
|
||||||
|
but applications can use low-level operations on certain operating systems.
|
||||||
|
|
||||||
|
Other Qt classes may be used to provide higher-level locking, like
|
||||||
|
\l{QLockFile}, or by acquiring a unique, system-wide resource. Such
|
||||||
|
techniques include \l{QTcpServer}{TCP} or \l{QUdpSocket}{UDP} ports or
|
||||||
|
well-known names in \l{QDBusConnection::registerService}{D-Bus}.
|
||||||
|
|
||||||
|
\section1 Byte-level data sharing
|
||||||
|
|
||||||
|
Using byte-level data, applications can implement any communication
|
||||||
|
protocol they may choose. Sharing of byte data can be stream-oriented
|
||||||
|
(serialized) or can allow random access (a similar condition to
|
||||||
|
QFileDevice::isSequential()).
|
||||||
|
|
||||||
|
For serial communication, Qt provides a number of different classes and
|
||||||
|
even full modules:
|
||||||
|
\list
|
||||||
|
\li Pipes and FIFOs: \l QFile
|
||||||
|
\li Child processes: \l QProcess
|
||||||
|
\li Sockets: \l QTcpSocket, \l QUdpSocket (in \l{Qt Network})
|
||||||
|
\li HTTP(S): \l QNetworkAccessManager (in \l{Qt Network}) and
|
||||||
|
\l QHttpServer (in \l{Qt HTTP Server})
|
||||||
|
\li CoAP(S): \l QCoapClient (in \l{Qt CoAP})
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
For random-access data sharing within the same system, Qt provides
|
||||||
|
\l{QSharedMemory}. See the \l{Shared Memory} documentation for detailed
|
||||||
|
information.
|
||||||
|
|
||||||
|
\section1 Structured message passing
|
||||||
|
|
||||||
|
Qt also provides a number of techniques to exchange structured messages
|
||||||
|
with other processes. Applications can build on top of the byte-level
|
||||||
|
solutions above, such as by using \l QJsonDocument or \l QXmlStreamReader /
|
||||||
|
\l QXmlStreamWriter over HTTP to perform JSONRPC or XMLRPC, respectively,
|
||||||
|
or \l QCborValue with QtCoAP.
|
||||||
|
|
||||||
|
Dedicated Qt modules for structured messages and remote procedure-calling
|
||||||
|
include:
|
||||||
|
\list
|
||||||
|
\li \l{Qt D-Bus}
|
||||||
|
\li \l{Qt Remote Objects}
|
||||||
|
\li \l{Qt WebSockets}
|
||||||
|
\endlist
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\page shared-memory.html
|
||||||
|
\title Shared Memory
|
||||||
|
\keyword ipc
|
||||||
|
\keyword shared memory
|
||||||
|
|
||||||
|
\brief Overview of the techniques for sharing memory between processes
|
||||||
|
|
||||||
|
Qt provides two techniques to share memory with other processes in the same
|
||||||
|
system: \l{QSharedMemory} and memory-mapped files using \l{QFile}. Memory
|
||||||
|
that is shared with other processes is often referred to as a "segment",
|
||||||
|
and although it may have been implemented as specific segments on
|
||||||
|
processors with segmented memory models in the past, this is not the case
|
||||||
|
in any modern operating system. Shared memory segments are simply regions
|
||||||
|
of memory that the operating system will ensure are available to all
|
||||||
|
processes participating.
|
||||||
|
|
||||||
|
\note The address at which the segment is located in memory will almost
|
||||||
|
always be different for each process that is participating in the sharing.
|
||||||
|
Therefore, applications must take care to share only position-independent
|
||||||
|
data, such as primitive C++ types or arrays of such types.
|
||||||
|
|
||||||
|
\section1 Sharing memory using QSharedMemory
|
||||||
|
|
||||||
|
QSharedMemory provides a simple API to create a shared memory segment of a
|
||||||
|
given size or attach to one that was created by another process.
|
||||||
|
Additionally, it provides a pair of methods to \l{QSharedMemory::}{lock}
|
||||||
|
and \l{QSharedMemory::}{unlock} the whole segment, using an internal
|
||||||
|
\l{QSystemSemaphore}.
|
||||||
|
|
||||||
|
Shared memory segments and system semaphores are globally identified in the
|
||||||
|
system through a "key", which in Qt is represented by the \l{QNativeIpcKey}
|
||||||
|
class. Additionally, depending on the OS, Qt may support multiple different
|
||||||
|
backends for sharing memory; see the \l{Native IPC Keys} documentation for
|
||||||
|
more information and limitations.
|
||||||
|
|
||||||
|
QSharedMemory is designed to share memory only within the same privilege
|
||||||
|
level (that is, not with untrusted other processes, such as those started
|
||||||
|
by other users). For backends that support it, QSharedMemory will create
|
||||||
|
segments such that only processes with the same privilege level can attach.
|
||||||
|
|
||||||
|
\section1 Sharing memory via memory-mapped files
|
||||||
|
|
||||||
|
Most files can be mapped to memory using QFile::map() and, if the
|
||||||
|
\l{QFileDevice::MapPrivateOption}{MapPrivateOption} option is not specified,
|
||||||
|
any writes to the mapped segment will be observed by all other processes
|
||||||
|
that have mapped the same file. Exceptions to files that can be mapped to
|
||||||
|
memory include remote files found in network shares or those located in
|
||||||
|
certain filesystems. Even if the operating system does allow mapping remote
|
||||||
|
files to memory, I/O operations on the file will likely be cached and
|
||||||
|
delayed, thus making true memory sharing impossible.
|
||||||
|
|
||||||
|
This solution has the major advantages of being independent of any backend
|
||||||
|
API and of being simpler to interoperate with from non-Qt applications.
|
||||||
|
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. One way may be to use \l QLockFile. Another
|
||||||
|
and less costly solution is to \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, applications can set the "pshared" flag in the mutex attribute
|
||||||
|
passed to \c{pthread_mutex_create()} to indicate that the mutex resides in
|
||||||
|
a shared memory segment.
|
||||||
|
|
||||||
|
Be aware that the operating system will likely attempt to commit to
|
||||||
|
permanent storage any writes made to the shared memory. This may be desired
|
||||||
|
or it may be a performance penalty if the file itself was meant to be
|
||||||
|
temporary. In that case, 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.
|
||||||
|
|
||||||
|
It is possible to use file-backed shared memory to communicate with
|
||||||
|
untrusted processes, in which case the application should exercise great
|
||||||
|
care. The files may be truncated/shrunk and cause applications accessing
|
||||||
|
memory beyond the file's size to crash.
|
||||||
|
|
||||||
|
\section2 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 the very purpose of sharing
|
||||||
|
memory. Do note that it is world-readable and writable (like \c{/tmp} and
|
||||||
|
\c{/var/tmp}), so applications 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.
|
||||||
|
|
||||||
|
\section2 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.
|
||||||
|
|
||||||
|
\section2 Windows hints on memory-mapped files
|
||||||
|
|
||||||
|
On Windows, the application can request the operating system avoid saving
|
||||||
|
the file's contents on permanent storage. This request is performed by
|
||||||
|
passing the \c{FILE_ATTRIBUTE_TEMPORARY} flag in the
|
||||||
|
\c{dwFlagsAndAttributes} parameter to the \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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\page native-ipc-keys.html
|
||||||
|
\title Native IPC Keys
|
||||||
|
\keyword ipc
|
||||||
|
\keyword shared memory
|
||||||
|
\keyword system semaphore
|
||||||
|
|
||||||
|
\brief An overview of keys for QSharedMemory and QSystemSemaphore
|
||||||
|
|
||||||
|
The \l QSharedMemory and \l QSystemSemaphore classes identify their
|
||||||
|
resource using a system-wide identifier known as a "key". The low-level key
|
||||||
|
value as well as the key type are encapsulated in Qt using the \l
|
||||||
|
QNativeIpcKey class. That class also provides the proper means of
|
||||||
|
exchanging the key with other processes, by way of
|
||||||
|
QNativeIpcKey::toString() and QNativeIpcKey::fromString().
|
||||||
|
|
||||||
|
Qt currently supports three distinct backends for those two classes, which
|
||||||
|
match the values available in the \l{QNativeIpcKey::Type} enumeration.
|
||||||
|
\list
|
||||||
|
\li POSIX Realtime extensions (IEEE 1003.1b, POSIX.1b)
|
||||||
|
\li X/Open System Interfaces (XSI) or System V (SVr4), though also now part of POSIX
|
||||||
|
\li Windows primitives
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
As the name indicates, the Windows primitives are only available on the
|
||||||
|
Windows operating system, where they are the default backend. The other two
|
||||||
|
are usually both available on Unix operating systems. The following table
|
||||||
|
provides an overview of typical availability since Qt 6.6:
|
||||||
|
|
||||||
|
\table
|
||||||
|
\header \li Operating system \li POSIX \li System V \li Windows
|
||||||
|
\row \li Android \li \li \li
|
||||||
|
\row \li INTEGRITY \li \li \li
|
||||||
|
\row \li QNX \li Yes \li \li
|
||||||
|
\row \li \macos \li Yes \li Usually (1) \li
|
||||||
|
\row \li Other Apple OSes \li Yes \li \li
|
||||||
|
\row \li Other Unix systems \li Yes \li Yes \li
|
||||||
|
\row \li Windows \li Rarely (2) \li \li Yes
|
||||||
|
\endtable
|
||||||
|
|
||||||
|
\note 1 Sandboxed \macos applications, which include all applications
|
||||||
|
distributed via the Apple App Store, may not use System V objects.
|
||||||
|
|
||||||
|
\note 2 Some GCC-compatible C runtimes on Windows provide POSIX-compatible
|
||||||
|
shared memory support, but this is rare. It is always absent with the
|
||||||
|
Microsoft compiler.
|
||||||
|
|
||||||
|
To determine whether a given key type is supported, applications should
|
||||||
|
call QSharedMemory::isKeyTypeSupported() and
|
||||||
|
QSystemSemaphore::isKeyTypeSupported().
|
||||||
|
|
||||||
|
QNativeIpcKey also provides support for compatibility with Qt applications
|
||||||
|
prior to its introduction. The following sections detail the limitations of
|
||||||
|
the backends, the contents of the string keys themselves, and
|
||||||
|
compatibility.
|
||||||
|
|
||||||
|
\section1 Cross-platform safe key format
|
||||||
|
|
||||||
|
QNativeIpcKey::setNativeKey() and QNativeIpcKey::nativeKey() handle the
|
||||||
|
low-level native key, which may be used with the native APIs and shared
|
||||||
|
with other, non-Qt processes (see below for the API). This format is not
|
||||||
|
usually cross-platform, so both QSharedMemory and QSystemSemaphore provide
|
||||||
|
a function to translate a cross-platform identifier string to the native
|
||||||
|
key: QSharedMemory::platformSafeKey() and
|
||||||
|
QSystemSemaphore::platformSafeKey().
|
||||||
|
|
||||||
|
The length of the cross-platform key on most platforms is the same as that
|
||||||
|
of a file name, but is severely limited on Apple platforms to only 30
|
||||||
|
usable bytes (be mindful of UTF-8 encoding if using characters outside the
|
||||||
|
US-ASCII range). The format of the key is also similar to that of a file
|
||||||
|
path component, meaning it should not contain any characters not allowed in
|
||||||
|
file names, in particular those that separate path components (slash and
|
||||||
|
backslash), with the exception of sandboxed applications on Apple operating
|
||||||
|
systems. The following are good examples of cross-platform keys: "myapp",
|
||||||
|
"org.example.myapp", "org.example.myapp-12345".
|
||||||
|
|
||||||
|
\b{Apple sandbox limitations:} if the application is running inside of a
|
||||||
|
sandbox in an Apple operating system, the key must be in a very specific
|
||||||
|
format: \c {<application group identifier>/<custom identifier>}. Sandboxing
|
||||||
|
is implied for all applications distributed through the Apple App Store.
|
||||||
|
See Apple's documentation
|
||||||
|
\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} for more information, including how to obtain the application's group identifier.
|
||||||
|
|
||||||
|
\section1 Native key format
|
||||||
|
|
||||||
|
This section details the format of the native keys of the supported
|
||||||
|
backends.
|
||||||
|
|
||||||
|
\section3 POSIX Realtime
|
||||||
|
Native keys resemble file names and may contain any character that file
|
||||||
|
names do, except for a slash. POSIX requires the first character in the key
|
||||||
|
name to be a slash and leaves undetermined whether any additional slashes
|
||||||
|
are permitted. On most operating systems, the key length is the same as a
|
||||||
|
file name, but it is limited to 32 characters on Apple operating systems
|
||||||
|
(this includes the first slash and the terminating null, so only 30 usable
|
||||||
|
characters are possible).
|
||||||
|
|
||||||
|
The following are good examples of native POSIX keys: "/myapp",
|
||||||
|
"/org.example.myapp", "/org.example.myapp-12345".
|
||||||
|
|
||||||
|
QSharedMemory::platformSafeKey() and QSystemSemaphore::platformSafeKey()
|
||||||
|
simply prepend the slash. On Apple operating systems, they also truncate
|
||||||
|
the result to the available size.
|
||||||
|
|
||||||
|
\section3 Windows
|
||||||
|
Windows key types are NT
|
||||||
|
\l{https://learn.microsoft.com/en-us/windows/win32/sync/object-namespaces}{kernel
|
||||||
|
object names} and may be up to \c{MAX_PATH} (260) characters in length.
|
||||||
|
They look like relative paths (that is, they don't start with a backslash
|
||||||
|
or a drive letter), but unlike file names on Windows, they are
|
||||||
|
case-sensitive.
|
||||||
|
|
||||||
|
The following are good examples of native Windows keys: "myapp",
|
||||||
|
"org.example.myapp", "org.example.myapp-12345".
|
||||||
|
|
||||||
|
QSharedMemory::platformSafeKey() and QSystemSemaphore::platformSafeKey()
|
||||||
|
insert a prefix to disambiguate shared memory and system semaphores,
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
\section3 X/Open System Interfaces (XSI) / System V
|
||||||
|
System V keys take the form of the name of a file in the system, and thus
|
||||||
|
have the exact same limitations as file paths do. Both QSharedMemory and
|
||||||
|
QSystemSemaphore will create this file if it does not exist when creating
|
||||||
|
the object. If auto-removal is disabled, it may also be shared between
|
||||||
|
QSharedMemory and QSystemSemaphore without conflict and can be any extant
|
||||||
|
file (for example, it can be the process executable itself, see
|
||||||
|
QCoreApplication::applicationFilePath()). The path should be an absolute
|
||||||
|
one to avoid mistakes due to different current directories.
|
||||||
|
|
||||||
|
QSharedMemory::platformSafeKey() and QSystemSemaphore::platformSafeKey()
|
||||||
|
always return an absolute path. If the input was already absolute, they
|
||||||
|
will return their input unchanged. Otherwise, they will prepend a suitable
|
||||||
|
path where the application usually has permission to create files in.
|
||||||
|
|
||||||
|
\section1 Ownership
|
||||||
|
|
||||||
|
Shared memory and system semaphore objects need to be created before use,
|
||||||
|
which is accomplished with QSharedMemory::create() or by passing
|
||||||
|
QSystemSemaphore::Create to the constructor, respectively.
|
||||||
|
|
||||||
|
On Unix systems, the Qt classes that created the object will be responsible
|
||||||
|
for cleaning up the object in question. Therefore, if the application with
|
||||||
|
that C++ object exits uncleanly (a crash, qFatal(), etc.), the object may
|
||||||
|
be left behind. If that happens, applications may fail to create the
|
||||||
|
object again and should instead attach to an existing one. For example, for
|
||||||
|
QSharedMemory:
|
||||||
|
|
||||||
|
\code
|
||||||
|
if (!shm.create(4096) && shm.error() == QSharedMemory::AlreadyExists)
|
||||||
|
shm.attach();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Re-attaching to a QSystemSemaphore is probably unwise, as the token counter
|
||||||
|
in it is probably in an unknown state and therefore may lead to deadlocks.
|
||||||
|
|
||||||
|
\section3 POSIX Realtime
|
||||||
|
POSIX Realtime object ownership is patterned after files, in the sense that
|
||||||
|
they exist independent of any process using them or not. Qt is unable to
|
||||||
|
determine if the object is still in use, so auto-removal will remove it
|
||||||
|
even then, which will make attaching to the same object impossible but
|
||||||
|
otherwise not affecting existing attachments.
|
||||||
|
|
||||||
|
Prior to Qt 6.6, Qt never cleaned up POSIX Realtime objects, except on QNX.
|
||||||
|
|
||||||
|
\section3 X/Open System Interfaces (XSI) / System V
|
||||||
|
There are two resources managed by the Qt classes: the file the key refers
|
||||||
|
to and the object itself. QSharedMemory manages the object cooperatively:
|
||||||
|
the last attachment is responsible for removing the object itself and then
|
||||||
|
removing the key file. QSystemSemaphore will remove the object if and only
|
||||||
|
if it was passed QSystemSemaphore::Create; additionally, if it created the
|
||||||
|
key file, it will remove that too.
|
||||||
|
|
||||||
|
Since Qt 6.6, it is possible to ask either class not to clean up.
|
||||||
|
|
||||||
|
\section3 Windows
|
||||||
|
The operating system owns the object and will clean up after the last
|
||||||
|
handle to the object is closed.
|
||||||
|
|
||||||
|
\section1 Interoperability with old Qt applications
|
||||||
|
|
||||||
|
The QNativeIpcKey class was introduced in Qt 6.6. Prior to this version,
|
||||||
|
QSharedMemory and QSystemSemaphore backends were determined at the time of
|
||||||
|
Qt's own build. For Windows systems, it was always the Windows backend. For
|
||||||
|
Unix systems, it defaulted to the System V backend if the configuration
|
||||||
|
script determined it was available. If it was not available, it fell back
|
||||||
|
to the POSIX one (since Qt 4.8). The POSIX backend could be explicitly
|
||||||
|
selected using the \c{-feature-ipc_posix} option to the Qt configure
|
||||||
|
script; if it was enabled, the \c{QT_POSIX_IPC} macro would be defined.
|
||||||
|
|
||||||
|
Qt 6.6 retains the configure script option but it no longer controls the
|
||||||
|
availability of the backends. Instead, it changes what
|
||||||
|
QNativeIpcKey::legacyDefaultTypeForOs() will return. Applications that need
|
||||||
|
to retain compatibility must use this key type exclusively to guarantee
|
||||||
|
interoperability.
|
||||||
|
|
||||||
|
The API in both QSharedMemory and QSystemSemaphore had the concept of a
|
||||||
|
cross-platform key, which is now deprecated in favor of using
|
||||||
|
QSharedMemory::legacyNativeKey() and QSystemSemaphore::legacyNativeKey().
|
||||||
|
Those two functions produce the same native key as the deprecated functions
|
||||||
|
did in prior versions. If the old code was for example:
|
||||||
|
|
||||||
|
\code
|
||||||
|
QSharedMemory shm("org.example.myapplication");
|
||||||
|
QSystemSemaphore sem("org.example.myapplication");
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
It can be updated to be:
|
||||||
|
\code
|
||||||
|
QSharedMemory shm(QSharedMemory::legacyNativeKey("org.example.myapplication"));
|
||||||
|
QSystemSempahore sem(QSystemSemaphore::legacyNativeKey("org.example.myapplication"));
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
If the two applications exchanged native keys, there is no need to update
|
||||||
|
code such as:
|
||||||
|
|
||||||
|
\code
|
||||||
|
QSharedMemory shm;
|
||||||
|
shm.setNativeKey(key);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Though if the older application did accept a native key, the new one may
|
||||||
|
opt to use \c{platformSafeKey()} with a second argument of
|
||||||
|
QNativeIpcKey::legacyDefaultTypeForOs().
|
||||||
|
|
||||||
|
\section3 X/Open System Interfaces (XSI) / System V
|
||||||
|
Never use existing files for QSharedMemory keys, as the old Qt application
|
||||||
|
may attempt to remove it. Instead, let QSharedMemory create it.
|
||||||
|
|
||||||
|
\section1 Interoperability with non-Qt applications
|
||||||
|
|
||||||
|
Interoperability with non-Qt applications is possible, with some limitations:
|
||||||
|
\list
|
||||||
|
\li Creation of shared memory segments must not race
|
||||||
|
\li QSharedMemory support for locking the segment is unavailable
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
Communication with non-Qt applications must always be through the native
|
||||||
|
key.
|
||||||
|
|
||||||
|
QSharedMemory always maps the entire segment to memory. The non-Qt
|
||||||
|
application may choose to only map a subset of it to memory, with no ill
|
||||||
|
effects.
|
||||||
|
|
||||||
|
\section3 POSIX Realtime
|
||||||
|
POSIX shared memory can be opened using
|
||||||
|
\l{https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html}{shm_open()}
|
||||||
|
and POSIX system semaphores can be opened using
|
||||||
|
\l{https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_open.html}{sem_open()}.
|
||||||
|
|
||||||
|
Both of those functions take a \c name parameter that is the result of
|
||||||
|
QNativeIpcKey::nativeKey(), encoded for file names using
|
||||||
|
QFile::encodeName() / QFile::decodeName().
|
||||||
|
|
||||||
|
\section3 Windows
|
||||||
|
Windows shared memory objects can be opened using
|
||||||
|
\l{https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw}{CreateFileMappingW}
|
||||||
|
and Windows system semaphore objects can be opened using
|
||||||
|
\l{https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createsemaphorew}{CreateSemaphoreW}.
|
||||||
|
Despite the name of both functions starting with "Create", they are able
|
||||||
|
to attach to existing objects.
|
||||||
|
|
||||||
|
The \c lpName parameter to those functions is the result of
|
||||||
|
QNativeIpcKey::nativeKey(), without transformation.
|
||||||
|
|
||||||
|
If the foreign application uses the non-Unicode version of those functions
|
||||||
|
(ending in "A"), the name can be converted to and from 8-bit using QString.
|
||||||
|
|
||||||
|
\section3 X/Open System Interfaces (XSI) / System V
|
||||||
|
System V shared memory can be obtained using
|
||||||
|
\l{https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmget.html}{shmget()}
|
||||||
|
and System V system semaphores can be obtained using
|
||||||
|
\l{https://pubs.opengroup.org/onlinepubs/9699919799/functions/semget.html}{semget()}.
|
||||||
|
|
||||||
|
The \c{key} parameter to either of those functions is the result of the
|
||||||
|
\l{https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftok.html}{ftok()}
|
||||||
|
function when passed the file name obtained from QNativeIpcKey::nativeKey()
|
||||||
|
with an \c id of 81 or 0x51 (the ASCII capital letter 'Q').
|
||||||
|
|
||||||
|
System V semaphore objects may contain multiple semaphores, but
|
||||||
|
QSystemSemaphore only uses the first one (number 0 for \c{sem_num}).
|
||||||
|
|
||||||
|
Both QSharedMemory and QSystemSemaphore default to removing the object
|
||||||
|
using the \c{IPC_RMID} operation to \c{shmctl()} and \c{semctl()}
|
||||||
|
respectively if they are the last attachment.
|
||||||
|
*/
|
@ -76,6 +76,7 @@
|
|||||||
\li \l{The Animation Framework}
|
\li \l{The Animation Framework}
|
||||||
\li \l{JSON Support in Qt}
|
\li \l{JSON Support in Qt}
|
||||||
\li \l{CBOR Support in Qt}
|
\li \l{CBOR Support in Qt}
|
||||||
|
\li \l{Inter-Process Communication}
|
||||||
\li \l{How to Create Qt Plugins}
|
\li \l{How to Create Qt Plugins}
|
||||||
\li \l{The Event System}
|
\li \l{The Event System}
|
||||||
\endlist
|
\endlist
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
// Copyright (C) 2022 Intel Corporation.
|
// Copyright (C) 2022 Intel Corporation.
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
#include "qtipccommon.h"
|
||||||
#include "qtipccommon_p.h"
|
#include "qtipccommon_p.h"
|
||||||
|
|
||||||
#include <qcryptographichash.h>
|
#include <qcryptographichash.h>
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
|
#include <qstringconverter.h>
|
||||||
|
#include <private/qurl_p.h>
|
||||||
|
|
||||||
#if defined(Q_OS_DARWIN)
|
#if defined(Q_OS_DARWIN)
|
||||||
# include "private/qcore_mac_p.h"
|
# include "private/qcore_mac_p.h"
|
||||||
@ -23,6 +26,65 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
static QStringView staticTypeToString(QNativeIpcKey::Type type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QNativeIpcKey::Type::SystemV:
|
||||||
|
return u"systemv";
|
||||||
|
case QNativeIpcKey::Type::PosixRealtime:
|
||||||
|
return u"posix";
|
||||||
|
case QNativeIpcKey::Type::Windows:
|
||||||
|
return u"windows";
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString typeToString(QNativeIpcKey::Type type)
|
||||||
|
{
|
||||||
|
QStringView typeString = staticTypeToString(type);
|
||||||
|
switch (type) {
|
||||||
|
case QNativeIpcKey::Type::SystemV:
|
||||||
|
case QNativeIpcKey::Type::PosixRealtime:
|
||||||
|
case QNativeIpcKey::Type::Windows:
|
||||||
|
return QString::fromRawData(typeString.constData(), typeString.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int value = int(type);
|
||||||
|
if (value >= 1 && value <= 0xff) {
|
||||||
|
// System V key with id different from 'Q'
|
||||||
|
typeString = staticTypeToString(QNativeIpcKey::Type::SystemV);
|
||||||
|
return typeString + QString::number(-value); // negative so it prepends a dash
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString(); // invalid!
|
||||||
|
}
|
||||||
|
|
||||||
|
static QNativeIpcKey::Type stringToType(QStringView typeString)
|
||||||
|
{
|
||||||
|
if (typeString == staticTypeToString(QNativeIpcKey::Type::PosixRealtime))
|
||||||
|
return QNativeIpcKey::Type::PosixRealtime;
|
||||||
|
if (typeString == staticTypeToString(QNativeIpcKey::Type::Windows))
|
||||||
|
return QNativeIpcKey::Type::Windows;
|
||||||
|
|
||||||
|
auto fromNumber = [](QStringView number, int low, int high) {
|
||||||
|
bool ok;
|
||||||
|
int n = -number.toInt(&ok, 10);
|
||||||
|
if (!ok || n < low || n > high)
|
||||||
|
return QNativeIpcKey::Type{};
|
||||||
|
return QNativeIpcKey::Type(n);
|
||||||
|
};
|
||||||
|
|
||||||
|
QStringView sysv = staticTypeToString(QNativeIpcKey::Type::SystemV);
|
||||||
|
if (typeString.startsWith(sysv)) {
|
||||||
|
if (typeString.size() == sysv.size())
|
||||||
|
return QNativeIpcKey::Type::SystemV;
|
||||||
|
return fromNumber(typeString.sliced(sysv.size()), 1, 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid!
|
||||||
|
return QNativeIpcKey::Type{};
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
|
|
||||||
@ -84,6 +146,340 @@ QString QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcT
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QNativeIpcKey
|
||||||
|
\inmodule QtCore
|
||||||
|
\since 6.6
|
||||||
|
\brief The QNativeIpcKey class holds a native key used by QSystemSemaphore and QSharedMemory.
|
||||||
|
|
||||||
|
The \l QSharedMemory and \l QSystemSemaphore classes identify their
|
||||||
|
resource using a system-wide identifier known as a "key". The low-level key
|
||||||
|
value as well as the key type are encapsulated in Qt using the \l
|
||||||
|
QNativeIpcKey class.
|
||||||
|
|
||||||
|
Those two classes also provide the means to create native keys from a
|
||||||
|
cross-platform identifier, using QSharedMemory::platformSafeKey() and
|
||||||
|
QSystemSemaphore::platformSafeKey(). Applications should never share the
|
||||||
|
input to those functions, as different versions of Qt may perform different
|
||||||
|
transformations, resulting in different native keys. Instead, the
|
||||||
|
application that created the IPC object should communicate the resulting
|
||||||
|
native key using the methods described below.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
\section1 Communicating keys to other processes
|
||||||
|
\section2 Communicating keys to other Qt processes
|
||||||
|
|
||||||
|
If the other process supports QNativeIpcKey, the best way of communicating
|
||||||
|
is via the string representation obtained from toString() and parsing it
|
||||||
|
using fromString(). This representation can be stored on a file whose name
|
||||||
|
is well-known or passed on the command-line to a child process using
|
||||||
|
QProcess::setArguments().
|
||||||
|
|
||||||
|
If the other process does not support QNativeIpcKey, then the two processes
|
||||||
|
can exchange the nativeKey() but the older code is likely unable to adjust
|
||||||
|
its key type. The legacyDefaultTypeForOs() function returns the type that
|
||||||
|
legacy code used, which may not match the \l{DefaultTypeForOs} constant.
|
||||||
|
This is still true even if the old application is not using the same build
|
||||||
|
as the new one (for example, it is a Qt 5 application), provided the
|
||||||
|
options passed to the Qt configure script are the same.
|
||||||
|
|
||||||
|
\section2 Communicating keys to non-Qt processes
|
||||||
|
|
||||||
|
When communicating with non-Qt processes, the application must arrange to
|
||||||
|
obtain the key type the other process is using. This is important
|
||||||
|
particularly on Unix systems, where both \l PosixRealtime and \l SystemV
|
||||||
|
are common.
|
||||||
|
|
||||||
|
\section1 String representation of native keys
|
||||||
|
|
||||||
|
The format of the string representation of a QNativeIpcKey is meant to be
|
||||||
|
stable and therefore backwards and forwards compatible, provided the key
|
||||||
|
type is supported by the Qt version in question. That is to say, an older
|
||||||
|
Qt will fail to parse the string representation of a key type introduced
|
||||||
|
after it was released. However, successfully parsing a string
|
||||||
|
representation does not imply the Qt classes can successfully create an
|
||||||
|
object of that type; applications should verify support using
|
||||||
|
QSharedMemory::isKeyTypeSupported() and QSystemSemaphore::isKeyTypeSupported().
|
||||||
|
|
||||||
|
The format of the string representation is formed by two components,
|
||||||
|
separated by a colon (':'). The first component is the key type, described
|
||||||
|
in the table below. The second component is a type-specific payload, using
|
||||||
|
\l{QByteArray::fromPercentEncoding}{percent-encoding}. For all currently
|
||||||
|
supported key types, the decoded form is identical to the contents of the
|
||||||
|
nativeKey() field.
|
||||||
|
|
||||||
|
\table
|
||||||
|
\row \li Key type \li String representation
|
||||||
|
\row \li \l PosixRealtime \li \c "posix"
|
||||||
|
\row \li \l SystemV \li \c "systemv"
|
||||||
|
\row \li \l Windows \li \c "windows"
|
||||||
|
\row \li Non-standard SystemV \li \c "systemv-" followed by a decimal number
|
||||||
|
\endtable
|
||||||
|
|
||||||
|
This format resembles a URI and allows parsing using URI/URL-parsing
|
||||||
|
functions, such as \l QUrl. When parsed by such API, the key type will show
|
||||||
|
up as the \l{QUrl::scheme()}{scheme}, and the payload will be the
|
||||||
|
\l{QUrl::path()}{path}. Use of query or fragments is reserved.
|
||||||
|
|
||||||
|
\sa QSharedMemory, QSystemSemaphore
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\enum QNativeIpcKey::Type
|
||||||
|
|
||||||
|
This enum describes the backend type for the IPC object. For details on the
|
||||||
|
key types, see the \l{Native IPC Key} documentation.
|
||||||
|
|
||||||
|
\value SystemV X/Open System Initiative (XSI) or System V (SVr4) API
|
||||||
|
\value PosixRealtime IEEE 1003.1b (POSIX.1b) API
|
||||||
|
\value Windows Win32 API
|
||||||
|
|
||||||
|
\sa setType(), type()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\variable QNativeIpcKey::DefaultTypeForOs
|
||||||
|
|
||||||
|
This constant expression variable holds the default native IPC type for the
|
||||||
|
current OS. It will be Type::Windows for Windows systems and
|
||||||
|
Type::PosixRealtime elsewhere. Note that this constant is different from
|
||||||
|
what \l QSharedMemory and \l QSystemSemaphore defaulted to on the majority
|
||||||
|
of Unix systems prior to Qt 6.6; see legacyDefaultTypeForOs() for more
|
||||||
|
information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::legacyDefaultTypeForOs() noexcept
|
||||||
|
|
||||||
|
Returns the \l{Type} that corresponds to the native IPC key that
|
||||||
|
\l{QSharedMemory} and \l{QSystemSemaphore} used to use prior to Qt 6.6.
|
||||||
|
Applications and libraries that must retain compatibility with code using
|
||||||
|
either class that was compiled with Qt prior to version 6.6 can use this
|
||||||
|
function to determine what IPC type the other applications may be using.
|
||||||
|
|
||||||
|
Note that this function relies on Qt having been built with identical
|
||||||
|
configure-time options.
|
||||||
|
*/
|
||||||
|
#if defined(Q_OS_DARWIN)
|
||||||
|
QNativeIpcKey::Type QNativeIpcKey::defaultTypeForOs_internal() noexcept
|
||||||
|
{
|
||||||
|
if (qt_apple_isSandboxed())
|
||||||
|
return Type::PosixRealtime;
|
||||||
|
return Type::SystemV;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::QNativeIpcKey(Type type) noexcept
|
||||||
|
\fn QNativeIpcKey::QNativeIpcKey(const QString &key, Type type)
|
||||||
|
|
||||||
|
Constructs a QNativeIpcKey object holding native key \a key (or empty on
|
||||||
|
the overload without the parameter) for type \a type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::QNativeIpcKey(const QNativeIpcKey &other)
|
||||||
|
\fn QNativeIpcKey::QNativeIpcKey(QNativeIpcKey &&other) noexcept
|
||||||
|
\fn QNativeIpcKey &QNativeIpcKey::operator=(const QNativeIpcKey &other)
|
||||||
|
\fn QNativeIpcKey &QNativeIpcKey::operator=(QNativeIpcKey &&other) noexcept
|
||||||
|
|
||||||
|
Copies or moves the content of \a other.
|
||||||
|
*/
|
||||||
|
void QNativeIpcKey::copy_internal(const QNativeIpcKey &)
|
||||||
|
{
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QNativeIpcKey::move_internal(QNativeIpcKey &&) noexcept
|
||||||
|
{
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &)
|
||||||
|
{
|
||||||
|
Q_UNREACHABLE_RETURN(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::~QNativeIpcKey()
|
||||||
|
|
||||||
|
Disposes of this QNativeIpcKey object.
|
||||||
|
*/
|
||||||
|
void QNativeIpcKey::destroy_internal() noexcept
|
||||||
|
{
|
||||||
|
Q_ASSERT(isSlowPath());
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::swap(QNativeIpcKey &other) noexcept
|
||||||
|
\fn swap(QNativeIpcKey &lhs, QNativeIpcKey &rhs) noexcept
|
||||||
|
|
||||||
|
Swaps the native IPC key and type \a other with this object, or \a lhs with
|
||||||
|
\a rhs. This operation is very fast and never fails.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::isEmpty() const
|
||||||
|
|
||||||
|
Returns true if the nativeKey() is empty.
|
||||||
|
|
||||||
|
\sa nativeKey()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::isValid() const
|
||||||
|
|
||||||
|
Returns true if this object contains a valid native IPC key type. Invalid
|
||||||
|
types are usually the result of a failure to parse a string representation
|
||||||
|
using fromString().
|
||||||
|
|
||||||
|
This function performs no check on the whether the key string is actually
|
||||||
|
supported or valid for the current operating system.
|
||||||
|
|
||||||
|
\sa type(), fromString()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::type() const noexcept
|
||||||
|
|
||||||
|
Returns the key type associated with this object.
|
||||||
|
|
||||||
|
\sa nativeKey(), setType()
|
||||||
|
*/
|
||||||
|
QNativeIpcKey::Type QNativeIpcKey::type_internal() const noexcept
|
||||||
|
{
|
||||||
|
Q_ASSERT(isSlowPath());
|
||||||
|
Q_UNREACHABLE_RETURN({});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::setType(Type type)
|
||||||
|
|
||||||
|
Sets the IPC type of this object to \a type.
|
||||||
|
|
||||||
|
\sa type(), setNativeKey()
|
||||||
|
*/
|
||||||
|
void QNativeIpcKey::setType_internal(Type)
|
||||||
|
{
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::nativeKey() const noexcept
|
||||||
|
|
||||||
|
Returns the native key string associated with this object.
|
||||||
|
|
||||||
|
\sa setNativeKey(), type()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QNativeIpcKey::setNativeKey(const QString &newKey)
|
||||||
|
|
||||||
|
Sets the native key for this object to \a newKey.
|
||||||
|
|
||||||
|
\sa nativeKey(), setType()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn bool QNativeIpcKey::operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
|
||||||
|
\fn bool QNativeIpcKey::operator!=(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
|
||||||
|
|
||||||
|
Returns true if the \a lhs and \a rhs objects hold the same (or different) contents.
|
||||||
|
*/
|
||||||
|
int QNativeIpcKey::compare_internal(const QNativeIpcKey &, const QNativeIpcKey &) noexcept
|
||||||
|
{
|
||||||
|
Q_UNREACHABLE_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the string representation of this object. String representations
|
||||||
|
are useful to inform other processes of the key this process created and
|
||||||
|
that they should attach to.
|
||||||
|
|
||||||
|
This function returns a null string if the current object is
|
||||||
|
\l{isValid()}{invalid}.
|
||||||
|
|
||||||
|
\sa fromString()
|
||||||
|
*/
|
||||||
|
QString QNativeIpcKey::toString() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(!isSlowPath());
|
||||||
|
QString prefix = typeToString(typeAndFlags.type);
|
||||||
|
if (prefix.isEmpty()) {
|
||||||
|
Q_ASSERT(prefix.isNull());
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix += u':';
|
||||||
|
QString copy = nativeKey();
|
||||||
|
copy.replace(u'%', "%25"_L1);
|
||||||
|
if (copy.startsWith("//"_L1))
|
||||||
|
copy.replace(0, 2, u"/%2F"_s); // ensure it's parsed as a URL path
|
||||||
|
|
||||||
|
const ushort recodeActions[] = {
|
||||||
|
'\\' | 0, // decode
|
||||||
|
'?' | 0x200, // encode
|
||||||
|
'#' | 0x200, // encode
|
||||||
|
0
|
||||||
|
};
|
||||||
|
if (!qt_urlRecode(prefix, copy, QUrl::DecodeReserved, recodeActions))
|
||||||
|
prefix += copy;
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Parses the string form \a text and returns the corresponding QNativeIpcKey.
|
||||||
|
String representations are useful to inform other processes of the key this
|
||||||
|
process created and they should attach to.
|
||||||
|
|
||||||
|
If the string could not be parsed, this function returns an
|
||||||
|
\l{isValid()}{invalid} object.
|
||||||
|
|
||||||
|
\sa toString(), isValid()
|
||||||
|
*/
|
||||||
|
QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
|
||||||
|
{
|
||||||
|
// this duplicates QUrlPrivate::parse a little
|
||||||
|
Type invalidType = {};
|
||||||
|
qsizetype colon = text.indexOf(u':');
|
||||||
|
if (colon < 0)
|
||||||
|
return QNativeIpcKey(invalidType);
|
||||||
|
|
||||||
|
Type type = stringToType(QStringView(text).left(colon));
|
||||||
|
if (type == invalidType)
|
||||||
|
return QNativeIpcKey(invalidType);
|
||||||
|
|
||||||
|
QNativeIpcKey result(QString(), type);
|
||||||
|
if (result.type() != type) // range check, just in case
|
||||||
|
return QNativeIpcKey(invalidType);
|
||||||
|
|
||||||
|
// decode the payload
|
||||||
|
QStringView payload = QStringView(text).sliced(colon + 1);
|
||||||
|
if (qsizetype pos = payload.indexOf(u'?'); pos >= 0)
|
||||||
|
payload.truncate(pos);
|
||||||
|
if (qsizetype pos = payload.indexOf(u'#'); pos >= 0)
|
||||||
|
payload.truncate(pos);
|
||||||
|
|
||||||
|
// qt_urlRecode requires a two-step decoding for non-ASCII content
|
||||||
|
QString nativeKey, intermediate;
|
||||||
|
if (qt_urlRecode(intermediate, payload, QUrl::PrettyDecoded))
|
||||||
|
payload = intermediate;
|
||||||
|
if (!qt_urlRecode(nativeKey, payload, QUrl::FullyDecoded))
|
||||||
|
nativeKey = payload.toString();
|
||||||
|
|
||||||
|
result.setNativeKey(nativeKey);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#include "moc_qtipccommon.cpp"
|
||||||
|
|
||||||
#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
|
#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
|
||||||
|
191
src/corelib/ipc/qtipccommon.h
Normal file
191
src/corelib/ipc/qtipccommon.h
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
// Copyright (C) 2022 Intel Corporation.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
#ifndef QNATIVEIPCKEY_H
|
||||||
|
#define QNATIVEIPCKEY_H
|
||||||
|
|
||||||
|
#include <QtCore/qglobal.h>
|
||||||
|
#include <QtCore/qtcore-config.h>
|
||||||
|
|
||||||
|
#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
|
||||||
|
# include <QtCore/qstring.h>
|
||||||
|
# include <QtCore/qobjectdefs.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QNativeIpcKeyPrivate;
|
||||||
|
class QNativeIpcKey
|
||||||
|
{
|
||||||
|
Q_GADGET_EXPORT(Q_CORE_EXPORT)
|
||||||
|
public:
|
||||||
|
enum class Type : quint16 {
|
||||||
|
// 0 is reserved for the invalid type
|
||||||
|
// keep 1 through 0xff free, except for SystemV
|
||||||
|
SystemV = 0x51, // 'Q'
|
||||||
|
|
||||||
|
PosixRealtime = 0x100,
|
||||||
|
Windows,
|
||||||
|
};
|
||||||
|
Q_ENUM(Type)
|
||||||
|
|
||||||
|
static constexpr Type DefaultTypeForOs =
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
Type::Windows
|
||||||
|
#elif !defined(QT_POSIX_IPC)
|
||||||
|
Type::SystemV
|
||||||
|
#else
|
||||||
|
Type::PosixRealtime
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
static Type legacyDefaultTypeForOs() noexcept;
|
||||||
|
|
||||||
|
explicit constexpr QNativeIpcKey(Type type = DefaultTypeForOs) noexcept
|
||||||
|
: d()
|
||||||
|
{
|
||||||
|
typeAndFlags.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_IMPLICIT QNativeIpcKey(const QString &k, Type type = DefaultTypeForOs)
|
||||||
|
: d(), key(k)
|
||||||
|
{
|
||||||
|
typeAndFlags.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
QNativeIpcKey(const QNativeIpcKey &other)
|
||||||
|
: d(other.d), key(other.key)
|
||||||
|
{
|
||||||
|
if (isSlowPath())
|
||||||
|
copy_internal(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
QNativeIpcKey(QNativeIpcKey &&other) noexcept
|
||||||
|
: d(other.d), key(std::move(other.key))
|
||||||
|
{
|
||||||
|
if (isSlowPath())
|
||||||
|
move_internal(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
~QNativeIpcKey()
|
||||||
|
{
|
||||||
|
if (isSlowPath())
|
||||||
|
destroy_internal();
|
||||||
|
}
|
||||||
|
|
||||||
|
QNativeIpcKey &operator=(const QNativeIpcKey &other)
|
||||||
|
{
|
||||||
|
if (isSlowPath() || other.isSlowPath())
|
||||||
|
return assign_internal(other);
|
||||||
|
d = other.d;
|
||||||
|
key = other.key;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QNativeIpcKey)
|
||||||
|
void swap(QNativeIpcKey &other)
|
||||||
|
{
|
||||||
|
qt_ptr_swap(d, other.d);
|
||||||
|
key.swap(other.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
return key.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return type() != Type{};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Type type() const noexcept
|
||||||
|
{
|
||||||
|
if (isSlowPath())
|
||||||
|
return type_internal();
|
||||||
|
return typeAndFlags.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void setType(Type type)
|
||||||
|
{
|
||||||
|
if (isSlowPath())
|
||||||
|
return setType_internal(type);
|
||||||
|
typeAndFlags.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString nativeKey() const noexcept
|
||||||
|
{ return key; }
|
||||||
|
void setNativeKey(const QString &newKey)
|
||||||
|
{ key = newKey; }
|
||||||
|
|
||||||
|
Q_CORE_EXPORT QString toString() const;
|
||||||
|
Q_CORE_EXPORT static QNativeIpcKey fromString(const QString &string);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct TypeAndFlags {
|
||||||
|
quint16 isExtended : 1;
|
||||||
|
Type type : 15;
|
||||||
|
quint16 reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bit 0: if set, holds a pointer (with the LSB set); if clear, holds the
|
||||||
|
// the TypeAndFlags structure.
|
||||||
|
union {
|
||||||
|
QNativeIpcKeyPrivate *d = nullptr;
|
||||||
|
TypeAndFlags typeAndFlags;
|
||||||
|
static_assert(sizeof(typeAndFlags) <= sizeof(d));
|
||||||
|
};
|
||||||
|
|
||||||
|
QString key;
|
||||||
|
|
||||||
|
constexpr bool isSlowPath() const noexcept
|
||||||
|
{ return Q_UNLIKELY(typeAndFlags.isExtended); }
|
||||||
|
|
||||||
|
friend bool operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
|
||||||
|
{
|
||||||
|
if (lhs.key != rhs.key)
|
||||||
|
return false;
|
||||||
|
if (lhs.d == rhs.d)
|
||||||
|
return true;
|
||||||
|
if (lhs.isSlowPath() && rhs.isSlowPath())
|
||||||
|
return compare_internal(lhs, rhs) == 0;
|
||||||
|
return lhs.d == rhs.d;
|
||||||
|
}
|
||||||
|
friend bool operator!=(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_CORE_EXPORT void copy_internal(const QNativeIpcKey &other);
|
||||||
|
Q_CORE_EXPORT void move_internal(QNativeIpcKey &&other) noexcept;
|
||||||
|
Q_CORE_EXPORT QNativeIpcKey &assign_internal(const QNativeIpcKey &other);
|
||||||
|
Q_CORE_EXPORT void destroy_internal() noexcept;
|
||||||
|
Q_DECL_PURE_FUNCTION Q_CORE_EXPORT Type type_internal() const noexcept;
|
||||||
|
Q_CORE_EXPORT void setType_internal(Type);
|
||||||
|
Q_DECL_PURE_FUNCTION Q_CORE_EXPORT static int
|
||||||
|
compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept;
|
||||||
|
|
||||||
|
#ifdef Q_OS_DARWIN
|
||||||
|
Q_DECL_CONST_FUNCTION Q_CORE_EXPORT static Type defaultTypeForOs_internal() noexcept;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// not a shared type, exactly, but this works too
|
||||||
|
Q_DECLARE_SHARED(QNativeIpcKey)
|
||||||
|
|
||||||
|
inline auto QNativeIpcKey::legacyDefaultTypeForOs() noexcept -> Type
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
return Type::Windows;
|
||||||
|
#elif defined(QT_POSIX_IPC)
|
||||||
|
return Type::PosixRealtime;
|
||||||
|
#elif defined(Q_OS_DARWIN)
|
||||||
|
return defaultTypeForOs_internal();
|
||||||
|
#else
|
||||||
|
return Type::SystemV;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QNATIVEIPCKEY_H
|
@ -2,6 +2,9 @@
|
|||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
if(NOT ANDROID AND NOT UIKIT)
|
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 AND QT_FEATURE_private_tests)
|
||||||
add_subdirectory(qsharedmemory)
|
add_subdirectory(qsharedmemory)
|
||||||
endif()
|
endif()
|
||||||
|
7
tests/auto/corelib/ipc/qnativeipckey/CMakeLists.txt
Normal file
7
tests/auto/corelib/ipc/qnativeipckey/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Copyright (C) 2022 Intel Corporation.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
qt_internal_add_test(tst_qnativeipckey
|
||||||
|
SOURCES
|
||||||
|
tst_qnativeipckey.cpp
|
||||||
|
)
|
347
tests/auto/corelib/ipc/qnativeipckey/tst_qnativeipckey.cpp
Normal file
347
tests/auto/corelib/ipc/qnativeipckey/tst_qnativeipckey.cpp
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
// Copyright (C) 2022 Intel Corporation.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include <QtCore/QNativeIpcKey>
|
||||||
|
#include <QtTest/QTest>
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
namespace QTest {
|
||||||
|
template<> inline char *toString(const QNativeIpcKey::Type &type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QNativeIpcKey::Type::SystemV: return qstrdup("SystemV");
|
||||||
|
case QNativeIpcKey::Type::PosixRealtime: return qstrdup("PosixRealTime");
|
||||||
|
case QNativeIpcKey::Type::Windows: return qstrdup("Windows");
|
||||||
|
}
|
||||||
|
if (type == QNativeIpcKey::Type{})
|
||||||
|
return qstrdup("Invalid");
|
||||||
|
|
||||||
|
char buf[32];
|
||||||
|
qsnprintf(buf, sizeof(buf), "%u", unsigned(type));
|
||||||
|
return qstrdup(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline char *toString(const QNativeIpcKey &key)
|
||||||
|
{
|
||||||
|
if (!key.isValid())
|
||||||
|
return qstrdup("<invalid>");
|
||||||
|
|
||||||
|
const char *type = toString(key.type());
|
||||||
|
const char *text = toString(key.nativeKey());
|
||||||
|
char buf[256];
|
||||||
|
qsnprintf(buf, sizeof(buf), "QNativeIpcKey(%s, %s)", text, type);
|
||||||
|
delete[] type;
|
||||||
|
delete[] text;
|
||||||
|
return qstrdup(buf);
|
||||||
|
}
|
||||||
|
} // namespace QTest
|
||||||
|
|
||||||
|
class tst_QNativeIpcKey : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void defaultTypes();
|
||||||
|
void construct();
|
||||||
|
void getSetCheck();
|
||||||
|
void equality();
|
||||||
|
void swap();
|
||||||
|
void toString_data();
|
||||||
|
void toString();
|
||||||
|
void fromString_data();
|
||||||
|
void fromString();
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::defaultTypes()
|
||||||
|
{
|
||||||
|
auto isKnown = [](QNativeIpcKey::Type t) {
|
||||||
|
switch (t) {
|
||||||
|
case QNativeIpcKey::Type::SystemV:
|
||||||
|
case QNativeIpcKey::Type::PosixRealtime:
|
||||||
|
case QNativeIpcKey::Type::Windows:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// because the letter Q looked nice in Håvard's Emacs font back in the 1990s
|
||||||
|
static_assert(qToUnderlying(QNativeIpcKey::Type::SystemV) == 'Q',
|
||||||
|
"QNativeIpcKey::Type::SystemV must be equal to the letter Q");
|
||||||
|
|
||||||
|
auto type = QNativeIpcKey::DefaultTypeForOs;
|
||||||
|
auto legacy = QNativeIpcKey::legacyDefaultTypeForOs();
|
||||||
|
QVERIFY(isKnown(type));
|
||||||
|
QVERIFY(isKnown(legacy));
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QCOMPARE(type, QNativeIpcKey::Type::Windows);
|
||||||
|
#elif !defined(QT_POSIX_IPC)
|
||||||
|
QCOMPARE(type, QNativeIpcKey::Type::SystemV);
|
||||||
|
#else
|
||||||
|
QCOMPARE(type, QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
QCOMPARE(legacy, QNativeIpcKey::Type::Windows);
|
||||||
|
#elif defined(QT_POSIX_IPC)
|
||||||
|
QCOMPARE(legacy, QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
#elif !defined(Q_OS_DARWIN)
|
||||||
|
QCOMPARE(legacy, QNativeIpcKey::Type::SystemV);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::construct()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
QNativeIpcKey invalid(QNativeIpcKey::Type{});
|
||||||
|
QVERIFY(!invalid.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QNativeIpcKey key;
|
||||||
|
QVERIFY(key.nativeKey().isEmpty());
|
||||||
|
QVERIFY(key.isEmpty());
|
||||||
|
QVERIFY(key.isValid());
|
||||||
|
QCOMPARE(key.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
|
||||||
|
QNativeIpcKey copy(key);
|
||||||
|
QVERIFY(copy.nativeKey().isEmpty());
|
||||||
|
QVERIFY(copy.isEmpty());
|
||||||
|
QVERIFY(copy.isValid());
|
||||||
|
QCOMPARE(copy.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
|
||||||
|
QNativeIpcKey moved(std::move(copy));
|
||||||
|
QVERIFY(moved.nativeKey().isEmpty());
|
||||||
|
QVERIFY(moved.isEmpty());
|
||||||
|
QVERIFY(moved.isValid());
|
||||||
|
QCOMPARE(moved.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
|
||||||
|
key.setType({});
|
||||||
|
key.setNativeKey("something else");
|
||||||
|
key = std::move(moved);
|
||||||
|
QVERIFY(key.nativeKey().isEmpty());
|
||||||
|
QVERIFY(key.isEmpty());
|
||||||
|
QVERIFY(key.isValid());
|
||||||
|
QCOMPARE(key.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
|
||||||
|
copy.setType({});
|
||||||
|
copy.setNativeKey("something else");
|
||||||
|
copy = key;
|
||||||
|
QVERIFY(copy.nativeKey().isEmpty());
|
||||||
|
QVERIFY(copy.isEmpty());
|
||||||
|
QVERIFY(copy.isValid());
|
||||||
|
QCOMPARE(copy.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QNativeIpcKey key("dummy");
|
||||||
|
QCOMPARE(key.nativeKey(), "dummy");
|
||||||
|
QVERIFY(!key.isEmpty());
|
||||||
|
QVERIFY(key.isValid());
|
||||||
|
QCOMPARE(key.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
|
||||||
|
QNativeIpcKey copy(key);
|
||||||
|
QCOMPARE(key.nativeKey(), "dummy");
|
||||||
|
QCOMPARE(copy.nativeKey(), "dummy");
|
||||||
|
QVERIFY(!copy.isEmpty());
|
||||||
|
QVERIFY(copy.isValid());
|
||||||
|
QCOMPARE(copy.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
|
||||||
|
QNativeIpcKey moved(std::move(copy));
|
||||||
|
QCOMPARE(key.nativeKey(), "dummy");
|
||||||
|
QCOMPARE(moved.nativeKey(), "dummy");
|
||||||
|
QVERIFY(!moved.isEmpty());
|
||||||
|
QVERIFY(moved.isValid());
|
||||||
|
QCOMPARE(moved.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
|
||||||
|
key.setType({});
|
||||||
|
key.setNativeKey("something else");
|
||||||
|
key = std::move(moved);
|
||||||
|
QCOMPARE(key.nativeKey(), "dummy");
|
||||||
|
QVERIFY(!key.isEmpty());
|
||||||
|
QVERIFY(key.isValid());
|
||||||
|
QCOMPARE(key.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
|
||||||
|
copy.setType({});
|
||||||
|
copy.setNativeKey("something else");
|
||||||
|
copy = key;
|
||||||
|
QCOMPARE(key.nativeKey(), "dummy");
|
||||||
|
QCOMPARE(copy.nativeKey(), "dummy");
|
||||||
|
QVERIFY(!copy.isEmpty());
|
||||||
|
QVERIFY(copy.isValid());
|
||||||
|
QCOMPARE(copy.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::getSetCheck()
|
||||||
|
{
|
||||||
|
QNativeIpcKey key("key1", QNativeIpcKey::Type::Windows);
|
||||||
|
QVERIFY(key.isValid());
|
||||||
|
QVERIFY(!key.isEmpty());
|
||||||
|
QCOMPARE(key.nativeKey(), "key1");
|
||||||
|
QCOMPARE(key.type(), QNativeIpcKey::Type::Windows);
|
||||||
|
|
||||||
|
key.setType(QNativeIpcKey::Type::SystemV);
|
||||||
|
QVERIFY(key.isValid());
|
||||||
|
QVERIFY(!key.isEmpty());
|
||||||
|
QCOMPARE(key.type(), QNativeIpcKey::Type::SystemV);
|
||||||
|
|
||||||
|
key.setNativeKey("key2");
|
||||||
|
QCOMPARE(key.nativeKey(), "key2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::equality()
|
||||||
|
{
|
||||||
|
QNativeIpcKey key1, key2;
|
||||||
|
QCOMPARE(key1, key2);
|
||||||
|
QVERIFY(!(key1 != key2));
|
||||||
|
|
||||||
|
key1.setNativeKey("key1");
|
||||||
|
QCOMPARE_NE(key1, key2);
|
||||||
|
QVERIFY(!(key1 == key2));
|
||||||
|
|
||||||
|
key2.setType({});
|
||||||
|
QCOMPARE_NE(key1, key2);
|
||||||
|
QVERIFY(!(key1 == key2));
|
||||||
|
|
||||||
|
key2.setNativeKey(key1.nativeKey());
|
||||||
|
QCOMPARE_NE(key1, key2);
|
||||||
|
QVERIFY(!(key1 == key2));
|
||||||
|
|
||||||
|
key2.setType(QNativeIpcKey::DefaultTypeForOs);
|
||||||
|
QCOMPARE(key1, key2);
|
||||||
|
QVERIFY(!(key1 != key2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::swap()
|
||||||
|
{
|
||||||
|
QNativeIpcKey key1("key1", QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
QNativeIpcKey key2("key2", QNativeIpcKey::Type::Windows);
|
||||||
|
|
||||||
|
// self-swaps
|
||||||
|
key1.swap(key1);
|
||||||
|
key2.swap(key2);
|
||||||
|
QCOMPARE(key1.nativeKey(), "key1");
|
||||||
|
QCOMPARE(key1.type(), QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
QCOMPARE(key2.nativeKey(), "key2");
|
||||||
|
QCOMPARE(key2.type(), QNativeIpcKey::Type::Windows);
|
||||||
|
|
||||||
|
key1.swap(key2);
|
||||||
|
QCOMPARE(key2.nativeKey(), "key1");
|
||||||
|
QCOMPARE(key2.type(), QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
QCOMPARE(key1.nativeKey(), "key2");
|
||||||
|
QCOMPARE(key1.type(), QNativeIpcKey::Type::Windows);
|
||||||
|
|
||||||
|
key1.swap(key2);
|
||||||
|
QCOMPARE(key1.nativeKey(), "key1");
|
||||||
|
QCOMPARE(key1.type(), QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
QCOMPARE(key2.nativeKey(), "key2");
|
||||||
|
QCOMPARE(key2.type(), QNativeIpcKey::Type::Windows);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::toString_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("string");
|
||||||
|
QTest::addColumn<QNativeIpcKey>("key");
|
||||||
|
|
||||||
|
QTest::newRow("invalid") << QString() << QNativeIpcKey(QNativeIpcKey::Type(0));
|
||||||
|
|
||||||
|
auto addRow = [](const char *prefix, QNativeIpcKey::Type type) {
|
||||||
|
auto add = [=](const char *name, QLatin1StringView key, QLatin1StringView encoded = {}) {
|
||||||
|
if (encoded.isNull())
|
||||||
|
encoded = key;
|
||||||
|
QTest::addRow("%s-%s", prefix, name)
|
||||||
|
<< prefix + u":"_s + encoded << QNativeIpcKey(key, type);
|
||||||
|
};
|
||||||
|
add("empty", {});
|
||||||
|
add("text", "foobar"_L1);
|
||||||
|
add("pathlike", "/sometext"_L1);
|
||||||
|
add("objectlike", "Global\\sometext"_L1);
|
||||||
|
add("colon-slash", ":/"_L1);
|
||||||
|
add("slash-colon", "/:"_L1);
|
||||||
|
add("non-ascii", "\xa0\xff"_L1);
|
||||||
|
add("percent", "%"_L1, "%25"_L1);
|
||||||
|
add("question-hash", "?#"_L1, "%3F%23"_L1);
|
||||||
|
add("hash-question", "#?"_L1, "%23%3F"_L1);
|
||||||
|
add("double-slash", "//"_L1, "/%2F"_L1);
|
||||||
|
add("triple-slash", "///"_L1, "/%2F/"_L1);
|
||||||
|
add("non-ascii", "/\xe9"_L1);
|
||||||
|
QTest::addRow("%s-%s", prefix, "non-latin1")
|
||||||
|
<< prefix + u":\u0100.\u2000.\U00010000"_s
|
||||||
|
<< QNativeIpcKey(u"\u0100.\u2000.\U00010000"_s, type);
|
||||||
|
};
|
||||||
|
addRow("systemv", QNativeIpcKey::Type::SystemV);
|
||||||
|
addRow("posix", QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
addRow("windows", QNativeIpcKey::Type::Windows);
|
||||||
|
|
||||||
|
addRow("systemv-1", QNativeIpcKey::Type(1));
|
||||||
|
addRow("systemv-84", QNativeIpcKey::Type('T'));
|
||||||
|
addRow("systemv-255", QNativeIpcKey::Type(0xff));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::toString()
|
||||||
|
{
|
||||||
|
QFETCH(QString, string);
|
||||||
|
QFETCH(QNativeIpcKey, key);
|
||||||
|
|
||||||
|
QCOMPARE(key.toString(), string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::fromString_data()
|
||||||
|
{
|
||||||
|
toString_data();
|
||||||
|
QTest::addRow("systemv-alias") << "systemv-81:" << QNativeIpcKey(QNativeIpcKey::Type::SystemV);
|
||||||
|
QTest::addRow("systemv-zeropadded") << "systemv-009:" << QNativeIpcKey(QNativeIpcKey::Type(9));
|
||||||
|
|
||||||
|
QNativeIpcKey valid("/foo", QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
QNativeIpcKey invalid(QNativeIpcKey::Type(0));
|
||||||
|
|
||||||
|
// percent-decoding
|
||||||
|
QTest::addRow("percent-encoded") << "posix:%2f%66o%6f" << valid;
|
||||||
|
QTest::addRow("percent-utf8")
|
||||||
|
<< "posix:%C4%80.%E2%80%80.%F0%90%80%80"
|
||||||
|
<< QNativeIpcKey(u"\u0100.\u2000.\U00010000"_s, QNativeIpcKey::Type::PosixRealtime);
|
||||||
|
|
||||||
|
// query and fragment are ignored
|
||||||
|
QTest::addRow("with-query") << "posix:/foo?bar" << valid;
|
||||||
|
QTest::addRow("with-fragment") << "posix:/foo#bar" << valid;
|
||||||
|
QTest::addRow("with-queryfragment") << "posix:/foo?bar#baz" << valid;
|
||||||
|
QTest::addRow("with-fragmentquery") << "posix:/foo#bar?baz" << valid;
|
||||||
|
|
||||||
|
// add some ones that won't parse well
|
||||||
|
QTest::addRow("positive-number") << "81" << invalid;
|
||||||
|
QTest::addRow("negative-number") << "-81" << invalid;
|
||||||
|
QTest::addRow("invalidprefix") << "invalidprefix:" << invalid;
|
||||||
|
QTest::addRow("systemv-nodash") << "systemv255" << invalid;
|
||||||
|
QTest::addRow("systemv-doubledash") << "systemv--255:" << invalid;
|
||||||
|
QTest::addRow("systemv-plus") << "systemv+255" << invalid;
|
||||||
|
QTest::addRow("systemv-hex") << "systemv-0x01:" << invalid;
|
||||||
|
QTest::addRow("systemv-too-low") << "systemv-0:" << invalid;
|
||||||
|
QTest::addRow("systemv-too-high") << "systemv-256" << invalid;
|
||||||
|
QTest::addRow("systemv-overflow-15bit") << "systemv-32769:" << invalid;
|
||||||
|
QTest::addRow("systemv-overflow-16bit") << "systemv-65537:" << invalid;
|
||||||
|
QTest::addRow("systemv-overflow-31bit") << "systemv-2147483649:" << invalid;
|
||||||
|
QTest::addRow("systemv-overflow-32bit") << "systemv-4294967297:" << invalid;
|
||||||
|
|
||||||
|
auto addRows = [=](const char *name) {
|
||||||
|
QTest::addRow("%s-nocolon", name) << name << invalid;
|
||||||
|
QTest::addRow("%s-junk", name) << name + u"junk"_s << invalid;
|
||||||
|
QTest::addRow("junk-%s-colon", name) << u"junk:"_s + name + u':' << invalid;
|
||||||
|
};
|
||||||
|
addRows("systemv");
|
||||||
|
addRows("posix");
|
||||||
|
addRows("windows");
|
||||||
|
addRows("systemv-1");
|
||||||
|
addRows("systemv-255");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QNativeIpcKey::fromString()
|
||||||
|
{
|
||||||
|
QFETCH(QString, string);
|
||||||
|
QFETCH(QNativeIpcKey, key);
|
||||||
|
|
||||||
|
QCOMPARE(QNativeIpcKey::fromString(string), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_QNativeIpcKey)
|
||||||
|
#include "tst_qnativeipckey.moc"
|
Loading…
Reference in New Issue
Block a user