Skip to content

Commit

Permalink
use shared pointer inside Chunk Cache
Browse files Browse the repository at this point in the history
In very seldom cases Minnutor still crashes with memory access
violations:
If a Chunk gets removed from the Cache but still gets loaded or
rendered, the data structure is removed from heap, but still written to.

By using QSharedPointer<Chunk> always some routine has ownership and the
heap is only freed when all routines are finished with processing.
  • Loading branch information
EtlamGit committed Aug 6, 2019
1 parent e0c1b08 commit ed12f90
Show file tree
Hide file tree
Showing 12 changed files with 47 additions and 38 deletions.
28 changes: 16 additions & 12 deletions chunkcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,39 +69,43 @@ QString ChunkCache::getPath() const {
return path;
}

Chunk *ChunkCache::fetchCached(int cx, int cz) {
QSharedPointer<Chunk> ChunkCache::fetchCached(int cx, int cz) {
// try to get Chunk from Cache
ChunkID id(cx, cz);
mutex.lock();
Chunk *chunk = cache[id]; // const operation
QSharedPointer<Chunk> * p_chunk(cache[id]); // const operation
mutex.unlock();

return chunk;
if (p_chunk != NULL )
return QSharedPointer<Chunk>(*p_chunk);
else
return QSharedPointer<Chunk>(NULL); // we're loading this chunk, or it's blank.
}

Chunk *ChunkCache::fetch(int cx, int cz) {
QSharedPointer<Chunk> ChunkCache::fetch(int cx, int cz) {
// try to get Chunk from Cache
ChunkID id(cx, cz);
mutex.lock();
Chunk *chunk = cache[id]; // const operation
QSharedPointer<Chunk> * p_chunk(cache[id]); // const operation
mutex.unlock();
if (chunk != NULL) {
if (p_chunk != NULL ) {
QSharedPointer<Chunk> chunk(*p_chunk);
if (chunk->loaded)
return chunk;
return NULL; // we're loading this chunk, or it's blank.
return QSharedPointer<Chunk>(NULL); // we're loading this chunk, or it's blank.
}
// launch background process to load this chunk
chunk = new Chunk();
connect(chunk, SIGNAL(structureFound(QSharedPointer<GeneratedStructure>)),
this, SLOT (routeStructure(QSharedPointer<GeneratedStructure>)));
p_chunk = new QSharedPointer<Chunk>(new Chunk());
connect(p_chunk->data(), SIGNAL(structureFound(QSharedPointer<GeneratedStructure>)),
this, SLOT (routeStructure(QSharedPointer<GeneratedStructure>)));
mutex.lock();
cache.insert(id, chunk); // non-const operation !
cache.insert(id, p_chunk); // non-const operation !
mutex.unlock();
ChunkLoader *loader = new ChunkLoader(path, cx, cz);
connect(loader, SIGNAL(loaded(int, int)),
this, SLOT(gotChunk(int, int)));
loaderThreadPool.start(loader);
return NULL;
return QSharedPointer<Chunk>(NULL);
}

void ChunkCache::gotChunk(int cx, int cz) {
Expand Down
17 changes: 10 additions & 7 deletions chunkcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <QCache>
#include "./chunk.h"

// ChunkID is the key used to identify entries in the Cache
// Chunks are identified by their coordinates (CX,CZ) but a single key is needed to access a map like structure
class ChunkID {
public:
ChunkID(int cx, int cz);
Expand All @@ -15,6 +17,7 @@ class ChunkID {
int cx, cz;
};


class ChunkCache : public QObject {
Q_OBJECT

Expand All @@ -32,8 +35,8 @@ class ChunkCache : public QObject {
void clear();
void setPath(QString path);
QString getPath() const;
Chunk *fetch(int cx, int cz); // fetch Chunk and load when not found
Chunk *fetchCached(int cx, int cz); // fetch Chunk only if cached
QSharedPointer<Chunk> fetch(int cx, int cz); // fetch Chunk and load when not found
QSharedPointer<Chunk> fetchCached(int cx, int cz); // fetch Chunk only if cached

signals:
void chunkLoaded(int cx, int cz);
Expand All @@ -47,11 +50,11 @@ class ChunkCache : public QObject {
void routeStructure(QSharedPointer<GeneratedStructure> structure);

private:
QString path; // path to folder with region files
QCache<ChunkID, Chunk> cache; // real Cache
QMutex mutex; // Mutex for accessing the Cache
int maxcache; // number of Chunks that fit into Cache
QThreadPool loaderThreadPool; // extra thread pool for loading
QString path; // path to folder with region files
QCache<ChunkID, QSharedPointer<Chunk>> cache; // real Cache
QMutex mutex; // Mutex for accessing the Cache
int maxcache; // number of Chunks that fit into Cache
QThreadPool loaderThreadPool; // extra thread pool for loading
};

#endif // CHUNKCACHE_H_
4 changes: 3 additions & 1 deletion chunkloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#include "./chunkcache.h"
#include "./chunk.h"


ChunkLoader::ChunkLoader(QString path, int cx, int cz)
: path(path)
, cx(cx), cz(cz)
, cache(ChunkCache::Instance())
{}

ChunkLoader::~ChunkLoader()
{}

Expand Down Expand Up @@ -44,7 +46,7 @@ void ChunkLoader::run() {
return;
}
// get existing Chunk entry from Cache
Chunk *chunk = cache.fetchCached(cx, cz);
QSharedPointer<Chunk> chunk(cache.fetchCached(cx, cz));
// parse Chunk data
// Chunk will be flagged "loaded" in a thread save way
if (chunk) {
Expand Down
6 changes: 3 additions & 3 deletions chunkrenderer.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** Copyright (c) 2019, Mc_Etlam */
/** Copyright (c) 2019, EtlamGit */

#include "./chunk.h"
#include "./chunkrenderer.h"
Expand All @@ -19,15 +19,15 @@ ChunkRenderer::ChunkRenderer(int cx, int cz, int y, int flags)

void ChunkRenderer::run() {
// get existing Chunk entry from Cache
Chunk *chunk = cache.fetchCached(cx, cz);
QSharedPointer<Chunk> chunk(cache.fetchCached(cx, cz));
// render Chunk data
if (chunk) {
renderChunk(chunk);
}
emit rendered(cx, cz);
}

void ChunkRenderer::renderChunk(Chunk *chunk) {
void ChunkRenderer::renderChunk(QSharedPointer<Chunk> chunk) {
int offset = 0;
uchar *bits = chunk->image;
uchar *depthbits = chunk->depth;
Expand Down
4 changes: 2 additions & 2 deletions chunkrenderer.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** Copyright (c) 2019, Mc_Etlam */
/** Copyright (c) 2019, EtlamGit */
#ifndef CHUNKRENDERER_H
#define CHUNKRENDERER_H

Expand All @@ -17,7 +17,7 @@ class ChunkRenderer : public QObject, public QRunnable {
void run();

public: // public to allow usage from WorldSave
void renderChunk(Chunk *chunk);
void renderChunk(QSharedPointer<Chunk> chunk);

signals:
void rendered(int cx, int cz);
Expand Down
2 changes: 1 addition & 1 deletion entity.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** Copyright 2014 Mc_Etlam */
/** Copyright 2014 EtlamGit */
#include <QPainter>
#include "./entity.h"
#include "./entityidentifier.h"
Expand Down
2 changes: 1 addition & 1 deletion entity.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** Copyright 2014 Mc_Etlam */
/** Copyright 2014 EtlamGit */
#ifndef ENTITY_H_
#define ENTITY_H_

Expand Down
2 changes: 1 addition & 1 deletion entityidentifier.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** Copyright (c) 2014, Mc_Etlam */
/** Copyright (c) 2014, EtlamGit */
#include <QDebug>
#include <assert.h>
#include "./entityidentifier.h"
Expand Down
2 changes: 1 addition & 1 deletion entityidentifier.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** Copyright (c) 2014, Mc_Etlam */
/** Copyright (c) 2014, EtlamGit */
#ifndef ENTITYIDENTIFIER_H_
#define ENTITYIDENTIFIER_H_

Expand Down
10 changes: 5 additions & 5 deletions mapview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ void MapView::redraw() {
// draw the entities
for (int cz = startz; cz < startz + blockstall; cz++) {
for (int cx = startx; cx < startx + blockswide; cx++) {
Chunk *chunk = cache.fetch(cx, cz);
QSharedPointer<Chunk> chunk(cache.fetch(cx, cz));
if (chunk) {
// Entities from Chunks
for (auto &type : overlayItemTypes) {
Expand Down Expand Up @@ -373,7 +373,7 @@ void MapView::drawChunk(int x, int z) {

uchar *src = placeholder;
// fetch the chunk
Chunk *chunk = cache.fetch(x, z);
QSharedPointer<Chunk> chunk(cache.fetch(x, z));
if (chunk && !chunk->loaded) return;

if (chunk && (chunk->renderedAt != depth ||
Expand Down Expand Up @@ -445,7 +445,7 @@ void MapView::drawChunk(int x, int z) {
void MapView::getToolTip(int x, int z) {
int cx = floor(x / 16.0);
int cz = floor(z / 16.0);
Chunk *chunk = cache.fetch(cx, cz);
QSharedPointer<Chunk> chunk(cache.fetch(cx, cz));
int offset = (x & 0xf) + (z & 0xf) * 16;
int y = 0;

Expand Down Expand Up @@ -544,15 +544,15 @@ void MapView::setVisibleOverlayItemTypes(const QSet<QString>& itemTypes) {
int MapView::getY(int x, int z) {
int cx = floor(x / 16.0);
int cz = floor(z / 16.0);
Chunk *chunk = cache.fetch(cx, cz);
QSharedPointer<Chunk> chunk(cache.fetch(cx, cz));
return chunk ? chunk->depth[(x & 0xf) + (z & 0xf) * 16] : -1;
}

QList<QSharedPointer<OverlayItem>> MapView::getItems(int x, int y, int z) {
QList<QSharedPointer<OverlayItem>> ret;
int cx = floor(x / 16.0);
int cz = floor(z / 16.0);
Chunk *chunk = cache.fetch(cx, cz);
QSharedPointer<Chunk> chunk(cache.fetch(cx, cz));

if (chunk) {
double invzoom = 10.0 / zoom;
Expand Down
6 changes: 3 additions & 3 deletions worldsave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ void WorldSave::run() {
} else {
uchar *raw = f.map(coffset * 4096, numSectors * 4096);
NBT nbt(raw);
Chunk *chunk = new Chunk();
QSharedPointer<Chunk> chunk(new Chunk());
chunk->load(nbt);
f.unmap(raw);
drawChunk(scanlines, width * 4 + 1, x - left, chunk);
delete chunk;
chunk.reset();
}
f.close();
}
Expand Down Expand Up @@ -289,7 +289,7 @@ void WorldSave::blankChunk(uchar *scanlines, int stride, int x) {
memset(scanlines + offset, 0, 16 * 4);
}

void WorldSave::drawChunk(uchar *scanlines, int stride, int x, Chunk *chunk) {
void WorldSave::drawChunk(uchar *scanlines, int stride, int x, QSharedPointer<Chunk> chunk) {
// calculate attenuation
float attenuation = 1.0f;
if (this->regionChecker && static_cast<int>(floor(chunk->chunkX / 32.0f) +
Expand Down
2 changes: 1 addition & 1 deletion worldsave.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class WorldSave : public QObject, public QRunnable {

private:
void blankChunk(uchar *scanlines, int stride, int x);
void drawChunk(uchar *scanlines, int stride, int x, Chunk *chunk);
void drawChunk(uchar *scanlines, int stride, int x, QSharedPointer<Chunk> chunk);

QString filename;
MapView *map;
Expand Down

0 comments on commit ed12f90

Please sign in to comment.