BGE: Fix issues with async libload.
authorPorteries Tristan <republicthunderbolt9@gmail.com>
Sun, 25 Oct 2015 18:22:29 +0000 (19:22 +0100)
committerPorteries Tristan <republicthunderbolt9@gmail.com>
Sun, 25 Oct 2015 18:22:29 +0000 (19:22 +0100)
This patch fixes:
- the call of LibFree on a unfinished loaded library;
- memory leak created on end of game : the async libraries are loaded but not converted, so not freed with the master scene.

Reviewers: campbellbarton, sybren, youle, hg1, moguri, lordloki

Reviewed By: moguri, lordloki

Differential Revision: https://developer.blender.org/D1571

doc/python_api/rst/bge_types/bge.types.KX_LibLoadStatus.rst
source/gameengine/Converter/KX_BlenderSceneConverter.cpp
source/gameengine/Converter/KX_BlenderSceneConverter.h
source/gameengine/Converter/KX_LibLoadStatus.cpp
source/gameengine/Converter/KX_LibLoadStatus.h
source/gameengine/Ketsji/KX_ISceneConverter.h
source/gameengine/Ketsji/KX_KetsjiEngine.cpp

index a5b7aaf5deee64b72eaa10f8f9b97da8f3921a1e..a37b4dad5857d71cdbfd09e4d907a6098741d786 100644 (file)
@@ -25,6 +25,12 @@ base class --- :class:`PyObjectPlus`
 
       :type: callable
 
+   .. attribute:: finished
+
+      The current status of the lib load.
+
+      :type: boolean
+
    .. attribute:: progress
 
       The current progress of the lib load as a normalized value from 0.0 to 1.0.
index 02c00b2835b5b6b414de79b31d039b305eb80381..95d8fe1f294d584cc34fb4c2b0d227f03e3fda55 100644 (file)
@@ -138,14 +138,6 @@ KX_BlenderSceneConverter::~KX_BlenderSceneConverter()
        // clears meshes, and hashmaps from blender to gameengine data
        // delete sumoshapes
 
-       if (m_threadinfo) {
-               BLI_task_pool_work_and_wait(m_threadinfo->m_pool);
-               BLI_task_pool_free(m_threadinfo->m_pool);
-
-               BLI_mutex_end(&m_threadinfo->m_mutex);
-               delete m_threadinfo;
-       }
-
        int numAdtLists = m_map_blender_to_gameAdtList.size();
        for (int i = 0; i < numAdtLists; i++) {
                BL_InterpolatorList *adtList = *m_map_blender_to_gameAdtList.at(i);
@@ -188,6 +180,15 @@ KX_BlenderSceneConverter::~KX_BlenderSceneConverter()
        }
 
        m_DynamicMaggie.clear();
+
+       if (m_threadinfo) {
+               /* Thread infos like mutex must be freed after FreeBlendFile function.
+               Because it needs to lock the mutex, even if there's no active task when it's
+               in the scene converter destructor. */
+               BLI_task_pool_free(m_threadinfo->m_pool);
+               BLI_mutex_end(&m_threadinfo->m_mutex);
+               delete m_threadinfo;
+       }
 }
 
 void KX_BlenderSceneConverter::SetNewFileName(const STR_String &filename)
@@ -806,6 +807,16 @@ void KX_BlenderSceneConverter::MergeAsyncLoads()
        BLI_mutex_unlock(&m_threadinfo->m_mutex);
 }
 
+void KX_BlenderSceneConverter::FinalizeAsyncLoads()
+{
+       // Finish all loading libraries.
+       if (m_threadinfo) {
+               BLI_task_pool_work_and_wait(m_threadinfo->m_pool);
+       }
+       // Merge all libraries data in the current scene, to avoid memory leak of unmerged scenes.
+       MergeAsyncLoads();
+}
+
 void KX_BlenderSceneConverter::AddScenesToMergeQueue(KX_LibLoadStatus *status)
 {
        BLI_mutex_lock(&m_threadinfo->m_mutex);
@@ -1017,7 +1028,19 @@ bool KX_BlenderSceneConverter::FreeBlendFile(Main *maggie)
 
        if (maggie == NULL)
                return false;
-       
+
+       // If the given library is currently in loading, we do nothing.
+       if (m_status_map.count(maggie->name)) {
+               BLI_mutex_lock(&m_threadinfo->m_mutex);
+               const bool finished = m_status_map[maggie->name]->IsFinished();
+               BLI_mutex_unlock(&m_threadinfo->m_mutex);
+
+               if (!finished) {
+                       printf("Library (%s) is currently being loaded asynchronously, and cannot be freed until this process is done\n", maggie->name);
+                       return false;
+               }
+       }
+
        /* tag all false except the one we remove */
        for (vector<Main *>::iterator it = m_DynamicMaggie.begin(); !(it == m_DynamicMaggie.end()); it++) {
                Main *main = *it;
index a40188d197da2c074730a6601e27be154ff7801f..40c71a4d74bbfb56a2463c6b7626e747d1b8f4be 100644 (file)
@@ -184,6 +184,7 @@ public:
        bool FreeBlendFile(const char *path);
 
        virtual void MergeAsyncLoads();
+       virtual void FinalizeAsyncLoads();
        void AddScenesToMergeQueue(class KX_LibLoadStatus *status);
  
        void PrintStats() {
index 2a38e062f8996aed6dbf0f318c96da4f51aace3e..66fcd998269952c0bca90bbe064654187f3dc97f 100644 (file)
@@ -36,7 +36,8 @@ KX_LibLoadStatus::KX_LibLoadStatus(class KX_BlenderSceneConverter* kx_converter,
                        m_mergescene(merge_scene),
                        m_data(NULL),
                        m_libname(path),
-                       m_progress(0.f)
+                       m_progress(0.0f),
+                       m_finished(false)
 #ifdef WITH_PYTHON
                        ,
                        m_finish_cb(NULL),
@@ -48,6 +49,7 @@ KX_LibLoadStatus::KX_LibLoadStatus(class KX_BlenderSceneConverter* kx_converter,
 
 void KX_LibLoadStatus::Finish()
 {
+       m_finished = true;
        m_progress = 1.f;
        m_endtime = PIL_check_seconds_timer();
 
@@ -157,6 +159,7 @@ PyAttributeDef KX_LibLoadStatus::Attributes[] = {
        KX_PYATTRIBUTE_FLOAT_RO("progress", KX_LibLoadStatus, m_progress),
        KX_PYATTRIBUTE_STRING_RO("libraryName", KX_LibLoadStatus, m_libname),
        KX_PYATTRIBUTE_RO_FUNCTION("timeTaken", KX_LibLoadStatus, pyattr_get_timetaken),
+       KX_PYATTRIBUTE_BOOL_RO("finished", KX_LibLoadStatus, m_finished),
        { NULL }        //Sentinel
 };
 
index bedf4498c9668ee7c6282fcf9107ed180ce75d27..fd51bfddd85c6543e9a9f431326612fe4cb9f56d 100644 (file)
@@ -43,6 +43,9 @@ private:
        double  m_starttime;
        double  m_endtime;
 
+       // The current status of this libload, used by the scene converter.
+       bool m_finished;
+
 #ifdef WITH_PYTHON
        PyObject*       m_finish_cb;
        PyObject*       m_progress_cb;
@@ -68,6 +71,11 @@ public:
        void SetData(void *data);
        void *GetData();
 
+       inline bool IsFinished() const
+       {
+               return m_finished;
+       }
+
        void SetProgress(float progress);
        float GetProgress();
        void AddProgress(float progress);
index 2e1c16c6e32dc5956e2ba0cad7597fada3fd2b6b..1963fc7c48f087a7b5906a53f27de311a4a71fff 100644 (file)
@@ -64,6 +64,7 @@ public:
 
        // handle any pending merges from asynchronous loads
        virtual void MergeAsyncLoads()=0;
+       virtual void FinalizeAsyncLoads() = 0;
 
        virtual void    SetAlwaysUseExpandFraming(bool to_what) = 0;
 
index c6e5f734c163d6651a3ccd83d6fe1a40b1cf970b..f42e3e104ac594b05705cb8c09e876d2948ea4c7 100644 (file)
@@ -1273,6 +1273,7 @@ void KX_KetsjiEngine::StopEngine()
 {
        if (m_bInitialized)
        {
+               m_sceneconverter->FinalizeAsyncLoads();
 
                if (m_animation_record)
                {