diff options
author | Oswald Buddenhagen <oswald.buddenhagen@digia.com> | 2013-04-19 16:11:05 (GMT) |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-08-12 16:58:01 (GMT) |
commit | 9c2c12b3ef1a17d54559d229bd788bcf1b731d55 (patch) | |
tree | 13c6e9dd34ea78930402c5acf67b9dbb3fc24edf | |
parent | e9ff4a5d3f01ce0e107a1f797446b92a6e52fc0e (diff) | |
download | Qt-9c2c12b3ef1a17d54559d229bd788bcf1b731d55.zip Qt-9c2c12b3ef1a17d54559d229bd788bcf1b731d55.tar.gz Qt-9c2c12b3ef1a17d54559d229bd788bcf1b731d55.tar.bz2 |
restore QProcessEnvironment shared data thread safety on unix
implicit sharing together with 'mutable' is a time bomb.
we need to protect the nameMap, because concurrent "reads" may try to
insert into the hash, which would go boom.
we need to protect the key/value of Hash objects, because while the
refcounting is atomic, the d pointer assignments are not, which would
also go boom.
we can simply use a QMutex to protect the whole environment, because it
is very cheap in the uncontended case.
Task-number: QTBUG-30779
Change-Id: Iaad5720041ca06691d75eb9c6c0e1c120d4a7b46
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
(cherry picked from qtbase/85e61297f7b02297641826332dbdbc845a88c34b)
-rw-r--r-- | src/corelib/io/qprocess.cpp | 35 | ||||
-rw-r--r-- | src/corelib/io/qprocess_p.h | 43 | ||||
-rw-r--r-- | src/corelib/io/qprocess_unix.cpp | 4 |
3 files changed, 74 insertions, 8 deletions
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index 6d465d7..4f1b09f 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -259,7 +259,13 @@ QProcessEnvironment &QProcessEnvironment::operator=(const QProcessEnvironment &o */ bool QProcessEnvironment::operator==(const QProcessEnvironment &other) const { - return d == other.d || (d && other.d && d->hash == other.d->hash); + if (d == other.d) + return true; + if (d && other.d) { + QProcessEnvironmentPrivate::OrderedMutexLocker locker(d, other.d); + return d->hash == other.d->hash; + } + return false; } /*! @@ -270,6 +276,7 @@ bool QProcessEnvironment::operator==(const QProcessEnvironment &other) const */ bool QProcessEnvironment::isEmpty() const { + // Needs no locking, as no hash nodes are accessed return d ? d->hash.isEmpty() : true; } @@ -299,7 +306,10 @@ void QProcessEnvironment::clear() */ bool QProcessEnvironment::contains(const QString &name) const { - return d ? d->hash.contains(d->prepareName(name)) : false; + if (!d) + return false; + QProcessEnvironmentPrivate::MutexLocker locker(d); + return d->hash.contains(d->prepareName(name)); } /*! @@ -320,7 +330,8 @@ bool QProcessEnvironment::contains(const QString &name) const */ void QProcessEnvironment::insert(const QString &name, const QString &value) { - // d detaches from null + // our re-impl of detach() detaches from null + d.detach(); // detach before prepareName() d->hash.insert(d->prepareName(name), d->prepareValue(value)); } @@ -337,8 +348,10 @@ void QProcessEnvironment::insert(const QString &name, const QString &value) */ void QProcessEnvironment::remove(const QString &name) { - if (d) + if (d) { + d.detach(); // detach before prepareName() d->hash.remove(d->prepareName(name)); + } } /*! @@ -357,6 +370,7 @@ QString QProcessEnvironment::value(const QString &name, const QString &defaultVa if (!d) return defaultValue; + QProcessEnvironmentPrivate::MutexLocker locker(d); QProcessEnvironmentPrivate::Hash::ConstIterator it = d->hash.constFind(d->prepareName(name)); if (it == d->hash.constEnd()) return defaultValue; @@ -379,7 +393,10 @@ QString QProcessEnvironment::value(const QString &name, const QString &defaultVa */ QStringList QProcessEnvironment::toStringList() const { - return d ? d->toList() : QStringList(); + if (!d) + return QStringList(); + QProcessEnvironmentPrivate::MutexLocker locker(d); + return d->toList(); } /*! @@ -390,7 +407,10 @@ QStringList QProcessEnvironment::toStringList() const */ QStringList QProcessEnvironment::keys() const { - return d ? d->keys() : QStringList(); + if (!d) + return QStringList(); + QProcessEnvironmentPrivate::MutexLocker locker(d); + return d->keys(); } /*! @@ -405,7 +425,8 @@ void QProcessEnvironment::insert(const QProcessEnvironment &e) if (!e.d) return; - // d detaches from null + // our re-impl of detach() detaches from null + QProcessEnvironmentPrivate::MutexLocker locker(e.d); d->insert(*e.d); } diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index c6e6f3d..a36b0ac 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -59,6 +59,9 @@ #include "QtCore/qshareddata.h" #include "private/qringbuffer_p.h" #include "private/qiodevice_p.h" +#ifdef Q_OS_UNIX +#include <QtCore/private/qorderedmutexlocker_p.h> +#endif #ifdef Q_OS_WIN #include "QtCore/qt_windows.h" @@ -150,6 +153,13 @@ public: inline QString nameToString(const Key &name) const { return name; } inline Value prepareValue(const QString &value) const { return value; } inline QString valueToString(const Value &value) const { return value; } + struct MutexLocker { + MutexLocker(const QProcessEnvironmentPrivate *) {} + }; + struct OrderedMutexLocker { + OrderedMutexLocker(const QProcessEnvironmentPrivate *, + const QProcessEnvironmentPrivate *) {} + }; #else inline Key prepareName(const QString &name) const { @@ -166,6 +176,37 @@ public: } inline Value prepareValue(const QString &value) const { return Value(value); } inline QString valueToString(const Value &value) const { return value.string(); } + + struct MutexLocker : public QMutexLocker + { + MutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->mutex) {} + }; + struct OrderedMutexLocker : public QOrderedMutexLocker + { + OrderedMutexLocker(const QProcessEnvironmentPrivate *d1, + const QProcessEnvironmentPrivate *d2) : + QOrderedMutexLocker(&d1->mutex, &d2->mutex) + {} + }; + + QProcessEnvironmentPrivate() : QSharedData() {} + QProcessEnvironmentPrivate(const QProcessEnvironmentPrivate &other) : + QSharedData() + { + // This being locked ensures that the functions that only assign + // d pointers don't need explicit locking. + // We don't need to lock our own mutex, as this object is new and + // consequently not shared. For the same reason, non-const methods + // do not need a lock, as they detach objects (however, we need to + // ensure that they really detach before using prepareName()). + MutexLocker locker(&other); + hash = other.hash; + nameMap = other.nameMap; + // We need to detach our members, so that our mutex can protect them. + // As we are being detached, they likely would be detached a moment later anyway. + hash.detach(); + nameMap.detach(); + } #endif typedef QHash<Key, Value> Hash; @@ -174,6 +215,8 @@ public: #ifdef Q_OS_UNIX typedef QHash<QString, Key> NameHash; mutable NameHash nameMap; + + mutable QMutex mutex; #endif static QProcessEnvironment fromList(const QStringList &list); diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index c422a89..d3cef1c 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -619,8 +619,10 @@ void QProcessPrivate::startProcess() // Duplicate the environment. int envc = 0; char **envp = 0; - if (environment.d.constData()) + if (environment.d.constData()) { + QProcessEnvironmentPrivate::MutexLocker locker(environment.d); envp = _q_dupEnvironment(environment.d.constData()->hash, &envc); + } // Encode the working directory if it's non-empty, otherwise just pass 0. const char *workingDirPtr = 0; |