From df57d4bfba49b41b692c7db85b9ef774fa772570 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 6 Feb 2016 16:57:54 +0100
Subject: [PATCH] Use a common base class for resource managers

Implement updateCache to delete unreferenced cached objects when they have not been referenced for a while.
---
 apps/openmw/mwphysics/physicssystem.cpp    |  5 +++
 apps/openmw/mwphysics/physicssystem.hpp    |  1 +
 apps/openmw/mwrender/renderingmanager.cpp  |  2 +-
 components/CMakeLists.txt                  |  2 +-
 components/resource/bulletshapemanager.cpp |  3 +-
 components/resource/bulletshapemanager.hpp | 16 +-------
 components/resource/imagemanager.cpp       |  3 +-
 components/resource/imagemanager.hpp       | 16 ++------
 components/resource/keyframemanager.cpp    |  3 +-
 components/resource/keyframemanager.hpp    | 19 +---------
 components/resource/niffilemanager.cpp     | 10 +----
 components/resource/niffilemanager.hpp     | 20 +---------
 components/resource/resourcemanager.cpp    | 34 +++++++++++++++++
 components/resource/resourcemanager.hpp    | 43 ++++++++++++++++++++++
 components/resource/resourcesystem.cpp     | 29 ++++++++++++++-
 components/resource/resourcesystem.hpp     | 17 ++++++++-
 components/resource/scenemanager.cpp       |  9 +----
 components/resource/scenemanager.hpp       | 19 ++--------
 components/vfs/manager.hpp                 |  6 +++
 19 files changed, 152 insertions(+), 105 deletions(-)
 create mode 100644 components/resource/resourcemanager.cpp
 create mode 100644 components/resource/resourcemanager.hpp

diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp
index c99456ae5..11d6d286a 100644
--- a/apps/openmw/mwphysics/physicssystem.cpp
+++ b/apps/openmw/mwphysics/physicssystem.cpp
@@ -640,12 +640,15 @@ namespace MWPhysics
 
     PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
         : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager()))
+        , mResourceSystem(resourceSystem)
         , mDebugDrawEnabled(false)
         , mTimeAccum(0.0f)
         , mWaterHeight(0)
         , mWaterEnabled(false)
         , mParentNode(parentNode)
     {
+        mResourceSystem->addResourceManager(mShapeManager.get());
+
         mCollisionConfiguration = new btDefaultCollisionConfiguration();
         mDispatcher = new btCollisionDispatcher(mCollisionConfiguration);
         mBroadphase = new btDbvtBroadphase();
@@ -659,6 +662,8 @@ namespace MWPhysics
 
     PhysicsSystem::~PhysicsSystem()
     {
+        mResourceSystem->removeResourceManager(mShapeManager.get());
+
         if (mWaterCollisionObject.get())
             mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get());
 
diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp
index f53d7e3d9..4263bc0ec 100644
--- a/apps/openmw/mwphysics/physicssystem.hpp
+++ b/apps/openmw/mwphysics/physicssystem.hpp
@@ -169,6 +169,7 @@ namespace MWPhysics
             btCollisionWorld* mCollisionWorld;
 
             std::auto_ptr<Resource::BulletShapeManager> mShapeManager;
+            Resource::ResourceSystem* mResourceSystem;
 
             typedef std::map<MWWorld::ConstPtr, Object*> ObjectMap;
             ObjectMap mObjects;
diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp
index 9b7ced4b5..d4199d5f6 100644
--- a/apps/openmw/mwrender/renderingmanager.cpp
+++ b/apps/openmw/mwrender/renderingmanager.cpp
@@ -233,7 +233,7 @@ namespace MWRender
 
     void RenderingManager::clearCache()
     {
-        mResourceSystem->clearCache();
+        mResourceSystem->updateCache(mViewer->getFrameStamp()->getReferenceTime());
         if (mTerrain.get())
             mTerrain->clearCache();
     }
diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt
index 0b54ddeb7..42323a68a 100644
--- a/components/CMakeLists.txt
+++ b/components/CMakeLists.txt
@@ -41,7 +41,7 @@ add_component_dir (vfs
     )
 
 add_component_dir (resource
-    scenemanager keyframemanager imagemanager resourcesystem bulletshapemanager bulletshape niffilemanager objectcache
+    scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache resourcesystem resourcemanager
     )
 
 add_component_dir (sceneutil
diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp
index 2ab7b243a..b5581cce2 100644
--- a/components/resource/bulletshapemanager.cpp
+++ b/components/resource/bulletshapemanager.cpp
@@ -97,10 +97,9 @@ private:
 };
 
 BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager)
-    : mVFS(vfs)
+    : ResourceManager(vfs)
     , mSceneManager(sceneMgr)
     , mNifFileManager(nifFileManager)
-    , mCache(new osgDB::ObjectCache)
 {
 
 }
diff --git a/components/resource/bulletshapemanager.hpp b/components/resource/bulletshapemanager.hpp
index c8db8849e..576268f75 100644
--- a/components/resource/bulletshapemanager.hpp
+++ b/components/resource/bulletshapemanager.hpp
@@ -7,16 +7,7 @@
 #include <osg/ref_ptr>
 
 #include "bulletshape.hpp"
-
-namespace VFS
-{
-    class Manager;
-}
-
-namespace osgDB
-{
-    class ObjectCache;
-}
+#include "resourcemanager.hpp"
 
 namespace Resource
 {
@@ -29,7 +20,7 @@ namespace Resource
     /// Handles loading, caching and "instancing" of bullet shapes.
     /// A shape 'instance' is a clone of another shape, with the goal of setting a different scale on this instance.
     /// @note May be used from any thread.
-    class BulletShapeManager
+    class BulletShapeManager : public ResourceManager
     {
     public:
         BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager);
@@ -38,11 +29,8 @@ namespace Resource
         osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);
 
     private:
-        const VFS::Manager* mVFS;
         SceneManager* mSceneManager;
         NifFileManager* mNifFileManager;
-
-        osg::ref_ptr<osgDB::ObjectCache> mCache;
     };
 
 }
diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp
index bd422fe2a..79da7d7ab 100644
--- a/components/resource/imagemanager.cpp
+++ b/components/resource/imagemanager.cpp
@@ -42,8 +42,7 @@ namespace Resource
 {
 
     ImageManager::ImageManager(const VFS::Manager *vfs)
-        : mVFS(vfs)
-        , mCache(new osgDB::ObjectCache)
+        : ResourceManager(vfs)
         , mWarningImage(createWarningImage())
         , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba"))
     {
diff --git a/components/resource/imagemanager.hpp b/components/resource/imagemanager.hpp
index bc1d7b4e4..8d9ad2c32 100644
--- a/components/resource/imagemanager.hpp
+++ b/components/resource/imagemanager.hpp
@@ -8,20 +8,16 @@
 #include <osg/Image>
 #include <osg/Texture2D>
 
+#include "resourcemanager.hpp"
+
 namespace osgViewer
 {
     class Viewer;
 }
 
-namespace VFS
-{
-    class Manager;
-}
-
 namespace osgDB
 {
     class Options;
-    class ObjectCache;
 }
 
 namespace Resource
@@ -29,7 +25,7 @@ namespace Resource
 
     /// @brief Handles loading/caching of Images.
     /// @note May be used from any thread.
-    class ImageManager
+    class ImageManager : public ResourceManager
     {
     public:
         ImageManager(const VFS::Manager* vfs);
@@ -39,15 +35,9 @@ namespace Resource
         /// Returns the dummy image if the given image is not found.
         osg::ref_ptr<osg::Image> getImage(const std::string& filename);
 
-        const VFS::Manager* getVFS() { return mVFS; }
-
         osg::Image* getWarningImage();
 
     private:
-        const VFS::Manager* mVFS;
-
-        osg::ref_ptr<osgDB::ObjectCache> mCache;
-
         osg::ref_ptr<osg::Image> mWarningImage;
         osg::ref_ptr<osgDB::Options> mOptions;
 
diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp
index 7e948dcb0..4392f84c1 100644
--- a/components/resource/keyframemanager.cpp
+++ b/components/resource/keyframemanager.cpp
@@ -9,8 +9,7 @@ namespace Resource
 {
 
     KeyframeManager::KeyframeManager(const VFS::Manager* vfs)
-        : mCache(new osgDB::ObjectCache)
-        , mVFS(vfs)
+        : ResourceManager(vfs)
     {
     }
 
diff --git a/components/resource/keyframemanager.hpp b/components/resource/keyframemanager.hpp
index 5a5cb3628..1c2c219bb 100644
--- a/components/resource/keyframemanager.hpp
+++ b/components/resource/keyframemanager.hpp
@@ -4,15 +4,7 @@
 #include <osg/ref_ptr>
 #include <string>
 
-namespace VFS
-{
-    class Manager;
-}
-
-namespace osgDB
-{
-    class ObjectCache;
-}
+#include "resourcemanager.hpp"
 
 namespace NifOsg
 {
@@ -24,22 +16,15 @@ namespace Resource
 
     /// @brief Managing of keyframe resources
     /// @note May be used from any thread.
-    class KeyframeManager
+    class KeyframeManager : public ResourceManager
     {
     public:
         KeyframeManager(const VFS::Manager* vfs);
         ~KeyframeManager();
 
-        void clearCache();
-
         /// Retrieve a read-only keyframe resource by name (case-insensitive).
         /// @note Throws an exception if the resource is not found.
         osg::ref_ptr<const NifOsg::KeyframeHolder> get(const std::string& name);
-
-    private:
-        osg::ref_ptr<osgDB::ObjectCache> mCache;
-
-        const VFS::Manager* mVFS;
     };
 
 }
diff --git a/components/resource/niffilemanager.cpp b/components/resource/niffilemanager.cpp
index 1d8019b69..3c7437520 100644
--- a/components/resource/niffilemanager.cpp
+++ b/components/resource/niffilemanager.cpp
@@ -29,9 +29,9 @@ namespace Resource
     };
 
     NifFileManager::NifFileManager(const VFS::Manager *vfs)
-        : mVFS(vfs)
+        : ResourceManager(vfs, 0.0) // NIF files aren't needed any more when the converted objects are cached in SceneManager / BulletShapeManager,
+                                    // so we'll use expiryDelay of 0 to instantly delete NIF files after use.
     {
-        mCache = new osgDB::ObjectCache;
     }
 
     NifFileManager::~NifFileManager()
@@ -39,12 +39,6 @@ namespace Resource
 
     }
 
-    void NifFileManager::clearCache()
-    {
-        // NIF files aren't needed any more when the converted objects are cached in SceneManager / BulletShapeManager,
-        // so we'll simply drop all nif files here, unlikely to need them again
-        mCache->clear();
-    }
 
     Nif::NIFFilePtr NifFileManager::get(const std::string &name)
     {
diff --git a/components/resource/niffilemanager.hpp b/components/resource/niffilemanager.hpp
index 4551cf227..4b43ff24b 100644
--- a/components/resource/niffilemanager.hpp
+++ b/components/resource/niffilemanager.hpp
@@ -5,39 +5,23 @@
 
 #include <components/nif/niffile.hpp>
 
-namespace VFS
-{
-    class Manager;
-}
-
-namespace osgDB
-{
-    class ObjectCache;
-}
+#include "resourcemanager.hpp"
 
 namespace Resource
 {
 
     /// @brief Handles caching of NIFFiles.
     /// @note May be used from any thread.
-    class NifFileManager
+    class NifFileManager : public ResourceManager
     {
     public:
         NifFileManager(const VFS::Manager* vfs);
         ~NifFileManager();
 
-        void clearCache();
-
         /// Retrieve a NIF file from the cache, or load it from the VFS if not cached yet.
         /// @note For performance reasons the NifFileManager does not handle case folding, needs
         /// to be done in advance by other managers accessing the NifFileManager.
         Nif::NIFFilePtr get(const std::string& name);
-
-    private:
-        // Use the osgDB::ObjectCache so objects are retrieved in thread safe way
-        osg::ref_ptr<osgDB::ObjectCache> mCache;
-
-        const VFS::Manager* mVFS;
     };
 
 }
diff --git a/components/resource/resourcemanager.cpp b/components/resource/resourcemanager.cpp
new file mode 100644
index 000000000..60233baa0
--- /dev/null
+++ b/components/resource/resourcemanager.cpp
@@ -0,0 +1,34 @@
+#include "resourcemanager.hpp"
+
+#include "objectcache.hpp"
+
+namespace Resource
+{
+
+    ResourceManager::ResourceManager(const VFS::Manager *vfs, const double expiryDelay)
+        : mVFS(vfs)
+        , mCache(new osgDB::ObjectCache)
+        , mExpiryDelay(expiryDelay)
+    {
+
+    }
+
+    void ResourceManager::updateCache(double referenceTime)
+    {
+        // NOTE: we could clear the cache from the background thread if the deletion proves too much of an overhead
+        // idea: customize objectCache to not hold a lock while doing the actual deletion
+        mCache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
+        mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay);
+    }
+
+    void ResourceManager::clearCache()
+    {
+        mCache->clear();
+    }
+
+    const VFS::Manager* ResourceManager::getVFS() const
+    {
+        return mVFS;
+    }
+
+}
diff --git a/components/resource/resourcemanager.hpp b/components/resource/resourcemanager.hpp
new file mode 100644
index 000000000..e45a2e9cb
--- /dev/null
+++ b/components/resource/resourcemanager.hpp
@@ -0,0 +1,43 @@
+#ifndef OPENMW_COMPONENTS_RESOURCE_MANAGER_H
+#define OPENMW_COMPONENTS_RESOURCE_MANAGER_H
+
+#include <osg/ref_ptr>
+
+namespace VFS
+{
+    class Manager;
+}
+
+namespace osgDB
+{
+    class ObjectCache;
+}
+
+namespace Resource
+{
+
+    /// @brief Base class for managers that require a virtual file system and object cache.
+    /// @par This base class implements clearing of the cache, but populating it and what it's used for is up to the individual sub classes.
+    class ResourceManager
+    {
+    public:
+        /// @param expiryDelay how long to keep objects in cache after no longer being referenced.
+        ResourceManager(const VFS::Manager* vfs, const double expiryDelay = 300.0);
+
+        /// Clear cache entries that have not been referenced for longer than expiryDelay.
+        virtual void updateCache(double referenceTime);
+
+        /// Clear all cache entries regardless of having external references.
+        virtual void clearCache();
+
+        const VFS::Manager* getVFS() const;
+
+    protected:
+        const VFS::Manager* mVFS;
+        osg::ref_ptr<osgDB::ObjectCache> mCache;
+        double mExpiryDelay;
+    };
+
+}
+
+#endif
diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp
index d39a723d6..f499c0016 100644
--- a/components/resource/resourcesystem.cpp
+++ b/components/resource/resourcesystem.cpp
@@ -15,11 +15,21 @@ namespace Resource
         mKeyframeManager.reset(new KeyframeManager(vfs));
         mImageManager.reset(new ImageManager(vfs));
         mSceneManager.reset(new SceneManager(vfs, mImageManager.get(), mNifFileManager.get()));
+
+        addResourceManager(mNifFileManager.get());
+        addResourceManager(mKeyframeManager.get());
+        // note, scene references images so add images afterwards for correct implementation of updateCache()
+        addResourceManager(mSceneManager.get());
+        addResourceManager(mImageManager.get());
     }
 
     ResourceSystem::~ResourceSystem()
     {
         // this has to be defined in the .cpp file as we can't delete incomplete types
+
+        mResourceManagers.clear();
+
+        // no delete, all handled by auto_ptr
     }
 
     SceneManager* ResourceSystem::getSceneManager()
@@ -42,9 +52,24 @@ namespace Resource
         return mKeyframeManager.get();
     }
 
