Skip to content

Commit

Permalink
Fix crashes related to tile loading in background
Browse files Browse the repository at this point in the history
- feature source must not be shared by multiple loaders (not thread safe)
- root rule must not be shared (not thread safe)
- delete map scene before map settings
  • Loading branch information
wonder-sk committed Jan 8, 2020
1 parent 108e8c8 commit e52bdf3
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 20 deletions.
41 changes: 30 additions & 11 deletions src/3d/qgsrulebasedchunkloader_p.cpp
Expand Up @@ -17,6 +17,7 @@ QgsRuleBasedChunkLoader::QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderF
: QgsChunkLoader( node )
, mFactory( factory )
, mContext( factory->mMap )
, mSource( new QgsVectorLayerFeatureSource( factory->mLayer ) )
{
if ( node->level() < mFactory->mLeafLevel )
{
Expand All @@ -31,10 +32,16 @@ QgsRuleBasedChunkLoader::QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderF
exprContext.setFields( layer->fields() );
mContext.setExpressionContext( exprContext );

mFactory->mRootRule->createHandlers( layer, mHandlers );
// factory is shared among multiple loaders which may be run at the same time
// so we need a local copy of our rule tree that does not intefere with others
// (e.g. it happened that filter expressions with invalid syntax would cause
// nasty crashes when trying to simultaneously record evaluation error)
mRootRule.reset( mFactory->mRootRule->clone() );

mRootRule->createHandlers( layer, mHandlers );

QSet<QString> attributeNames;
mFactory->mRootRule->prepare( mContext, attributeNames, mHandlers );
mRootRule->prepare( mContext, attributeNames, mHandlers );

// build the feature request
QgsFeatureRequest req;
Expand All @@ -54,20 +61,32 @@ QgsRuleBasedChunkLoader::QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderF
QgsEventTracing::ScopedEvent e( "3D", "RB chunk load" );

QgsFeature f;
QgsFeatureIterator fi = mFactory->mSource->getFeatures( req );
QgsFeatureIterator fi = mSource->getFeatures( req );
while ( fi.nextFeature( f ) )
{
if ( mCanceled )
break;
mContext.expressionContext().setFeature( f );
mFactory->mRootRule->registerFeature( f, mContext, mHandlers );
mRootRule->registerFeature( f, mContext, mHandlers );
}
} );

// emit finished() as soon as the handler is populated with features
QFutureWatcher<void> *fw = new QFutureWatcher<void>( nullptr );
fw->setFuture( future );
connect( fw, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
mFutureWatcher = new QFutureWatcher<void>( this );
mFutureWatcher->setFuture( future );
connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
}

QgsRuleBasedChunkLoader::~QgsRuleBasedChunkLoader()
{
if ( mFutureWatcher && !mFutureWatcher->isFinished() )
{
disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
mFutureWatcher->waitForFinished();
}

qDeleteAll( mHandlers );
mHandlers.clear();
}

void QgsRuleBasedChunkLoader::cancel()
Expand All @@ -86,9 +105,6 @@ Qt3DCore::QEntity *QgsRuleBasedChunkLoader::createEntity( Qt3DCore::QEntity *par
for ( QgsFeature3DHandler *handler : mHandlers.values() )
handler->finalize( entity, mContext );

qDeleteAll( mHandlers );
mHandlers.clear();

return entity;
}

Expand All @@ -101,7 +117,10 @@ QgsRuleBasedChunkLoaderFactory::QgsRuleBasedChunkLoaderFactory( const Qgs3DMapSe
, mLayer( vl )
, mRootRule( rootRule->clone() )
, mLeafLevel( leafLevel )
, mSource( new QgsVectorLayerFeatureSource( vl ) )
{
}

QgsRuleBasedChunkLoaderFactory::~QgsRuleBasedChunkLoaderFactory()
{
}

Expand Down
6 changes: 5 additions & 1 deletion src/3d/qgsrulebasedchunkloader_p.h
Expand Up @@ -17,6 +17,7 @@ class QgsRuleBasedChunkLoaderFactory : public QgsChunkLoaderFactory
{
public:
QgsRuleBasedChunkLoaderFactory( const Qgs3DMapSettings &map, QgsVectorLayer *vl, QgsRuleBased3DRenderer::Rule *rootRule, int leafLevel );
~QgsRuleBasedChunkLoaderFactory();

//! Creates loader for the given chunk node. Ownership of the returned is passed to the caller.
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const;
Expand All @@ -25,7 +26,6 @@ class QgsRuleBasedChunkLoaderFactory : public QgsChunkLoaderFactory
QgsVectorLayer *mLayer;
std::unique_ptr<QgsRuleBased3DRenderer::Rule> mRootRule;
int mLeafLevel;
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
};


Expand All @@ -35,6 +35,7 @@ class QgsRuleBasedChunkLoader : public QgsChunkLoader
{
public:
QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderFactory *factory, QgsChunkNode *node );
~QgsRuleBasedChunkLoader();

virtual void cancel();
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent );
Expand All @@ -43,7 +44,10 @@ class QgsRuleBasedChunkLoader : public QgsChunkLoader
const QgsRuleBasedChunkLoaderFactory *mFactory;
QgsRuleBased3DRenderer::RuleToHandlerMap mHandlers;
Qgs3DRenderContext mContext;
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
bool mCanceled = false;
QFutureWatcher<void> *mFutureWatcher = nullptr;
std::unique_ptr<QgsRuleBased3DRenderer::Rule> mRootRule;
};


