1
0
Fork 0

#553: decouple OSG callbacks from Nasal subsystem

This commit is contained in:
ThorstenB 2012-02-06 22:19:33 +01:00
parent 33c63a6d72
commit 578df0f484
5 changed files with 130 additions and 64 deletions

View file

@ -917,7 +917,7 @@ int FGAIBase::_newAIModelID() {
FGAIModelData::FGAIModelData(SGPropertyNode *root)
: _nasal( new FGNasalModelData(root) ),
: _nasal( new FGNasalModelDataProxy(root) ),
_ready(false),
_initialized(false)
{
@ -926,25 +926,17 @@ FGAIModelData::FGAIModelData(SGPropertyNode *root)
FGAIModelData::~FGAIModelData()
{
delete _nasal;
_nasal = NULL;
}
void FGAIModelData::modelLoaded(const string& path, SGPropertyNode *prop, osg::Node *n)
{
// WARNING: All this is called in a separate OSG thread! Only use thread-safe stuff
// here that is fine to be run concurrently with stuff in the main loop!
// WARNING: Called in a separate OSG thread! Only use thread-safe stuff here...
if (_ready)
return;
_fxpath = _prop->getStringValue("sound/path");
_prop = prop;
_path = path;
_fxpath = prop->getStringValue("sound/path");
_nasal->modelLoaded(path, prop, n);
_ready = true;
}
// do Nasal initialization (must be called in the main loop)
void FGAIModelData::init()
{
// call FGNasalSys to create context and run hooks (not-thread safe!)
_nasal->modelLoaded(_path, _prop, 0);
_prop = 0;
_initialized = true;
}

View file

@ -42,7 +42,7 @@ class SGMaterial;
class FGAIManager;
class FGAIFlightPlan;
class FGFX;
class FGNasalModelData;
class FGNasalModelDataProxy;
class FGAIModelData; // defined below
@ -456,16 +456,15 @@ public:
/** init hook to be called after model is loaded.
* Not thread-safe. Call from main thread only. */
void init(void);
void init(void) { _initialized = true; }
bool needInitilization(void) { return _ready && !_initialized;}
bool isInitialized(void) { return _initialized;}
inline std::string& get_sound_path() { return _fxpath;}
private:
FGNasalModelData *_nasal;
SGPropertyNode_ptr _prop;
std::string _path, _fxpath;
FGNasalModelDataProxy *_nasal;
std::string _fxpath;
bool _ready;
bool _initialized;
};

View file

@ -244,21 +244,12 @@ FGTileMgr::loadTileModel(const string& modelPath, bool cacheModel)
if(cacheModel)
result =
SGModelLib::loadModel(fullPath.str(), globals->get_props(),
new FGNasalModelData);
_disableNasalHooks->getBoolValue() ? NULL : new FGNasalModelDataProxy);
else
{
/* TODO FGNasalModelData's callback "modelLoaded" isn't thread-safe.
* But deferred (or paged) OSG loading runs in a separate thread, which would
* trigger the FGNasalModelData::modelLoaded callback. We're easily doomed
* when this happens and the model actually contains a Nasal "load" hook - which
* would run the Nasal parser and Nasal script execution in a separate thread...
* => Disabling the callback for now, to test if all Nasal related segfaults are
* gone. Proper resolution is TBD. We'll need to somehow decouple the OSG callback,
* so we can run the Nasal stuff in the main thread.
*/
result=
SGModelLib::loadDeferredModel(fullPath.str(), globals->get_props(),
_disableNasalHooks->getBoolValue() ? NULL : new FGNasalModelData);
_disableNasalHooks->getBoolValue() ? NULL : new FGNasalModelDataProxy);
}
} catch (const sg_io_exception& exc) {
string m(exc.getMessage());

View file

@ -878,6 +878,19 @@ void FGNasalSys::update(double)
_dead_listener.clear();
}
if (!_loadList.empty())
{
// process Nasal load hook (only one per update loop to avoid excessive lags)
_loadList.pop()->load();
}
else
if (!_unloadList.empty())
{
// process pending Nasal unload hooks after _all_ load hooks were processed
// (only unload one per update loop to avoid excessive lags)
_unloadList.pop()->unload();
}
// The global context is a legacy thing. We use dynamically
// created contexts for naCall() now, so that we can call them
// recursively. But there are still spots that want to use it for
@ -1358,36 +1371,26 @@ bool FGNasalListener::changed(SGPropertyNode* node)
unsigned int FGNasalModelData::_module_id = 0;
void FGNasalModelData::modelLoaded(const string& path, SGPropertyNode *prop,
osg::Node *)
void FGNasalModelData::load()
{
if(!prop)
return;
SGPropertyNode *nasal = prop->getNode("nasal");
if(!nasal)
return;
SGPropertyNode *load = nasal->getNode("load");
_unload = nasal->getNode("unload");
if(!load && !_unload)
return;
std::stringstream m;
m << "__model" << _module_id++;
_module = m.str();
const char *s = load ? load->getStringValue() : "";
SG_LOG(SG_NASAL, SG_DEBUG, "Loading nasal module " << _module.c_str());
const char *s = _load ? _load->getStringValue() : "";
naRef arg[2];
arg[0] = nasalSys->propNodeGhost(_root);
arg[1] = nasalSys->propNodeGhost(prop);
nasalSys->createModule(_module.c_str(), path.c_str(), s, strlen(s),
arg[1] = nasalSys->propNodeGhost(_prop);
nasalSys->createModule(_module.c_str(), _path.c_str(), s, strlen(s),
_root, 2, arg);
}
FGNasalModelData::~FGNasalModelData()
void FGNasalModelData::unload()
{
if(_module.empty())
if (_module.empty())
return;
if(!nasalSys) {
@ -1396,14 +1399,52 @@ FGNasalModelData::~FGNasalModelData()
return;
}
if(_unload) {
SG_LOG(SG_NASAL, SG_DEBUG, "Unloading nasal module " << _module.c_str());
if (_unload)
{
const char *s = _unload->getStringValue();
nasalSys->createModule(_module.c_str(), _module.c_str(), s, strlen(s), _root);
}
nasalSys->deleteModule(_module.c_str());
}
void FGNasalModelDataProxy::modelLoaded(const string& path, SGPropertyNode *prop,
osg::Node *)
{
if(!nasalSys) {
SG_LOG(SG_NASAL, SG_WARN, "Trying to run a <load> script "
"without Nasal subsystem present.");
return;
}
if(!prop)
return;
SGPropertyNode *nasal = prop->getNode("nasal");
if(!nasal)
return;
SGPropertyNode* load = nasal->getNode("load");
SGPropertyNode* unload = nasal->getNode("unload");
if ((!load) && (!unload))
return;
_data = new FGNasalModelData(_root, path, prop, load, unload);
// register Nasal module to be created and loaded in the main thread.
nasalSys->registerToLoad(_data);
}
FGNasalModelDataProxy::~FGNasalModelDataProxy()
{
// when necessary, register Nasal module to be destroyed/unloaded
// in the main thread.
if ((_data.valid())&&(nasalSys))
nasalSys->registerToUnload(_data);
}
// NasalXMLVisitor class: handles EasyXML visitor callback for parsexml()
//

View file

@ -7,6 +7,7 @@
#include <simgear/nasal/nasal.h>
#include <simgear/scene/model/modellib.hxx>
#include <simgear/xml/easyxml.hxx>
#include <simgear/threads/SGQueue.hxx>
#include <map>
using std::map;
@ -15,6 +16,56 @@ using std::map;
class FGNasalScript;
class FGNasalListener;
/** Nasal model data container.
* load and unload methods must be run in main thread (not thread-safe). */
class FGNasalModelData : public SGReferenced
{
public:
/** Constructor to be run in an arbitrary thread. */
FGNasalModelData(SGPropertyNode *root, const string& path, SGPropertyNode *prop,
SGPropertyNode* load, SGPropertyNode* unload) :
_path(path),
_root(root), _prop(prop),
_load(load), _unload(unload)
{
}
/** Load hook. Always call from inside the main loop. */
void load();
/** Unload hook. Always call from inside the main loop. */
void unload();
private:
static unsigned int _module_id;
string _module, _path;
SGPropertyNode_ptr _root, _prop;
SGConstPropertyNode_ptr _load, _unload;
};
/** Thread-safe proxy for FGNasalModelData.
* modelLoaded/destroy methods only register the requested
* operation. Actual (un)loading of Nasal module is deferred
* and done in the main loop. */
class FGNasalModelDataProxy : public simgear::SGModelData
{
public:
FGNasalModelDataProxy(SGPropertyNode *root = 0) :
_root(root), _data(0)
{
}
~FGNasalModelDataProxy();
void modelLoaded(const string& path, SGPropertyNode *prop, osg::Node *);
protected:
SGPropertyNode_ptr _root;
SGSharedPtr<FGNasalModelData> _data;
};
class FGNasalSys : public SGSubsystem
{
public:
@ -63,11 +114,17 @@ public:
naRef call(naRef code, int argc, naRef* args, naRef locals);
naRef propNodeGhost(SGPropertyNode* handle);
void registerToLoad(FGNasalModelData* data) { _loadList.push(data);}
void registerToUnload(FGNasalModelData* data) { _unloadList.push(data);}
private:
friend class FGNasalScript;
friend class FGNasalListener;
friend class FGNasalModuleListener;
SGLockedQueue<SGSharedPtr<FGNasalModelData> > _loadList;
SGLockedQueue<SGSharedPtr<FGNasalModelData> > _unloadList;
//
// FGTimer subclass for handling Nasal timer callbacks.
// See the implementation of the settimer() extension function for
@ -165,20 +222,6 @@ private:
};
class FGNasalModelData : public simgear::SGModelData {
public:
FGNasalModelData(SGPropertyNode *root = 0) : _root(root), _unload(0) {}
~FGNasalModelData();
void modelLoaded(const string& path, SGPropertyNode *prop, osg::Node *);
private:
static unsigned int _module_id;
string _module;
SGPropertyNode_ptr _root;
SGConstPropertyNode_ptr _unload;
};
class NasalXMLVisitor : public XMLVisitor {
public:
NasalXMLVisitor(naContext c, int argc, naRef* args);