-    void ResourceSystem::clearCache()
+    void ResourceSystem::updateCache(double referenceTime)
     {
-        mNifFileManager->clearCache();
+        osg::Timer timer;
+        for (std::vector<ResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)
+            (*it)->updateCache(referenceTime);
+        std::cout << "updateCache took " << timer.time_m() << " ms" << std::endl;
+    }
+
+    void ResourceSystem::addResourceManager(ResourceManager *resourceMgr)
+    {
+        mResourceManagers.push_back(resourceMgr);
+    }
+
+    void ResourceSystem::removeResourceManager(ResourceManager *resourceMgr)
+    {
+        std::vector<ResourceManager*>::iterator found = std::find(mResourceManagers.begin(), mResourceManagers.end(), resourceMgr);
+        if (found != mResourceManagers.end())
+            mResourceManagers.erase(found);
     }
 
     const VFS::Manager* ResourceSystem::getVFS() const
diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp
index 13a96e8c7..30607432d 100644
--- a/components/resource/resourcesystem.hpp
+++ b/components/resource/resourcesystem.hpp
@@ -2,6 +2,7 @@
 #define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H
 
 #include <memory>
+#include <vector>
 
 namespace VFS
 {
@@ -15,6 +16,7 @@ namespace Resource
     class ImageManager;
     class NifFileManager;
     class KeyframeManager;
+    class ResourceManager;
 
     /// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems.
     /// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but
@@ -31,8 +33,17 @@ namespace Resource
         KeyframeManager* getKeyframeManager();
 
         /// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced.
-        void clearCache();
+        /// @note May be called from any thread if you do not add or remove resource managers at that point.
+        void updateCache(double referenceTime);
 
+        /// Add this ResourceManager to be handled by the ResourceSystem.
+        /// @note Does not transfer ownership.
+        void addResourceManager(ResourceManager* resourceMgr);
+        /// @note Do nothing if resourceMgr does not exist.
+        /// @note Does not delete resourceMgr.
+        void removeResourceManager(ResourceManager* resourceMgr);
+
+        /// @note May be called from any thread.
         const VFS::Manager* getVFS() const;
 
     private:
@@ -41,6 +52,10 @@ namespace Resource
         std::auto_ptr<NifFileManager> mNifFileManager;
         std::auto_ptr<KeyframeManager> mKeyframeManager;
 
+        // Store the base classes separately to get convenient access to the common interface
+        // Here users can register their own resourcemanager as well
+        std::vector<ResourceManager*> mResourceManagers;
+
         const VFS::Manager* mVFS;
 
         ResourceSystem(const ResourceSystem&);
diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp
index 95e03b389..4ed14241a 100644
--- a/components/resource/scenemanager.cpp
+++ b/components/resource/scenemanager.cpp
@@ -230,7 +230,7 @@ namespace Resource
 
 
     SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
-        : mVFS(vfs)
+        : ResourceManager(vfs)
         , mImageManager(imageManager)
         , mNifFileManager(nifFileManager)
         , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)
@@ -238,7 +238,6 @@ namespace Resource
         , mMaxAnisotropy(1)
         , mUnRefImageDataAfterApply(false)
         , mParticleSystemMask(~0u)
-        , mCache(new osgDB::ObjectCache)
     {
     }
 
@@ -403,11 +402,6 @@ namespace Resource
         node->accept(visitor);
     }
 
-    const VFS::Manager* SceneManager::getVFS() const
-    {
-        return mVFS;
-    }
-
     Resource::ImageManager* SceneManager::getImageManager()
     {
         return mImageManager;
@@ -483,5 +477,4 @@ namespace Resource
         mUnRefImageDataAfterApply = unref;
     }
 
-
 }
diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp
index 32bd80660..173131e66 100644
--- a/components/resource/scenemanager.hpp
+++ b/components/resource/scenemanager.hpp
@@ -8,27 +8,19 @@
 #include <osg/Node>
 #include <osg/Texture>
 
+#include "resourcemanager.hpp"
+
 namespace Resource
 {
     class ImageManager;
     class NifFileManager;
 }
 
-namespace VFS
-{
-    class Manager;
-}
-
 namespace osgUtil
 {
     class IncrementalCompileOperation;
 }
 
-namespace osgDB
-{
-    class ObjectCache;
-}
-
 namespace osgViewer
 {
     class Viewer;
@@ -39,7 +31,7 @@ namespace Resource
 
     /// @brief Handles loading and caching of scenes, e.g. .nif files or .osg files
     /// @note Some methods of the scene manager can be used from any thread, see the methods documentation for more details.
-    class SceneManager
+    class SceneManager : public ResourceManager
     {
     public:
         SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager);
@@ -78,8 +70,6 @@ namespace Resource
         /// @note SceneManager::attachTo calls this method automatically, only needs to be called by users if manually attaching
         void notifyAttached(osg::Node* node) const;
 
-        const VFS::Manager* getVFS() const;
-
         Resource::ImageManager* getImageManager();
 
         /// @param mask The node mask to apply to loaded particle system nodes.
@@ -99,7 +89,6 @@ namespace Resource
         void setUnRefImageDataAfterApply(bool unref);
 
     private:
-        const VFS::Manager* mVFS;
         Resource::ImageManager* mImageManager;
         Resource::NifFileManager* mNifFileManager;
 
@@ -112,8 +101,6 @@ namespace Resource
 
         unsigned int mParticleSystemMask;
 
-        osg::ref_ptr<osgDB::ObjectCache> mCache;
-
         SceneManager(const SceneManager&);
         void operator = (const SceneManager&);
     };
diff --git a/components/vfs/manager.hpp b/components/vfs/manager.hpp
index f74914977..6592a65a8 100644
--- a/components/vfs/manager.hpp
+++ b/components/vfs/manager.hpp
@@ -16,6 +16,7 @@ namespace VFS
     /// @par Various archive types (e.g. directories on the filesystem, or compressed archives)
     /// can be registered, and will be merged into a single file tree. If the same filename is
     /// contained in multiple archives, the last added archive will have priority.
+    /// @par Most of the methods in this class are considered thread-safe, see each method documentation for details.
     class Manager
     {
     public:
@@ -33,20 +34,25 @@ namespace VFS
         void buildIndex();
 
         /// Does a file with this name exist?
+        /// @note May be called from any thread once the index has been built.
         bool exists(const std::string& name) const;
 
         /// Get a complete list of files from all archives
+        /// @note May be called from any thread once the index has been built.
         const std::map<std::string, File*>& getIndex() const;
 
         /// Normalize the given filename, making slashes/backslashes consistent, and lower-casing if mStrict is false.
+        /// @note May be called from any thread once the index has been built.
         void normalizeFilename(std::string& name) const;
 
         /// Retrieve a file by name.
         /// @note Throws an exception if the file can not be found.
+        /// @note May be called from any thread once the index has been built.
         Files::IStreamPtr get(const std::string& name) const;
 
         /// Retrieve a file by name (name is already normalized).
         /// @note Throws an exception if the file can not be found.
+        /// @note May be called from any thread once the index has been built.
         Files::IStreamPtr getNormalized(const std::string& normalizedName) const;
 
     private: