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:
parent
2bdcde5a4b
commit
1e5bd162e7
2 changed files with 108 additions and 27 deletions
|
@ -58,6 +58,8 @@
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
#include <simgear/structure/subsystem_mgr.hxx>
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
#include <simgear/xml/easyxml.hxx>
|
#include <simgear/xml/easyxml.hxx>
|
||||||
|
#include <simgear/threads/SGThread.hxx>
|
||||||
|
#include <simgear/threads/SGGuard.hxx>
|
||||||
|
|
||||||
#include <AIModel/AIAircraft.hxx>
|
#include <AIModel/AIAircraft.hxx>
|
||||||
#include <AIModel/AIFlightPlan.hxx>
|
#include <AIModel/AIFlightPlan.hxx>
|
||||||
|
@ -72,6 +74,77 @@
|
||||||
using std::sort;
|
using std::sort;
|
||||||
using std::strcmp;
|
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
|
* TrafficManager
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
@ -95,6 +168,15 @@ FGTrafficManager::~FGTrafficManager()
|
||||||
|
|
||||||
void FGTrafficManager::shutdown()
|
void FGTrafficManager::shutdown()
|
||||||
{
|
{
|
||||||
|
if (!inited) {
|
||||||
|
if (doingInit) {
|
||||||
|
scheduleParser.reset();
|
||||||
|
doingInit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Save the heuristics data
|
// Save the heuristics data
|
||||||
bool saveData = false;
|
bool saveData = false;
|
||||||
ofstream cachefile;
|
ofstream cachefile;
|
||||||
|
@ -140,6 +222,13 @@ void FGTrafficManager::shutdown()
|
||||||
inited = false;
|
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()
|
void FGTrafficManager::init()
|
||||||
{
|
{
|
||||||
|
@ -150,14 +239,9 @@ void FGTrafficManager::init()
|
||||||
assert(!doingInit);
|
assert(!doingInit);
|
||||||
doingInit = true;
|
doingInit = true;
|
||||||
if (string(fgGetString("/sim/traffic-manager/datafile")) == string("")) {
|
if (string(fgGetString("/sim/traffic-manager/datafile")) == string("")) {
|
||||||
simgear::Dir trafficDir(SGPath(globals->get_fg_root(), "AI/Traffic"));
|
scheduleParser.reset(new ScheduleParseThread(this));
|
||||||
simgear::PathList d = trafficDir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
|
scheduleParser->setTrafficDir(SGPath(globals->get_fg_root(), "AI/Traffic"));
|
||||||
|
scheduleParser->start();
|
||||||
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());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fgSetBool("/sim/traffic-manager/heuristics", false);
|
fgSetBool("/sim/traffic-manager/heuristics", false);
|
||||||
SGPath path = string(fgGetString("/sim/traffic-manager/datafile"));
|
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()
|
void FGTrafficManager::finishInit()
|
||||||
{
|
{
|
||||||
|
@ -292,10 +364,11 @@ void FGTrafficManager::update(double /*dt */ )
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
initStep();
|
if (!scheduleParser->isFinished()) {
|
||||||
if (!inited) {
|
return;
|
||||||
return; // still more to do on next update() call
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finishInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
|
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
|
||||||
|
|
|
@ -47,6 +47,8 @@
|
||||||
#define _TRAFFICMGR_HXX_
|
#define _TRAFFICMGR_HXX_
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <simgear/structure/subsystem_mgr.hxx>
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
#include <simgear/props/propertyObject.hxx>
|
#include <simgear/props/propertyObject.hxx>
|
||||||
#include <simgear/xml/easyxml.hxx>
|
#include <simgear/xml/easyxml.hxx>
|
||||||
|
@ -77,6 +79,7 @@ typedef HeuristicMap::iterator HeuristicMapIterator;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ScheduleParseThread;
|
||||||
|
|
||||||
class FGTrafficManager : public SGSubsystem, public XMLVisitor
|
class FGTrafficManager : public SGSubsystem, public XMLVisitor
|
||||||
{
|
{
|
||||||
|
@ -111,12 +114,17 @@ private:
|
||||||
|
|
||||||
void loadHeuristics();
|
void loadHeuristics();
|
||||||
|
|
||||||
void initStep();
|
|
||||||
void finishInit();
|
void finishInit();
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
// during incremental init, contains the XML files still be read in
|
friend class ScheduleParseThread;
|
||||||
simgear::PathList schedulesToRead;
|
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:
|
public:
|
||||||
FGTrafficManager();
|
FGTrafficManager();
|
||||||
~FGTrafficManager();
|
~FGTrafficManager();
|
||||||
|
|
Loading…
Reference in a new issue