Use a helper thread to rebuild the navcache.
Avoid the application becoming unresponsive during nav-cache rebuilds. We still have to wait for the rebuild, but perform it on a helper thread so the main GUI thread stays responsive and hence doesn't trigger a beach-ball / 'not responding' alert. Also ensures there's some feedback (the spinner) during the rebuild operation, so users don't think we've hung.
This commit is contained in:
parent
ae6218ff10
commit
a10638c6b4
4 changed files with 91 additions and 15 deletions
|
@ -450,17 +450,23 @@ bool fgInitConfig ( int argc, char **argv )
|
|||
/**
|
||||
* Initialize vor/ndb/ils/fix list management and query systems (as
|
||||
* well as simple airport db list)
|
||||
* This is called multiple times in the case of a cache rebuild,
|
||||
* to allow length caching to take place in the background, without
|
||||
* blocking the main/UI thread.
|
||||
*/
|
||||
bool
|
||||
fgInitNav ()
|
||||
{
|
||||
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
|
||||
if (cache->isRebuildRequired()) {
|
||||
SGTimeStamp st;
|
||||
st.stamp();
|
||||
cache->rebuild();
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "rebuilding NavDataCache took:" << st.elapsedMSec());
|
||||
static bool doingRebuild = false;
|
||||
if (doingRebuild || cache->isRebuildRequired()) {
|
||||
doingRebuild = true;
|
||||
bool finished = cache->rebuild();
|
||||
if (!finished) {
|
||||
// sleep to give the rebuild thread more time
|
||||
SGTimeStamp::sleepForMSec(50);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FGTACANList *channellist = new FGTACANList;
|
||||
|
|
|
@ -189,11 +189,15 @@ static void fgIdleFunction ( void ) {
|
|||
fgSplashProgress("loading-nav-data");
|
||||
|
||||
} else if ( idle_state == 3 ) {
|
||||
idle_state++;
|
||||
fgInitNav();
|
||||
|
||||
fgSplashProgress("init-scenery");
|
||||
|
||||
|
||||
bool done = fgInitNav();
|
||||
if (done) {
|
||||
++idle_state;
|
||||
fgSplashProgress("init-scenery");
|
||||
} else {
|
||||
fgSplashProgress("loading-nav-data");
|
||||
}
|
||||
|
||||
} else if ( idle_state == 4 ) {
|
||||
idle_state+=2;
|
||||
// based on the requested presets, calculate the true starting
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/threads/SGThread.hxx>
|
||||
#include <simgear/threads/SGGuard.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include "markerbeacon.hxx"
|
||||
|
@ -131,6 +133,47 @@ static string cleanRunwayNo(const string& aRwyNo)
|
|||
namespace flightgear
|
||||
{
|
||||
|
||||
/**
|
||||
* Thread encapsulating a cache rebuild. This is not used to parallelise
|
||||
* the rebuild - we must still wait until completion before doing other
|
||||
* startup, since many things rely on a complete cache. The thread is used
|
||||
* so we don't block the main event loop for an unacceptable duration,
|
||||
* which causes 'not responding' / spinning beachballs on Windows & Mac
|
||||
*/
|
||||
class RebuildThread : public SGThread
|
||||
{
|
||||
public:
|
||||
RebuildThread(NavDataCache* cache) :
|
||||
_cache(cache),
|
||||
_isFinished(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool isFinished() const
|
||||
{
|
||||
SGGuard<SGMutex> g(_lock);
|
||||
return _isFinished;
|
||||
}
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
SGTimeStamp st;
|
||||
st.stamp();
|
||||
_cache->doRebuild();
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "cache rebuild took:" << st.elapsedMSec() << "msec");
|
||||
|
||||
SGGuard<SGMutex> g(_lock);
|
||||
_isFinished = true;
|
||||
}
|
||||
private:
|
||||
NavDataCache* _cache;
|
||||
mutable SGMutex _lock;
|
||||
bool _isFinished;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef std::map<PositionedID, FGPositionedRef> PositionedCache;
|
||||
|
||||
class AirportTower : public FGPositioned
|
||||
|
@ -785,6 +828,10 @@ public:
|
|||
StmtVec prepared;
|
||||
|
||||
std::set<Octree::Branch*> deferredOctreeUpdates;
|
||||
|
||||
// if we're performing a rebuild, the thread that is doing the work.
|
||||
// otherwise, NULL
|
||||
std::auto_ptr<RebuildThread> rebuilder;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -946,7 +993,22 @@ bool NavDataCache::isRebuildRequired()
|
|||
return false;
|
||||
}
|
||||
|
||||
void NavDataCache::rebuild()
|
||||
bool NavDataCache::rebuild()
|
||||
{
|
||||
if (!d->rebuilder.get()) {
|
||||
d->rebuilder.reset(new RebuildThread(this));
|
||||
d->rebuilder->start();
|
||||
}
|
||||
|
||||
// poll the rebuild thread
|
||||
bool fin = d->rebuilder->isFinished();
|
||||
if (fin) {
|
||||
d->rebuilder.reset(); // all done!
|
||||
}
|
||||
return fin;
|
||||
}
|
||||
|
||||
void NavDataCache::doRebuild()
|
||||
{
|
||||
try {
|
||||
d->runSQL("BEGIN");
|
||||
|
|
|
@ -65,9 +65,10 @@ public:
|
|||
bool isRebuildRequired();
|
||||
|
||||
/**
|
||||
* run the cache rebuild
|
||||
* run the cache rebuild - returns true if rebuild is complete,
|
||||
* otherwise keep going.
|
||||
*/
|
||||
void rebuild();
|
||||
bool rebuild();
|
||||
|
||||
bool isCachedFileModified(const SGPath& path) const;
|
||||
void stampCacheFile(const SGPath& path);
|
||||
|
@ -180,7 +181,10 @@ public:
|
|||
AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos);
|
||||
private:
|
||||
NavDataCache();
|
||||
|
||||
|
||||
friend class RebuildThread;
|
||||
void doRebuild();
|
||||
|
||||
class NavDataCachePrivate;
|
||||
std::auto_ptr<NavDataCachePrivate> d;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue