Commit 9ba577af authored by Andrey Kamakin's avatar Andrey Kamakin

Fixed errors with zero KisChunk && KisTileData.

Implemented simple version of garbage collector.

Ref T8874
parent 8db97e0a
......@@ -26,7 +26,8 @@ public:
private:
Atomic<typename Details::Table*> m_root;
QSBR m_gc;
// QSBR m_gc;
GarbageCollector<Property> m_gc;
public:
ConcurrentMap(quint64 capacity = Details::InitialSize) : m_root(Details::Table::create(capacity))
......@@ -39,7 +40,12 @@ public:
table->destroy();
}
QSBR &getGC()
// QSBR &getGC()
// {
// return m_gc;
// }
GarbageCollector<Property> &getGC()
{
return m_gc;
}
......
......@@ -93,7 +93,7 @@ struct Leapfrog {
}
};
class TableMigration : public SimpleJobCoordinator::Job
class TableMigration : public SimpleJobCoordinator::Job, public Property
{
public:
struct Source {
......@@ -127,20 +127,26 @@ struct Leapfrog {
}
virtual ~TableMigration() override
{
}
void destroy()
{
// Destroy all source tables.
for (quint64 i = 0; i < m_numSources; i++)
if (getSources()[i].table)
for (quint64 i = 0; i < m_numSources; i++) {
if (getSources()[i].table) {
getSources()[i].table->destroy();
// Delete the migration object itself.
this->TableMigration::~TableMigration();
std::free(this);
}
}
}
// void destroy()
// {
// // Destroy all source tables.
// for (quint64 i = 0; i < m_numSources; i++)
// if (getSources()[i].table)
// getSources()[i].table->destroy();
// // Delete the migration object itself.
// this->TableMigration::~TableMigration();
// std::free(this);
// }
Source* getSources() const
{
return (Source*)(this + 1);
......@@ -541,7 +547,7 @@ endMigration:
}
// We're done with this TableMigration. Queue it for GC.
m_map.getGC().enqueue(&TableMigration::destroy, this);
// m_map.getGC().enqueue(this);
}
#endif // LEAPFROG_H
......@@ -17,6 +17,88 @@
#define CALL_MEMBER(obj, pmf) ((obj).*(pmf))
struct Property {
Property() = default;
virtual ~Property() = default;
};
template <class T>
class GarbageCollector
{
public:
~GarbageCollector()
{
cleanUpNodes();
}
void enqueue(T *data)
{
m_rawPointerUsers.ref();
Node *newNode = new Node(data);
Node *head;
do {
head = m_freeNodes.loadAcquire();
newNode->next = head;
} while (!m_freeNodes.testAndSetRelease(head, newNode));
m_rawPointerUsers.deref();
}
void update()
{
m_rawPointerUsers.ref();
if (m_rawPointerUsers == 1) {
Node *head = m_freeNodes.loadAcquire();
if (!(reinterpret_cast<std::uintptr_t>(head) & 1)) {
if (m_freeNodes.testAndSetOrdered(head, reinterpret_cast<Node *>(reinterpret_cast<std::uintptr_t>(head) | 1))) {
if (m_rawPointerUsers == 1) {
if (m_freeNodes.testAndSetOrdered(reinterpret_cast<Node *>(reinterpret_cast<std::uintptr_t>(head) | 1), 0)) {
cleanUpNodes();
}
} else {
m_freeNodes.testAndSetOrdered(reinterpret_cast<Node *>(reinterpret_cast<std::uintptr_t>(head) | 1), head);
}
}
}
}
m_rawPointerUsers.deref();
}
private:
struct Node {
Node(T *data) : d(data) {}
Node *next;
T* d;
};
inline void cleanUpNodes()
{
Node *head = m_freeNodes.fetchAndStoreOrdered(0);
if (head) {
freeList(head);
}
}
inline void freeList(Node *first)
{
Node *next;
while (first) {
next = first->next;
delete first;
first = next;
}
}
private:
QAtomicInt m_rawPointerUsers;
QAtomicPointer<Node> m_freeNodes;
};
class QSBR
{
private:
......
......@@ -5,8 +5,8 @@
#include "kis_shared_ptr.h"
#include "3rdparty/lock_free_map/concurrent_map.h"
#include "kis_lockless_stack.h"
#include "kis_tile.h"
#include <boost/functional/hash.hpp>
template <class T>
......@@ -27,89 +27,43 @@ public:
TileTypeSP insert(quint32 key, TileTypeSP value)
{
m_rawPointerUsers.fetchAndAddRelaxed(1);
TileTypeSP::ref(&value, value.data());
TileType *result = m_map.assign(key, value.data());
if (result) {
MemoryReclaimer *tmp = new MemoryReclaimer(result);
m_map.getGC().enqueue(&MemoryReclaimer::destroy, tmp);
m_map.getGC().enqueue(new MemoryReclaimer(result));
} else {
m_numTiles.fetchAndAddRelaxed(1);
}
m_rawPointerUsers.fetchAndSubRelaxed(1);
// m_map.getGC().update();
return TileTypeSP(result);
}
TileTypeSP erase(quint32 key)
{
m_rawPointerUsers.fetchAndAddRelaxed(1);
TileType *result = m_map.erase(key);
TileTypeSP ptr(result);
if (result) {
m_numTiles.fetchAndSubRelaxed(1);
MemoryReclaimer *tmp = new MemoryReclaimer(result);
m_map.getGC().enqueue(&MemoryReclaimer::destroy, tmp);
}
if (m_rawPointerUsers == 1) {
m_map.getGC().update(m_context);
m_map.getGC().enqueue(new MemoryReclaimer(result));
}
m_rawPointerUsers.fetchAndSubRelaxed(1);
// m_map.getGC().update();
return ptr;
}
TileTypeSP get(quint32 key)
{
m_rawPointerUsers.fetchAndAddRelaxed(1);
TileTypeSP result(m_map.get(key));
if (m_rawPointerUsers == 1) {
m_map.getGC().update(m_context);
}
m_rawPointerUsers.fetchAndSubRelaxed(1);
// m_map.getGC().update();
return result;
}
TileTypeSP getLazy(quint32 key, TileTypeSP value, bool &newTile)
{
m_rawPointerUsers.fetchAndAddRelaxed(1);
newTile = false;
TileType *tile;
typename ConcurrentMap<quint32, TileType*>::Mutator mutator = m_map.insertOrFind(key);
if (!mutator.getValue()) {
TileTypeSP::ref(&value, value.data());
TileType *result = mutator.exchangeValue(value.data());
if (result) {
MemoryReclaimer *tmp = new MemoryReclaimer(result);
m_map.getGC().enqueue(&MemoryReclaimer::destroy, tmp);
} else {
m_numTiles.fetchAndAddRelaxed(1);
}
newTile = true;
tile = m_map.get(key);
} else {
tile = mutator.getValue();
}
if (m_rawPointerUsers == 1) {
m_map.getGC().update(m_context);
}
m_rawPointerUsers.fetchAndSubRelaxed(1);
return TileTypeSP(tile);
}
bool isEmpty()
{
return !m_numTiles;
return !m_numTiles.load();
}
bool tileExists(qint32 col, qint32 row);
......@@ -150,11 +104,11 @@ public:
void clear();
void setDefaultTileData(KisTileData *defaultTileData);
KisTileData* defaultTileData() const;
KisTileData* defaultTileData();
qint32 numTiles()
{
return m_numTiles;
return m_numTiles.load();
}
void debugPrintInfo();
......@@ -168,15 +122,11 @@ public:
private:
static inline quint32 calculateHash(qint32 col, qint32 row);
struct MemoryReclaimer {
struct MemoryReclaimer : public Property {
MemoryReclaimer(TileType *data) : d(data) {}
~MemoryReclaimer() = default;
void destroy()
~MemoryReclaimer()
{
TileTypeSP::deref(reinterpret_cast<TileTypeSP *>(this), d);
this->MemoryReclaimer::~MemoryReclaimer();
delete this;
}
private:
......@@ -185,10 +135,9 @@ private:
private:
ConcurrentMap<quint32, TileType*> m_map;
QSBR::Context m_context;
QAtomicInt m_rawPointerUsers;
QAtomicInt m_numTiles;
QReadWriteLock m_rwLock;
KisLocklessStack<TileType*> m_stack;
KisTileData *m_defaultTileData;
KisMementoManager *m_mementoManager;
......@@ -214,7 +163,7 @@ public:
TileTypeSP tile() const
{
return m_iter.getValue();
return TileTypeSP(m_iter.getValue());
}
bool isDone() const
......@@ -243,10 +192,8 @@ private:
template <class T>
KisTileHashTableTraits2<T>::KisTileHashTableTraits2()
: m_rawPointerUsers(0), m_numTiles(0),
m_defaultTileData(0), m_mementoManager(0)
: m_numTiles(0), m_rwLock(QReadWriteLock::NonRecursive), m_defaultTileData(0), m_mementoManager(0)
{
m_context = m_map.getGC().createContext();
}
template <class T>
......@@ -271,8 +218,7 @@ KisTileHashTableTraits2<T>::KisTileHashTableTraits2(const KisTileHashTableTraits
template <class T>
KisTileHashTableTraits2<T>::~KisTileHashTableTraits2()
{
clear();
m_map.getGC().destroyContext(m_context);
// clear();
}
template<class T>
......@@ -291,19 +237,38 @@ typename KisTileHashTableTraits2<T>::TileTypeSP KisTileHashTableTraits2<T>::getE
template <class T>
typename KisTileHashTableTraits2<T>::TileTypeSP KisTileHashTableTraits2<T>::getTileLazy(qint32 col, qint32 row, bool &newTile)
{
newTile = false;
TileTypeSP tile;
{
QReadLocker guard(&m_rwLock);
tile = new TileType(col, row, m_defaultTileData, m_mementoManager);
}
quint32 idx = calculateHash(col, row);
return getLazy(idx, tile, newTile);
typename ConcurrentMap<quint32, TileType*>::Mutator mutator = m_map.insertOrFind(idx);
if (!mutator.getValue()) {
{
QReadLocker guard(&m_rwLock);
tile = new TileType(col, row, m_defaultTileData, m_mementoManager);
}
TileTypeSP::ref(&tile, tile.data());
TileType *result = mutator.exchangeValue(tile.data());
if (result) {
m_map.getGC().enqueue(new MemoryReclaimer(result));
} else {
m_numTiles.fetchAndAddRelaxed(1);
}
newTile = true;
tile = m_map.get(idx);
} else {
tile = mutator.getValue();
}
// m_map.getGC().update();
return tile;
}
template <class T>
typename KisTileHashTableTraits2<T>::TileTypeSP KisTileHashTableTraits2<T>::getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile)
{
m_rawPointerUsers.fetchAndAddRelaxed(1);
quint32 idx = calculateHash(col, row);
TileTypeSP tile(m_map.get(idx));
existingTile = tile;
......@@ -313,7 +278,7 @@ typename KisTileHashTableTraits2<T>::TileTypeSP KisTileHashTableTraits2<T>::getR
tile = new TileType(col, row, m_defaultTileData, 0);
}
m_rawPointerUsers.fetchAndSubRelaxed(1);
// m_map.getGC().update();
return tile;
}
......@@ -363,8 +328,9 @@ inline void KisTileHashTableTraits2<T>::setDefaultTileData(KisTileData *defaultT
}
template <class T>
inline KisTileData* KisTileHashTableTraits2<T>::defaultTileData() const
inline KisTileData* KisTileHashTableTraits2<T>::defaultTileData()
{
QReadLocker guard(&m_rwLock);
return m_defaultTileData;
}
......
......@@ -122,67 +122,4 @@ void LockFreeMapTest::testMainOperations()
QVERIFY(insertSum == eraseSum);
}
void LockFreeMapTest::testLazy()
{
const qint32 numCycles = 50000;
const qint32 numTypes = 2;
QList<StressJob *> jobs;
KisTileHashTableTraits2<Wrapper> map;
auto func = [&](qint64 & eraseSum, qint64 & insertSum) {
for (qint32 i = 1; i < numCycles + 1; ++i) {
auto type = i % numTypes;
switch (type) {
case 0: {
auto result = map.erase(i - 1);
if (result.data()) {
eraseSum += result->member();
}
break;
}
case 1: {
bool newTile = false;
auto result = map.getLazy(i, KisSharedPtr<Wrapper>(new Wrapper()), newTile);
if (result.data()) {
insertSum += result->member();
}
break;
}
}
}
};
for (qint32 i = 0; i < NUM_THREADS; ++i) {
StressJob *job = new StressJob(func);
job->setAutoDelete(false);
jobs.append(job);
}
QThreadPool pool;
pool.setMaxThreadCount(NUM_THREADS);
QBENCHMARK {
for (auto &job : jobs)
{
pool.start(job);
}
pool.waitForDone();
}
qint64 insertSum = 0;
qint64 eraseSum = 0;
for (qint32 i = 0; i < NUM_THREADS; ++i) {
StressJob *job = jobs.takeLast();
eraseSum += job->eraseSum();
insertSum += job->insertSum();
delete job;
}
QVERIFY(insertSum == eraseSum);
}
QTEST_GUILESS_MAIN(LockFreeMapTest)
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