Expand Down
24 changes: 17 additions & 7 deletions src/3d/qgsvectorlayerchunkloader_p.cpp
Expand Up @@ -38,6 +38,7 @@ QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkL
: QgsChunkLoader( node )
, mFactory( factory )
, mContext( factory->mMap )
, mSource( new QgsVectorLayerFeatureSource( factory->mLayer ) )
{
if ( node->level() < mFactory->mLeafLevel )
{
Expand Down Expand Up @@ -83,7 +84,7 @@ QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkL
QgsEventTracing::ScopedEvent e( "3D", "VL chunk load" );

QgsFeature f;
QgsFeatureIterator fi = mFactory->mSource->getFeatures( req );
QgsFeatureIterator fi = mSource->getFeatures( req );
while ( fi.nextFeature( f ) )
{
if ( mCanceled )
Expand All @@ -94,9 +95,21 @@ QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkL
} );

// emit finished() as soon as the handler is populated with features
QFutureWatcher<void> *fw = new QFutureWatcher<void>( nullptr );
fw->setFuture( future );
connect( fw, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
mFutureWatcher = new QFutureWatcher<void>( this );
mFutureWatcher->setFuture( future );
connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
}

QgsVectorLayerChunkLoader::~QgsVectorLayerChunkLoader()
{
if ( mFutureWatcher && !mFutureWatcher->isFinished() )
{
disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
mFutureWatcher->waitForFinished();
}

delete mHandler;
mHandler = nullptr;
}

void QgsVectorLayerChunkLoader::cancel()
Expand All @@ -113,8 +126,6 @@ Qt3DCore::QEntity *QgsVectorLayerChunkLoader::createEntity( Qt3DCore::QEntity *p

Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
mHandler->finalize( entity, mContext );
delete mHandler;
mHandler = nullptr;
return entity;
}

Expand All @@ -127,7 +138,6 @@ QgsVectorLayerChunkLoaderFactory::QgsVectorLayerChunkLoaderFactory( const Qgs3DM
, mLayer( vl )
, mSymbol( symbol->clone() )
, mLeafLevel( leafLevel )
, mSource( new QgsVectorLayerFeatureSource( vl ) )
{
}

Expand Down
6 changes: 5 additions & 1 deletion src/3d/qgsvectorlayerchunkloader_p.h
Expand Up @@ -25,6 +25,8 @@ class QgsVectorLayerFeatureSource;
class QgsAbstract3DSymbol;
class QgsFeature3DHandler;

#include <QFutureWatcher>


class QgsVectorLayerChunkLoaderFactory : public QgsChunkLoaderFactory
{
Expand All @@ -38,14 +40,14 @@ class QgsVectorLayerChunkLoaderFactory : public QgsChunkLoaderFactory
QgsVectorLayer *mLayer;
std::unique_ptr<QgsAbstract3DSymbol> mSymbol;
int mLeafLevel;
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
};


class QgsVectorLayerChunkLoader : public QgsChunkLoader
{
public:
QgsVectorLayerChunkLoader( const QgsVectorLayerChunkLoaderFactory *factory, QgsChunkNode *node );
~QgsVectorLayerChunkLoader();

virtual void cancel();
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent );
Expand All @@ -54,7 +56,9 @@ class QgsVectorLayerChunkLoader : public QgsChunkLoader
const QgsVectorLayerChunkLoaderFactory *mFactory;
QgsFeature3DHandler *mHandler = nullptr;
Qgs3DRenderContext mContext;
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
bool mCanceled = false;
QFutureWatcher<void> *mFutureWatcher = nullptr;
};

#include "qgschunkedentity_p.h"
Expand Down
5 changes: 5 additions & 0 deletions src/app/3d/qgs3dmapcanvas.cpp
Expand Up @@ -57,7 +57,12 @@ Qgs3DMapCanvas::Qgs3DMapCanvas( QWidget *parent )

Qgs3DMapCanvas::~Qgs3DMapCanvas()
{
// make sure the scene is deleted while map settings object is still alive
delete mScene;
mScene = nullptr;

delete mMap;
mMap = nullptr;
}

void Qgs3DMapCanvas::resizeEvent( QResizeEvent *ev )
Expand Down

0 comments on commit e52bdf3

Please sign in to comment.