forked from qt-creator/qt-creator
Utils: Add SynchronizedValue<T>
Change-Id: I0af6998f540ba688fa54d9e43e33cb3cb0fc54e8 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
370
src/libs/utils/synchronizedvalue.h
Normal file
370
src/libs/utils/synchronizedvalue.h
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief A wrapper that provides thread-safe access to the wrapped type using a read/write mutex.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
\code
|
||||||
|
|
||||||
|
void writeAndGet() {
|
||||||
|
SynchronizedValue<QString> synchronizedString;
|
||||||
|
// To update the value of the synchronized object, you can use the write function.
|
||||||
|
synchronizedString.write([](QString &str) { str = "Hello World"; });
|
||||||
|
|
||||||
|
// If you just need a value from the synchronized object, you can use the get function
|
||||||
|
qDebug() << "New value is:" << synchronizedString.get<QString>([](const QString &str) { return str; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void read() {
|
||||||
|
SynchronizedValue<QPair<QString, QString>> synchronized;
|
||||||
|
|
||||||
|
QString both;
|
||||||
|
|
||||||
|
// If you want to access multiple members of the synchronized object, you can use the read function
|
||||||
|
synchronized.read([&both](const QPair<QString, QString> &pair) {
|
||||||
|
qDebug() << "First value is:" << pair.first();
|
||||||
|
qDebug() << "Second value is:" << pair.second();
|
||||||
|
both = pair.first() + pair.second();
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can use the SynchronizedValue<T>::update() to return whether the value was changed:
|
||||||
|
void setString(const QString &newString) {
|
||||||
|
const bool wasChanged = m_synchronizedString.update([&newString](QString &str) {
|
||||||
|
if (newString == str)
|
||||||
|
return false;
|
||||||
|
str = newString;
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (wasChanged)
|
||||||
|
emit stringChanged(newString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can also use a lock type to get access
|
||||||
|
void withLocks() {
|
||||||
|
SynchronizedValue<QString> synchronizedData;
|
||||||
|
*synchronizedData.writeLocked() = "Hello World";
|
||||||
|
qDebug() << *synchronizedData.readLocked() << "== Hello World";
|
||||||
|
|
||||||
|
auto lk = synchronizedData.writeLocked();
|
||||||
|
assert(lk.ownsLock());
|
||||||
|
*lk = "I am locked";
|
||||||
|
}
|
||||||
|
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class SynchronizedValue
|
||||||
|
{
|
||||||
|
template<typename... SV>
|
||||||
|
friend std::tuple<typename SV::unique_lock...> synchronize(SV &...sv);
|
||||||
|
|
||||||
|
public:
|
||||||
|
SynchronizedValue() = default;
|
||||||
|
|
||||||
|
SynchronizedValue(const SynchronizedValue<T> &other)
|
||||||
|
{
|
||||||
|
std::shared_lock lk(other.mutex);
|
||||||
|
value = other.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizedValue(const T &other)
|
||||||
|
: value(other)
|
||||||
|
{}
|
||||||
|
|
||||||
|
class shared_lock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
shared_lock(T const &value_, std::shared_mutex &mutex)
|
||||||
|
: lock(mutex)
|
||||||
|
, value(value_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
shared_lock(T const &value_, std::shared_mutex &mutex, std::try_to_lock_t)
|
||||||
|
: lock(mutex, std::try_to_lock)
|
||||||
|
, value(value_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
shared_lock(T const &value_, std::shared_mutex &mutex, std::defer_lock_t)
|
||||||
|
: lock(mutex, std::defer_lock)
|
||||||
|
, value(value_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
shared_lock(T const &value_, std::shared_mutex &mutex, std::adopt_lock_t)
|
||||||
|
: lock(mutex, std::adopt_lock)
|
||||||
|
, value(value_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool ownsLock() const { return lock.owns_lock(); }
|
||||||
|
|
||||||
|
const T *operator->() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(ownsLock());
|
||||||
|
return &value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &operator*() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(ownsLock());
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_lock<std::shared_mutex> lock;
|
||||||
|
T const &value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class unique_lock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
unique_lock(T &value_, std::shared_mutex &mutex)
|
||||||
|
: lock(mutex)
|
||||||
|
, value(value_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
unique_lock(T &value_, std::shared_mutex &mutex, std::try_to_lock_t)
|
||||||
|
: lock(mutex, std::try_to_lock)
|
||||||
|
, value(value_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
unique_lock(T &value_, std::shared_mutex &mutex, std::defer_lock_t)
|
||||||
|
: lock(mutex, std::defer_lock)
|
||||||
|
, value(value_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
unique_lock(T &value_, std::shared_mutex &mutex, std::adopt_lock_t)
|
||||||
|
: lock(mutex, std::adopt_lock)
|
||||||
|
, value(value_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool ownsLock() const { return lock.owns_lock(); }
|
||||||
|
|
||||||
|
T *operator->() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(ownsLock());
|
||||||
|
return &value;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator*() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(ownsLock());
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_lock<std::shared_mutex> lock;
|
||||||
|
T &value;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] shared_lock readLocked() const { return shared_lock(value, mutex); }
|
||||||
|
[[nodiscard]] shared_lock readLocked(std::try_to_lock_t) const
|
||||||
|
{
|
||||||
|
return shared_lock(value, mutex, std::try_to_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] unique_lock writeLocked() { return unique_lock(value, mutex); }
|
||||||
|
[[nodiscard]] unique_lock writeLocked(std::try_to_lock_t)
|
||||||
|
{
|
||||||
|
return unique_lock(value, mutex, std::try_to_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Call func with a const reference to the wrapped object
|
||||||
|
void read(const std::function<void(const T &)> &func) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
func(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Call func with a const reference to the wrapped object and returns the result of func
|
||||||
|
template<typename R>
|
||||||
|
[[nodiscard]] R get(const std::function<R(const T &)> &func) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
return func(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] T get() const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Call func with a mutable reference to the wrapped object
|
||||||
|
void write(const std::function<void(T &)> &func)
|
||||||
|
{
|
||||||
|
std::unique_lock lk(mutex);
|
||||||
|
func(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Call func with a mutable reference to the wrapped object and returns the result of func
|
||||||
|
template<typename R>
|
||||||
|
[[nodiscard]] R update(const std::function<R(T &)> &func)
|
||||||
|
{
|
||||||
|
std::unique_lock lk(mutex);
|
||||||
|
return func(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizedValue<T> &operator=(const SynchronizedValue<T> &other)
|
||||||
|
{
|
||||||
|
std::unique_lock lk(mutex, std::defer_lock);
|
||||||
|
std::shared_lock lkOther(other.mutex, std::defer_lock);
|
||||||
|
std::lock(lk, lkOther);
|
||||||
|
value = other.value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizedValue<T> &operator=(const T &other)
|
||||||
|
{
|
||||||
|
std::unique_lock lk(mutex);
|
||||||
|
value = other;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const SynchronizedValue<T> &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex, std ::defer_lock);
|
||||||
|
std::shared_lock lkOther(rhs.mutex, std ::defer_lock);
|
||||||
|
std::lock(lk, lkOther);
|
||||||
|
return value != rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const SynchronizedValue<T> &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex, std ::defer_lock);
|
||||||
|
std::shared_lock lkOther(rhs.mutex, std ::defer_lock);
|
||||||
|
std::lock(lk, lkOther);
|
||||||
|
return value == rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const SynchronizedValue<T> &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex, std ::defer_lock);
|
||||||
|
std::shared_lock lkOther(rhs.mutex, std ::defer_lock);
|
||||||
|
std::lock(lk, lkOther);
|
||||||
|
return value < rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const SynchronizedValue<T> &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex, std ::defer_lock);
|
||||||
|
std::shared_lock lkOther(rhs.mutex, std ::defer_lock);
|
||||||
|
std::lock(lk, lkOther);
|
||||||
|
return value <= rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const SynchronizedValue<T> &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex, std ::defer_lock);
|
||||||
|
std::shared_lock lkOther(rhs.mutex, std ::defer_lock);
|
||||||
|
std::lock(lk, lkOther);
|
||||||
|
return value > rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const SynchronizedValue<T> &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex, std ::defer_lock);
|
||||||
|
std::shared_lock lkOther(rhs.mutex, std ::defer_lock);
|
||||||
|
std::lock(lk, lkOther);
|
||||||
|
return value >= rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const T &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
return value > rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const T &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
return value >= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const T &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
return value != rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const T &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
return value == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const T &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
return value < rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const T &rhs) const
|
||||||
|
{
|
||||||
|
std::shared_lock lk(mutex);
|
||||||
|
return value <= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename L>
|
||||||
|
friend bool operator!=(const L &lhs, const SynchronizedValue<T> &rhs)
|
||||||
|
{
|
||||||
|
return rhs != lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
friend bool operator==(const L &lhs, const SynchronizedValue<T> &rhs)
|
||||||
|
{
|
||||||
|
return rhs == lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
friend bool operator<(const L &lhs, const SynchronizedValue<T> &rhs)
|
||||||
|
{
|
||||||
|
return rhs > lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
friend bool operator<=(const L &lhs, const SynchronizedValue<T> &rhs)
|
||||||
|
{
|
||||||
|
return rhs >= lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
friend bool operator>(const L &lhs, const SynchronizedValue<T> &rhs)
|
||||||
|
{
|
||||||
|
return rhs < lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
friend bool operator>=(const L &lhs, const SynchronizedValue<T> &rhs)
|
||||||
|
{
|
||||||
|
return rhs <= lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::shared_mutex mutex;
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Lock a number of SynchronizedValue's using a dead-lock free algorithm. ( see std::lock() )
|
||||||
|
template<typename... SV>
|
||||||
|
std::tuple<typename SV::unique_lock...> synchronize(SV &...sv)
|
||||||
|
{
|
||||||
|
std::lock(sv.mutex...);
|
||||||
|
typedef std::tuple<typename SV::unique_lock...> t_type;
|
||||||
|
return t_type(typename SV::unique_lock(sv.value, sv.mutex, std::adopt_lock)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Utils
|
@@ -8,6 +8,7 @@ add_subdirectory(fileutils)
|
|||||||
add_subdirectory(fsengine)
|
add_subdirectory(fsengine)
|
||||||
add_subdirectory(fuzzymatcher)
|
add_subdirectory(fuzzymatcher)
|
||||||
add_subdirectory(indexedcontainerproxyconstiterator)
|
add_subdirectory(indexedcontainerproxyconstiterator)
|
||||||
|
add_subdirectory(synchronizedvalue)
|
||||||
add_subdirectory(mathutils)
|
add_subdirectory(mathutils)
|
||||||
add_subdirectory(multicursor)
|
add_subdirectory(multicursor)
|
||||||
add_subdirectory(persistentsettings)
|
add_subdirectory(persistentsettings)
|
||||||
|
4
tests/auto/utils/synchronizedvalue/CMakeLists.txt
Normal file
4
tests/auto/utils/synchronizedvalue/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
add_qtc_test(tst_utils_synchronizedvalue
|
||||||
|
DEPENDS Utils
|
||||||
|
SOURCES tst_synchronizedvalue.cpp
|
||||||
|
)
|
7
tests/auto/utils/synchronizedvalue/synchronizedvalue.qbs
Normal file
7
tests/auto/utils/synchronizedvalue/synchronizedvalue.qbs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import qbs
|
||||||
|
|
||||||
|
QtcAutotest {
|
||||||
|
name: "synchronizedvalue autotest"
|
||||||
|
Depends { name: "Utils" }
|
||||||
|
files: "tst_synchronizedvalue.cpp"
|
||||||
|
}
|
219
tests/auto/utils/synchronizedvalue/tst_synchronizedvalue.cpp
Normal file
219
tests/auto/utils/synchronizedvalue/tst_synchronizedvalue.cpp
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <utils/synchronizedvalue.h>
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
struct TestData
|
||||||
|
{
|
||||||
|
TestData() = default;
|
||||||
|
TestData(int iValue)
|
||||||
|
: i(iValue)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool operator==(const TestData &other) const { return i == other.i && str == other.str; }
|
||||||
|
bool operator!=(const TestData &other) const { return !(*this == other); }
|
||||||
|
QString str;
|
||||||
|
int i{20};
|
||||||
|
};
|
||||||
|
|
||||||
|
class tst_synchronizedvalue : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase() {}
|
||||||
|
|
||||||
|
void read()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data;
|
||||||
|
|
||||||
|
data.read([](const auto &d) {
|
||||||
|
QCOMPARE(d.str, QString());
|
||||||
|
QCOMPARE(d.i, 20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctor()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data(200);
|
||||||
|
QCOMPARE(data.get<int>([](const auto &d) { return d.i; }), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializerCtor()
|
||||||
|
{
|
||||||
|
SynchronizedValue<QList<int>> data({1, 2, 3});
|
||||||
|
QCOMPARE(data.get<QList<int>>([](const auto &d) { return d; }), QList<int>({1, 2, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void get()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data(200);
|
||||||
|
QCOMPARE(data.get<int>([](const auto &d) { return d.i; }), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void constLock()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data(200);
|
||||||
|
QCOMPARE(data.readLocked()->i, 200);
|
||||||
|
QCOMPARE(data.get().i, 200);
|
||||||
|
|
||||||
|
auto lk = data.readLocked();
|
||||||
|
QCOMPARE(lk->i, 200);
|
||||||
|
|
||||||
|
// This should fail to compile:
|
||||||
|
// data.readLocked()->i = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data(123);
|
||||||
|
data.writeLocked()->i = 200;
|
||||||
|
QCOMPARE(data.get<int>([](const auto &d) { return d.i; }), 200);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto wlk = data.writeLocked();
|
||||||
|
wlk->str = "Write locked";
|
||||||
|
}
|
||||||
|
|
||||||
|
QCOMPARE(data.readLocked()->str, "Write locked");
|
||||||
|
|
||||||
|
SynchronizedValue<QString> lockedStr("Hello World");
|
||||||
|
QCOMPARE(*lockedStr.readLocked(), "Hello World");
|
||||||
|
*lockedStr.writeLocked() = "Hello World 2";
|
||||||
|
QCOMPARE(*lockedStr.readLocked(), "Hello World 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void trivialGet()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data(200);
|
||||||
|
TestData d = data.get();
|
||||||
|
QCOMPARE(data, d);
|
||||||
|
QCOMPARE(d.i, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void equalop()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data(200);
|
||||||
|
SynchronizedValue<TestData> data2(300);
|
||||||
|
|
||||||
|
data = data2;
|
||||||
|
|
||||||
|
QCOMPARE(data.get<int>([](const auto &d) { return d.i; }), 300);
|
||||||
|
QCOMPARE(data2.get<int>([](const auto &d) { return d.i; }), 300);
|
||||||
|
|
||||||
|
TestData dataNoLock(1337);
|
||||||
|
data = dataNoLock;
|
||||||
|
|
||||||
|
QCOMPARE(data.get<int>([](const auto &d) { return d.i; }), 1337);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compareEq()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data(200);
|
||||||
|
SynchronizedValue<TestData> data2(300);
|
||||||
|
|
||||||
|
QVERIFY(data != data2);
|
||||||
|
QVERIFY(!(data == data2));
|
||||||
|
|
||||||
|
data2.write([](auto &d) { d.i = 200; });
|
||||||
|
|
||||||
|
QVERIFY(data == data2);
|
||||||
|
QVERIFY(!(data != data2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void operators()
|
||||||
|
{
|
||||||
|
SynchronizedValue<int> data(200);
|
||||||
|
SynchronizedValue<int> data2(300);
|
||||||
|
|
||||||
|
QVERIFY(data < data2);
|
||||||
|
QVERIFY(data <= data2);
|
||||||
|
QVERIFY(data2 > data);
|
||||||
|
QVERIFY(data2 >= data);
|
||||||
|
QVERIFY(data2 != data);
|
||||||
|
QVERIFY(!(data2 == data));
|
||||||
|
|
||||||
|
QVERIFY(data > 100);
|
||||||
|
QVERIFY(data >= 100);
|
||||||
|
QVERIFY(data >= 200);
|
||||||
|
QVERIFY(data < 300);
|
||||||
|
QVERIFY(data <= 300);
|
||||||
|
|
||||||
|
QVERIFY(100 < data);
|
||||||
|
QVERIFY(200 <= data);
|
||||||
|
QVERIFY(199 <= data);
|
||||||
|
QVERIFY(300 > data);
|
||||||
|
QVERIFY(200 >= data);
|
||||||
|
QVERIFY(201 >= data);
|
||||||
|
QVERIFY(200 == data);
|
||||||
|
QVERIFY(200 != data2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyCtor()
|
||||||
|
{
|
||||||
|
SynchronizedValue<TestData> data(123);
|
||||||
|
SynchronizedValue<TestData> data2(data);
|
||||||
|
|
||||||
|
QCOMPARE(data.get<int>([](const auto &d) { return d.i; }), 123);
|
||||||
|
QCOMPARE(data2.get<int>([](const auto &d) { return d.i; }), 123);
|
||||||
|
|
||||||
|
TestData dataNoLock(1337);
|
||||||
|
SynchronizedValue<TestData> data3(dataNoLock);
|
||||||
|
|
||||||
|
QCOMPARE(data3.get<int>([](const auto &d) { return d.i; }), 1337);
|
||||||
|
}
|
||||||
|
|
||||||
|
void multilock()
|
||||||
|
{
|
||||||
|
SynchronizedValue<int> value(100);
|
||||||
|
|
||||||
|
// Multiple reader, no writer
|
||||||
|
{
|
||||||
|
QCOMPARE(value.get(), 100);
|
||||||
|
auto firstLock = value.readLocked();
|
||||||
|
QVERIFY(firstLock.ownsLock());
|
||||||
|
auto secondLock = value.readLocked();
|
||||||
|
QVERIFY(secondLock.ownsLock());
|
||||||
|
|
||||||
|
QCOMPARE(*firstLock, 100);
|
||||||
|
QCOMPARE(*secondLock, 100);
|
||||||
|
|
||||||
|
auto thirdLock = value.writeLocked(std::try_to_lock);
|
||||||
|
QVERIFY(!thirdLock.ownsLock());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single writer, no readers
|
||||||
|
{
|
||||||
|
auto firstLock = value.writeLocked();
|
||||||
|
QVERIFY(firstLock.ownsLock());
|
||||||
|
auto secondLock = value.writeLocked(std::try_to_lock);
|
||||||
|
QVERIFY(!secondLock.ownsLock());
|
||||||
|
|
||||||
|
auto readLock = value.readLocked(std::try_to_lock);
|
||||||
|
QVERIFY(!readLock.ownsLock());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void synchronizeMultiple()
|
||||||
|
{
|
||||||
|
SynchronizedValue<int> sv1;
|
||||||
|
SynchronizedValue<QString> sv2;
|
||||||
|
|
||||||
|
auto [lk1, lk2] = synchronize(sv1, sv2);
|
||||||
|
|
||||||
|
QVERIFY(lk1.ownsLock());
|
||||||
|
QVERIFY(lk2.ownsLock());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Utils
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(Utils::tst_synchronizedvalue)
|
||||||
|
|
||||||
|
#include "tst_synchronizedvalue.moc"
|
@@ -13,6 +13,7 @@ Project {
|
|||||||
"fsengine/fsengine.qbs",
|
"fsengine/fsengine.qbs",
|
||||||
"fuzzymatcher/fuzzymatcher.qbs",
|
"fuzzymatcher/fuzzymatcher.qbs",
|
||||||
"indexedcontainerproxyconstiterator/indexedcontainerproxyconstiterator.qbs",
|
"indexedcontainerproxyconstiterator/indexedcontainerproxyconstiterator.qbs",
|
||||||
|
"synchronizedvalue/synchronizedvalue.qbs",
|
||||||
"mathutils/mathutils.qbs",
|
"mathutils/mathutils.qbs",
|
||||||
"multicursor/multicursor.qbs",
|
"multicursor/multicursor.qbs",
|
||||||
"persistentsettings/persistentsettings.qbs",
|
"persistentsettings/persistentsettings.qbs",
|
||||||
|
Reference in New Issue
Block a user