#553: decouple OSG callbacks from Nasal subsystem
This commit is contained in:
parent
33c63a6d72
commit
578df0f484
5 changed files with 130 additions and 64 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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()
|
||||
//
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue