File Open Cache

This commit is contained in:
David Sidrane 2015-05-29 17:59:31 -10:00
parent b8ca0baa70
commit 4733a0d1a0

View File

@ -34,7 +34,287 @@ class BasicFileSeverBackend : public uavcan::IFileServerBackend
{
enum { FilePermissions = 438 }; ///< 0o666
protected:
class FDCacheBase {
public:
FDCacheBase() { }
virtual ~FDCacheBase() { }
virtual int open(const char *path, int oflags)
{
using namespace std;
return ::open(path, oflags);
}
virtual int close(int fd, bool done = true)
{
using namespace std;
return ::close(fd);
}
};
FDCacheBase fallback_;
class FDCache : public FDCacheBase {
enum { MaxAgeSeconds = 3 };
class FDCacheItem {
friend FDCache;
FDCacheItem *next_;
time_t last_access_;
int fd_;
int oflags_;
const char *path_;
public:
enum { InvalidFD = -1 };
FDCacheItem() :
next_(0),
last_access_(0),
fd_(InvalidFD),
oflags_(0),
path_(0)
{ }
FDCacheItem(int fd, const char * path, int oflags) :
next_(0),
last_access_(0),
fd_(fd),
oflags_(oflags),
path_(strdup(path))
{
}
~FDCacheItem()
{
if (valid())
{
delete path_;
}
}
inline bool valid()
{
return path_ != 0;
}
inline int getFD()
{
return fd_;
}
inline time_t getAccess()
{
return last_access_;
}
time_t acessed()
{
last_access_ = time(0);
return getAccess();
}
void expire()
{
last_access_ = 0;
}
bool expired()
{
return 0 == last_access_ || (time(0) - last_access_) > MaxAgeSeconds;
}
int compare(const char * path, int oflags)
{
return (oflags_ == oflags && 0 == ::strcmp(path, path_)) ? 0 : 1;
}
int compare(int fd)
{
return fd_ == fd ? 0 : 1;
}
};
FDCacheItem* head_;
FDCacheItem* find(const char *path, int oflags)
{
for(FDCacheItem* pi = head_; pi; pi = pi->next_)
{
if (0 == pi->compare(path, oflags))
{
return pi;
}
}
return 0;
}
FDCacheItem* find(int fd)
{
for(FDCacheItem* pi = head_; pi; pi = pi->next_)
{
if(0 == pi->compare(fd))
{
return pi;
}
}
return 0;
}
FDCacheItem* add(FDCacheItem* pi)
{
pi->next_ = head_;
head_ = pi;
pi->acessed();
return pi;
}
void removeExpired(FDCacheItem** pi)
{
while (*pi)
{
if ((*pi)->expired())
{
FDCacheItem* next = (*pi)->next_;
(void)FDCacheBase::close((*pi)->fd_);
delete(*pi);
*pi = next;
continue;
}
pi = &(*pi)->next_;
}
}
void remove(FDCacheItem* pi, bool done)
{
if (done)
{
pi->expire();
}
removeExpired(&head_);
}
void clear()
{
FDCacheItem* tmp;
for(FDCacheItem* pi = head_; pi; pi = tmp)
{
tmp = pi->next_;
(void)FDCacheBase::close(pi->fd_);
delete pi;
}
}
public:
FDCache() :
head_(0)
{ }
virtual ~FDCache()
{
clear();
}
virtual int open(const char *path, int oflags)
{
int fd = FDCacheItem::InvalidFD;
FDCacheItem *pi = find(path, oflags);
if (pi != 0)
{
pi->acessed();
}
else
{
fd = FDCacheBase::open(path, oflags);
if (fd < 0)
{
return fd;
}
/* Allocate and clone path */
pi = new FDCacheItem(fd, path, oflags);
/* Allocation worked but check clone */
if (pi && !pi->valid())
{
/* Allocation worked but clone or path failed */
delete pi;
pi = 0;
}
if (pi == 0)
{
/*
* If allocation fails no harm just can not cache it
* return open fd
*/
return fd;
}
/* add new */
add(pi);
}
return pi->getFD();
}
virtual int close(int fd, bool done)
{
FDCacheItem *pi = find(fd);
if (pi == 0)
{
/*
* If not found just close it
*/
return FDCacheBase::close(fd);
}
remove(pi, done);
return 0;
}
};
FDCacheBase *fdcache_;
FDCacheBase& getFDCache()
{
if (fdcache_ == 0)
{
fdcache_ = new FDCache();
if (fdcache_ == 0)
{
fdcache_ = &fallback_;
}
}
return *fdcache_;
}
/**
* Back-end for uavcan.protocol.file.GetInfo.
* Implementation of this method is required.
@ -42,11 +322,13 @@ protected:
*/
virtual int16_t getInfo(const Path& path, uint64_t& out_crc64, uint32_t& out_size, EntryType& out_type)
{
int rv = uavcan::protocol::file::Error::INVALID_VALUE;
FileCRC crc;
if (path.size() > 0)
{
using namespace std;
out_size = 0;
out_crc64 = 0;
@ -93,7 +375,7 @@ protected:
rv = 0;
out_close:
close(fd);
(void)::close(fd);
}
}
return rv;
@ -106,14 +388,14 @@ protected:
* if the end of file is reached.
* On success the method must return zero.
*/
virtual int16_t read(const Path& path, const uint32_t offset, uint8_t* out_buffer, uint16_t& inout_size)
{
int rv = uavcan::protocol::file::Error::INVALID_VALUE;
if (path.size() > 0)
{
int fd = open(path.c_str(), O_RDONLY);
FDCacheBase& cache = getFDCache();
int fd = cache.open(path.c_str(), O_RDONLY);
if (fd < 0)
{
@ -121,14 +403,18 @@ protected:
}
else
{
if (::lseek(fd, offset, SEEK_SET) < 0)
rv = ::lseek(fd, offset, SEEK_SET);
ssize_t len = 0;
if (rv < 0)
{
rv = errno;
}
else
{
// TODO use a read at offset to fill on EAGAIN
ssize_t len = ::read(fd, out_buffer, inout_size);
len = ::read(fd, out_buffer, inout_size);
if (len < 0)
{
@ -136,17 +422,32 @@ protected:
}
else
{
inout_size = len;
rv = 0;
}
}
(void)close(fd);
(void)cache.close(fd, rv != 0 || len != inout_size);
inout_size = len;
}
}
return rv;
}
public:
BasicFileSeverBackend() :
fdcache_(0)
{
}
~BasicFileSeverBackend()
{
if (fdcache_ != &fallback_)
{
delete fdcache_;
fdcache_ = 0;
}
}
};
}