summaryrefslogtreecommitdiffstats
path: root/src/corelib/ipc/qsystemsemaphore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/ipc/qsystemsemaphore.cpp')
-rw-r--r--src/corelib/ipc/qsystemsemaphore.cpp402
1 files changed, 402 insertions, 0 deletions
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)