1
0
Fork 0

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:
James Turner 2012-09-25 17:24:12 +01:00
parent ae6218ff10
commit a10638c6b4
4 changed files with 91 additions and 15 deletions

View file

@ -450,17 +450,23 @@ bool fgInitConfig ( int argc, char **argv )
/** /**
* Initialize vor/ndb/ils/fix list management and query systems (as * Initialize vor/ndb/ils/fix list management and query systems (as
* well as simple airport db list) * 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 bool
fgInitNav () fgInitNav ()
{ {
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
if (cache->isRebuildRequired()) { static bool doingRebuild = false;
SGTimeStamp st; if (doingRebuild || cache->isRebuildRequired()) {
st.stamp(); doingRebuild = true;
cache->rebuild(); bool finished = cache->rebuild();
if (!finished) {
SG_LOG(SG_GENERAL, SG_INFO, "rebuilding NavDataCache took:" << st.elapsedMSec()); // sleep to give the rebuild thread more time
SGTimeStamp::sleepForMSec(50);
return false;
}
} }
FGTACANList *channellist = new FGTACANList; FGTACANList *channellist = new FGTACANList;

View file

@ -189,10 +189,14 @@ static void fgIdleFunction ( void ) {
fgSplashProgress("loading-nav-data"); fgSplashProgress("loading-nav-data");
} else if ( idle_state == 3 ) { } else if ( idle_state == 3 ) {
idle_state++;
fgInitNav();
bool done = fgInitNav();
if (done) {
++idle_state;
fgSplashProgress("init-scenery"); fgSplashProgress("init-scenery");
} else {
fgSplashProgress("loading-nav-data");
}
} else if ( idle_state == 4 ) { } else if ( idle_state == 4 ) {
idle_state+=2; idle_state+=2;

View file

@ -45,6 +45,8 @@
#include <simgear/bucket/newbucket.hxx> #include <simgear/bucket/newbucket.hxx>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx> #include <simgear/misc/strutils.hxx>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <Main/globals.hxx> #include <Main/globals.hxx>
#include "markerbeacon.hxx" #include "markerbeacon.hxx"
@ -131,6 +133,47 @@ static string cleanRunwayNo(const string& aRwyNo)
namespace flightgear 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; typedef std::map<PositionedID, FGPositionedRef> PositionedCache;
class AirportTower : public FGPositioned class AirportTower : public FGPositioned
@ -785,6 +828,10 @@ public:
StmtVec prepared; StmtVec prepared;
std::set<Octree::Branch*> deferredOctreeUpdates; 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; 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 { try {
d->runSQL("BEGIN"); d->runSQL("BEGIN");

View file

@ -65,9 +65,10 @@ public:
bool isRebuildRequired(); 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; bool isCachedFileModified(const SGPath& path) const;
void stampCacheFile(const SGPath& path); void stampCacheFile(const SGPath& path);
@ -181,6 +182,9 @@ public:
private: private:
NavDataCache(); NavDataCache();
friend class RebuildThread;
void doRebuild();
class NavDataCachePrivate; class NavDataCachePrivate;
std::auto_ptr<NavDataCachePrivate> d; std::auto_ptr<NavDataCachePrivate> d;
}; };