]> git.mxchange.org Git - flightgear.git/commitdiff
#553: decouple OSG callbacks from Nasal subsystem
authorThorstenB <brehmt@gmail.com>
Mon, 6 Feb 2012 21:19:33 +0000 (22:19 +0100)
committerThorstenB <brehmt@gmail.com>
Mon, 6 Feb 2012 21:19:33 +0000 (22:19 +0100)
src/AIModel/AIBase.cxx
src/AIModel/AIBase.hxx
src/Scenery/tilemgr.cxx
src/Scripting/NasalSys.cxx
src/Scripting/NasalSys.hxx

index a9d64e341683d619fa4cc78132faf1cb9c0720c3..1431cc41148c42d30a29c42734b4d7937567fbc4 100644 (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;
-    _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;
 }
index b4782eb4374cc6fdc8b34d062227d35f3b9141cd..eaf2c3ea14441a0ebdbc154c125e09a4c1e97836 100644 (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;
 };
index e235a8eda68103d24137c1671f29b21f6b9319b8..4ebce444cabacd8aac80694a2264e70b42bb0ac2 100644 (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());
index 777d1eedea4082c80ee51f4ad25f99f29d022c31..eca5d59c48a500e87f87999f6c03cb7a26d9a713 100644 (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()
 //
index ceff996dab1531375abb4d1243424db55e8e8933..c8367f41b5c05fad03d37eebaea0057a61a5d474 100644 (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);