summaryrefslogtreecommitdiff
path: root/ACE/ace/Filecache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/ace/Filecache.cpp')
-rw-r--r--ACE/ace/Filecache.cpp751
1 files changed, 751 insertions, 0 deletions
diff --git a/ACE/ace/Filecache.cpp b/ACE/ace/Filecache.cpp
new file mode 100644
index 00000000000..b76a3255d16
--- /dev/null
+++ b/ACE/ace/Filecache.cpp
@@ -0,0 +1,751 @@
+// $Id$
+
+#include "ace/Filecache.h"
+#include "ace/Object_Manager.h"
+#include "ace/Log_Msg.h"
+#include "ace/ACE.h"
+#include "ace/Guard_T.h"
+#include "ace/OS_NS_string.h"
+#include "ace/OS_NS_time.h"
+#include "ace/OS_NS_unistd.h"
+#include "ace/OS_NS_fcntl.h"
+
+ACE_RCSID (ace,
+ Filecache,
+ "$Id$")
+
+#if defined (ACE_WIN32)
+// Specifies no sharing flags.
+#define R_MASK ACE_DEFAULT_OPEN_PERMS
+#define W_MASK 0
+#else
+#define R_MASK S_IRUSR|S_IRGRP|S_IROTH
+#define W_MASK S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH
+#endif /* ACE_WIN32 */
+
+#if defined (ACE_WIN32)
+// See if you can get rid of some of these.
+#define READ_FLAGS (FILE_FLAG_SEQUENTIAL_SCAN | \
+ FILE_FLAG_OVERLAPPED | \
+ O_RDONLY)
+// static const int RCOPY_FLAGS = (FILE_FLAG_SEQUENTIAL_SCAN |
+// O_RDONLY);
+#define WRITE_FLAGS (FILE_FLAG_SEQUENTIAL_SCAN | \
+ FILE_FLAG_OVERLAPPED | \
+ O_RDWR | O_CREAT | O_TRUNC)
+// static const int WCOPY_FLAGS = (FILE_FLAG_SEQUENTIAL_SCAN |
+// O_RDWR | O_CREAT | O_TRUNC);
+#else
+#define READ_FLAGS O_RDONLY
+// static const int RCOPY_FLAGS = O_RDONLY;
+#define WRITE_FLAGS (O_RDWR | O_CREAT | O_TRUNC)
+// static const int WCOPY_FLAGS = O_RDWR | O_CREAT | O_TRUNC;
+#endif /* ACE_WIN32 */
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+// static data members
+ACE_Filecache *ACE_Filecache::cvf_ = 0;
+
+void
+ACE_Filecache_Handle::init (void)
+{
+ this->file_ = 0;
+ this->handle_ = ACE_INVALID_HANDLE;
+}
+
+ACE_Filecache_Handle::ACE_Filecache_Handle (void)
+ : file_ (0), handle_ (0), mapit_ (0)
+{
+ this->init ();
+}
+
+ACE_Filecache_Handle::ACE_Filecache_Handle (const ACE_TCHAR *filename,
+ ACE_Filecache_Flag mapit)
+ : file_ (0), handle_ (0), mapit_ (mapit)
+{
+ this->init ();
+ // Fetch the file from the Virtual_Filesystem let the
+ // Virtual_Filesystem do the work of cache coherency.
+
+ // Filecache will also do the acquire, since it holds the lock at
+ // that time.
+ this->file_ = ACE_Filecache::instance ()->fetch (filename, mapit);
+}
+
+ACE_Filecache_Handle::ACE_Filecache_Handle (const ACE_TCHAR *filename,
+ int size,
+ ACE_Filecache_Flag mapit)
+ : file_ (0), handle_ (0), mapit_ (mapit)
+{
+ this->init ();
+
+ if (size == 0)
+ ACE_Filecache::instance ()->remove (filename);
+ else
+ {
+ // Since this is being opened for a write, simply create a new
+ // ACE_Filecache_Object now, and let the destructor add it into CVF
+ // later
+
+ // Filecache will also do the acquire, since it holds the lock at
+ // that time.
+ this->file_ = ACE_Filecache::instance ()->create (filename, size);
+ }
+}
+
+ACE_Filecache_Handle::~ACE_Filecache_Handle (void)
+{
+ if (this->handle_ != ACE_INVALID_HANDLE)
+ // this was dup ()'d
+ ACE_OS::close (this->handle_);
+
+ ACE_Filecache::instance ()->finish (this->file_);
+}
+
+void *
+ACE_Filecache_Handle::address (void) const
+{
+ return this->file_ == 0 ? 0 : this->file_->address ();
+}
+
+ACE_HANDLE
+ACE_Filecache_Handle::handle (void) const
+{
+ if (this->handle_ == ACE_INVALID_HANDLE && this->file_ != 0)
+ {
+ ACE_Filecache_Handle *mutable_this =
+ const_cast<ACE_Filecache_Handle *> (this);
+ mutable_this->handle_ = ACE_OS::dup (this->file_->handle ());
+ }
+ return this->handle_;
+}
+
+int
+ACE_Filecache_Handle::error (void) const
+{
+ if (this->file_ == 0)
+ return -1;
+ else
+ return this->file_->error ();
+}
+
+ACE_LOFF_T
+ACE_Filecache_Handle::size (void) const
+{
+ if (this->file_ == 0)
+ return -1;
+ else
+ return this->file_->size ();
+}
+
+// ------------------
+// ACE_Filecache_Hash
+// ------------------
+
+#define ACE_Filecache_Hash \
+ ACE_Hash_Map_Manager_Ex<const ACE_TCHAR *, ACE_Filecache_Object *, ACE_Hash<const ACE_TCHAR *>, ACE_Equal_To<const ACE_TCHAR *>, ACE_Null_Mutex>
+#define ACE_Filecache_Hash_Entry \
+ ACE_Hash_Map_Entry<const ACE_TCHAR *, ACE_Filecache_Object *>
+
+template <>
+ACE_Filecache_Hash_Entry::ACE_Hash_Map_Entry (
+ const ACE_TCHAR *const &ext_id,
+ ACE_Filecache_Object *const &int_id,
+ ACE_Filecache_Hash_Entry *next,
+ ACE_Filecache_Hash_Entry *prev)
+ : ext_id_ (ext_id
+ ? ACE_OS::strdup (ext_id)
+ : ACE_OS::strdup (ACE_LIB_TEXT (""))),
+ int_id_ (int_id),
+ next_ (next),
+ prev_ (prev)
+{
+}
+
+template <>
+ACE_Filecache_Hash_Entry::ACE_Hash_Map_Entry (ACE_Filecache_Hash_Entry *next,
+ ACE_Filecache_Hash_Entry *prev)
+ : ext_id_ (0),
+ next_ (next),
+ prev_ (prev)
+{
+}
+
+template <>
+ACE_Filecache_Hash_Entry::~ACE_Hash_Map_Entry (void)
+{
+ ACE_OS::free ((void *) ext_id_);
+}
+
+// We need these template specializations since KEY is defined as a
+// ACE_TCHAR*, which doesn't have a hash() or equal() method defined on it.
+
+template <>
+unsigned long
+ACE_Filecache_Hash::hash (const ACE_TCHAR *const &ext_id)
+{
+ return ACE::hash_pjw (ext_id);
+}
+
+template <>
+int
+ACE_Filecache_Hash::equal (const ACE_TCHAR *const &id1,
+ const ACE_TCHAR *const &id2)
+{
+ return ACE_OS::strcmp (id1, id2) == 0;
+}
+
+#undef ACE_Filecache_Hash
+#undef ACE_Filecache_Hash_Entry
+
+
+// -------------
+// ACE_Filecache
+// -------------
+
+ACE_Filecache *
+ACE_Filecache::instance (void)
+{
+ // Double check locking pattern.
+ if (ACE_Filecache::cvf_ == 0)
+ {
+ ACE_SYNCH_RW_MUTEX &lock =
+ *ACE_Managed_Object<ACE_SYNCH_RW_MUTEX>::get_preallocated_object
+ (ACE_Object_Manager::ACE_FILECACHE_LOCK);
+ ACE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX, ace_mon, lock, 0);
+
+ // @@ James, please check each of the ACE_NEW_RETURN calls to
+ // make sure that it is safe to return if allocation fails.
+ if (ACE_Filecache::cvf_ == 0)
+ ACE_NEW_RETURN (ACE_Filecache::cvf_,
+ ACE_Filecache,
+ 0);
+ }
+
+ return ACE_Filecache::cvf_;
+}
+
+ACE_Filecache::ACE_Filecache (void)
+ : size_ (ACE_DEFAULT_VIRTUAL_FILESYSTEM_TABLE_SIZE),
+ hash_ (this->size_)
+{
+}
+
+ACE_Filecache::~ACE_Filecache (void)
+{
+}
+
+ACE_Filecache_Object *
+ACE_Filecache::insert_i (const ACE_TCHAR *filename,
+ ACE_SYNCH_RW_MUTEX &filelock,
+ int mapit)
+{
+ ACE_Filecache_Object *handle = 0;
+
+ if (this->hash_.find (filename, handle) == -1)
+ {
+ ACE_NEW_RETURN (handle,
+ ACE_Filecache_Object (filename, filelock, 0, mapit),
+ 0);
+
+ // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" (%t) CVF: creating %s\n"), filename));
+
+ if (this->hash_.bind (filename, handle) == -1)
+ {
+ delete handle;
+ handle = 0;
+ }
+ }
+ else
+ handle = 0;
+
+ return handle;
+}
+
+ACE_Filecache_Object *
+ACE_Filecache::remove_i (const ACE_TCHAR *filename)
+{
+ ACE_Filecache_Object *handle = 0;
+
+ // Disassociate file from the cache.
+ if (this->hash_.unbind (filename, handle) == 0)
+ {
+ handle->stale_ = 1;
+
+ // Try a lock. If it succeds, we can delete it now.
+ // Otherwise, it will clean itself up later.
+ if (handle->lock_.tryacquire_write () == 0)
+ {
+ delete handle;
+ handle = 0;
+ }
+ }
+ else
+ handle = 0;
+
+ return handle;
+}
+
+ACE_Filecache_Object *
+ACE_Filecache::update_i (const ACE_TCHAR *filename,
+ ACE_SYNCH_RW_MUTEX &filelock,
+ int mapit)
+{
+ ACE_Filecache_Object *handle = 0;
+
+ handle = this->remove_i (filename);
+ handle = this->insert_i (filename, filelock, mapit);
+
+ return handle;
+}
+
+int
+ACE_Filecache::find (const ACE_TCHAR *filename)
+{
+ return this->hash_.find (filename);
+}
+
+
+ACE_Filecache_Object *
+ACE_Filecache::remove (const ACE_TCHAR *filename)
+{
+ ACE_Filecache_Object *handle = 0;
+
+ u_long loc = ACE::hash_pjw (filename) % this->size_;
+ ACE_SYNCH_RW_MUTEX &hashlock = this->hash_lock_[loc];
+ // ACE_SYNCH_RW_MUTEX &filelock = this->file_lock_[loc];
+
+ if (this->hash_.find (filename, handle) != -1)
+ {
+ ACE_WRITE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX,
+ ace_mon,
+ hashlock,
+ 0);
+
+ return this->remove_i (filename);
+ }
+
+ return 0;
+}
+
+
+ACE_Filecache_Object *
+ACE_Filecache::fetch (const ACE_TCHAR *filename, int mapit)
+{
+ ACE_Filecache_Object *handle = 0;
+
+ u_long loc = ACE::hash_pjw (filename) % this->size_;
+ ACE_SYNCH_RW_MUTEX &hashlock = this->hash_lock_[loc];
+ ACE_SYNCH_RW_MUTEX &filelock = this->file_lock_[loc];
+
+ filelock.acquire_read ();
+
+ if (this->hash_.find (filename, handle) == -1)
+ {
+ ACE_WRITE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX,
+ ace_mon,
+ hashlock,
+ 0);
+
+ // Second check in the method call
+ handle = this->insert_i (filename, filelock, mapit);
+
+ if (handle == 0)
+ filelock.release ();
+ }
+ else
+ {
+ if (handle->update ())
+ {
+ {
+ // Double check locking pattern
+ ACE_WRITE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX,
+ ace_mon,
+ hashlock,
+ 0);
+
+ // Second check in the method call
+ handle = this->update_i (filename, filelock, mapit);
+
+ if (handle == 0)
+ filelock.release ();
+ }
+ }
+ // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" (%t) CVF: found %s\n"), filename));
+ }
+
+ return handle;
+}
+
+ACE_Filecache_Object *
+ACE_Filecache::create (const ACE_TCHAR *filename, int size)
+{
+ ACE_Filecache_Object *handle = 0;
+
+ u_long loc = ACE::hash_pjw (filename) % this->size_;
+ ACE_SYNCH_RW_MUTEX &filelock = this->file_lock_[loc];
+
+ ACE_NEW_RETURN (handle,
+ ACE_Filecache_Object (filename, size, filelock),
+ 0);
+ handle->acquire ();
+
+ return handle;
+}
+
+ACE_Filecache_Object *
+ACE_Filecache::finish (ACE_Filecache_Object *&file)
+{
+ if (file == 0)
+ return file;
+
+ u_long loc = ACE::hash_pjw (file->filename_) % this->size_;
+ ACE_SYNCH_RW_MUTEX &hashlock = this->hash_lock_[loc];
+
+ if (file != 0)
+ switch (file->action_)
+ {
+ case ACE_Filecache_Object::ACE_WRITING:
+ {
+ ACE_WRITE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX,
+ ace_mon,
+ hashlock,
+ 0);
+
+ file->release ();
+
+ this->remove_i (file->filename_);
+#if 0
+ int result = this->hash_.bind (file->filename (), file);
+
+ if (result == 0)
+ file->acquire ();
+#else
+ // Last one using a stale file is resposible for deleting it.
+ if (file->stale_)
+ {
+ // Try a lock. If it succeds, we can delete it now.
+ // Otherwise, it will clean itself up later.
+ if (file->lock_.tryacquire_write () == 0)
+ {
+ delete file;
+ file = 0;
+ }
+ }
+#endif
+ }
+
+ break;
+ default:
+ file->release ();
+
+ // Last one using a stale file is resposible for deleting it.
+ if (file->stale_)
+ {
+ // Try a lock. If it succeds, we can delete it now.
+ // Otherwise, it will clean itself up later.
+ if (file->lock_.tryacquire_write () == 0)
+ {
+ delete file;
+ file = 0;
+ }
+ }
+
+ break;
+ }
+
+ return file;
+}
+
+void
+ACE_Filecache_Object::init (void)
+{
+ this->filename_[0] = '\0';
+ this->handle_ = ACE_INVALID_HANDLE;
+ this->error_ = ACE_SUCCESS;
+ this->tempname_ = 0;
+ this->size_ = 0;
+
+ ACE_OS::memset (&(this->stat_), 0, sizeof (this->stat_));
+}
+
+ACE_Filecache_Object::ACE_Filecache_Object (void)
+ : tempname_ (0),
+ mmap_ (),
+ handle_ (0),
+ // stat_ (),
+ size_ (0),
+ action_ (0),
+ error_ (0),
+ stale_ (0),
+ // sa_ (),
+ junklock_ (),
+ lock_ (junklock_)
+{
+ this->init ();
+}
+
+ACE_Filecache_Object::ACE_Filecache_Object (const ACE_TCHAR *filename,
+ ACE_SYNCH_RW_MUTEX &lock,
+ LPSECURITY_ATTRIBUTES sa,
+ int mapit)
+ : tempname_ (0),
+ mmap_ (),
+ handle_ (0),
+ // stat_ (),
+ size_ (0),
+ action_ (0),
+ error_ (0),
+ stale_ (0),
+ sa_ (sa),
+ junklock_ (),
+ lock_ (lock)
+{
+ this->init ();
+
+ // ASSERT strlen(filename) < sizeof (this->filename_)
+ ACE_OS::strcpy (this->filename_, filename);
+ this->action_ = ACE_Filecache_Object::ACE_READING;
+ // place ourselves into the READING state
+
+ // Can we access the file?
+ if (ACE_OS::access (this->filename_, R_OK) == -1)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_ACCESS_FAILED);
+ return;
+ }
+
+ // Can we stat the file?
+ if (ACE_OS::stat (this->filename_, &this->stat_) == -1)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_STAT_FAILED);
+ return;
+ }
+
+ this->size_ = this->stat_.st_size;
+ this->tempname_ = this->filename_;
+
+ // Can we open the file?
+ this->handle_ = ACE_OS::open (this->tempname_,
+ READ_FLAGS, R_MASK, this->sa_);
+ if (this->handle_ == ACE_INVALID_HANDLE)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_OPEN_FAILED,
+ ACE_LIB_TEXT ("ACE_Filecache_Object::ctor: open"));
+ return;
+ }
+
+ if (mapit)
+ {
+ // Can we map the file?
+ if (this->mmap_.map (this->handle_, -1,
+ PROT_READ, ACE_MAP_PRIVATE, 0, 0, this->sa_) != 0)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_MEMMAP_FAILED,
+ ACE_LIB_TEXT ("ACE_Filecache_Object::ctor: map"));
+ ACE_OS::close (this->handle_);
+ this->handle_ = ACE_INVALID_HANDLE;
+ return;
+ }
+ }
+
+ // Ok, finished!
+ this->action_ = ACE_Filecache_Object::ACE_READING;
+}
+
+ACE_Filecache_Object::ACE_Filecache_Object (const ACE_TCHAR *filename,
+ off_t size,
+ ACE_SYNCH_RW_MUTEX &lock,
+ LPSECURITY_ATTRIBUTES sa)
+ : stale_ (0),
+ sa_ (sa),
+ lock_ (lock)
+{
+ this->init ();
+
+ this->size_ = size;
+ ACE_OS::strcpy (this->filename_, filename);
+ this->action_ = ACE_Filecache_Object::ACE_WRITING;
+
+ // Can we access the file?
+ if (ACE_OS::access (this->filename_, R_OK|W_OK) == -1
+ // Does it exist?
+ && ACE_OS::access (this->filename_, F_OK) != -1)
+ {
+ // File exists, but we cannot access it.
+ this->error_i (ACE_Filecache_Object::ACE_ACCESS_FAILED);
+ return;
+ }
+
+ this->tempname_ = this->filename_;
+
+ // Can we open the file?
+ this->handle_ = ACE_OS::open (this->tempname_, WRITE_FLAGS, W_MASK, this->sa_);
+ if (this->handle_ == ACE_INVALID_HANDLE)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_OPEN_FAILED,
+ ACE_LIB_TEXT ("ACE_Filecache_Object::acquire: open"));
+ return;
+ }
+
+ // Can we write?
+ if (ACE_OS::pwrite (this->handle_, "", 1, this->size_ - 1) != 1)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_WRITE_FAILED,
+ ACE_LIB_TEXT ("ACE_Filecache_Object::acquire: write"));
+ ACE_OS::close (this->handle_);
+ return;
+ }
+
+ // Can we map?
+ if (this->mmap_.map (this->handle_, this->size_, PROT_RDWR, MAP_SHARED,
+ 0, 0, this->sa_) != 0)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_MEMMAP_FAILED,
+ ACE_LIB_TEXT ("ACE_Filecache_Object::acquire: map"));
+ ACE_OS::close (this->handle_);
+ }
+
+ // Ok, done!
+}
+
+ACE_Filecache_Object::~ACE_Filecache_Object (void)
+{
+ if (this->error_ == ACE_SUCCESS)
+ {
+ this->mmap_.unmap ();
+ ACE_OS::close (this->handle_);
+ this->handle_ = ACE_INVALID_HANDLE;
+ }
+}
+
+int
+ACE_Filecache_Object::acquire (void)
+{
+ return this->lock_.tryacquire_read ();
+}
+
+int
+ACE_Filecache_Object::release (void)
+{
+ if (this->action_ == ACE_WRITING)
+ {
+ // We are safe since only one thread has a writable Filecache_Object
+
+#if 0
+ ACE_HANDLE original = ACE_OS::open (this->filename_, WRITE_FLAGS, W_MASK,
+ this->sa_);
+ if (original == ACE_INVALID_HANDLE)
+ this->error_ = ACE_Filecache_Object::ACE_OPEN_FAILED;
+ else if (ACE_OS::write (original, this->mmap_.addr (),
+ this->size_) == -1)
+ {
+ this->error_ = ACE_Filecache_Object::ACE_WRITE_FAILED;
+ ACE_OS::close (original);
+ ACE_OS::unlink (this->filename_);
+ }
+ else if (ACE_OS::stat (this->filename_, &this->stat_) == -1)
+ this->error_ = ACE_Filecache_Object::ACE_STAT_FAILED;
+#endif
+
+ this->mmap_.unmap ();
+ ACE_OS::close (this->handle_);
+ this->handle_ = ACE_INVALID_HANDLE;
+
+#if 0
+ // Leave the file in an acquirable state.
+ this->handle_ = ACE_OS::open (this->tempname_, READ_FLAGS, R_MASK);
+ if (this->handle_ == ACE_INVALID_HANDLE)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_OPEN_FAILED,
+ "ACE_Filecache_Object::acquire: open");
+ }
+ else if (this->mmap_.map (this->handle_, -1,
+ PROT_READ,
+ ACE_MAP_PRIVATE,
+ 0,
+ 0,
+ this->sa_) != 0)
+ {
+ this->error_i (ACE_Filecache_Object::ACE_MEMMAP_FAILED,
+ "ACE_Filecache_Object::acquire: map");
+ ACE_OS::close (this->handle_);
+ this->handle_ = ACE_INVALID_HANDLE;
+ }
+
+ this->action_ = ACE_Filecache_Object::ACE_READING;
+#endif
+ }
+
+ return this->lock_.release ();
+}
+
+int
+ACE_Filecache_Object::error (void) const
+{
+ // The existence of the object means a read lock is being held.
+ return this->error_;
+}
+
+int
+ACE_Filecache_Object::error_i (int error_value, const ACE_TCHAR *s)
+{
+ s = s;
+ ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT ("%p.\n"), s));
+ this->error_ = error_value;
+ return error_value;
+}
+
+const ACE_TCHAR *
+ACE_Filecache_Object::filename (void) const
+{
+ // The existence of the object means a read lock is being held.
+ return this->filename_;
+}
+
+ACE_LOFF_T
+ACE_Filecache_Object::size (void) const
+{
+ // The existence of the object means a read lock is being held.
+ return this->size_;
+}
+
+ACE_HANDLE
+ACE_Filecache_Object::handle (void) const
+{
+ // The existence of the object means a read lock is being held.
+ return this->handle_;
+}
+
+void *
+ACE_Filecache_Object::address (void) const
+{
+ // The existence of the object means a read lock is being held.
+ return this->mmap_.addr ();
+}
+
+int
+ACE_Filecache_Object::update (void) const
+{
+ // The existence of the object means a read lock is being held.
+ int result;
+ ACE_stat statbuf;
+
+ if (ACE_OS::stat (this->filename_, &statbuf) == -1)
+ result = 1;
+ else
+ // non-portable code may follow
+#if defined (ACE_HAS_WINCE)
+ // Yup, non-portable... there's probably a way to safely implement
+ // difftime() on WinCE, but for now, this will have to do. It flags
+ // every file as having changed since cached.
+ result = 1;
+#else
+ result = ACE_OS::difftime (this->stat_.st_mtime, statbuf.st_mtime) < 0;
+#endif /* ACE_HAS_WINCE */
+
+ return result;
+}
+
+ACE_END_VERSIONED_NAMESPACE_DECL