#ifndef PtexCache_h
#define PtexCache_h
#include "PtexPlatform.h"
#include <assert.h>
#include "PtexMutex.h"
#include "Ptexture.h"
#include "PtexDict.h"
#define USE_SPIN // use spinlocks instead of mutex for main cache lock
namespace PtexInternal {
#ifdef USE_SPIN
typedef SpinLock CacheLock;
#else
typedef Mutex CacheLock;
#endif
typedef AutoLock<CacheLock> AutoLockCache;
#ifndef NDEBUG
#define GATHER_STATS
#endif
#ifdef GATHER_STATS
struct CacheStats{
int nfilesOpened;
int nfilesClosed;
int ndataAllocated;
int ndataFreed;
int nblocksRead;
long int nbytesRead;
int nseeks;
CacheStats()
: nfilesOpened(0),
nfilesClosed(0),
ndataAllocated(0),
ndataFreed(0),
nblocksRead(0),
nbytesRead(0),
nseeks(0) {}
~CacheStats();
void print();
static void inc(int& val) {
static SpinLock spinlock;
AutoSpin lock(spinlock);
val++;
}
static void add(long int& val, int inc) {
static SpinLock spinlock;
AutoSpin lock(spinlock);
val+=inc;
}
};
extern CacheStats stats;
#define STATS_INC(x) stats.inc(stats.x);
#define STATS_ADD(x, y) stats.add(stats.x, y);
#else
#define STATS_INC(x)
#define STATS_ADD(x, y)
#endif
}
using namespace PtexInternal;
class PtexLruItem {
public:
bool inuse() { return _prev == 0; }
void orphan()
{
_parent = 0;
assert(p && *p == this);
if (!inuse()) delete this;
*p = 0;
}
template <typename T> static void orphanList(T& list)
{
for (typename T::iterator i=list.begin(); i != list.end(); i++) {
if (obj) {
assert(obj->_parent == (void**)&*i);
obj->orphan();
}
}
}
protected:
PtexLruItem(void** parent=0)
: _parent(parent), _prev(0), _next(0) {}
virtual ~PtexLruItem()
{
if (_parent) { assert(*_parent == this); *_parent = 0; }
if (_prev) {
_prev->_next = _next;
_next->_prev = _prev;
}
}
private:
friend class PtexLruList;
void** _parent;
PtexLruItem* _prev;
PtexLruItem* _next;
};
class PtexLruList {
public:
PtexLruList() { _end._prev = _end._next = &_end; }
~PtexLruList() { while (pop()); }
void extract(PtexLruItem* node)
{
node->_prev->_next = node->_next;
node->_next->_prev = node->_prev;
node->_next = node->_prev = 0;
}
void push(PtexLruItem* node)
{
if (!node->_parent) delete node;
else {
node->_next = &_end;
node->_prev = _end._prev;
_end._prev->_next = node;
_end._prev = node;
}
}
bool pop()
{
if (_end._next == &_end) return 0;
delete _end._next;
return 1;
}
private:
PtexLruItem _end;
};
class PtexCacheImpl : public PtexCache {
public:
PtexCacheImpl(int maxFiles, int maxMem)
: _pendingDelete(false),
_maxFiles(maxFiles), _unusedFileCount(0),
_maxDataSize(maxMem),
_unusedDataSize(0), _unusedDataCount(0)
{
_minDataCount = 10 * maxFiles;
if (_minDataCount > 1000) _minDataCount = 1000;
}
virtual void release() { delete this; }
Mutex openlock;
CacheLock cachelock;
void setPendingDelete() { _pendingDelete = true; }
void handlePendingDelete() { if (_pendingDelete) delete this; }
static void addFile() { STATS_INC(nfilesOpened); }
void setFileInUse(PtexLruItem* file);
void setFileUnused(PtexLruItem* file);
void removeFile();
static void addData() { STATS_INC(ndataAllocated); }
void setDataInUse(PtexLruItem*
data,
int size);
void setDataUnused(PtexLruItem*
data,
int size);
void removeData(
int size);
void purgeFiles() {
while (_unusedFileCount > _maxFiles)
{
if (!_unusedFiles.pop()) break;
}
}
void purgeData() {
while ((_unusedDataSize > _maxDataSize) &&
(_unusedDataCount > _minDataCount))
{
if (!_unusedData.pop()) break;
}
}
protected:
~PtexCacheImpl();
private:
bool _pendingDelete;
int _maxFiles, _unusedFileCount;
long int _maxDataSize, _unusedDataSize;
int _minDataCount, _unusedDataCount;
PtexLruList _unusedFiles, _unusedData;
};
class PtexCachedFile : public PtexLruItem
{
public:
PtexCachedFile(void** parent, PtexCacheImpl* cache)
: PtexLruItem(parent), _cache(cache), _refcount(1)
{ _cache->addFile(); }
void ref() { assert(_cache->cachelock.locked());
if (!_refcount++) _cache->setFileInUse(
this); }
void unref() { assert(_cache->cachelock.locked()); if (!--_refcount) _cache->setFileUnused(this); }
protected:
virtual ~PtexCachedFile() { _cache->removeFile(); }
PtexCacheImpl* _cache;
private:
int _refcount;
};
class PtexCachedData : public PtexLruItem
{
public:
PtexCachedData(
void** parent, PtexCacheImpl* cache,
int size)
: PtexLruItem(parent), _cache(cache), _refcount(1), _size(size)
{ _cache->addData(); }
void ref() { assert(_cache->cachelock.locked());
if (!_refcount++) _cache->setDataInUse(
this, _size); }
void unref() { assert(_cache->cachelock.locked()); if (!--_refcount) _cache->setDataUnused(this, _size); }
protected:
void incSize(
int size) { _size +=
size; }
virtual ~PtexCachedData() { _cache->removeData(_size); }
PtexCacheImpl* _cache;
private:
int _refcount;
int _size;
};
#endif