]> git.mxchange.org Git - flightgear.git/commitdiff
Use a helper thread to rebuild the navcache.
authorJames Turner <zakalawe@mac.com>
Tue, 25 Sep 2012 16:24:12 +0000 (17:24 +0100)
committerJames Turner <zakalawe@mac.com>
Tue, 25 Sep 2012 16:24:12 +0000 (17:24 +0100)
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.

src/Main/fg_init.cxx
src/Main/main.cxx
src/Navaids/NavDataCache.cxx
src/Navaids/NavDataCache.hxx

index cb6b2fd6e8cdddaa4cce3ab06647e80bfa72a350..ad8701e2732eee4a2db3d6b2803a78bcbdb0b247 100644 (file)
@@ -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;
index 792a20cb98416de085feff55417d10aeda18e043..686585560d0c1f544e11a6eef6adc3302f1dafec 100644 (file)
@@ -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
index 8a7082aac89bd395848ab01bc140d23f8c79b9ce..e073f863f1b0f478ecb8683297d5f217e668f642 100644 (file)
@@ -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");
index 19edb91d7d4c7e0381598df88ed5d2ff996d35bb..e7909818c307d00ad8cb544c1d385bd2ee5892d2 100644 (file)
@@ -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;      
 };