1
0
Fork 0

Use a helper thread to init the traffic-manager.

Make traffic manager startup asynchronous, but also avoid frame-stutter due to file parsing on the main thread. Note only XML parsing happens on a thread - once that's complete, everything happens as normal on the main thread, as before.
This commit is contained in:
James Turner 2012-10-01 09:21:34 +01:00
parent 2bdcde5a4b
commit 1e5bd162e7
2 changed files with 108 additions and 27 deletions

View file

@ -58,6 +58,8 @@
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/xml/easyxml.hxx>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <AIModel/AIAircraft.hxx>
#include <AIModel/AIFlightPlan.hxx>
@ -72,6 +74,77 @@
using std::sort;
using std::strcmp;
/**
* Thread encapsulating parsing the traffic schedules.
*/
class ScheduleParseThread : public SGThread
{
public:
ScheduleParseThread(FGTrafficManager* traffic) :
_trafficManager(traffic),
_isFinished(false),
_cancelThread(false)
{
}
// if we're destroyed while running, ensure the thread exits cleanly
~ScheduleParseThread()
{
_lock.lock();
if (!_isFinished) {
_cancelThread = true; // request cancellation so we don't wait ages
_lock.unlock();
join();
} else {
_lock.unlock();
}
}
void setTrafficDir(const SGPath& trafficDirPath)
{
_trafficDirPath = trafficDirPath;
}
bool isFinished() const
{
SGGuard<SGMutex> g(_lock);
return _isFinished;
}
virtual void run()
{
SGTimeStamp st;
st.stamp();
simgear::Dir trafficDir(_trafficDirPath);
simgear::PathList d = trafficDir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
BOOST_FOREACH(SGPath p, d) {
simgear::Dir d2(p);
simgear::PathList trafficFiles = d2.children(simgear::Dir::TYPE_FILE, ".xml");
BOOST_FOREACH(SGPath xml, trafficFiles) {
_trafficManager->parseSchedule(xml);
if (_cancelThread) {
return;
}
}
} // of sub-directories in AI/Traffic iteration
// _trafficManager->parseSchedules(schedulesToRead);
SG_LOG(SG_AI, SG_INFO, "parsing traffic schedules took:" << st.elapsedMSec() << "msec");
SGGuard<SGMutex> g(_lock);
_isFinished = true;
}
private:
FGTrafficManager* _trafficManager;
mutable SGMutex _lock;
bool _isFinished;
bool _cancelThread;
SGPath _trafficDirPath;
};
/******************************************************************************
* TrafficManager
*****************************************************************************/
@ -95,6 +168,15 @@ FGTrafficManager::~FGTrafficManager()
void FGTrafficManager::shutdown()
{
if (!inited) {
if (doingInit) {
scheduleParser.reset();
doingInit = false;
}
return;
}
// Save the heuristics data
bool saveData = false;
ofstream cachefile;
@ -140,6 +222,13 @@ void FGTrafficManager::shutdown()
inited = false;
}
/// caution - this is run on the helper thread to improve startup
/// responsiveness - do not access properties or global state from
/// here, since there's no locking protection at all
void FGTrafficManager::parseSchedule(const SGPath& path)
{
readXML(path.str(), *this);
}
void FGTrafficManager::init()
{
@ -150,14 +239,9 @@ void FGTrafficManager::init()
assert(!doingInit);
doingInit = true;
if (string(fgGetString("/sim/traffic-manager/datafile")) == string("")) {
simgear::Dir trafficDir(SGPath(globals->get_fg_root(), "AI/Traffic"));
simgear::PathList d = trafficDir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
BOOST_FOREACH(SGPath p, d) {
simgear::Dir d2(p);
simgear::PathList trafficFiles = d2.children(simgear::Dir::TYPE_FILE, ".xml");
schedulesToRead.insert(schedulesToRead.end(), trafficFiles.begin(), trafficFiles.end());
}
scheduleParser.reset(new ScheduleParseThread(this));
scheduleParser->setTrafficDir(SGPath(globals->get_fg_root(), "AI/Traffic"));
scheduleParser->start();
} else {
fgSetBool("/sim/traffic-manager/heuristics", false);
SGPath path = string(fgGetString("/sim/traffic-manager/datafile"));
@ -179,19 +263,7 @@ void FGTrafficManager::init()
}
}
void FGTrafficManager::initStep()
{
assert(doingInit);
if (schedulesToRead.empty()) {
finishInit();
return;
}
SGPath path = schedulesToRead.front();
schedulesToRead.erase(schedulesToRead.begin());
SG_LOG(SG_AI, SG_DEBUG, path << " for traffic");
readXML(path.str(), *this);
}
void FGTrafficManager::finishInit()
{
@ -292,10 +364,11 @@ void FGTrafficManager::update(double /*dt */ )
init();
}
initStep();
if (!inited) {
return; // still more to do on next update() call
if (!scheduleParser->isFinished()) {
return;
}
finishInit();
}
time_t now = time(NULL) + fgGetLong("/sim/time/warp");

View file

@ -47,6 +47,8 @@
#define _TRAFFICMGR_HXX_
#include <set>
#include <memory>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/props/propertyObject.hxx>
#include <simgear/xml/easyxml.hxx>
@ -77,6 +79,7 @@ typedef HeuristicMap::iterator HeuristicMapIterator;
class ScheduleParseThread;
class FGTrafficManager : public SGSubsystem, public XMLVisitor
{
@ -111,12 +114,17 @@ private:
void loadHeuristics();
void initStep();
void finishInit();
void shutdown();
// during incremental init, contains the XML files still be read in
simgear::PathList schedulesToRead;
friend class ScheduleParseThread;
std::auto_ptr<ScheduleParseThread> scheduleParser;
// helper to read and parse the schedule data.
// this is run on a helper thread, so be careful about
// accesing properties during parsing
void parseSchedule(const SGPath& path);
public:
FGTrafficManager();
~FGTrafficManager();