Commit 59230b15 authored by Andrey Kamakin's avatar Andrey Kamakin

Added wrapper on concurrent map for auto GC.

Ref T8874
parent 5f8e2b66
......@@ -12,6 +12,7 @@
#define CONCURRENTMAP_LEAPFROG_H
#include "Leapfrog.h"
#include <QDebug>
template <typename K, typename V, class KT = DefaultKeyTraits<K>, class VT = DefaultValueTraits<V> >
class ConcurrentMap_Leapfrog
......@@ -205,7 +206,7 @@ public:
if (m_cell->value.compareExchangeStrong(m_value, Value(ValueTraits::NullValue), Consume)) {
// Exchange was successful and a non-NULL value was erased and returned by reference in m_value.
Q_ASSERT(m_value != ValueTraits::NullValue); // Implied by the test at the start of the loop.
// Q_ASSERT(m_value != ValueTraits::NullValue); // Implied by the test at the start of the loop.
Value result = m_value;
m_value = Value(ValueTraits::NullValue); // Leave the mutator in a valid state
return result;
......@@ -345,4 +346,55 @@ public:
};
};
struct Foo {
void destroy()
{
delete this;
}
};
template <class T>
class KisLockFreeMap
{
public:
KisLockFreeMap() : m_currentThreads(0)
{
m_context = QSBR::instance().createContext();
}
~KisLockFreeMap()
{
QSBR::instance().destroyContext(m_context);
}
T insert(qint32 key, T value)
{
return m_map.assign(key, value);
}
void erase(qint32 key)
{
qint32 currentThreads = m_currentThreads.fetchAdd(1, ConsumeRelease);
T val = m_map.erase(key);
if (QTypeInfo<T>::isPointer) {
QSBR::instance().enqueue(&Foo::destroy, val);
}
qint32 expected = 1;
if (m_currentThreads.compareExchangeStrong(expected, currentThreads, Consume)) {
QSBR::instance().update(m_context);
}
m_currentThreads.fetchSub(1, ConsumeRelease);
}
T get(qint32 key)
{
return m_map.get(key);
}
ConcurrentMap_Leapfrog<qint32, T> m_map;
private:
QSBR::Context m_context;
Atomic<qint32> m_currentThreads;
};
#endif // CONCURRENTMAP_LEAPFROG_H
......@@ -41,7 +41,7 @@ struct DefaultKeyTraits {
static Hash hash(T key)
{
return std::hash<qint32>()(Hash(key)) & std::hash<qint32>()(Hash(key));
return std::hash<Hash>()(Hash(key));
}
};
......
......@@ -15,6 +15,8 @@
#include <QMutex>
#include <QMutexLocker>
// TODO: Think about changing signature for std::function for more flexible usage
#define CALL_MEMBER(obj, pmf) ((obj).*(pmf))
class QSBR
......
......@@ -4,6 +4,7 @@
#include "kis_debug.h"
#include "tiles3/LockFreeMap/ConcurrentMap_Leapfrog.h"
#include "kis_shared_ptr.h"
#define NUM_TYPES 2
......@@ -139,4 +140,56 @@ void LockfreeMapTest::iteratorTest()
QVERIFY(sum == testSum);
}
class StressJobWrapper : public QRunnable
{
public:
StressJobWrapper(KisLockFreeMap<Foo*> &map) : m_map(map) {}
protected:
void run() override
{
for (int i = 1; i < NUM_CYCLES + 1; i++) {
auto type = i % NUM_TYPES;
switch (type) {
case 0:
m_map.erase(i);
break;
case 1:
m_map.insert(i + 1, new Foo);
break;
}
}
}
private:
KisLockFreeMap<Foo*> &m_map;
};
void LockfreeMapTest::testWrapper()
{
QThreadPool pool;
pool.setMaxThreadCount(NUM_THREADS);
KisLockFreeMap<Foo*> map;
for (auto i = 0; i < NUM_THREADS; ++i) {
StressJobWrapper *task = new StressJobWrapper(map);
task->setAutoDelete(true);
pool.start(task);
}
pool.waitForDone();
ConcurrentMap_Leapfrog<qint32, Foo*>::Iterator iter(map.m_map);
qint32 sum = 0;
while (iter.isValid()) {
if (iter.getValue()) {
++sum;
}
iter.next();
}
qDebug() << sum;
}
QTEST_GUILESS_MAIN(LockfreeMapTest)
......@@ -11,6 +11,7 @@ private Q_SLOTS:
void testOperations();
void stressTestLockless();
void iteratorTest();
void testWrapper();
};
#endif // KIS_LOCK_FREE_MAP_TEST_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment