]> git.tdb.fi Git - libs/gl.git/blobdiff - source/resourcemanager.cpp
Avoid segfaults when LoadingThread's resource is cleared
[libs/gl.git] / source / resourcemanager.cpp
index cc69b895aa2ecd33196df0b087e0d3521ee2999c..999b86c190451dae5a8d33967a3c461c5282862c 100644 (file)
@@ -1,5 +1,7 @@
+#include <algorithm>
 #include <msp/time/utils.h>
 #include "resourcemanager.h"
+#include "resourcewatcher.h"
 
 using namespace std;
 
@@ -9,6 +11,11 @@ namespace GL {
 ResourceManager::ResourceManager():
        policy(LOAD_ON_DEMAND),
        async_loads(true),
+       size_limit(0),
+       frame(0),
+       min_retain_frames(30),
+       max_retain_frames(0),
+       next_unload(0),
        thread(*this)
 { }
 
@@ -16,8 +23,8 @@ ResourceManager::~ResourceManager()
 {
        thread.terminate();
 
-       for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
-               i->second.resource->set_manager(0);
+       while(!resources.empty())
+               resources.begin()->second.resource->set_manager(0);
 }
 
 void ResourceManager::set_loading_policy(LoadingPolicy p)
@@ -30,6 +37,21 @@ void ResourceManager::set_async_loads(bool a)
        async_loads = a;
 }
 
+void ResourceManager::set_size_limit(UInt64 s)
+{
+       size_limit = s;
+}
+
+void ResourceManager::set_min_retain_frames(unsigned f)
+{
+       min_retain_frames = f;
+}
+
+void ResourceManager::set_max_retain_frames(unsigned f)
+{
+       max_retain_frames = f;
+}
+
 void ResourceManager::add_resource(Resource &r)
 {
        insert_unique(resources, &r, ManagedResource(r));
@@ -56,15 +78,17 @@ void ResourceManager::load_resource(Resource &r)
        if(!managed.collection)
                throw runtime_error("no location");
 
-       if(managed.loader)
+       if(managed.state!=ManagedResource::NOT_LOADED)
                return;
 
-       managed.start_loading();
-
        if(async_loads)
+       {
+               managed.state = ManagedResource::LOAD_QUEUED;
                queue.push_back(&managed);
+       }
        else
        {
+               managed.start_loading();
                while(!managed.loader->process()) ;
                managed.finish_loading();
        }
@@ -73,8 +97,12 @@ void ResourceManager::load_resource(Resource &r)
 void ResourceManager::resource_used(const Resource &r)
 {
        ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
-       if(!managed->loaded && !managed->loader && policy!=LOAD_MANUALLY)
+       if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
                load_resource(*managed->resource);
+
+       managed->last_used = frame;
+       if(max_retain_frames && !next_unload)
+               next_unload = frame+max_retain_frames;
 }
 
 void ResourceManager::remove_resource(Resource &r)
@@ -86,17 +114,84 @@ void ResourceManager::remove_resource(Resource &r)
        remove_existing(resources, &r);
 }
 
+void ResourceManager::watch_resource(const Resource &r, ResourceWatcher &w)
+{
+       get_item(resources, &r).add_watcher(w);
+}
+
+void ResourceManager::unwatch_resource(const Resource &r, ResourceWatcher &w)
+{
+       get_item(resources, &r).remove_watcher(w);
+}
+
 void ResourceManager::tick()
 {
        LoadingThread::State thread_state = thread.get_state();
-       if(thread_state==LoadingThread::SYNC_PENDING)
+       bool check_total_size = false;
+       if(thread_state==LoadingThread::SYNC_PENDING || thread_state==LoadingThread::LOAD_FINISHED)
+       {
                thread.sync();
+               check_total_size = true;
+       }
        else if(thread_state==LoadingThread::IDLE && !queue.empty())
        {
                ManagedResource *managed = queue.front();
                queue.pop_front();
                thread.set_resource(managed);
        }
+
+       ++frame;
+       if(frame==next_unload)
+       {
+               unsigned unload_limit = frame-max_retain_frames;
+               next_unload = 0;
+
+               for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
+                       if(i->second.state==ManagedResource::LOADED)
+                       {
+                               if(i->second.last_used<=unload_limit)
+                                       i->second.unload();
+                               else if(!next_unload || i->second.last_used<next_unload)
+                                       next_unload = i->second.last_used;
+                       }
+
+               if(next_unload)
+                       next_unload += max_retain_frames;
+       }
+
+       if(check_total_size)
+       {
+               while(get_total_data_size()>size_limit)
+               {
+                       unsigned unload_limit = frame-min_retain_frames;
+                       ManagedResource *best = 0;
+                       UInt64 best_impact = 0;
+                       for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
+                               if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
+                               {
+                                       UInt64 impact = i->second.data_size*(frame-i->second.last_used);
+                                       if(!best || impact>best_impact)
+                                       {
+                                               best = &i->second;
+                                               best_impact = impact;
+                                       }
+                               }
+
+                       if(!best)
+                               break;
+
+                       best->unload();
+               }
+       }
+}
+
+UInt64 ResourceManager::get_total_data_size() const
+{
+       UInt64 total = 0;
+       for(ResourceMap::const_iterator i=resources.begin(); i!=resources.end(); ++i)
+               if(i->second.state==ManagedResource::LOADED)
+                       total += i->second.data_size;
+       return total;
 }
 
 
@@ -105,7 +200,9 @@ ResourceManager::ManagedResource::ManagedResource(Resource &r):
        collection(0),
        io(0),
        loader(0),
-       loaded(false)
+       state(NOT_LOADED),
+       last_used(0),
+       data_size(0)
 { }
 
 void ResourceManager::ManagedResource::start_loading()
@@ -118,15 +215,42 @@ void ResourceManager::ManagedResource::start_loading()
                io = 0;
                throw logic_error("no loader created");
        }
+       state = LOADING;
 }
 
 void ResourceManager::ManagedResource::finish_loading()
 {
        delete loader;
        loader = 0;
-       loaded = true;
+       state = LOADED;
        delete io;
        io = 0;
+       data_size = resource->get_data_size();
+
+       for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
+               (*i)->resource_loaded(*resource);
+}
+
+void ResourceManager::ManagedResource::unload()
+{
+       resource->unload();
+       state = NOT_LOADED;
+
+       for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
+               (*i)->resource_unloaded(*resource);
+}
+
+void ResourceManager::ManagedResource::add_watcher(ResourceWatcher &w)
+{
+       if(find(watchers.begin(), watchers.end(), &w)==watchers.end())
+               watchers.push_back(&w);
+}
+
+void ResourceManager::ManagedResource::remove_watcher(ResourceWatcher &w)
+{
+       vector<ResourceWatcher *>::iterator end = remove(watchers.begin(), watchers.end(), &w);
+       if(end!=watchers.end())
+               watchers.erase(end, watchers.end());
 }
 
 
@@ -171,8 +295,12 @@ void ResourceManager::LoadingThread::set_resource(ManagedResource *r)
        }
 
        resource = r;
-       state = BUSY;
-       sem.signal();
+       if(resource)
+       {
+               resource->start_loading();
+               state = BUSY;
+               sem.signal();
+       }
 }
 
 void ResourceManager::LoadingThread::sync()