|
|
|
@ -34,10 +34,159 @@
|
|
|
|
|
#include "controller.hpp"
|
|
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
//TODO: when threading is needed, enable these
|
|
|
|
|
//#include <boost/mutex.hpp>
|
|
|
|
|
//#include <boost/thread/locks.hpp>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace Nif;
|
|
|
|
|
using namespace Misc;
|
|
|
|
|
|
|
|
|
|
class NIFFile::LoadedCache
|
|
|
|
|
{
|
|
|
|
|
//TODO: enable this to make cache thread safe...
|
|
|
|
|
//typedef boost::mutex mutex;
|
|
|
|
|
|
|
|
|
|
struct mutex
|
|
|
|
|
{
|
|
|
|
|
void lock () {};
|
|
|
|
|
void unlock () {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef boost::lock_guard <mutex> lock_guard;
|
|
|
|
|
typedef std::map < std::string, boost::weak_ptr <NIFFile> > loaded_map;
|
|
|
|
|
typedef std::vector < boost::shared_ptr <NIFFile> > locked_files;
|
|
|
|
|
|
|
|
|
|
static int sLockLevel;
|
|
|
|
|
static mutex sProtector;
|
|
|
|
|
static loaded_map sLoadedMap;
|
|
|
|
|
static locked_files sLockedFiles;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
static ptr create (const std::string &name)
|
|
|
|
|
{
|
|
|
|
|
lock_guard _ (sProtector);
|
|
|
|
|
|
|
|
|
|
ptr result;
|
|
|
|
|
|
|
|
|
|
// lookup the resource
|
|
|
|
|
loaded_map::iterator i = sLoadedMap.find (name);
|
|
|
|
|
|
|
|
|
|
if (i == sLoadedMap.end ()) // it doesn't existing currently,
|
|
|
|
|
{ // or hasn't in the very near past
|
|
|
|
|
|
|
|
|
|
// create it now, for smoother threading if needed, the
|
|
|
|
|
// loading should be performed outside of the sLoaderMap
|
|
|
|
|
// lock and an alternate mechanism should be used to
|
|
|
|
|
// synchronize threads competing to load the same resource
|
|
|
|
|
result = boost::make_shared <NIFFile> (name, psudo_private_modifier());
|
|
|
|
|
|
|
|
|
|
// if we are locking the cache add an extra reference
|
|
|
|
|
// to keep the file in memory
|
|
|
|
|
if (sLockLevel > 0)
|
|
|
|
|
sLockedFiles.push_back (result);
|
|
|
|
|
|
|
|
|
|
// stash a reference to the resource so that future
|
|
|
|
|
// calls can benefit
|
|
|
|
|
sLoadedMap [name] = boost::weak_ptr <NIFFile> (result);
|
|
|
|
|
}
|
|
|
|
|
else // it may (probably) still exists
|
|
|
|
|
{
|
|
|
|
|
// attempt to get the reference
|
|
|
|
|
result = i->second.lock ();
|
|
|
|
|
|
|
|
|
|
if (!result) // resource is in the process of being destroyed
|
|
|
|
|
{
|
|
|
|
|
// create a new instance, to replace the one that has
|
|
|
|
|
// begun the irreversible process of being destroyed
|
|
|
|
|
result = boost::make_shared <NIFFile> (name, psudo_private_modifier());
|
|
|
|
|
|
|
|
|
|
// respect the cache lock...
|
|
|
|
|
if (sLockLevel > 0)
|
|
|
|
|
sLockedFiles.push_back (result);
|
|
|
|
|
|
|
|
|
|
// we potentially overwrite an expired pointer here
|
|
|
|
|
// but the other thread performing the delete on
|
|
|
|
|
// the previous copy of this resource will detect it
|
|
|
|
|
// and make sure not to erase the new reference
|
|
|
|
|
sLoadedMap [name] = boost::weak_ptr <NIFFile> (result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we made it!
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void release (NIFFile * file)
|
|
|
|
|
{
|
|
|
|
|
lock_guard _ (sProtector);
|
|
|
|
|
|
|
|
|
|
loaded_map::iterator i = sLoadedMap.find (file->filename);
|
|
|
|
|
|
|
|
|
|
// its got to be in here, it just might not be us...
|
|
|
|
|
assert (i != sLoadedMap.end ());
|
|
|
|
|
|
|
|
|
|
// if weak_ptr is still expired, this resource hasn't been recreated
|
|
|
|
|
// between the initiation of the final release due to destruction
|
|
|
|
|
// of the last shared pointer and this thread acquiring the lock on
|
|
|
|
|
// the loader map
|
|
|
|
|
if (i->second.expired ())
|
|
|
|
|
sLoadedMap.erase (i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void lockCache ()
|
|
|
|
|
{
|
|
|
|
|
lock_guard _ (sProtector);
|
|
|
|
|
|
|
|
|
|
sLockLevel++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void unlockCache ()
|
|
|
|
|
{
|
|
|
|
|
locked_files resetList;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
lock_guard _ (sProtector);
|
|
|
|
|
|
|
|
|
|
if (--sLockLevel)
|
|
|
|
|
sLockedFiles.swap(resetList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this not necessary, but makes it clear that the
|
|
|
|
|
// deletion of the locked cache entries is being done
|
|
|
|
|
// outside the protection of sProtector
|
|
|
|
|
resetList.clear ();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int NIFFile::LoadedCache::sLockLevel = 0;
|
|
|
|
|
NIFFile::LoadedCache::mutex NIFFile::LoadedCache::sProtector;
|
|
|
|
|
NIFFile::LoadedCache::loaded_map NIFFile::LoadedCache::sLoadedMap;
|
|
|
|
|
NIFFile::LoadedCache::locked_files NIFFile::LoadedCache::sLockedFiles;
|
|
|
|
|
|
|
|
|
|
// these three calls are forwarded to the cache implementation...
|
|
|
|
|
void NIFFile::lockCache () { LoadedCache::lockCache (); }
|
|
|
|
|
void NIFFile::unlockCache () { LoadedCache::unlockCache (); }
|
|
|
|
|
NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::create (name); }
|
|
|
|
|
|
|
|
|
|
/// Open a NIF stream. The name is used for error messages.
|
|
|
|
|
NIFFile::NIFFile(const std::string &name, psudo_private_modifier)
|
|
|
|
|
: filename(name)
|
|
|
|
|
{
|
|
|
|
|
inp = Ogre::ResourceGroupManager::getSingleton().openResource(name);
|
|
|
|
|
parse();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NIFFile::~NIFFile()
|
|
|
|
|
{
|
|
|
|
|
LoadedCache::release (this);
|
|
|
|
|
|
|
|
|
|
for(std::size_t i=0; i<records.size(); i++)
|
|
|
|
|
delete records[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This file implements functions from the NIFFile class. It is also
|
|
|
|
|
where we stash all the functions we couldn't add as inline
|
|
|
|
|
definitions in the record types.
|
|
|
|
|