FGAIModelData::FGAIModelData(SGPropertyNode *root)
- : _nasal( new FGNasalModelData(root) ),
+ : _nasal( new FGNasalModelDataProxy(root) ),
_ready(false),
_initialized(false)
{
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;
- _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;
+ _fxpath = prop->getStringValue("sound/path");
+ _nasal->modelLoaded(path, prop, n);
+
+ _ready = true;
}
class FGAIManager;
class FGAIFlightPlan;
class FGFX;
-class FGNasalModelData;
+class FGNasalModelDataProxy;
class FGAIModelData; // defined below
/** 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;
};
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());
_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
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) {
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()
//
#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;
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:
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
};
-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);