summaryrefslogtreecommitdiffstats
path: root/src/corelib/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/ipc')
-rw-r--r--src/corelib/ipc/qsharedmemory.cpp689
-rw-r--r--src/corelib/ipc/qsharedmemory.h99
-rw-r--r--src/corelib/ipc/qsharedmemory_p.h215
-rw-r--r--src/corelib/ipc/qsharedmemory_posix.cpp196
-rw-r--r--src/corelib/ipc/qsharedmemory_systemv.cpp212
-rw-r--r--src/corelib/ipc/qsharedmemory_win.cpp148
-rw-r--r--src/corelib/ipc/qsystemsemaphore.cpp402
-rw-r--r--src/corelib/ipc/qsystemsemaphore.h75
-rw-r--r--src/corelib/ipc/qsystemsemaphore_p.h152
-rw-r--r--src/corelib/ipc/qsystemsemaphore_posix.cpp171
-rw-r--r--src/corelib/ipc/qsystemsemaphore_systemv.cpp201
-rw-r--r--src/corelib/ipc/qsystemsemaphore_win.cpp98
-rw-r--r--src/corelib/ipc/qtipccommon.cpp610
-rw-r--r--src/corelib/ipc/qtipccommon.h206
-rw-r--r--src/corelib/ipc/qtipccommon_p.h173
15 files changed, 3647 insertions, 0 deletions
diff --git a/src/corelib/ipc/qsharedmemory.cpp b/src/corelib/ipc/qsharedmemory.cpp
new file mode 100644
index 0000000000..02761c0263
--- /dev/null
+++ b/src/corelib/ipc/qsharedmemory.cpp
@@ -0,0 +1,689 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsharedmemory.h"
+#include "qsharedmemory_p.h"
+
+#include "qtipccommon_p.h"
+#include "qsystemsemaphore.h"
+
+#include <q20memory.h>
+#include <qdebug.h>
+#ifdef Q_OS_WIN
+# include <qt_windows.h>
+#endif
+
+#ifndef MAX_PATH
+# define MAX_PATH PATH_MAX
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(sharedmemory)
+
+using namespace QtIpcCommon;
+using namespace Qt::StringLiterals;
+
+QSharedMemoryPrivate::~QSharedMemoryPrivate()
+{
+ destructBackend();
+}
+
+inline void QSharedMemoryPrivate::constructBackend()
+{
+ using namespace q20;
+ visit([](auto p) { construct_at(p); });
+}
+
+inline void QSharedMemoryPrivate::destructBackend()
+{
+ visit([](auto p) { std::destroy_at(p); });
+}
+
+#if QT_CONFIG(systemsemaphore)
+inline QNativeIpcKey QSharedMemoryPrivate::semaphoreNativeKey() const
+{
+ if (isIpcSupported(IpcType::SharedMemory, QNativeIpcKey::Type::Windows)
+ && nativeKey.type() == QNativeIpcKey::Type::Windows) {
+ // native keys are plain kernel object names, limited to MAX_PATH
+ auto suffix = "_sem"_L1;
+ QString semkey = nativeKey.nativeKey();
+ semkey.truncate(MAX_PATH - suffix.size() - 1);
+ semkey += suffix;
+ return { semkey, QNativeIpcKey::Type::Windows };
+ }
+
+ // System V and POSIX keys appear to operate in different namespaces, so we
+ // can just use the same native key
+ return nativeKey;
+}
+#endif
+
+/*!
+ \class QSharedMemory
+ \inmodule QtCore
+ \since 4.4
+
+ \brief The QSharedMemory class provides access to a shared memory segment.
+
+ QSharedMemory provides access to a \l{Shared Memory}{shared memory segment}
+ by multiple threads and processes. Shared memory segments are identified by a
+ key, represented by \l QNativeIpcKey. A key can be created in a
+ cross-platform manner by using platformSafeKey().
+
+ One QSharedMemory object must create() the segment and this call specifies
+ the size of the segment. All other processes simply attach() to the segment
+ that must already exist. After either operation is successful, the
+ application may call data() to obtain a pointer to the data.
+
+ 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.
+
+ By default, QSharedMemory automatically destroys the shared memory segment
+ when the last instance of QSharedMemory is \l{detach()}{detached} from the
+ segment, and no references to the segment remain.
+
+ For details on the key types, platform-specific limitations, and
+ interoperability with older or non-Qt applications, see the \l{Native IPC
+ Keys} documentation. That includes important information for sandboxed
+ applications on Apple platforms, including all apps obtained via the Apple
+ App Store.
+
+ \sa {Inter-Process Communication}, QSystemSemaphore
+ */
+
+/*!
+ \overload QSharedMemory()
+
+ Constructs a shared memory object with the given \a parent. The shared memory
+ object's key is not set by the constructor, so the shared memory object does
+ not have an underlying shared memory segment attached. The key must be set
+ with setNativeKey() before create() or attach() can be used.
+
+ \sa setNativeKey()
+ */
+
+QSharedMemory::QSharedMemory(QObject *parent)
+ : QSharedMemory(QNativeIpcKey(), parent)
+{
+}
+
+/*!
+ \overload
+
+ Constructs a shared memory object with the given \a parent and with
+ its key set to \a key. Because its key is set, its create() and
+ attach() functions can be called.
+
+ \sa setNativeKey(), create(), attach()
+ */
+QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
+ : QObject(*new QSharedMemoryPrivate(key.type()), parent)
+{
+ setNativeKey(key);
+}
+
+/*!
+ 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.
+
+ \sa setKey(), create(), attach()
+ */
+QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
+ : QSharedMemory(legacyNativeKey(key), parent)
+{
+}
+
+/*!
+ The destructor clears the key, which forces the shared memory object
+ to \l {detach()} {detach} from its underlying shared memory
+ segment. If this shared memory object is the last one connected to
+ the shared memory segment, the detach() operation destroys the
+ shared memory segment.
+
+ \sa detach(), isAttached()
+ */
+QSharedMemory::~QSharedMemory()
+{
+ Q_D(QSharedMemory);
+ if (isAttached())
+ detach();
+ d->cleanHandle();
+}
+
+/*!
+ \overload
+
+ Sets the legacy \a key for this shared memory object. If \a key is the same
+ as the current key, the function returns without doing anything. Otherwise,
+ if the shared memory object is attached to an underlying shared memory
+ segment, it will \l {detach()} {detach} from it before setting the new key.
+ This function does not do an attach().
+
+ You can call key() to retrieve the legacy key. This function is mostly the
+ same as:
+
+ \code
+ shm.setNativeKey(QSharedMemory::legacyNativeKey(key));
+ \endcode
+
+ except that it enables obtaining the legacy key using key().
+
+ \sa key(), nativeKey(), isAttached()
+*/
+void QSharedMemory::setKey(const QString &key)
+{
+ setNativeKey(legacyNativeKey(key));
+}
+
+/*!
+ \since 4.8
+ \fn void QSharedMemory::setNativeKey(const QString &key, QNativeIpcKey::Type type)
+
+ Sets the native, platform specific, \a key for this shared memory object of
+ type \a type (the type parameter has been available since Qt 6.6). If \a key
+ is the same as the current native key, the function returns without doing
+ anything. Otherwise, if the shared memory object is attached to an underlying
+ shared memory segment, it will \l {detach()} {detach} from it before setting
+ the new key. This function does not do an attach().
+
+ This function is useful if the native key was shared from another process,
+ though the application must take care to ensure the key type matches what the
+ other process expects. See \l{Native IPC Keys} for more information.
+
+ Portable native keys can be obtained using platformSafeKey().
+
+ You can call nativeKey() to retrieve the native key.
+
+ \sa nativeKey(), nativeIpcKey(), isAttached()
+*/
+
+/*!
+ \since 6.6
+
+ Sets the native, platform specific, \a key for this shared memory object. If
+ \a key is the same as the current native key, the function returns without
+ doing anything. 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.
+ 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(), nativeIpcKey(), isAttached()
+*/
+void QSharedMemory::setNativeKey(const QNativeIpcKey &key)
+{
+ Q_D(QSharedMemory);
+ if (key == d->nativeKey && key.isEmpty())
+ return;
+ if (!isKeyTypeSupported(key.type())) {
+ d->setError(KeyError, tr("%1: unsupported key type")
+ .arg("QSharedMemory::setNativeKey"_L1));
+ return;
+ }
+
+ if (isAttached())
+ detach();
+ d->cleanHandle();
+ if (key.type() == d->nativeKey.type()) {
+ // we can reuse the backend
+ d->nativeKey = key;
+ } else {
+ // we must recreate the backend
+ d->destructBackend();
+ d->nativeKey = key;
+ d->constructBackend();
+ }
+}
+
+bool QSharedMemoryPrivate::initKey(SemaphoreAccessMode mode)
+{
+ if (!cleanHandle())
+ return false;
+#if QT_CONFIG(systemsemaphore)
+ const QString legacyKey = QNativeIpcKeyPrivate::legacyKey(nativeKey);
+ const QNativeIpcKey semKey = legacyKey.isEmpty()
+ ? semaphoreNativeKey()
+ : QSystemSemaphore::legacyNativeKey(legacyKey, nativeKey.type());
+ systemSemaphore.setNativeKey(semKey, 1, mode);
+ if (systemSemaphore.error() != QSystemSemaphore::NoError) {
+ QString function = "QSharedMemoryPrivate::initKey"_L1;
+ errorString = QSharedMemory::tr("%1: unable to set key on lock (%2)")
+ .arg(function, systemSemaphore.errorString());
+ switch(systemSemaphore.error()) {
+ case QSystemSemaphore::PermissionDenied:
+ error = QSharedMemory::PermissionDenied;
+ break;
+ case QSystemSemaphore::KeyError:
+ error = QSharedMemory::KeyError;
+ break;
+ case QSystemSemaphore::AlreadyExists:
+ error = QSharedMemory::AlreadyExists;
+ break;
+ case QSystemSemaphore::NotFound:
+ error = QSharedMemory::NotFound;
+ break;
+ case QSystemSemaphore::OutOfResources:
+ error = QSharedMemory::OutOfResources;
+ break;
+ case QSystemSemaphore::UnknownError:
+ default:
+ error = QSharedMemory::UnknownError;
+ break;
+ }
+ return false;
+ }
+#else
+ Q_UNUSED(mode);
+#endif
+ errorString = QString();
+ error = QSharedMemory::NoError;
+ return true;
+}
+
+/*!
+ Returns the legacy key assigned with setKey() to this shared memory, or a null key
+ if no key has been assigned, or if the segment is using a nativeKey(). The
+ key is the identifier used by Qt applications to identify the shared memory
+ segment.
+
+ You can find the native, platform specific, key used by the operating system
+ by calling nativeKey().
+
+ \sa setKey(), setNativeKey()
+ */
+QString QSharedMemory::key() const
+{
+ Q_D(const QSharedMemory);
+ return QNativeIpcKeyPrivate::legacyKey(d->nativeKey);
+}
+
+/*!
+ \since 4.8
+
+ Returns the native, platform specific, key for this shared memory object. The
+ native key is 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 setNativeKey(), nativeIpcKey()
+*/
+QString QSharedMemory::nativeKey() const
+{
+ Q_D(const QSharedMemory);
+ return d->nativeKey.nativeKey();
+}
+
+/*!
+ \since 6.6
+
+ Returns the key type for this shared memory object. The key type complements
+ the nativeKey() as the identifier used by the operating system to identify
+ the shared memory segment.
+
+ You can use the native key to access shared memory segments that have not
+ been created by Qt, or to grant shared memory access to non-Qt applications.
+ See \l{Native IPC Keys} for more information.
+
+ \sa nativeKey(), setNativeKey()
+*/
+QNativeIpcKey QSharedMemory::nativeIpcKey() const
+{
+ Q_D(const QSharedMemory);
+ return d->nativeKey;
+}
+
+/*!
+ Creates a shared memory segment of \a size bytes with the key passed to the
+ constructor or set with setNativeKey(), then attaches to
+ the new shared memory segment with the given access \a mode and returns
+ \tt true. If a shared memory segment identified by the key already exists,
+ the attach operation is not performed and \tt false is returned. When the
+ return value is \tt false, call error() to determine which error occurred.
+
+ \sa error()
+ */
+bool QSharedMemory::create(qsizetype size, AccessMode mode)
+{
+ Q_D(QSharedMemory);
+ QLatin1StringView function = "QSharedMemory::create"_L1;
+
+#if QT_CONFIG(systemsemaphore)
+ if (!d->initKey(QSystemSemaphore::Create))
+ return false;
+ QSharedMemoryLocker lock(this);
+ if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, function))
+ return false;
+#else
+ if (!d->initKey({}))
+ return false;
+#endif
+
+ if (size <= 0) {
+ d->error = QSharedMemory::InvalidSize;
+ d->errorString =
+ QSharedMemory::tr("%1: create size is less then 0").arg(function);
+ return false;
+ }
+
+ if (!d->create(size))
+ return false;
+
+ return d->attach(mode);
+}
+
+/*!
+ Returns the size of the attached shared memory segment. If no shared
+ memory segment is attached, 0 is returned.
+
+ \note The size of the segment may be larger than the requested size that was
+ passed to create().
+
+ \sa create(), attach()
+ */
+qsizetype QSharedMemory::size() const
+{
+ Q_D(const QSharedMemory);
+ return d->size;
+}
+
+/*!
+ \enum QSharedMemory::AccessMode
+
+ \value ReadOnly The shared memory segment is read-only. Writing to
+ the shared memory segment is not allowed. An attempt to write to a
+ shared memory segment created with ReadOnly causes the program to
+ abort.
+
+ \value ReadWrite Reading and writing the shared memory segment are
+ both allowed.
+*/
+
+/*!
+ Attempts to attach the process to the shared memory segment
+ identified by the key that was passed to the constructor or to a
+ call to setNativeKey(). The access \a mode is \l {QSharedMemory::}
+ {ReadWrite} by default. It can also be \l {QSharedMemory::}
+ {ReadOnly}. Returns \c true if the attach operation is successful. If
+ false is returned, call error() to determine which error occurred.
+ After attaching the shared memory segment, a pointer to the shared
+ memory can be obtained by calling data().
+
+ \sa isAttached(), detach(), create()
+ */
+bool QSharedMemory::attach(AccessMode mode)
+{
+ Q_D(QSharedMemory);
+
+ if (isAttached() || !d->initKey({}))
+ return false;
+#if QT_CONFIG(systemsemaphore)
+ QSharedMemoryLocker lock(this);
+ if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, "QSharedMemory::attach"_L1))
+ return false;
+#endif
+
+ if (isAttached() || !d->handle())
+ return false;
+
+ return d->attach(mode);
+}
+
+/*!
+ Returns \c true if this process is attached to the shared memory
+ segment.
+
+ \sa attach(), detach()
+ */
+bool QSharedMemory::isAttached() const
+{
+ Q_D(const QSharedMemory);
+ return (nullptr != d->memory);
+}
+
+/*!
+ Detaches the process from the shared memory segment. If this was the
+ last process attached to the shared memory segment, then the shared
+ memory segment is released by the system, i.e., the contents are
+ destroyed. The function returns \c true if it detaches the shared
+ memory segment. If it returns \c false, it usually means the segment
+ either isn't attached, or it is locked by another process.
+
+ \sa attach(), isAttached()
+ */
+bool QSharedMemory::detach()
+{
+ Q_D(QSharedMemory);
+ if (!isAttached())
+ return false;
+
+#if QT_CONFIG(systemsemaphore)
+ QSharedMemoryLocker lock(this);
+ if (!d->nativeKey.isEmpty() && !d->tryLocker(&lock, "QSharedMemory::detach"_L1))
+ return false;
+#endif
+
+ return d->detach();
+}
+
+/*!
+ Returns a pointer to the contents of the shared memory segment, if one is
+ attached. Otherwise it returns null. The value returned by this function will
+ not change until a \l {detach()}{detach} happens, so it is safe to store this
+ pointer.
+
+ If the memory operations are not atomic, you may lock the shared memory with
+ lock() before reading from or writing, but remember to release the lock with
+ unlock() after you are done.
+
+ \sa attach()
+ */
+void *QSharedMemory::data()
+{
+ Q_D(QSharedMemory);
+ return d->memory;
+}
+
+/*!
+ Returns a const pointer to the contents of the shared memory segment, if one
+ is attached. Otherwise it returns null. The value returned by this function
+ will not change until a \l {detach()}{detach} happens, so it is safe to store
+ this pointer.
+
+ If the memory operations are not atomic, you may lock the shared memory with
+ lock() before reading from or writing, but remember to release the lock with
+ unlock() after you are done.
+
+ \sa attach(), create()
+ */
+const void *QSharedMemory::constData() const
+{
+ Q_D(const QSharedMemory);
+ return d->memory;
+}
+
+/*!
+ \overload data()
+ */
+const void *QSharedMemory::data() const
+{
+ Q_D(const QSharedMemory);
+ return d->memory;
+}
+
+#if QT_CONFIG(systemsemaphore)
+/*!
+ This is a semaphore that locks the shared memory segment for access
+ by this process and returns \c true. If another process has locked the
+ segment, this function blocks until the lock is released. Then it
+ acquires the lock and returns \c true. If this function returns \c false,
+ it means that you have ignored a false return from create() or attach(),
+ that you have set the key with setNativeKey() or that
+ QSystemSemaphore::acquire() failed due to an unknown system error.
+
+ \sa unlock(), data(), QSystemSemaphore::acquire()
+ */
+bool QSharedMemory::lock()
+{
+ Q_D(QSharedMemory);
+ if (d->lockedByMe) {
+ qWarning("QSharedMemory::lock: already locked");
+ return true;
+ }
+ if (d->systemSemaphore.acquire()) {
+ d->lockedByMe = true;
+ return true;
+ }
+ const auto function = "QSharedMemory::lock"_L1;
+ d->errorString = QSharedMemory::tr("%1: unable to lock").arg(function);
+ d->error = QSharedMemory::LockError;
+ return false;
+}
+
+/*!
+ Releases the lock on the shared memory segment and returns \c true, if
+ the lock is currently held by this process. If the segment is not
+ locked, or if the lock is held by another process, nothing happens
+ and false is returned.
+
+ \sa lock()
+ */
+bool QSharedMemory::unlock()
+{
+ Q_D(QSharedMemory);
+ if (!d->lockedByMe)
+ return false;
+ d->lockedByMe = false;
+ if (d->systemSemaphore.release())
+ return true;
+ const auto function = "QSharedMemory::unlock"_L1;
+ d->errorString = QSharedMemory::tr("%1: unable to unlock").arg(function);
+ d->error = QSharedMemory::LockError;
+ return false;
+}
+#endif // QT_CONFIG(systemsemaphore)
+
+/*!
+ \enum QSharedMemory::SharedMemoryError
+
+ \value NoError No error occurred.
+
+ \value PermissionDenied The operation failed because the caller
+ didn't have the required permissions.
+
+ \value InvalidSize A create operation failed because the requested
+ size was invalid.
+
+ \value KeyError The operation failed because of an invalid key.
+
+ \value AlreadyExists A create() operation failed because a shared
+ memory segment with the specified key already existed.
+
+ \value NotFound An attach() failed because a shared memory segment
+ with the specified key could not be found.
+
+ \value LockError The attempt to lock() the shared memory segment
+ failed because create() or attach() failed and returned false, or
+ because a system error occurred in QSystemSemaphore::acquire().
+
+ \value OutOfResources A create() operation failed because there was
+ not enough memory available to fill the request.
+
+ \value UnknownError Something else happened and it was bad.
+*/
+
+/*!
+ Returns a value indicating whether an error occurred, and, if so,
+ which error it was.
+
+ \sa errorString()
+ */
+QSharedMemory::SharedMemoryError QSharedMemory::error() const
+{
+ Q_D(const QSharedMemory);
+ return d->error;
+}
+
+/*!
+ Returns a text description of the last error that occurred. If
+ error() returns an \l {QSharedMemory::SharedMemoryError} {error
+ value}, call this function to get a text string that describes the
+ error.
+
+ \sa error()
+ */
+QString QSharedMemory::errorString() const
+{
+ Q_D(const QSharedMemory);
+ return d->errorString;
+}
+
+void QSharedMemoryPrivate::setUnixErrorString(QLatin1StringView function)
+{
+ // EINVAL is handled in functions so they can give better error strings
+ switch (errno) {
+ case EACCES:
+ errorString = QSharedMemory::tr("%1: permission denied").arg(function);
+ error = QSharedMemory::PermissionDenied;
+ break;
+ case EEXIST:
+ errorString = QSharedMemory::tr("%1: already exists").arg(function);
+ error = QSharedMemory::AlreadyExists;
+ break;
+ case ENOENT:
+ errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
+ error = QSharedMemory::NotFound;
+ break;
+ case EMFILE:
+ case ENOMEM:
+ case ENOSPC:
+ errorString = QSharedMemory::tr("%1: out of resources").arg(function);
+ error = QSharedMemory::OutOfResources;
+ break;
+ default:
+ errorString = QSharedMemory::tr("%1: unknown error: %2")
+ .arg(function, qt_error_string(errno));
+ error = QSharedMemory::UnknownError;
+#if defined QSHAREDMEMORY_DEBUG
+ qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
+#endif
+ }
+}
+
+bool QSharedMemory::isKeyTypeSupported(QNativeIpcKey::Type type)
+{
+ if (!isIpcSupported(IpcType::SharedMemory, type))
+ return false;
+ using Variant = decltype(QSharedMemoryPrivate::backend);
+ return Variant::staticVisit(type, [](auto ptr) {
+ using Impl = std::decay_t<decltype(*ptr)>;
+ return Impl::runtimeSupportCheck();
+ });
+}
+
+QNativeIpcKey QSharedMemory::platformSafeKey(const QString &key, QNativeIpcKey::Type type)
+{
+ return QtIpcCommon::platformSafeKey(key, IpcType::SharedMemory, type);
+}
+
+QNativeIpcKey QSharedMemory::legacyNativeKey(const QString &key, QNativeIpcKey::Type type)
+{
+ return QtIpcCommon::legacyPlatformSafeKey(key, IpcType::SharedMemory, type);
+}
+
+#endif // QT_CONFIG(sharedmemory)
+
+QT_END_NAMESPACE
+
+#include "moc_qsharedmemory.cpp"
diff --git a/src/corelib/ipc/qsharedmemory.h b/src/corelib/ipc/qsharedmemory.h
new file mode 100644
index 0000000000..ab448b15c1
--- /dev/null
+++ b/src/corelib/ipc/qsharedmemory.h
@@ -0,0 +1,99 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSHAREDMEMORY_H
+#define QSHAREDMEMORY_H
+
+#include <QtCore/qtipccommon.h>
+#ifndef QT_NO_QOBJECT
+# include <QtCore/qobject.h>
+#else
+# include <QtCore/qobjectdefs.h>
+# include <QtCore/qscopedpointer.h>
+# include <QtCore/qstring.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(sharedmemory)
+
+class QSharedMemoryPrivate;
+
+class Q_CORE_EXPORT QSharedMemory : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSharedMemory)
+
+public:
+ enum AccessMode
+ {
+ ReadOnly,
+ ReadWrite
+ };
+ Q_ENUM(AccessMode)
+
+ enum SharedMemoryError
+ {
+ NoError,
+ PermissionDenied,
+ InvalidSize,
+ KeyError,
+ AlreadyExists,
+ NotFound,
+ LockError,
+ OutOfResources,
+ UnknownError
+ };
+ Q_ENUM(SharedMemoryError)
+
+ QSharedMemory(QObject *parent = nullptr);
+ QSharedMemory(const QNativeIpcKey &key, QObject *parent = nullptr);
+ ~QSharedMemory();
+
+ QSharedMemory(const QString &key, QObject *parent = nullptr);
+ void setKey(const QString &key);
+ QString key() const;
+
+ void setNativeKey(const QNativeIpcKey &key);
+ void setNativeKey(const QString &key, QNativeIpcKey::Type type = QNativeIpcKey::legacyDefaultTypeForOs())
+ { setNativeKey({ key, type }); }
+ QString nativeKey() const;
+ QNativeIpcKey nativeIpcKey() const;
+#if QT_CORE_REMOVED_SINCE(6, 5)
+ void setNativeKey(const QString &key);
+#endif
+
+ bool create(qsizetype size, AccessMode mode = ReadWrite);
+ qsizetype size() const;
+
+ bool attach(AccessMode mode = ReadWrite);
+ bool isAttached() const;
+ bool detach();
+
+ void *data();
+ const void* constData() const;
+ const void *data() const;
+
+#if QT_CONFIG(systemsemaphore)
+ bool lock();
+ bool unlock();
+#endif
+
+ SharedMemoryError error() const;
+ QString errorString() const;
+
+ static bool isKeyTypeSupported(QNativeIpcKey::Type type) Q_DECL_CONST_FUNCTION;
+ static QNativeIpcKey platformSafeKey(const QString &key,
+ QNativeIpcKey::Type type = QNativeIpcKey::DefaultTypeForOs);
+ static QNativeIpcKey legacyNativeKey(const QString &key,
+ QNativeIpcKey::Type type = QNativeIpcKey::legacyDefaultTypeForOs());
+
+private:
+ Q_DISABLE_COPY(QSharedMemory)
+};
+
+#endif // QT_CONFIG(sharedmemory)
+
+QT_END_NAMESPACE
+
+#endif // QSHAREDMEMORY_H
diff --git a/src/corelib/ipc/qsharedmemory_p.h b/src/corelib/ipc/qsharedmemory_p.h
new file mode 100644
index 0000000000..987bb38642
--- /dev/null
+++ b/src/corelib/ipc/qsharedmemory_p.h
@@ -0,0 +1,215 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSHAREDMEMORY_P_H
+#define QSHAREDMEMORY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsharedmemory.h"
+
+#include <QtCore/qstring.h>
+
+#if QT_CONFIG(sharedmemory)
+#include "qsystemsemaphore.h"
+#include "qtipccommon_p.h"
+#include "private/qobject_p.h"
+
+#if QT_CONFIG(posix_shm)
+# include <sys/mman.h>
+#endif
+#if QT_CONFIG(sysv_shm)
+# include <sys/shm.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QSharedMemoryPrivate;
+
+#if QT_CONFIG(systemsemaphore)
+/*!
+ Helper class
+ */
+class QSharedMemoryLocker
+{
+
+public:
+ Q_NODISCARD_CTOR QSharedMemoryLocker(QSharedMemory *sharedMemory) : q_sm(sharedMemory)
+ {
+ Q_ASSERT(q_sm);
+ }
+
+ inline ~QSharedMemoryLocker()
+ {
+ if (q_sm)
+ q_sm->unlock();
+ }
+
+ inline bool lock()
+ {
+ if (q_sm && q_sm->lock())
+ return true;
+ q_sm = nullptr;
+ return false;
+ }
+
+private:
+ QSharedMemory *q_sm;
+};
+#endif // QT_CONFIG(systemsemaphore)
+
+class QSharedMemoryPosix
+{
+public:
+ static constexpr bool Enabled = QT_CONFIG(posix_shm);
+ static bool supports(QNativeIpcKey::Type type)
+ { return type == QNativeIpcKey::Type::PosixRealtime; }
+ static bool runtimeSupportCheck();
+
+ bool handle(QSharedMemoryPrivate *self);
+ bool cleanHandle(QSharedMemoryPrivate *self);
+ bool create(QSharedMemoryPrivate *self, qsizetype size);
+ bool attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode);
+ bool detach(QSharedMemoryPrivate *self);
+
+ int hand = -1;
+};
+
+class QSharedMemorySystemV
+{
+public:
+ static constexpr bool Enabled = QT_CONFIG(sysv_shm);
+ static bool supports(QNativeIpcKey::Type type)
+ { return quint16(type) <= 0xff; }
+ static bool runtimeSupportCheck();
+
+#if QT_CONFIG(sysv_sem)
+ key_t handle(QSharedMemoryPrivate *self);
+ bool cleanHandle(QSharedMemoryPrivate *self);
+ bool create(QSharedMemoryPrivate *self, qsizetype size);
+ bool attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode);
+ bool detach(QSharedMemoryPrivate *self);
+
+private:
+ void updateNativeKeyFile(const QNativeIpcKey &nativeKey);
+
+ QByteArray nativeKeyFile;
+ key_t unix_key = 0;
+#endif
+};
+
+class QSharedMemoryWin32
+{
+public:
+#ifdef Q_OS_WIN32
+ static constexpr bool Enabled = true;
+#else
+ static constexpr bool Enabled = false;
+#endif
+ static bool runtimeSupportCheck() { return Enabled; }
+ static bool supports(QNativeIpcKey::Type type)
+ { return type == QNativeIpcKey::Type::Windows; }
+
+ Qt::HANDLE handle(QSharedMemoryPrivate *self);
+ bool cleanHandle(QSharedMemoryPrivate *self);
+ bool create(QSharedMemoryPrivate *self, qsizetype size);
+ bool attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode);
+ bool detach(QSharedMemoryPrivate *self);
+
+ Qt::HANDLE hand = nullptr;
+};
+
+class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSharedMemory)
+
+public:
+ QSharedMemoryPrivate(QNativeIpcKey::Type type) : nativeKey(type)
+ { constructBackend(); }
+ ~QSharedMemoryPrivate();
+
+ void *memory = nullptr;
+ qsizetype size = 0;
+ QNativeIpcKey nativeKey;
+ QString errorString;
+#if QT_CONFIG(systemsemaphore)
+ using SemaphoreAccessMode = QSystemSemaphore::AccessMode;
+ QSystemSemaphore systemSemaphore{ QNativeIpcKey() };
+ bool lockedByMe = false;
+#else
+ enum SemaphoreAccessMode {};
+#endif
+ QSharedMemory::SharedMemoryError error = QSharedMemory::NoError;
+
+ union Backend {
+ Backend() {}
+ ~Backend() {}
+ QSharedMemoryPosix posix;
+ QSharedMemorySystemV sysv;
+ QSharedMemoryWin32 win32;
+ };
+ QtIpcCommon::IpcStorageVariant<&Backend::posix, &Backend::sysv, &Backend::win32> backend;
+
+ void constructBackend();
+ void destructBackend();
+ bool initKey(SemaphoreAccessMode mode);
+
+ template <typename Lambda> auto visit(const Lambda &lambda)
+ {
+ return backend.visit(nativeKey.type(), lambda);
+ }
+
+ bool handle()
+ {
+ return visit([&](auto p) { return !!p->handle(this); });
+ }
+ bool cleanHandle()
+ {
+ return visit([&](auto p) { return p->cleanHandle(this); });
+ }
+ bool create(qsizetype size)
+ {
+ return visit([&](auto p) { return p->create(this, size); });
+ }
+ bool attach(QSharedMemory::AccessMode mode)
+ {
+ return visit([&](auto p) { return p->attach(this, mode); });
+ }
+ bool detach()
+ {
+ return visit([&](auto p) { return p->detach(this); });
+ }
+
+ inline void setError(QSharedMemory::SharedMemoryError e, const QString &message)
+ { error = e; errorString = message; }
+ void setUnixErrorString(QLatin1StringView function);
+ void setWindowsErrorString(QLatin1StringView function);
+
+#if QT_CONFIG(systemsemaphore)
+ bool tryLocker(QSharedMemoryLocker *locker, const QString &function) {
+ if (!locker->lock()) {
+ errorString = QSharedMemory::tr("%1: unable to lock").arg(function);
+ error = QSharedMemory::LockError;
+ return false;
+ }
+ return true;
+ }
+ QNativeIpcKey semaphoreNativeKey() const;
+#endif // QT_CONFIG(systemsemaphore)
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(sharedmemory)
+
+#endif // QSHAREDMEMORY_P_H
+
diff --git a/src/corelib/ipc/qsharedmemory_posix.cpp b/src/corelib/ipc/qsharedmemory_posix.cpp
new file mode 100644
index 0000000000..582c6628e1
--- /dev/null
+++ b/src/corelib/ipc/qsharedmemory_posix.cpp
@@ -0,0 +1,196 @@
+// Copyright (C) 2015 Konstantin Ritt <ritt.ks@gmail.com>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsharedmemory.h"
+#include "qsharedmemory_p.h"
+#include "qtipccommon_p.h"
+#include <qfile.h>
+
+#include <errno.h>
+
+#if QT_CONFIG(sharedmemory)
+#if QT_CONFIG(posix_shm)
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "private/qcore_unix_p.h"
+
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+using namespace QtIpcCommon;
+
+bool QSharedMemoryPosix::runtimeSupportCheck()
+{
+ static const bool result = []() {
+ (void)shm_open("", 0, 0); // this WILL fail
+ return errno != ENOSYS;
+ }();
+ return result;
+}
+
+bool QSharedMemoryPosix::handle(QSharedMemoryPrivate *self)
+{
+ // don't allow making handles on empty keys
+ if (self->nativeKey.isEmpty()) {
+ self->setError(QSharedMemory::KeyError,
+ QSharedMemory::tr("%1: key is empty").arg("QSharedMemory::handle"_L1));
+ return false;
+ }
+
+ return true;
+}
+
+bool QSharedMemoryPosix::cleanHandle(QSharedMemoryPrivate *)
+{
+ if (hand != -1)
+ qt_safe_close(hand);
+ hand = -1;
+
+ return true;
+}
+
+bool QSharedMemoryPosix::create(QSharedMemoryPrivate *self, qsizetype size)
+{
+ if (!handle(self))
+ return false;
+
+ const QByteArray shmName = QFile::encodeName(self->nativeKey.nativeKey());
+
+ int fd;
+ QT_EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600));
+ if (fd == -1) {
+ const int errorNumber = errno;
+ const auto function = "QSharedMemory::attach (shm_open)"_L1;
+ switch (errorNumber) {
+ case EINVAL:
+ self->setError(QSharedMemory::KeyError,
+ QSharedMemory::tr("%1: bad name").arg(function));
+ break;
+ default:
+ self->setUnixErrorString(function);
+ }
+ return false;
+ }
+
+ // the size may only be set once
+ int ret;
+ QT_EINTR_LOOP(ret, QT_FTRUNCATE(fd, size));
+ if (ret == -1) {
+ self->setUnixErrorString("QSharedMemory::create (ftruncate)"_L1);
+ qt_safe_close(fd);
+ return false;
+ }
+
+ qt_safe_close(fd);
+
+ return true;
+}
+
+bool QSharedMemoryPosix::attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode)
+{
+ const QByteArray shmName = QFile::encodeName(self->nativeKey.nativeKey());
+
+ const int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR);
+ const mode_t omode = (mode == QSharedMemory::ReadOnly ? 0400 : 0600);
+
+ QT_EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag | O_CLOEXEC, omode));
+ if (hand == -1) {
+ const int errorNumber = errno;
+ const auto function = "QSharedMemory::attach (shm_open)"_L1;
+ switch (errorNumber) {
+ case EINVAL:
+ self->setError(QSharedMemory::KeyError,
+ QSharedMemory::tr("%1: bad name").arg(function));
+ break;
+ default:
+ self->setUnixErrorString(function);
+ }
+ hand = -1;
+ return false;
+ }
+
+ // grab the size
+ QT_STATBUF st;
+ if (QT_FSTAT(hand, &st) == -1) {
+ self->setUnixErrorString("QSharedMemory::attach (fstat)"_L1);
+ cleanHandle(self);
+ return false;
+ }
+ self->size = qsizetype(st.st_size);
+
+ // grab the memory
+ const int mprot = (mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE);
+ self->memory = QT_MMAP(0, size_t(self->size), mprot, MAP_SHARED, hand, 0);
+ if (self->memory == MAP_FAILED || !self->memory) {
+ self->setUnixErrorString("QSharedMemory::attach (mmap)"_L1);
+ cleanHandle(self);
+ self->memory = 0;
+ self->size = 0;
+ return false;
+ }
+
+#ifdef F_ADD_SEALS
+ // Make sure the shared memory region will not shrink
+ // otherwise someone could cause SIGBUS on us.
+ // (see http://lwn.net/Articles/594919/)
+ fcntl(hand, F_ADD_SEALS, F_SEAL_SHRINK);
+#endif
+
+ return true;
+}
+
+bool QSharedMemoryPosix::detach(QSharedMemoryPrivate *self)
+{
+ // detach from the memory segment
+ if (::munmap(self->memory, size_t(self->size)) == -1) {
+ self->setUnixErrorString("QSharedMemory::detach (munmap)"_L1);
+ return false;
+ }
+ self->memory = 0;
+ self->size = 0;
+
+#ifdef Q_OS_QNX
+ // On QNX the st_nlink field of struct stat contains the number of
+ // active shm_open() connections to the shared memory file, so we
+ // can use it to automatically clean up the file once the last
+ // user has detached from it.
+
+ // get the number of current attachments
+ int shm_nattch = 0;
+ QT_STATBUF st;
+ if (QT_FSTAT(hand, &st) == 0) {
+ // subtract 2 from linkcount: one for our own open and one for the dir entry
+ shm_nattch = st.st_nlink - 2;
+ }
+
+ cleanHandle(self);
+
+ // if there are no attachments then unlink the shared memory
+ if (shm_nattch == 0) {
+ const QByteArray shmName = QFile::encodeName(self->nativeKey.nativeKey());
+ if (::shm_unlink(shmName.constData()) == -1 && errno != ENOENT)
+ self->setUnixErrorString("QSharedMemory::detach (shm_unlink)"_L1);
+ }
+#else
+ // On non-QNX systems (tested Linux and Haiku), the st_nlink field is always 1,
+ // so we'll simply leak the shared memory files.
+ cleanHandle(self);
+#endif
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(posix_shm)
+#endif // QT_CONFIG(sharedmemory)
diff --git a/src/corelib/ipc/qsharedmemory_systemv.cpp b/src/corelib/ipc/qsharedmemory_systemv.cpp
new file mode 100644
index 0000000000..dc9de11091
--- /dev/null
+++ b/src/corelib/ipc/qsharedmemory_systemv.cpp
@@ -0,0 +1,212 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsharedmemory.h"
+#include "qsharedmemory_p.h"
+
+#include "qtipccommon_p.h"
+
+#include <qdir.h>
+#include <qdebug.h>
+
+#include <errno.h>
+
+#if QT_CONFIG(sharedmemory)
+#if QT_CONFIG(sysv_shm)
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "private/qcore_unix_p.h"
+#if defined(Q_OS_DARWIN)
+#include "private/qcore_mac_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+using namespace QtIpcCommon;
+
+bool QSharedMemorySystemV::runtimeSupportCheck()
+{
+#if defined(Q_OS_DARWIN)
+ if (qt_apple_isSandboxed())
+ return false;
+#endif
+ static const bool result = []() {
+ (void)shmget(IPC_PRIVATE, ~size_t(0), 0); // this will fail
+ return errno != ENOSYS;
+ }();
+ return result;
+}
+
+
+inline void QSharedMemorySystemV::updateNativeKeyFile(const QNativeIpcKey &nativeKey)
+{
+ Q_ASSERT(nativeKeyFile.isEmpty() );
+ if (!nativeKey.nativeKey().isEmpty())
+ nativeKeyFile = QFile::encodeName(nativeKey.nativeKey());
+}
+
+/*!
+ \internal
+
+ If not already made create the handle used for accessing the shared memory.
+*/
+key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self)
+{
+ // already made
+ if (unix_key)
+ return unix_key;
+
+ // don't allow making handles on empty keys
+ if (nativeKeyFile.isEmpty())
+ updateNativeKeyFile(self->nativeKey);
+ if (nativeKeyFile.isEmpty()) {
+ self->setError(QSharedMemory::KeyError,
+ QSharedMemory::tr("%1: key is empty")
+ .arg("QSharedMemory::handle:"_L1));
+ return 0;
+ }
+
+ unix_key = ftok(nativeKeyFile, int(self->nativeKey.type()));
+ if (unix_key < 0) {
+ self->setUnixErrorString("QSharedMemory::handle"_L1);
+ nativeKeyFile.clear();
+ unix_key = 0;
+ }
+ return unix_key;
+}
+
+bool QSharedMemorySystemV::cleanHandle(QSharedMemoryPrivate *self)
+{
+ if (unix_key == 0)
+ return true;
+
+ // Get the number of current attachments
+ struct shmid_ds shmid_ds;
+ QByteArray keyfile = std::exchange(nativeKeyFile, QByteArray());
+
+ int id = shmget(unix_key, 0, 0400);
+ unix_key = 0;
+ if (shmctl(id, IPC_STAT, &shmid_ds))
+ return errno != EINVAL;
+
+ // If there are still attachments, keep the keep file and shm
+ if (shmid_ds.shm_nattch != 0)
+ return true;
+
+ if (shmctl(id, IPC_RMID, &shmid_ds) < 0) {
+ if (errno != EINVAL) {
+ self->setUnixErrorString("QSharedMemory::remove"_L1);
+ return false;
+ }
+ };
+
+ // remove file
+ return unlink(keyfile) == 0;
+}
+
+bool QSharedMemorySystemV::create(QSharedMemoryPrivate *self, qsizetype size)
+{
+ // build file if needed
+ bool createdFile = false;
+ updateNativeKeyFile(self->nativeKey);
+ int built = createUnixKeyFile(nativeKeyFile);
+ if (built == -1) {
+ self->setError(QSharedMemory::KeyError,
+ QSharedMemory::tr("%1: unable to make key")
+ .arg("QSharedMemory::handle:"_L1));
+ return false;
+ }
+ if (built == 1) {
+ createdFile = true;
+ }
+
+ // get handle
+ if (!handle(self)) {
+ if (createdFile)
+ unlink(nativeKeyFile);
+ return false;
+ }
+
+ // create
+ if (-1 == shmget(unix_key, size_t(size), 0600 | IPC_CREAT | IPC_EXCL)) {
+ const auto function = "QSharedMemory::create"_L1;
+ switch (errno) {
+ case EINVAL:
+ self->setError(QSharedMemory::InvalidSize,
+ QSharedMemory::tr("%1: system-imposed size restrictions")
+ .arg("QSharedMemory::handle"_L1));
+ break;
+ default:
+ self->setUnixErrorString(function);
+ }
+ if (createdFile && self->error != QSharedMemory::AlreadyExists)
+ unlink(nativeKeyFile);
+ return false;
+ }
+
+ return true;
+}
+
+bool QSharedMemorySystemV::attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode)
+{
+ // grab the shared memory segment id
+ int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0400 : 0600));
+ if (-1 == id) {
+ self->setUnixErrorString("QSharedMemory::attach (shmget)"_L1);
+ unix_key = 0;
+ nativeKeyFile.clear();
+ return false;
+ }
+
+ // grab the memory
+ self->memory = shmat(id, nullptr, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0));
+ if (self->memory == MAP_FAILED) {
+ self->memory = nullptr;
+ self->setUnixErrorString("QSharedMemory::attach (shmat)"_L1);
+ return false;
+ }
+
+ // grab the size
+ shmid_ds shmid_ds;
+ if (!shmctl(id, IPC_STAT, &shmid_ds)) {
+ self->size = (qsizetype)shmid_ds.shm_segsz;
+ } else {
+ self->setUnixErrorString("QSharedMemory::attach (shmctl)"_L1);
+ return false;
+ }
+
+ return true;
+}
+
+bool QSharedMemorySystemV::detach(QSharedMemoryPrivate *self)
+{
+ // detach from the memory segment
+ if (shmdt(self->memory) < 0) {
+ const auto function = "QSharedMemory::detach"_L1;
+ switch (errno) {
+ case EINVAL:
+ self->setError(QSharedMemory::NotFound,
+ QSharedMemory::tr("%1: not attached").arg(function));
+ break;
+ default:
+ self->setUnixErrorString(function);
+ }
+ return false;
+ }
+ self->memory = nullptr;
+ self->size = 0;
+
+ return cleanHandle(self);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(sysv_shm)
+#endif // QT_CONFIG(sharedmemory)
diff --git a/src/corelib/ipc/qsharedmemory_win.cpp b/src/corelib/ipc/qsharedmemory_win.cpp
new file mode 100644
index 0000000000..472f34f9a1
--- /dev/null
+++ b/src/corelib/ipc/qsharedmemory_win.cpp
@@ -0,0 +1,148 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsharedmemory.h"
+#include "qsharedmemory_p.h"
+#include "qsystemsemaphore.h"
+#include <qdebug.h>
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+#if QT_CONFIG(sharedmemory)
+
+void QSharedMemoryPrivate::setWindowsErrorString(QLatin1StringView function)
+{
+ DWORD windowsError = GetLastError();
+ if (windowsError == 0)
+ return;
+ switch (windowsError) {
+ case ERROR_ALREADY_EXISTS:
+ error = QSharedMemory::AlreadyExists;
+ errorString = QSharedMemory::tr("%1: already exists").arg(function);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ error = QSharedMemory::NotFound;
+ errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
+ break;
+ case ERROR_COMMITMENT_LIMIT:
+ error = QSharedMemory::InvalidSize;
+ errorString = QSharedMemory::tr("%1: invalid size").arg(function);
+ break;
+ case ERROR_NO_SYSTEM_RESOURCES:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ error = QSharedMemory::OutOfResources;
+ errorString = QSharedMemory::tr("%1: out of resources").arg(function);
+ break;
+ case ERROR_ACCESS_DENIED:
+ error = QSharedMemory::PermissionDenied;
+ errorString = QSharedMemory::tr("%1: permission denied").arg(function);
+ break;
+ default:
+ errorString = QSharedMemory::tr("%1: unknown error: %2")
+ .arg(function, qt_error_string(windowsError));
+ error = QSharedMemory::UnknownError;
+#if defined QSHAREDMEMORY_DEBUG
+ qDebug() << errorString << "key" << key;
+#endif
+ }
+}
+
+HANDLE QSharedMemoryWin32::handle(QSharedMemoryPrivate *self)
+{
+ if (!hand) {
+ const auto function = "QSharedMemory::handle"_L1;
+ if (self->nativeKey.isEmpty()) {
+ self->setError(QSharedMemory::KeyError,
+ QSharedMemory::tr("%1: unable to make key").arg(function));
+ return 0;
+ }
+ hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false,
+ reinterpret_cast<const wchar_t *>(self->nativeKey.nativeKey().utf16()));
+ if (!hand) {
+ self->setWindowsErrorString(function);
+ return 0;
+ }
+ }
+ return hand;
+}
+
+bool QSharedMemoryWin32::cleanHandle(QSharedMemoryPrivate *)
+{
+ if (hand != 0 && !CloseHandle(hand)) {
+ hand = 0;
+ return false;
+ }
+ hand = 0;
+ return true;
+}
+
+bool QSharedMemoryWin32::create(QSharedMemoryPrivate *self, qsizetype size)
+{
+ const auto function = "QSharedMemory::create"_L1;
+ if (self->nativeKey.isEmpty()) {
+ self->setError(QSharedMemory::KeyError,
+ QSharedMemory::tr("%1: key error").arg(function));
+ return false;
+ }
+
+ // Create the file mapping.
+ DWORD high, low;
+ if constexpr (sizeof(qsizetype) == 8)
+ high = DWORD(quint64(size) >> 32);
+ else
+ high = 0;
+ low = DWORD(size_t(size) & 0xffffffff);
+ hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, high, low,
+ reinterpret_cast<const wchar_t *>(self->nativeKey.nativeKey().utf16()));
+ self->setWindowsErrorString(function);
+
+ // hand is valid when it already exists unlike unix so explicitly check
+ return self->error != QSharedMemory::AlreadyExists && hand;
+}
+
+bool QSharedMemoryWin32::attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode)
+{
+ // Grab a pointer to the memory block
+ int permissions = (mode == QSharedMemory::ReadOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS);
+ self->memory = (void *)MapViewOfFile(handle(self), permissions, 0, 0, 0);
+ if (!self->memory) {
+ self->setWindowsErrorString("QSharedMemory::attach"_L1);
+ cleanHandle(self);
+ return false;
+ }
+
+ // Grab the size of the memory we have been given (a multiple of 4K on windows)
+ MEMORY_BASIC_INFORMATION info;
+ if (!VirtualQuery(self->memory, &info, sizeof(info))) {
+ // Windows doesn't set an error code on this one,
+ // it should only be a kernel memory error.
+ self->setError(QSharedMemory::UnknownError,
+ QSharedMemory::tr("%1: size query failed")
+ .arg("QSharedMemory::attach: "_L1));
+ return false;
+ }
+ self->size = qsizetype(info.RegionSize);
+
+ return true;
+}
+
+bool QSharedMemoryWin32::detach(QSharedMemoryPrivate *self)
+{
+ // umap memory
+ if (!UnmapViewOfFile(self->memory)) {
+ self->setWindowsErrorString("QSharedMemory::detach"_L1);
+ return false;
+ }
+ self->memory = 0;
+ self->size = 0;
+
+ // close handle
+ return cleanHandle(self);
+}
+
+#endif // QT_CONFIG(sharedmemory)
+
+QT_END_NAMESPACE
diff --git a/src/corelib/ipc/qsystemsemaphore.cpp b/src/corelib/ipc/qsystemsemaphore.cpp
new file mode 100644
index 0000000000..4c24ef6043
--- /dev/null
+++ b/src/corelib/ipc/qsystemsemaphore.cpp
@@ -0,0 +1,402 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsystemsemaphore.h"
+#include "qsystemsemaphore_p.h"
+
+#if QT_CONFIG(systemsemaphore)
+#include <QtCore/q20memory.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QtIpcCommon;
+using namespace Qt::StringLiterals;
+
+inline void QSystemSemaphorePrivate::constructBackend()
+{
+ visit([](auto p) { q20::construct_at(p); });
+}
+
+inline void QSystemSemaphorePrivate::destructBackend()
+{
+ visit([](auto p) { std::destroy_at(p); });
+}
+
+/*!
+ \class QSystemSemaphore
+ \inmodule QtCore
+ \since 4.4
+
+ \brief The QSystemSemaphore class provides a general counting system semaphore.
+
+ A system semaphore is a generalization of \l QSemaphore. Typically, a
+ semaphore is used to protect a certain number of identical resources.
+
+ Like its lighter counterpart, a QSystemSemaphore can be
+ accessed from multiple \l {QThread} {threads}. Unlike QSemaphore, a
+ QSystemSemaphore can also be accessed from multiple \l {QProcess}
+ {processes}. This means QSystemSemaphore is a much heavier class, so
+ if your application doesn't need to access your semaphores across
+ multiple processes, you will probably want to use QSemaphore.
+
+ Semaphores support two fundamental operations, acquire() and release():
+
+ acquire() tries to acquire one resource. If there isn't a resource
+ available, the call blocks until a resource becomes available. Then
+ the resource is acquired and the call returns.
+
+ release() releases one resource so it can be acquired by another
+ process. The function can also be called with a parameter n > 1,
+ which releases n resources.
+
+ System semaphores are identified by a key, represented by \l QNativeIpcKey. A
+ key can be created in a cross-platform manner by using platformSafeKey(). A
+ system semaphore is created by the QSystemSemaphore constructor when passed
+ an access mode parameter of AccessMode::Create. Once it is created, other
+ processes may attach to the same semaphore using the same key and an access
+ mode parameter of AccessMode::Open.
+
+ Example: Create a system semaphore
+ \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 0
+
+ For details on the key types, platform-specific limitations, and
+ interoperability with older or non-Qt applications, see the \l{Native IPC
+ Keys} documentation. That includes important information for sandboxed
+ applications on Apple platforms, including all apps obtained via the Apple
+ App Store.
+
+ \sa {Inter-Process Communication}, QSharedMemory, QSemaphore
+ */
+
+/*!
+ Requests a system semaphore identified by the legacy key \a key.
+ */
+QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode)
+ : QSystemSemaphore(legacyNativeKey(key), initialValue, mode)
+{
+}
+
+/*!
+ Requests a system semaphore for the specified \a key. The parameters
+ \a initialValue and \a mode are used according to the following
+ rules, which are system dependent.
+
+ In Unix, if the \a mode is \l {QSystemSemaphore::} {Open} and the
+ system already has a semaphore identified by \a key, that semaphore
+ is used, and the semaphore's resource count is not changed, i.e., \a
+ initialValue is ignored. But if the system does not already have a
+ semaphore identified by \a key, it creates a new semaphore for that
+ key and sets its resource count to \a initialValue.
+
+ In Unix, if the \a mode is \l {QSystemSemaphore::} {Create} and the
+ system already has a semaphore identified by \a key, that semaphore
+ is used, and its resource count is set to \a initialValue. If the
+ system does not already have a semaphore identified by \a key, it
+ creates a new semaphore for that key and sets its resource count to
+ \a initialValue.
+
+ In Windows, \a mode is ignored, and the system always tries to
+ create a semaphore for the specified \a key. If the system does not
+ already have a semaphore identified as \a key, it creates the
+ semaphore and sets its resource count to \a initialValue. But if the
+ system already has a semaphore identified as \a key it uses that
+ semaphore and ignores \a initialValue.
+
+ The \l {QSystemSemaphore::AccessMode} {mode} parameter is only used
+ in Unix systems to handle the case where a semaphore survives a
+ process crash. In that case, the next process to allocate a
+ semaphore with the same \a key will get the semaphore that survived
+ the crash, and unless \a mode is \l {QSystemSemaphore::} {Create},
+ the resource count will not be reset to \a initialValue but will
+ retain the initial value it had been given by the crashed process.
+
+ \sa acquire(), key()
+ */
+QSystemSemaphore::QSystemSemaphore(const QNativeIpcKey &key, int initialValue, AccessMode mode)
+ : d(new QSystemSemaphorePrivate(key.type()))
+{
+ setNativeKey(key, initialValue, mode);
+}
+
+/*!
+ The destructor destroys the QSystemSemaphore object, but the
+ underlying system semaphore is not removed from the system unless
+ this instance of QSystemSemaphore is the last one existing for that
+ system semaphore.
+
+ Two important side effects of the destructor depend on the system.
+ In Windows, if acquire() has been called for this semaphore but not
+ release(), release() will not be called by the destructor, nor will
+ the resource be released when the process exits normally. This would
+ be a program bug which could be the cause of a deadlock in another
+ process trying to acquire the same resource. In Unix, acquired
+ resources that are not released before the destructor is called are
+ automatically released when the process exits.
+*/
+QSystemSemaphore::~QSystemSemaphore()
+{
+ d->cleanHandle();
+}
+
+/*!
+ \enum QSystemSemaphore::AccessMode
+
+ This enum is used by the constructor and setKey(). Its purpose is to
+ enable handling the problem in Unix implementations of semaphores
+ that survive a crash. In Unix, when a semaphore survives a crash, we
+ need a way to force it to reset its resource count, when the system
+ reuses the semaphore. In Windows, where semaphores can't survive a
+ crash, this enum has no effect.
+
+ \value Open If the semaphore already exists, its initial resource
+ count is not reset. If the semaphore does not already exist, it is
+ created and its initial resource count set.
+
+ \value Create QSystemSemaphore takes ownership of the semaphore and
+ sets its resource count to the requested value, regardless of
+ whether the semaphore already exists by having survived a crash.
+ This value should be passed to the constructor, when the first
+ semaphore for a particular key is constructed and you know that if
+ the semaphore already exists it could only be because of a crash. In
+ Windows, where a semaphore can't survive a crash, Create and Open
+ have the same behavior.
+*/
+
+/*!
+ This function works the same as the constructor. It reconstructs
+ this QSystemSemaphore object. If the new \a key is different from
+ the old key, calling this function is like calling the destructor of
+ the semaphore with the old key, then calling the constructor to
+ create a new semaphore with the new \a key. The \a initialValue and
+ \a mode parameters are as defined for the constructor.
+
+ This function is useful if the native key was shared from another process.
+ See \l{Native IPC Keys} for more information.
+
+ \sa QSystemSemaphore(), nativeIpcKey()
+ */
+void QSystemSemaphore::setNativeKey(const QNativeIpcKey &key, int initialValue, AccessMode mode)
+{
+ if (key == d->nativeKey && mode == Open)
+ return;
+ if (!isKeyTypeSupported(key.type())) {
+ d->setError(KeyError, tr("%1: unsupported key type")
+ .arg("QSystemSemaphore::setNativeKey"_L1));
+ return;
+ }
+
+ d->clearError();
+ d->cleanHandle();
+ if (key.type() == d->nativeKey.type()) {
+ // we can reuse the backend
+ d->nativeKey = key;
+ } else {
+ // we must recreate the backend
+ d->destructBackend();
+ d->nativeKey = key;
+ d->constructBackend();
+ }
+ d->initialValue = initialValue;
+ d->handle(mode);
+}
+
+/*!
+ Returns the key assigned to this system semaphore. The key is the
+ name by which the semaphore can be accessed from other processes.
+
+ You can use the native key to access system semaphores that have not been
+ created by Qt, or to grant access to non-Qt applications. See \l{Native IPC
+ Keys} for more information.
+
+ \sa setNativeKey()
+ */
+QNativeIpcKey QSystemSemaphore::nativeIpcKey() const
+{
+ return d->nativeKey;
+}
+
+/*!
+ This function works the same as the constructor. It reconstructs
+ this QSystemSemaphore object. If the new \a key is different from
+ the old key, calling this function is like calling the destructor of
+ the semaphore with the old key, then calling the constructor to
+ create a new semaphore with the new \a key. The \a initialValue and
+ \a mode parameters are as defined for the constructor.
+
+ \sa QSystemSemaphore(), key()
+ */
+void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
+{
+ setNativeKey(legacyNativeKey(key), initialValue, mode);
+}
+
+/*!
+ Returns the legacy key assigned to this system semaphore. The key is the
+ name by which the semaphore can be accessed from other processes.
+
+ \sa setKey()
+ */
+QString QSystemSemaphore::key() const
+{
+ return QNativeIpcKeyPrivate::legacyKey(d->nativeKey);
+}
+
+/*!
+ Acquires one of the resources guarded by this semaphore, if there is
+ one available, and returns \c true. If all the resources guarded by this
+ semaphore have already been acquired, the call blocks until one of
+ them is released by another process or thread having a semaphore
+ with the same key.
+
+ If false is returned, a system error has occurred. Call error()
+ to get a value of QSystemSemaphore::SystemSemaphoreError that
+ indicates which error occurred.
+
+ \sa release()
+ */
+bool QSystemSemaphore::acquire()
+{
+ return d->modifySemaphore(-1);
+}
+
+/*!
+ Releases \a n resources guarded by the semaphore. Returns \c true
+ unless there is a system error.
+
+ Example: Create a system semaphore having five resources; acquire
+ them all and then release them all.
+
+ \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 1
+
+ This function can also "create" resources. For example, immediately
+ following the sequence of statements above, suppose we add the
+ statement:
+
+ \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 2
+
+ Ten new resources are now guarded by the semaphore, in addition to
+ the five that already existed. You would not normally use this
+ function to create more resources.
+
+ \sa acquire()
+ */
+bool QSystemSemaphore::release(int n)
+{
+ if (n == 0)
+ return true;
+ if (n < 0) {
+ qWarning("QSystemSemaphore::release: n is negative.");
+ return false;
+ }
+ return d->modifySemaphore(n);
+}
+
+/*!
+ Returns a value indicating whether an error occurred, and, if so,
+ which error it was.
+
+ \sa errorString()
+ */
+QSystemSemaphore::SystemSemaphoreError QSystemSemaphore::error() const
+{
+ return d->error;
+}
+
+/*!
+ \enum QSystemSemaphore::SystemSemaphoreError
+
+ \value NoError No error occurred.
+
+ \value PermissionDenied The operation failed because the caller
+ didn't have the required permissions.
+
+ \value KeyError The operation failed because of an invalid key.
+
+ \value AlreadyExists The operation failed because a system
+ semaphore with the specified key already existed.
+
+ \value NotFound The operation failed because a system semaphore
+ with the specified key could not be found.
+
+ \value OutOfResources The operation failed because there was
+ not enough memory available to fill the request.
+
+ \value UnknownError Something else happened and it was bad.
+*/
+
+/*!
+ Returns a text description of the last error that occurred. If
+ error() returns an \l {QSystemSemaphore::SystemSemaphoreError} {error
+ value}, call this function to get a text string that describes the
+ error.
+
+ \sa error()
+ */
+QString QSystemSemaphore::errorString() const
+{
+ return d->errorString;
+}
+
+void QSystemSemaphorePrivate::setUnixErrorString(QLatin1StringView function)
+{
+ // EINVAL is handled in functions so they can give better error strings
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ errorString = QSystemSemaphore::tr("%1: permission denied").arg(function);
+ error = QSystemSemaphore::PermissionDenied;
+ break;
+ case EEXIST:
+ errorString = QSystemSemaphore::tr("%1: already exists").arg(function);
+ error = QSystemSemaphore::AlreadyExists;
+ break;
+ case ENOENT:
+ errorString = QSystemSemaphore::tr("%1: does not exist").arg(function);
+ error = QSystemSemaphore::NotFound;
+ break;
+ case ERANGE:
+ case ENOSPC:
+ case EMFILE:
+ errorString = QSystemSemaphore::tr("%1: out of resources").arg(function);
+ error = QSystemSemaphore::OutOfResources;
+ break;
+ case ENAMETOOLONG:
+ errorString = QSystemSemaphore::tr("%1: key too long").arg(function);
+ error = QSystemSemaphore::KeyError;
+ break;
+ default:
+ errorString = QSystemSemaphore::tr("%1: unknown error: %2")
+ .arg(function, qt_error_string(errno));
+ error = QSystemSemaphore::UnknownError;
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
+#endif
+ }
+}
+
+bool QSystemSemaphore::isKeyTypeSupported(QNativeIpcKey::Type type)
+{
+ if (!isIpcSupported(IpcType::SystemSemaphore, type))
+ return false;
+ using Variant = decltype(QSystemSemaphorePrivate::backend);
+ return Variant::staticVisit(type, [](auto ptr) {
+ using Impl = std::decay_t<decltype(*ptr)>;
+ return Impl::runtimeSupportCheck();
+ });
+}
+
+QNativeIpcKey QSystemSemaphore::platformSafeKey(const QString &key, QNativeIpcKey::Type type)
+{
+ return QtIpcCommon::platformSafeKey(key, IpcType::SystemSemaphore, type);
+}
+
+QNativeIpcKey QSystemSemaphore::legacyNativeKey(const QString &key, QNativeIpcKey::Type type)
+{
+ return QtIpcCommon::legacyPlatformSafeKey(key, IpcType::SystemSemaphore, type);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsystemsemaphore.cpp"
+
+#endif // QT_CONFIG(systemsemaphore)
diff --git a/src/corelib/ipc/qsystemsemaphore.h b/src/corelib/ipc/qsystemsemaphore.h
new file mode 100644
index 0000000000..df6fd28342
--- /dev/null
+++ b/src/corelib/ipc/qsystemsemaphore.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSYSTEMSEMAPHORE_H
+#define QSYSTEMSEMAPHORE_H
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qtipccommon.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(systemsemaphore)
+
+class QSystemSemaphorePrivate;
+
+class Q_CORE_EXPORT QSystemSemaphore
+{
+ Q_GADGET
+ Q_DECLARE_TR_FUNCTIONS(QSystemSemaphore)
+public:
+ enum AccessMode
+ {
+ Open,
+ Create
+ };
+ Q_ENUM(AccessMode)
+
+ enum SystemSemaphoreError
+ {
+ NoError,
+ PermissionDenied,
+ KeyError,
+ AlreadyExists,
+ NotFound,
+ OutOfResources,
+ UnknownError
+ };
+
+ QSystemSemaphore(const QNativeIpcKey &key, int initialValue = 0, AccessMode = Open);
+ ~QSystemSemaphore();
+
+ void setNativeKey(const QNativeIpcKey &key, int initialValue = 0, AccessMode = Open);
+ void setNativeKey(const QString &key, int initialValue = 0, AccessMode mode = Open,
+ QNativeIpcKey::Type type = QNativeIpcKey::legacyDefaultTypeForOs())
+ { setNativeKey({ key, type }, initialValue, mode); }
+ QNativeIpcKey nativeIpcKey() const;
+
+ QSystemSemaphore(const QString &key, int initialValue = 0, AccessMode mode = Open);
+ void setKey(const QString &key, int initialValue = 0, AccessMode mode = Open);
+ QString key() const;
+
+ bool acquire();
+ bool release(int n = 1);
+
+ SystemSemaphoreError error() const;
+ QString errorString() const;
+
+ static bool isKeyTypeSupported(QNativeIpcKey::Type type) Q_DECL_CONST_FUNCTION;
+ static QNativeIpcKey platformSafeKey(const QString &key,
+ QNativeIpcKey::Type type = QNativeIpcKey::DefaultTypeForOs);
+ static QNativeIpcKey legacyNativeKey(const QString &key,
+ QNativeIpcKey::Type type = QNativeIpcKey::legacyDefaultTypeForOs());
+
+private:
+ Q_DISABLE_COPY(QSystemSemaphore)
+ QScopedPointer<QSystemSemaphorePrivate> d;
+};
+
+#endif // QT_CONFIG(systemsemaphore)
+
+QT_END_NAMESPACE
+
+#endif // QSYSTEMSEMAPHORE_H
diff --git a/src/corelib/ipc/qsystemsemaphore_p.h b/src/corelib/ipc/qsystemsemaphore_p.h
new file mode 100644
index 0000000000..788c4fb784
--- /dev/null
+++ b/src/corelib/ipc/qsystemsemaphore_p.h
@@ -0,0 +1,152 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// 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 QSYSTEMSEMAPHORE_P_H
+#define QSYSTEMSEMAPHORE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsystemsemaphore.h"
+
+#if QT_CONFIG(systemsemaphore)
+
+#include "qcoreapplication.h"
+#include "qtipccommon_p.h"
+#include "private/qtcore-config_p.h"
+
+#include <sys/types.h>
+#if QT_CONFIG(posix_sem)
+# include <semaphore.h>
+#endif
+#ifndef SEM_FAILED
+# define SEM_FAILED nullptr
+struct sem_t;
+#endif
+#if QT_CONFIG(sysv_sem)
+# include <sys/sem.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QSystemSemaphorePrivate;
+
+struct QSystemSemaphorePosix
+{
+ static constexpr bool Enabled = QT_CONFIG(posix_sem);
+ static bool supports(QNativeIpcKey::Type type)
+ { return type == QNativeIpcKey::Type::PosixRealtime; }
+ static bool runtimeSupportCheck();
+
+ bool handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode);
+ void cleanHandle(QSystemSemaphorePrivate *self);
+ bool modifySemaphore(QSystemSemaphorePrivate *self, int count);
+
+ sem_t *semaphore = SEM_FAILED;
+ bool createdSemaphore = false;
+};
+
+struct QSystemSemaphoreSystemV
+{
+ static constexpr bool Enabled = QT_CONFIG(sysv_sem);
+ static bool supports(QNativeIpcKey::Type type)
+ { return quint16(type) <= 0xff; }
+ static bool runtimeSupportCheck();
+
+#if QT_CONFIG(sysv_sem)
+ key_t handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode);
+ void cleanHandle(QSystemSemaphorePrivate *self);
+ bool modifySemaphore(QSystemSemaphorePrivate *self, int count);
+
+ QByteArray nativeKeyFile;
+ key_t unix_key = -1;
+ int semaphore = -1;
+ bool createdFile = false;
+ bool createdSemaphore = false;
+#endif
+};
+
+struct QSystemSemaphoreWin32
+{
+#ifdef Q_OS_WIN32
+ static constexpr bool Enabled = true;
+#else
+ static constexpr bool Enabled = false;
+#endif
+ static bool supports(QNativeIpcKey::Type type)
+ { return type == QNativeIpcKey::Type::Windows; }
+ static bool runtimeSupportCheck() { return Enabled; }
+
+ // we can declare the members without the #if
+ Qt::HANDLE handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode);
+ void cleanHandle(QSystemSemaphorePrivate *self);
+ bool modifySemaphore(QSystemSemaphorePrivate *self, int count);
+
+ Qt::HANDLE semaphore = nullptr;
+};
+
+class QSystemSemaphorePrivate
+{
+public:
+ QSystemSemaphorePrivate(QNativeIpcKey::Type type) : nativeKey(type)
+ { constructBackend(); }
+ ~QSystemSemaphorePrivate() { destructBackend(); }
+
+ void setWindowsErrorString(QLatin1StringView function); // Windows only
+ void setUnixErrorString(QLatin1StringView function);
+ inline void setError(QSystemSemaphore::SystemSemaphoreError e, const QString &message)
+ { error = e; errorString = message; }
+ inline void clearError()
+ { setError(QSystemSemaphore::NoError, QString()); }
+
+ QNativeIpcKey nativeKey;
+ QString errorString;
+ int initialValue;
+ QSystemSemaphore::SystemSemaphoreError error = QSystemSemaphore::NoError;
+
+ union Backend {
+ Backend() {}
+ ~Backend() {}
+ QSystemSemaphorePosix posix;
+ QSystemSemaphoreSystemV sysv;
+ QSystemSemaphoreWin32 win32;
+ };
+ QtIpcCommon::IpcStorageVariant<&Backend::posix, &Backend::sysv, &Backend::win32> backend;
+
+ void constructBackend();
+ void destructBackend();
+
+ template <typename Lambda> auto visit(const Lambda &lambda)
+ {
+ return backend.visit(nativeKey.type(), lambda);
+ }
+
+ void handle(QSystemSemaphore::AccessMode mode)
+ {
+ visit([&](auto p) { p->handle(this, mode); });
+ }
+ void cleanHandle()
+ {
+ visit([&](auto p) { p->cleanHandle(this); });
+ }
+ bool modifySemaphore(int count)
+ {
+ return visit([&](auto p) { return p->modifySemaphore(this, count); });
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(systemsemaphore)
+
+#endif // QSYSTEMSEMAPHORE_P_H
+
diff --git a/src/corelib/ipc/qsystemsemaphore_posix.cpp b/src/corelib/ipc/qsystemsemaphore_posix.cpp
new file mode 100644
index 0000000000..7df9593513
--- /dev/null
+++ b/src/corelib/ipc/qsystemsemaphore_posix.cpp
@@ -0,0 +1,171 @@
+// Copyright (C) 2015 Konstantin Ritt <ritt.ks@gmail.com>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com>
+// 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
+
+#include "qsystemsemaphore.h"
+#include "qsystemsemaphore_p.h"
+
+#include <qdebug.h>
+#include <qfile.h>
+#include <qcoreapplication.h>
+
+#if QT_CONFIG(posix_sem)
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef Q_OS_UNIX
+# include "private/qcore_unix_p.h"
+#else
+# define QT_EINTR_LOOP_VAL(var, val, cmd) \
+ (void)var; var = cmd
+# define QT_EINTR_LOOP(var, cmd) QT_EINTR_LOOP_VAL(var, -1, cmd)
+#endif
+
+// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
+// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
+#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
+#define EIDRM EINVAL
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+bool QSystemSemaphorePosix::runtimeSupportCheck()
+{
+ static const bool result = []() {
+ sem_open("/", 0, 0, 0); // this WILL fail
+ return errno != ENOSYS;
+ }();
+ return result;
+}
+
+bool QSystemSemaphorePosix::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode)
+{
+ if (semaphore != SEM_FAILED)
+ return true; // we already have a semaphore
+
+ const QByteArray semName = QFile::encodeName(self->nativeKey.nativeKey());
+ if (semName.isEmpty()) {
+ self->setError(QSystemSemaphore::KeyError,
+ QSystemSemaphore::tr("%1: key is empty")
+ .arg("QSystemSemaphore::handle"_L1));
+ return false;
+ }
+
+ // Always try with O_EXCL so we know whether we created the semaphore.
+ int oflag = O_CREAT | O_EXCL;
+ for (int tryNum = 0, maxTries = 1; tryNum < maxTries; ++tryNum) {
+ do {
+ semaphore = ::sem_open(semName.constData(), oflag, 0600, self->initialValue);
+ } while (semaphore == SEM_FAILED && errno == EINTR);
+ if (semaphore == SEM_FAILED && errno == EEXIST) {
+ if (mode == QSystemSemaphore::Create) {
+ if (::sem_unlink(semName.constData()) == -1 && errno != ENOENT) {
+ self->setUnixErrorString("QSystemSemaphore::handle (sem_unlink)"_L1);
+ return false;
+ }
+ // Race condition: the semaphore might be recreated before
+ // we call sem_open again, so we'll retry several times.
+ maxTries = 3;
+ } else {
+ // Race condition: if it no longer exists at the next sem_open
+ // call, we won't realize we created it, so we'll leak it later.
+ oflag &= ~O_EXCL;
+ maxTries = 2;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (semaphore == SEM_FAILED) {
+ self->setUnixErrorString("QSystemSemaphore::handle"_L1);
+ return false;
+ }
+
+ createdSemaphore = (oflag & O_EXCL) != 0;
+
+ return true;
+}
+
+void QSystemSemaphorePosix::cleanHandle(QSystemSemaphorePrivate *self)
+{
+ if (semaphore != SEM_FAILED) {
+ if (::sem_close(semaphore) == -1) {
+ self->setUnixErrorString("QSystemSemaphore::cleanHandle (sem_close)"_L1);
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphore::cleanHandle sem_close failed.");
+#endif
+ }
+ semaphore = SEM_FAILED;
+ }
+
+ if (createdSemaphore) {
+ const QByteArray semName = QFile::encodeName(self->nativeKey.nativeKey());
+ if (::sem_unlink(semName) == -1 && errno != ENOENT) {
+ self->setUnixErrorString("QSystemSemaphore::cleanHandle (sem_unlink)"_L1);
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphorePosix::cleanHandle sem_unlink failed.");
+#endif
+ }
+ createdSemaphore = false;
+ }
+}
+
+bool QSystemSemaphorePosix::modifySemaphore(QSystemSemaphorePrivate *self, int count)
+{
+ if (!handle(self, QSystemSemaphore::Open))
+ return false;
+
+ if (count > 0) {
+ int cnt = count;
+ do {
+ if (::sem_post(semaphore) == -1) {
+#if defined(Q_OS_VXWORKS)
+ if (errno == EINVAL) {
+ semaphore = SEM_FAILED;
+ return modifySemaphore(self, cnt);
+ }
+#endif
+ self->setUnixErrorString("QSystemSemaphore::modifySemaphore (sem_post)"_L1);
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphorePosix::modify sem_post failed %d %d", count, errno);
+#endif
+ // rollback changes to preserve the SysV semaphore behavior
+ for ( ; cnt < count; ++cnt) {
+ int res;
+ QT_EINTR_LOOP(res, ::sem_wait(semaphore));
+ }
+ return false;
+ }
+ --cnt;
+ } while (cnt > 0);
+ } else {
+ int res;
+ QT_EINTR_LOOP(res, ::sem_wait(semaphore));
+ if (res == -1) {
+ // If the semaphore was removed be nice and create it and then modifySemaphore again
+ if (errno == EINVAL || errno == EIDRM) {
+ semaphore = SEM_FAILED;
+ return modifySemaphore(self, count);
+ }
+ self->setUnixErrorString("QSystemSemaphore::modifySemaphore (sem_wait)"_L1);
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphorePosix::modify sem_wait failed %d %d", count, errno);
+#endif
+ return false;
+ }
+ }
+
+ self->clearError();
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(posix_sem)
diff --git a/src/corelib/ipc/qsystemsemaphore_systemv.cpp b/src/corelib/ipc/qsystemsemaphore_systemv.cpp
new file mode 100644
index 0000000000..e5d231d1d4
--- /dev/null
+++ b/src/corelib/ipc/qsystemsemaphore_systemv.cpp
@@ -0,0 +1,201 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// 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
+
+#include "qsystemsemaphore.h"
+#include "qsystemsemaphore_p.h"
+
+#include <qdebug.h>
+#include <qfile.h>
+#include <qcoreapplication.h>
+
+#if QT_CONFIG(sysv_sem)
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#if defined(Q_OS_DARWIN)
+#include "private/qcore_mac_p.h"
+#endif
+
+#include "private/qcore_unix_p.h"
+
+// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
+// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
+#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
+#define EIDRM EINVAL
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+bool QSystemSemaphoreSystemV::runtimeSupportCheck()
+{
+#if defined(Q_OS_DARWIN)
+ if (qt_apple_isSandboxed())
+ return false;
+#endif
+ static const bool result = []() {
+ (void)semget(IPC_PRIVATE, -1, 0); // this will fail
+ return errno != ENOSYS;
+ }();
+ return result;
+}
+
+/*!
+ \internal
+
+ Setup unix_key
+ */
+key_t QSystemSemaphoreSystemV::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode)
+{
+ if (unix_key != -1)
+ return unix_key; // we already have a semaphore
+
+#if defined(Q_OS_DARWIN)
+ if (qt_apple_isSandboxed()) {
+ // attempting to use System V semaphores will get us a SIGSYS
+ self->setError(QSystemSemaphore::PermissionDenied,
+ QSystemSemaphore::tr("%1: System V semaphores are not available for "
+ "sandboxed applications. Please build Qt with "
+ "-feature-ipc_posix")
+ .arg("QSystemSemaphore::handle:"_L1));
+ return -1;
+ }
+#endif
+
+ nativeKeyFile = QFile::encodeName(self->nativeKey.nativeKey());
+ if (nativeKeyFile.isEmpty()) {
+ self->setError(QSystemSemaphore::KeyError,
+ QSystemSemaphore::tr("%1: key is empty")
+ .arg("QSystemSemaphore::handle:"_L1));
+ return -1;
+ }
+
+ // ftok requires that an actual file exists somewhere
+ int built = QtIpcCommon::createUnixKeyFile(nativeKeyFile);
+ if (-1 == built) {
+ self->setError(QSystemSemaphore::KeyError,
+ QSystemSemaphore::tr("%1: unable to make key")
+ .arg("QSystemSemaphore::handle:"_L1));
+
+ return -1;
+ }
+ createdFile = (1 == built);
+
+ // Get the unix key for the created file
+ unix_key = ftok(nativeKeyFile, int(self->nativeKey.type()));
+ if (-1 == unix_key) {
+ self->setError(QSystemSemaphore::KeyError,
+ QSystemSemaphore::tr("%1: ftok failed")
+ .arg("QSystemSemaphore::handle:"_L1));
+ return -1;
+ }
+
+ // Get semaphore
+ semaphore = semget(unix_key, 1, 0600 | IPC_CREAT | IPC_EXCL);
+ if (-1 == semaphore) {
+ if (errno == EEXIST)
+ semaphore = semget(unix_key, 1, 0600 | IPC_CREAT);
+ if (-1 == semaphore) {
+ self->setUnixErrorString("QSystemSemaphore::handle"_L1);
+ cleanHandle(self);
+ return -1;
+ }
+ } else {
+ createdSemaphore = true;
+ // Force cleanup of file, it is possible that it can be left over from a crash
+ createdFile = true;
+ }
+
+ if (mode == QSystemSemaphore::Create) {
+ createdSemaphore = true;
+ createdFile = true;
+ }
+
+ // Created semaphore so initialize its value.
+ if (createdSemaphore && self->initialValue >= 0) {
+ qt_semun init_op;
+ init_op.val = self->initialValue;
+ if (-1 == semctl(semaphore, 0, SETVAL, init_op)) {
+ self->setUnixErrorString("QSystemSemaphore::handle"_L1);
+ cleanHandle(self);
+ return -1;
+ }
+ }
+
+ return unix_key;
+}
+
+/*!
+ \internal
+
+ Cleanup the unix_key
+ */
+void QSystemSemaphoreSystemV::cleanHandle(QSystemSemaphorePrivate *self)
+{
+ unix_key = -1;
+
+ // remove the file if we made it
+ if (createdFile) {
+ unlink(nativeKeyFile.constData());
+ createdFile = false;
+ }
+
+ if (createdSemaphore) {
+ if (-1 != semaphore) {
+ if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) {
+ self->setUnixErrorString("QSystemSemaphore::cleanHandle"_L1);
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphoreSystemV::cleanHandle semctl failed.");
+#endif
+ }
+ semaphore = -1;
+ }
+ createdSemaphore = false;
+ }
+}
+
+/*!
+ \internal
+ */
+bool QSystemSemaphoreSystemV::modifySemaphore(QSystemSemaphorePrivate *self, int count)
+{
+ if (handle(self, QSystemSemaphore::Open) == -1)
+ return false;
+
+ struct sembuf operation;
+ operation.sem_num = 0;
+ operation.sem_op = count;
+ operation.sem_flg = SEM_UNDO;
+
+ int res;
+ QT_EINTR_LOOP(res, semop(semaphore, &operation, 1));
+ if (-1 == res) {
+ // If the semaphore was removed be nice and create it and then modifySemaphore again
+ if (errno == EINVAL || errno == EIDRM) {
+ semaphore = -1;
+ cleanHandle(self);
+ handle(self, QSystemSemaphore::Open);
+ return modifySemaphore(self, count);
+ }
+ self->setUnixErrorString("QSystemSemaphore::modifySemaphore"_L1);
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphoreSystemV::modify failed %d %d %d %d %d",
+ count, int(semctl(semaphore, 0, GETVAL)), int(errno), int(EIDRM), int(EINVAL);
+#endif
+ return false;
+ }
+
+ self->clearError();
+ return true;
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(sysv_sem)
diff --git a/src/corelib/ipc/qsystemsemaphore_win.cpp b/src/corelib/ipc/qsystemsemaphore_win.cpp
new file mode 100644
index 0000000000..f42fecf71f
--- /dev/null
+++ b/src/corelib/ipc/qsystemsemaphore_win.cpp
@@ -0,0 +1,98 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsystemsemaphore.h"
+#include "qsystemsemaphore_p.h"
+#include "qcoreapplication.h"
+#include <qdebug.h>
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+#if QT_CONFIG(systemsemaphore)
+
+void QSystemSemaphorePrivate::setWindowsErrorString(QLatin1StringView function)
+{
+ BOOL windowsError = GetLastError();
+ if (windowsError == 0)
+ return;
+
+ switch (windowsError) {
+ case ERROR_NO_SYSTEM_RESOURCES:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ error = QSystemSemaphore::OutOfResources;
+ errorString = QCoreApplication::translate("QSystemSemaphore", "%1: out of resources").arg(function);
+ break;
+ case ERROR_ACCESS_DENIED:
+ error = QSystemSemaphore::PermissionDenied;
+ errorString = QCoreApplication::translate("QSystemSemaphore", "%1: permission denied").arg(function);
+ break;
+ default:
+ errorString = QCoreApplication::translate("QSystemSemaphore", "%1: unknown error: %2")
+ .arg(function, qt_error_string(windowsError));
+ error = QSystemSemaphore::UnknownError;
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug() << errorString << "key" << key;
+#endif
+ }
+}
+
+HANDLE QSystemSemaphoreWin32::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode)
+{
+ // don't allow making handles on empty keys
+ if (self->nativeKey.isEmpty())
+ return 0;
+
+ // Create it if it doesn't already exists.
+ if (semaphore == 0) {
+ semaphore = CreateSemaphore(0, self->initialValue, MAXLONG,
+ reinterpret_cast<const wchar_t*>(self->nativeKey.nativeKey().utf16()));
+ if (semaphore == NULL)
+ self->setWindowsErrorString("QSystemSemaphore::handle"_L1);
+ }
+
+ return semaphore;
+}
+
+void QSystemSemaphoreWin32::cleanHandle(QSystemSemaphorePrivate *)
+{
+ if (semaphore && !CloseHandle(semaphore)) {
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphoreWin32::CloseHandle: sem failed");
+#endif
+ }
+ semaphore = 0;
+}
+
+bool QSystemSemaphoreWin32::modifySemaphore(QSystemSemaphorePrivate *self, int count)
+{
+ if (handle(self, QSystemSemaphore::Open) == nullptr)
+ return false;
+
+ if (count > 0) {
+ if (0 == ReleaseSemaphore(semaphore, count, 0)) {
+ self->setWindowsErrorString("QSystemSemaphore::modifySemaphore"_L1);
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphore::modifySemaphore ReleaseSemaphore failed");
+#endif
+ return false;
+ }
+ } else {
+ if (WAIT_OBJECT_0 != WaitForSingleObjectEx(semaphore, INFINITE, FALSE)) {
+ self->setWindowsErrorString("QSystemSemaphore::modifySemaphore"_L1);
+#if defined QSYSTEMSEMAPHORE_DEBUG
+ qDebug("QSystemSemaphore::modifySemaphore WaitForSingleObject failed");
+#endif
+ return false;
+ }
+ }
+
+ self->clearError();
+ return true;
+}
+
+#endif // QT_CONFIG(systemsemaphore)
+
+QT_END_NAMESPACE
diff --git a/src/corelib/ipc/qtipccommon.cpp b/src/corelib/ipc/qtipccommon.cpp
new file mode 100644
index 0000000000..b2ae9172fa
--- /dev/null
+++ b/src/corelib/ipc/qtipccommon.cpp
@@ -0,0 +1,610 @@
+// 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
+
+#include "qtipccommon.h"
+#include "qtipccommon_p.h"
+
+#include <qcryptographichash.h>
+#include <qstandardpaths.h>
+#include <qstringconverter.h>
+#include <qurl.h>
+#include <qurlquery.h>
+
+#if defined(Q_OS_DARWIN)
+# include "private/qcore_mac_p.h"
+# if !defined(SHM_NAME_MAX)
+ // Based on PSEMNAMLEN in XNU's posix_sem.c, which would
+ // indicate the max length is 31, _excluding_ the zero
+ // terminator. But in practice (possibly due to an off-
+ // by-one bug in the kernel) the usable bytes are only 30.
+# define SHM_NAME_MAX 30
+# endif
+#elif defined(Q_OS_WINDOWS)
+# include "qt_windows.h"
+#endif
+
+#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
+
+QT_BEGIN_NAMESPACE
+
+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
+
+ Legacy: this exists for compatibility with QSharedMemory and
+ QSystemSemaphore between 4.4 and 6.6.
+
+ Returns a QNativeIpcKey that contains a platform-safe key using rules
+ similar to QtIpcCommon::platformSafeKey() below, but using an algorithm
+ that is compatible with Qt 4.4 to 6.6. Additionally, the returned
+ QNativeIpcKey will record the input \a key so it can be included in the
+ string form if necessary to pass to other processes.
+*/
+QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
+ QNativeIpcKey::Type type)
+{
+ QNativeIpcKey k(type);
+ if (key.isEmpty())
+ return k;
+
+ QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex();
+
+ if (type == QNativeIpcKey::Type::PosixRealtime) {
+#if defined(Q_OS_DARWIN)
+ if (qt_apple_isSandboxed()) {
+ // Sandboxed applications on Apple platforms require the shared memory name
+ // to be in the form <application group identifier>/<custom identifier>.
+ // Since we don't know which application group identifier the user wants
+ // to apply, we instead document that requirement, and use the key directly.
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, key, key);
+ } else {
+ // The shared memory name limit on Apple platforms is very low (30 characters),
+ // so we can't use the logic below of combining the prefix, key, and a hash,
+ // to ensure a unique and valid name. Instead we use the first part of the
+ // hash, which should still long enough to avoid collisions in practice.
+ QString native = u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1);
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, native, key);
+ }
+ return k;
+#endif
+ }
+
+ QString result;
+ result.reserve(1 + 18 + key.size() + 40);
+ switch (ipcType) {
+ case IpcType::SharedMemory:
+ result += "qipc_sharedmemory_"_L1;
+ break;
+ case IpcType::SystemSemaphore:
+ result += "qipc_systemsem_"_L1;
+ break;
+ }
+
+ for (QChar ch : key) {
+ if ((ch >= u'a' && ch <= u'z') ||
+ (ch >= u'A' && ch <= u'Z'))
+ result += ch;
+ }
+ result.append(QLatin1StringView(hex));
+
+ switch (type) {
+ case QNativeIpcKey::Type::Windows:
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows))
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
+ return k;
+ case QNativeIpcKey::Type::PosixRealtime:
+ result.prepend(u'/');
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime))
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
+ return k;
+ case QNativeIpcKey::Type::SystemV:
+ break;
+ }
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
+ result = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u'/' + result;
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
+ }
+ return k;
+}
+
+/*!
+ \internal
+ Returns a QNativeIpcKey of type \a type, suitable for QSystemSemaphore or
+ QSharedMemory depending on \a ipcType. The returned native key is generated
+ from the Unicode input \a key and is safe for use on for the key type in
+ question in the current OS.
+*/
+QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
+ QNativeIpcKey::Type type)
+{
+ QNativeIpcKey k(type);
+ if (key.isEmpty())
+ return k;
+
+ switch (type) {
+ case QNativeIpcKey::Type::PosixRealtime:
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime)) {
+#ifdef SHM_NAME_MAX
+ // The shared memory name limit on Apple platforms is very low (30
+ // characters), so we have to cut it down to avoid ENAMETOOLONG. We
+ // hope that there won't be too many collisions...
+ k.setNativeKey(u'/' + QStringView(key).left(SHM_NAME_MAX - 1));
+#else
+ k.setNativeKey(u'/' + key);
+#endif
+ }
+ return k;
+
+ case QNativeIpcKey::Type::Windows:
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows)) {
+ QStringView prefix;
+ QStringView payload = key;
+ // see https://learn.microsoft.com/en-us/windows/win32/termserv/kernel-object-namespaces
+ for (QStringView candidate : { u"Local\\", u"Global\\" }) {
+ if (!key.startsWith(candidate))
+ continue;
+ prefix = candidate;
+ payload = payload.sliced(prefix.size());
+ break;
+ }
+
+ QStringView mid;
+ switch (ipcType) {
+ case IpcType::SharedMemory: mid = u"shm_"; break;
+ case IpcType::SystemSemaphore: mid = u"sem_"; break;
+ }
+
+ QString result = prefix + mid + payload;
+#ifdef Q_OS_WINDOWS
+ result.truncate(MAX_PATH);
+#endif
+ k.setNativeKey(result);
+ }
+ return k;
+
+ case QNativeIpcKey::Type::SystemV:
+ break;
+ }
+
+ // System V
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
+ if (key.startsWith(u'/')) {
+ k.setNativeKey(key);
+ } else {
+ QString baseDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
+ k.setNativeKey(baseDir + u'/' + key);
+ }
+ }
+ return k;
+}
+
+/*!
+ \class QNativeIpcKey
+ \inmodule QtCore
+ \since 6.6
+ \brief The QNativeIpcKey class holds a native key used by QSystemSemaphore and QSharedMemory.
+
+ \compares equality
+
+ 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
+ Keys} 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 Keys} 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() noexcept
+
+ Constructs a QNativeIpcKey object of type \l DefaultTypeForOs with an empty key.
+*/
+
+/*!
+ \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 &other)
+{
+ d = new QNativeIpcKeyPrivate(*other.d);
+}
+
+void QNativeIpcKey::move_internal(QNativeIpcKey &&) noexcept
+{
+ // inline code already moved properly, nothing for us to do here
+}
+
+QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &other)
+{
+ Q_ASSERT(d || other.d); // only 3 cases to handle
+ if (d && !other.d)
+ *d = {};
+ else if (d)
+ *d = *other.d;
+ else
+ d = new QNativeIpcKeyPrivate(*other.d);
+ return *this;
+}
+
+/*!
+ \fn QNativeIpcKey::~QNativeIpcKey()
+
+ Disposes of this QNativeIpcKey object.
+*/
+void QNativeIpcKey::destroy_internal() noexcept
+{
+ delete d;
+}
+
+/*!
+ \fn QNativeIpcKey::swap(QNativeIpcKey &other) noexcept
+
+ Swaps the native IPC key and type \a other with this object.
+ This operation is very fast and never fails.
+*/
+
+/*!
+ \fn swap(QNativeIpcKey &value1, QNativeIpcKey &value2) noexcept
+ \relates QNativeIpcKey
+
+ Swaps the native IPC key and type \a value1 with \a value2.
+ 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()
+*/
+
+/*!
+ \fn QNativeIpcKey::setType(Type type)
+
+ Sets the IPC type of this object to \a type.
+
+ \sa type(), setNativeKey()
+*/
+void QNativeIpcKey::setType_internal(Type type)
+{
+ Q_UNUSED(type);
+}
+
+/*!
+ \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()
+*/
+void QNativeIpcKey::setNativeKey_internal(const QString &)
+{
+ d->legacyKey_.clear();
+}
+
+/*!
+ \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &ipcKey) noexcept
+
+ Returns the hash value for \a ipcKey, using a default seed of \c 0.
+*/
+
+/*!
+ \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
+
+ Returns the hash value for \a ipcKey, using \a seed to seed the calculation.
+*/
+size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
+{
+ // by *choice*, we're not including d->legacyKey_ in the hash -- it's
+ // already partially encoded in the key
+ return qHashMulti(seed, ipcKey.key, ipcKey.type());
+}
+
+/*!
+ \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 &lhs, const QNativeIpcKey &rhs) noexcept
+{
+ return (QNativeIpcKeyPrivate::legacyKey(lhs) == QNativeIpcKeyPrivate::legacyKey(rhs)) ? 0 : 1;
+}
+
+/*!
+ 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
+{
+ QString prefix = typeToString(type());
+ if (prefix.isEmpty()) {
+ Q_ASSERT(prefix.isNull());
+ return prefix;
+ }
+
+ 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
+
+ QUrl u;
+ u.setScheme(prefix);
+ u.setPath(copy, QUrl::TolerantMode);
+ if (isSlowPath()) {
+ QUrlQuery q;
+ if (!d->legacyKey_.isEmpty())
+ q.addQueryItem(u"legacyKey"_s, QString(d->legacyKey_).replace(u'%', "%25"_L1));
+ u.setQuery(q);
+ }
+ return u.toString(QUrl::DecodeReserved);
+}
+
+/*!
+ 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)
+{
+ QUrl u(text, QUrl::TolerantMode);
+ Type invalidType = {};
+ Type type = stringToType(u.scheme());
+ if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty()
+ || u.port() != -1)
+ return QNativeIpcKey(invalidType);
+
+ QNativeIpcKey result(QString(), type);
+ if (result.type() != type) // range check, just in case
+ return QNativeIpcKey(invalidType);
+
+ // decode the payload
+ result.setNativeKey(u.path());
+
+ if (u.hasQuery()) {
+ const QList items = QUrlQuery(u).queryItems();
+ for (const auto &item : items) {
+ if (item.first == u"legacyKey"_s) {
+ QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8());
+ QNativeIpcKeyPrivate::setLegacyKey(result, std::move(legacyKey));
+ } else {
+ // unknown query item
+ return QNativeIpcKey(invalidType);
+ }
+ }
+ }
+ return result;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtipccommon.cpp"
+
+#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
diff --git a/src/corelib/ipc/qtipccommon.h b/src/corelib/ipc/qtipccommon.h
new file mode 100644
index 0000000000..74f30cb6a4
--- /dev/null
+++ b/src/corelib/ipc/qtipccommon.h
@@ -0,0 +1,206 @@
+// 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
+#else
+ Type::PosixRealtime
+#endif
+ ;
+ static Type legacyDefaultTypeForOs() noexcept;
+
+ constexpr QNativeIpcKey() noexcept = default;
+
+ explicit constexpr QNativeIpcKey(Type type) noexcept
+ : typeAndFlags{type}
+ {
+ }
+
+ Q_IMPLICIT QNativeIpcKey(const QString &k, Type type = DefaultTypeForOs)
+ : key(k), typeAndFlags{type}
+ {
+ }
+
+ QNativeIpcKey(const QNativeIpcKey &other)
+ : d(other.d), key(other.key), typeAndFlags(other.typeAndFlags)
+ {
+ if (isSlowPath())
+ copy_internal(other);
+ }
+
+ QNativeIpcKey(QNativeIpcKey &&other) noexcept
+ : d(std::exchange(other.d, nullptr)), key(std::move(other.key)),
+ typeAndFlags(std::move(other.typeAndFlags))
+ {
+ if (isSlowPath())
+ move_internal(std::move(other));
+ }
+
+ ~QNativeIpcKey()
+ {
+ if (isSlowPath())
+ destroy_internal();
+ }
+
+ QNativeIpcKey &operator=(const QNativeIpcKey &other)
+ {
+ typeAndFlags = other.typeAndFlags;
+ key = other.key;
+ if (isSlowPath() || other.isSlowPath())
+ return assign_internal(other);
+ Q_ASSERT(!d);
+ return *this;
+ }
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QNativeIpcKey)
+ void swap(QNativeIpcKey &other) noexcept
+ {
+ std::swap(d, other.d);
+ key.swap(other.key);
+ typeAndFlags.swap(other.typeAndFlags);
+ }
+
+ bool isEmpty() const noexcept
+ {
+ return key.isEmpty();
+ }
+
+ bool isValid() const noexcept
+ {
+ return type() != Type{};
+ }
+
+ constexpr Type type() const noexcept
+ {
+ 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;
+ if (isSlowPath())
+ setNativeKey_internal(newKey);
+ }
+
+ Q_CORE_EXPORT QString toString() const;
+ Q_CORE_EXPORT static QNativeIpcKey fromString(const QString &string);
+
+private:
+ struct TypeAndFlags {
+ Type type = DefaultTypeForOs;
+ quint16 reserved1 = {};
+ quint32 reserved2 = {};
+
+ void swap(TypeAndFlags &other) noexcept
+ {
+ std::swap(type, other.type);
+ std::swap(reserved1, other.reserved1);
+ std::swap(reserved2, other.reserved2);
+ }
+
+ friend constexpr bool operator==(const TypeAndFlags &lhs, const TypeAndFlags &rhs) noexcept
+ {
+ return lhs.type == rhs.type &&
+ lhs.reserved1 == rhs.reserved1 &&
+ lhs.reserved2 == rhs.reserved2;
+ }
+ };
+
+ QNativeIpcKeyPrivate *d = nullptr;
+ QString key;
+ TypeAndFlags typeAndFlags;
+
+ friend class QNativeIpcKeyPrivate;
+ constexpr bool isSlowPath() const noexcept
+ { return Q_UNLIKELY(d); }
+
+ friend Q_CORE_EXPORT size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept;
+ friend size_t qHash(const QNativeIpcKey &ipcKey) noexcept
+ { return qHash(ipcKey, 0); }
+
+ 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_CORE_EXPORT void setType_internal(Type);
+ Q_CORE_EXPORT void setNativeKey_internal(const QString &);
+ 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
+ friend bool comparesEqual(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
+ {
+ if (!(lhs.typeAndFlags == rhs.typeAndFlags))
+ return false;
+ if (lhs.key != rhs.key)
+ return false;
+ if (lhs.d == rhs.d)
+ return true;
+ return compare_internal(lhs, rhs) == 0;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QNativeIpcKey)
+};
+
+// 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
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
+
+
+#endif // QNATIVEIPCKEY_H
diff --git a/src/corelib/ipc/qtipccommon_p.h b/src/corelib/ipc/qtipccommon_p.h
new file mode 100644
index 0000000000..72762c5ba7
--- /dev/null
+++ b/src/corelib/ipc/qtipccommon_p.h
@@ -0,0 +1,173 @@
+// 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 QTIPCCOMMON_P_H
+#define QTIPCCOMMON_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtipccommon.h"
+#include <private/qglobal_p.h>
+#include <private/qtcore-config_p.h>
+
+#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
+
+#if defined(Q_OS_UNIX)
+# include <qfile.h>
+# include <private/qcore_unix_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QNativeIpcKeyPrivate
+{
+public:
+ QString legacyKey_;
+
+ static QString legacyKey(const QNativeIpcKey &key)
+ {
+ if (key.isSlowPath())
+ return key.d->legacyKey_;
+ return QString();
+ }
+ static void setLegacyKey(QNativeIpcKey &key, const QString &legacyKey)
+ {
+ QNativeIpcKeyPrivate::makeExtended(key)->legacyKey_ = legacyKey;
+ }
+ static void setNativeAndLegacyKeys(QNativeIpcKey &key, const QString &nativeKey,
+ const QString &legacyKey)
+ {
+ key.setNativeKey(nativeKey);
+ setLegacyKey(key, legacyKey);
+ }
+
+private:
+ static QNativeIpcKeyPrivate *makeExtended(QNativeIpcKey &key)
+ {
+ if (!key.isSlowPath())
+ key.d = new QNativeIpcKeyPrivate;
+ return key.d;
+ }
+};
+
+namespace QtIpcCommon {
+enum class IpcType {
+ SharedMemory,
+ SystemSemaphore
+};
+
+static constexpr bool isIpcSupported(IpcType ipcType, QNativeIpcKey::Type type)
+{
+ switch (type) {
+ case QNativeIpcKey::Type::SystemV:
+ break;
+
+ case QNativeIpcKey::Type::PosixRealtime:
+ if (ipcType == IpcType::SharedMemory)
+ return QT_CONFIG(posix_shm);
+ return QT_CONFIG(posix_sem);
+
+ case QNativeIpcKey::Type::Windows:
+#ifdef Q_OS_WIN
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ if (ipcType == IpcType::SharedMemory)
+ return QT_CONFIG(sysv_shm);
+ return QT_CONFIG(sysv_sem);
+}
+
+template <auto Member1, auto... Members> class IpcStorageVariant
+{
+ template <typename T, typename C> static C extractClass(T C::*);
+ template <typename T, typename C> static T extractObject(T C::*);
+
+ template <auto M>
+ static constexpr bool IsEnabled = decltype(extractObject(M))::Enabled;
+
+ static_assert(std::is_member_object_pointer_v<decltype(Member1)>);
+ using StorageType = decltype(extractClass(Member1));
+ StorageType d;
+
+public:
+ template <typename Lambda> static auto
+ visit_internal(StorageType &storage, QNativeIpcKey::Type keyType, const Lambda &lambda)
+ {
+ if constexpr ((IsEnabled<Member1> || ... || IsEnabled<Members>)) {
+ if constexpr (IsEnabled<Member1>) {
+ using MemberType1 = decltype(extractObject(Member1));
+ if (MemberType1::supports(keyType))
+ return lambda(&(storage.*Member1));
+ }
+ if constexpr ((... || IsEnabled<Members>))
+ return IpcStorageVariant<Members...>::visit_internal(storage, keyType, lambda);
+ Q_UNREACHABLE();
+ } else {
+ // no backends enabled, but we can't return void
+ return false;
+ }
+ }
+
+ template <typename Lambda> auto visit(QNativeIpcKey::Type keyType, const Lambda &lambda)
+ {
+ return visit_internal(d, keyType, lambda);
+ }
+
+ template <typename Lambda> static auto
+ staticVisit(QNativeIpcKey::Type keyType, const Lambda &lambda)
+ {
+ if constexpr ((IsEnabled<Member1> || ... || IsEnabled<Members>)) {
+ if constexpr (IsEnabled<Member1>) {
+ using MemberType1 = decltype(extractObject(Member1));
+ if (MemberType1::supports(keyType))
+ return lambda(static_cast<MemberType1 *>(nullptr));
+ }
+ if constexpr ((... || IsEnabled<Members>))
+ return IpcStorageVariant<Members...>::staticVisit(keyType, lambda);
+ Q_UNREACHABLE();
+ } else {
+ // no backends enabled, but we can't return void
+ return false;
+ }
+ }
+};
+
+QNativeIpcKey legacyPlatformSafeKey(const QString &key, IpcType ipcType, QNativeIpcKey::Type type);
+QNativeIpcKey platformSafeKey(const QString &key, IpcType ipcType, QNativeIpcKey::Type type);
+
+#ifdef Q_OS_UNIX
+// Convenience function to create the file if needed
+inline int createUnixKeyFile(const QByteArray &fileName)
+{
+ int fd = qt_safe_open(fileName.constData(), O_EXCL | O_CREAT | O_RDWR, 0640);
+ if (fd < 0) {
+ if (errno == EEXIST)
+ return 0;
+ return -1;
+ } else {
+ close(fd);
+ }
+ return 1;
+
+}
+#endif // Q_OS_UNIX
+} // namespace QtIpcCommon
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
+
+
+#endif // QTIPCCOMMON_P_H