diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index cb6b2fd6e..ad8701e27 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -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; diff --git a/src/Main/main.cxx b/src/Main/main.cxx index 792a20cb9..686585560 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -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 diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 8a7082aac..e073f863f 100644 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include
#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 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 g(_lock); + _isFinished = true; + } +private: + NavDataCache* _cache; + mutable SGMutex _lock; + bool _isFinished; +}; + +//////////////////////////////////////////////////////////////////////////// + typedef std::map PositionedCache; class AirportTower : public FGPositioned @@ -785,6 +828,10 @@ public: StmtVec prepared; std::set deferredOctreeUpdates; + + // if we're performing a rebuild, the thread that is doing the work. + // otherwise, NULL + std::auto_ptr 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"); diff --git a/src/Navaids/NavDataCache.hxx b/src/Navaids/NavDataCache.hxx index 19edb91d7..e7909818c 100644 --- a/src/Navaids/NavDataCache.hxx +++ b/src/Navaids/NavDataCache.hxx @@ -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 d; };