#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"
namespace {
-const int SCHEMA_VERSION = 3;
+const int SCHEMA_VERSION = 4;
// bind a std::string to a sqlite statement. The std::string must live the
// entire duration of the statement execution - do not pass a temporary
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
void prepareQueries()
{
+ clearProperty = prepare("DELETE FROM properties WHERE key=?1");
+ writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?1,?2)");
+
#define POSITIONED_COLS "rowid, type, ident, name, airport, lon, lat, elev_m, octree_node"
#define AND_TYPED "AND type>=?2 AND type <=?3"
statCacheCheck = prepare("SELECT stamp FROM stat_cache WHERE path=?");
int range = sqlite3_column_int(loadCommStation, 0);
int freqKhz = sqlite3_column_int(loadCommStation, 1);
- CommStation* c = new CommStation(rowId, id, ty, pos, freqKhz, range);
+ CommStation* c = new CommStation(rowId, name, ty, pos, freqKhz, range);
c->setAirport(airport);
return c;
}
stampFileCache, statCacheCheck,
loadAirportStmt, loadCommStation, loadPositioned, loadNavaid,
loadRunwayStmt;
+ sqlite3_stmt_ptr writePropertyMulti, clearProperty;
sqlite3_stmt_ptr insertPositionedQuery, insertAirport, insertTower, insertRunway,
insertCommStation, insertNavaid;
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;
};
//////////////////////////////////////////////////////////////////////
// reached this point with no exception, success
break;
} catch (sg_exception& e) {
- SG_LOG(SG_NAVCACHE, SG_WARN, "NavCache: init failed:" << e.what()
+ SG_LOG(SG_NAVCACHE, t == 0 ? SG_WARN : SG_ALERT, "NavCache: init failed:" << e.what()
<< " (attempt " << t << ")");
- homePath.remove();
d.reset();
+ homePath.remove();
}
} // of retry loop
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");
d->execSelect(d->writePropertyQuery);
}
-
+string_list NavDataCache::readStringListProperty(const string& key)
+{
+ d->reset(d->readPropertyQuery);
+ sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
+ string_list result;
+ while (d->stepSelect(d->readPropertyQuery)) {
+ result.push_back((char*) sqlite3_column_text(d->readPropertyQuery, 0));
+ }
+
+ return result;
+}
+
+void NavDataCache::writeStringListProperty(const string& key, const string_list& values)
+{
+ d->reset(d->clearProperty);
+ sqlite_bind_stdstring(d->clearProperty, 1, key);
+ d->execUpdate(d->clearProperty);
+
+ BOOST_FOREACH(string value, values) {
+ d->reset(d->writePropertyMulti);
+ sqlite_bind_stdstring(d->writePropertyMulti, 1, key);
+ sqlite_bind_stdstring(d->writePropertyMulti, 2, value);
+ d->execInsert(d->writePropertyMulti);
+ }
+}
+
bool NavDataCache::isCachedFileModified(const SGPath& path) const
{
if (!path.exists()) {
sqlite3_bind_double(d->findCommByFreq, 5, cartPos.y());
sqlite3_bind_double(d->findCommByFreq, 6, cartPos.z());
- if (!d->execSelect(d->findCommByFreq)) {
- return NULL;
+ while (d->execSelect(d->findCommByFreq)) {
+ FGPositioned* p = loadById(sqlite3_column_int64(d->findCommByFreq, 0));
+ if (aFilter && !aFilter->pass(p)) {
+ continue;
+ }
+
+ return p;
}
- return loadById(sqlite3_column_int64(d->findCommByFreq, 0));
+ return NULL;
}
PositionedIDVec